Spring IoC Bean 創建方法總結


Spring IoC Bean 創建方法總結

Spring 核心編程思想目錄:https://www.cnblogs.com/binarylei/p/12290153.html

本文是對 Spring Bean 實例化(Instantiation)方式的總結。常見的實例 bean 的方式有五種,都有 XML、Java 注解和 Java API 三種配置方式。所謂 Java API 指的是通過最底層的 BeanDenifition 的方式注冊,無論是 xml 還是 java 注解,最終被解析成 BeanDefinition。

  • 常規方式
    • 通過無參構造器(配置元信息:XML、Java 注解和 Java API)
    • 通過有參構造器(配置元信息:XML、Java 注解和 Java API)
    • 通過 FactoryBean(配置元信息:XML、Java 注解和 Java API)
    • 通過靜態工廠方法(配置元信息:XML 和 Java API)
    • 通過實例工廠方法(配置元信息:XML 和 Java API)
  • 特殊方式
    • 通過 ServiceLoaderFactoryBean(配置元信息:XML、Java 注解和Java API )
    • 通過 AutowireCapableBeanFactory#createBean(java.lang.Class, int, boolean)
    • 通過 BeanDefinitionRegistry#registerBeanDefinition(String, BeanDefinition)

1. 無參構造器

無參構造器的實例化方法,我們分 XML、Java 注解和 Java API 三種配置方式進行講解。

(1)XML 配置

<bean id="user" class="com.binarylei.spring.ioc.domain.User">
    <property name="id" value="1"/>
    <property name="name" value="binarylei"/>
</bean>

(2)Java 注解

@Bean
public User user() {
    return new User();
}

(3) Java API

// 1.通過 BeanDefinitionBuilder 構建
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(User.class);
// 通過屬性設置
beanDefinitionBuilder.addPropertyValue("id", 1)
    .addPropertyValue("name", "binarylei");
BeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();

// 2. 通過 AbstractBeanDefinition 以及派生類
GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
genericBeanDefinition.setBeanClass(User.class);
MutablePropertyValues propertyValues = new MutablePropertyValues();
propertyValues.add("id", 1)
    .add("name", "binarylei");
genericBeanDefinition.setPropertyValues(propertyValues);

2. 有參構造器

有參構造器的實例化方法,我們也分 XML、Java 注解和 Java API 三種配置方式進行講解。

(1)XML 配置

<bean id="user2" class="com.binarylei.spring.ioc.domain.User">
    <constructor-arg index="0" value="1"/>
    <constructor-arg index="1" value="binarylei"/>
</bean>

(2)Java 注解

@Bean
public User user2(long id, String name) {
    return new User(id, name);
}

(3) Java API

BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(User.class);
beanDefinitionBuilder.addConstructorArgValue(1)
    .addConstructorArgValue("binarylei");
BeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();

思考:對比無參構造器,有參構造器實例化對象時,為什么不需要指定參數名稱?

  • 無參構造器:通過 setter 方法注入,必須指定字段名稱。注入順序是不定的。

  • 有參構造器:可以通過參數個數和參數類型匹配具體的構造函數,一旦確定了構造函數,參數的順序也就固定了。

  • 這里其實也就構造器注入和 setter 方法注入的一個區別,構造器注入是有序的,setter 注入是無序的。而且構造器注入可以將參數設置為 final,從而保證 bean 的不變性。

  • 構造注入無法解決循環依賴的問題。如果 setter 注入,則可以通過提前暴露 bean 的方式解決循環依賴。當然,這其實也不是一個很大的問題,因為如果出現循環依賴,那么我們首先想到的應該是重構我們的代碼,而不是想辦法繞過。

    注意:只有單例才能解決循環依賴。

3. FactoryBean

(1)XML 配置

<bean id="user3" class="com.binarylei.spring.ioc.factory.UserFactoryBean"/>

(2)Java 注解

@Bean
public UserFactoryBean user3() {
    return new UserFactoryBean();
}

(3) Java API

BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(User.class);
beanDefinitionBuilder.addConstructorArgValue(1)
    .addConstructorArgValue("binarylei");
BeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();

思考:為什么要有 FactoryBean?

Spring 是面向 POJO 編程,一般我們自己的項目都是通過屬性或字段注入的方式創建 bean,也用不到 FactoryBean。但很多復雜的對象,通過 xml 的方式配置非常復雜,特別是第三方框架,如 Spring 整合 Mybatis 的 SqlSessionFactoryBean。

4. 靜態工廠

(1)XML 配置

<bean id="user4" class="com.binarylei.spring.ioc.domain.User" factory-method="createUser"/>

(2)Java 注解

@Bean
public User user3() {
    return User.createUser();
}

(3) Java API

public BeanDefinition createBeanDefinition4() {
    BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(User.class);
    beanDefinitionBuilder.setFactoryMethod("createUser");
    return beanDefinitionBuilder.getBeanDefinition();
}

5. 實例工廠

(1)XML 配置

<bean id="userFactory" class="com.binarylei.spring.ioc.bean.factory.DefaultUserFactory"/>
<bean id="user5" class="com.binarylei.spring.ioc.domain.User" factory-bean="userFactory" factory-method="createUser"/>

(2)Java 注解

@Bean
public User UserFactory() {
    return new DefaultUserFactory();
}
@Bean
public User user5(UserFactory userFactory) {
    return UserFactory.createUser();
}

(3) Java API

public BeanDefinition createBeanDefinition5() {
    BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(User.class);
    beanDefinitionBuilder.setFactoryMethodOnBean("createUser", "userFacory");
    return beanDefinitionBuilder.getBeanDefinition();
}

思考:靜態工廠和實例工廠有什么區別?

  1. 靜態工廠直接調用靜態方法,不會初始化工廠類,而實例工廠會先實例化工廠類再初始化 bean。
  2. 同理,調用 beanFactory.getBeansOfType() 如果無法通過 BeanDefinition 獲取對象類型,可能會先獲取 bean 實例來獲取 bean 對象類型,如果是靜態方法可以???待補充...

6. 其它

6.1 ServiceFactoryBean

通過 Java SPI 加載類。有 ServiceLoaderFactoryBean(加載 ServiceLoader)、ServiceFactoryBean(加載單個對象)、ServiceListFactoryBean(加載全部對象) 三類。

ServiceFactoryBean 實現非常簡單,我們直接看使用方法:

  1. 配置 META-INF/services 配置 com.binarylei.spring.ioc.bean.factory.UserFactory 文件

    com.binarylei.spring.ioc.bean.factory.DefaultUserFactory
    
  2. 配置 xml 文件

    <bean id="userFactoryServiceLoader" class="org.springframework.beans.factory.serviceloader.ServiceFactoryBean">
        <property name="serviceType" value="com.binarylei.spring.ioc.bean.factory.UserFactory" />
    </bean>
    
    

更多關於 AbstractFactoryBean 參考:Spring 循環引用(三)AbstractFactoryBean 如何解決循環依賴

6.2 AutowireCapableBeanFactory#createBean

UserManager userManager = (UserManager) autowireCapableBeanFactory.createBean(
                UserManager.class, AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, false);

說明: 不推薦使用。通過 createBean 方法可以正常進行依賴注入等,但創建的對象都是多例,而且不會注冊到 Spring 容器中,通過 beanFactory.getBeanDefinitionNames() 也不查到對應的 BeanDefinition 信息。

6.3 BeanDefinitionRegistry#registerBeanDefinition

Spring 官方大量采用 registerBeanDefinition 進行擴展,如 AnnotationConfigUtils#registerAnnotationConfigProcessors

public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
			BeanDefinitionRegistry registry, @Nullable Object source) {
    if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
        RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
        def.setSource(source);
        beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
    }
    ...
}

// 將 BeanDefinition 注冊到容器中
private static BeanDefinitionHolder registerPostProcessor(
    BeanDefinitionRegistry registry, RootBeanDefinition definition, String beanName) {

    definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    registry.registerBeanDefinition(beanName, definition);
    return new BeanDefinitionHolder(definition, beanName);
}

說明: 推薦使用。通過 BeanDefinition 方式向容器中注入 bean,Spring 所有的擴展也都是采用向 Spring 容器中注入 BeanDefinition。

7. Bean 生命周期

7.1 初始化

Spring 提供了三種初始化方式:

  1. JSR 規范 @PostConstruct 注解。
  2. Spring 標准接口 InitializingBean。
  3. 自定義初始化方法,有 XML、注解、Java API 三種配置方法。

(1)JSR 規范 @PostConstruct 注解

@PostConstruct
public void init1() {
}

(2)Spring 標准接口 InitializingBean

public class User implements InitializingBean {
    @Override
    public void afterPropertiesSet() throws Exception {
    }
}

(3)自定義初始化方法

自定義方法初始化方法也有 XML、注解、Java API 三種配置方法。

XML配置方式:

<bean init-method=”initMethod” destroy-method=”destroyMethod” ... />

注解配置方式:

@Bean(initMethod = "initMethod", destroyMethod = "destroyMethod")
public User user() {
    return new User();
}

Java API 配置方式:

BeanDefinitionBuilder.rootBeanDefinition(User.class)
                .setInitMethodName("initMethod")
                .setDestroyMethodName("destroyMethod");

思考:假設以上三種方式均在同一 Bean 中定義,那么這些方法的執行順序是怎樣?

執行順序:JSR 規范 > Spring 規范 > 自定義。

7.2 延遲初始化

Bean 延遲初始化(Lazy Initialization)

  • XML 配置:<bean lazy-init=”true” ... />
  • Java 注解:@Lazy(true)

思考:當某個 Bean 定義為延遲初始化,那么,Spring 容器返回的對象與非延遲的對象存在怎樣的差異?

非延遲 Bean 在容器初始化時已經初始化,而延遲 Bean 在使用時才會初始化。

7.3 銷毀

Spring 提供了三種銷毀方式:

  1. JSR 規范 @PreDestroy 注解
  2. Spring 標准接口 DisposableBean
  3. 自定義銷毀方法,有 XML、注解、Java API 三種配置方法。

(1)JSR 規范 @PreDestroy 注解

@PreDestroy 
public void preDestroy() {
}

(2)Spring 標准接口 DisposableBean

public class User implements DisposableBean {
    @Override
    public void destroy() throws Exception {
    }
}

(3)自定義銷毀方法

自定義方法初始化方法也有 XML、注解、Java API 三種配置方法。

@Bean(initMethod = "initMethod", destroyMethod = "destroyMethod")
public User user() {
    return new User();
}


每天用心記錄一點點。內容也許不重要,但習慣很重要!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM