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();
}


每天用心记录一点点。内容也许不重要,但习惯很重要!

posted on 2020-02-11 09:42  binarylei  阅读(2262)  评论(0编辑  收藏  举报

导航