zoukankan      html  css  js  c++  java
  • 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();
    }
    
    

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

  • 相关阅读:
    关于容器和里面元素的间距的排版技巧
    Grafana 通过api create&update dashboard
    .net(c#)生成xml并输出(api的设计)
    Ajax学习总结
    网站内容更新之伪原创七绝招
    并发和多线程(十九)ConcurrentHashMap源码解析(jdk1.8) Diamond
    分布式事务(一)分布式事务理论基础 Diamond
    分布式事务(二)事务基础ACID隔离级别MVCC Diamond
    并发和多线程(十八)CountDownLatch、Semaphore和CyclicBarrier源码解析 Diamond
    分布式事务(三)XA、2PC、3PC Diamond
  • 原文地址:https://www.cnblogs.com/binarylei/p/12293885.html
Copyright © 2011-2022 走看看