1. 程式人生 > >Spring 建立Bean的6種方式

Spring 建立Bean的6種方式

 

前言

本文講解了在Spring 應用中建立Bean的多種方式,包括自動建立,以及手動建立注入方式,實際開發中可以根據業務場景選擇合適的方案。

方式1:

使用Spring XML方式配置,該方式用於在純Spring 應用中,適用於簡單的小應用,當應用變得複雜,將會導致XMl配置檔案膨脹 ,不利於物件管理。

<bean id="xxxx"  class="xxxx.xxxx"/>

方式2:

使用@Component,@Service,@Controler,@Repository註解

這幾個註解都是同樣的功能,被註解的類將會被Spring 容器建立單例物件。

@Component : 側重於通用的Bean類

@Service:標識該類用於業務邏輯

@Controler:標識該類為Spring MVC的控制器類

@Repository: 標識該類是一個實體類,只有屬性和Setter,Getter

@Component
public class User{
}

當用於Spring Boot應用時,被註解的類必須在啟動類的根路徑或者子路徑下,否則不會生效。

如果不在,可以使用@ComponentScan標註掃描的路徑。

spring xml 也有相關的標籤<component-scan />

@ComponentScan(value={"com.microblog.blog","com.microblog.common"})
public class MicroblogBlogApplication {
    public static void main(String args[]){
        SpringApplication.run(MicroblogBlogApplication.class,args);
    }
}

 

方式3:

使用@Bean註解,這種方式用在Spring Boot 應用中。

@Configuration 標識這是一個Spring Boot 配置類,其將會掃描該類中是否存在@Bean 註解的方法,比如如下程式碼,將會建立User物件並放入容器中。

@ConditionalOnBean 用於判斷存在某個Bean時才會建立User Bean.

這裡建立的Bean名稱預設為方法的名稱user。也可以@Bean("xxxx")定義。

@Configuration
public class UserConfiguration{
     
      @Bean
    @ConditionalOnBean(Location.class) public User user(){ return new User(); } }

 

Spring boot 還為我們提供了更多類似的註解。

也和方式2一樣,也會存在掃描路徑的問題,除了以上的解決方式,還有使用Spring boot starter 的解決方式

在resources下建立如下檔案。META-INF/spring.factories.

Spring Boot 在啟動的時候將會掃描該檔案,從何獲取到配置類UserConfiguration。

spring.factories.

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.log.config.UserConfiguration

 

如果不成功,請引入該依賴

    <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>    

 

這個方式也是建立SpringBoot-starter的方式。

 

方式4:

使用註解@Import,也會建立物件並注入容器中

@Import(User.class)
public class MicroblogUserWebApplication {
    public static void main(String args[]) {
        SpringApplication.run(MicroblogUserWebApplication.class, args);
    }
}

方式5:

使用ImportSelector或者ImportBeanDefinitionRegistrar介面,配合@Import實現。

在使用一些Spring Boot第三方元件時,經常會看到@EnableXXX來使能相關的服務,這裡以一個例子來實現。

 

建立測試類

@Slf4j
public class House {

    public void run(){

        log.info("House  run ....");
    }
}

@Slf4j
public class User {


    public void run(){

        log.info("User  run ....");

    }

}

@Slf4j
public class Student {

    public void run(){

        log.info("Student  run ....");

    }

}

 

 

實現ImportSelector介面

selectImports方法的返回值為需要建立Bean的類名稱。這裡建立User類。

@Slf4j
public class MyImportSelector implements ImportSelector {

    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {


        log.info("MyImportSelector selectImports ...");
        return new String[]{
            User.class.getName()};
    }
}

 

實現ImportBeanDefinitionRegistrar介面

beanDefinitionRegistry.registerBeanDefinition用於設定需要建立Bean的類名稱。這裡建立House類。

@Slf4j
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {

        log.info("MyImportBeanDefinitionRegistrar  registerBeanDefinitions .....");
        BeanDefinition beanDefinition =  new RootBeanDefinition(House.class.getName());
        beanDefinitionRegistry.registerBeanDefinition(House.class.getName(),beanDefinition);
    }
}

 

建立一個配置類

這裡建立Student類。

@Configuration
public class ImportAutoconfiguration {

    @Bean
    public Student student(){
        return new Student();
    }
}

 

建立EnableImportSelector註解

EnableImportSelector註解上使用@Import,引入以上的三個類。

@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target(ElementType.TYPE)
@Import({MyImportSelector.class,ImportAutoconfiguration.class,MyImportBeanDefinitionRegistrar.class})
public @interface EnableImportSelector {

    String value();

}

 

 測試

@EnableImportSelector(value = "xxx")
@SpringBootApplication
public class ImportDemoApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext context =  SpringApplication.run(ImportDemoApplication.class, args);

        User user =  context.getBean(User.class);
        user.run();

        Student student =  context.getBean(Student.class);
        student.run();

        House house =  context.getBean(House.class);
        house.run();

    }

}

 

輸出,可以看到,三個類User Student House都建立成功,都可從Spring 容器中獲取到。

2019-06-20 17:53:39.528  INFO 27255 --- [           main] com.springboot.importselector.pojo.User  : User  run ....
2019-06-20 17:53:39.530  INFO 27255 --- [           main] c.s.importselector.pojo.Student          : Student  run ....
2019-06-20 17:53:39.531  INFO 27255 --- [           main] c.springboot.importselector.pojo.House   : House  run ....

 

 

方式6

手動注入Bean容器,有些場景下需要程式碼動態注入,以上方式都不適用。這時就需要建立 物件手動注入。

通過DefaultListableBeanFactory注入。

registerSingleton(String beanName,Object object);

這裡手動使用new建立了一個Location物件。並注入容器中。

 


@Component
public class LocationRegister implements BeanFactoryAware {

@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
DefaultListableBeanFactory listableBeanFactory = (DefaultListableBeanFactory)beanFactory;
Location location = new Location();
listableBeanFactory.registerSingleton("location1",location);
}
}

這種方式的應用場景是為介面建立動態代理物件,並向SPRING容器註冊。

比如MyBatis中的Mapper介面,Mapper沒有實現類,啟動時建立動態代理物件,將該物件註冊到容器中,使用時只要@Autowired注入即可使用,呼叫介面方法將會被代理攔截,進而呼叫相關的SqlSession執行相關的SQL業務邏輯。

 

可以看以下它的繼承體系

DefaultListableBeanFactory 是ConfigurableListableBeanFactory的實現類。是對BeanFactory功能的擴充套件。

測試程式碼和以上一樣

Location location =  context.getBean(Location.class);
location.run();

 

本文的相關程式碼位於 例項程式碼 測試類

 

完結。

===========

&n