【bean的生命周期】--- BeanDefinition和BeanFactoryPostProcessor简介

源码地址:https://github.com/nieandsun/spring-study


1 单例业务bean的创建流程简介

单实例业务bean创建的基本过程如下图所示:
在这里插入图片描述
即:

(1)源码被编译后生成一个个的class文件
(2)这些class文件被JVM加载到内存
(3)spring为标有@Bean、@Component、@Controller等注解的bean创建一个个的BeanDefinition对象,并将其放入到beanDefinitionMap
(4)在具体的业务bean创建之前BeanFactoryPostProcessor可以对BeanDefinition对象进行修改
(5)循环从beanDefinitionMap中取出BeanDefinition对象,进行具体业务对象的创建+实例化
(6)将创建的业务对象放入到单例缓存Map中

之前写过多篇用于介绍bean的创建过程(即bean的生命周期)的文章:

【bean的生命周期】详解InitializingBean、initMethod和@PostConstruct
【bean的生命周期】— 对象创建+初始化流程分析 — 【重点@Autowired的作用时机】
【bean的生命周期】— 构造方法、@Autowired、BeanPostProcessor、InitializingBean等的执行顺序解析
【bean的生命周期】BeanPostProcessor简介
【bean的生命周期 - spring注解】@Value
【bean的生命周期】— DisposableBean、destroyMethod和@PreDestroy
【bean的生命周期】— InstantiationAwareBeanPostProcessor接口简介

但是这些文章其实介绍的都是循环从beanDefinitionMap中取出BeanDefinition对象,进行具体业务对象的创建+实例化这一阶段的子过程。本篇文章讲解一下bean的另一段生命周期 — 通过BeanFactoryPostProcessor对BeanDefinition对象进行修改来干预bean的创建。
其实bean的这段生命周期在我们平时进行业务开发时一般都不会涉及到,但是如果你想了解框架整合的原理(如spring和Mybatis的整合)时,这块内容就不得不了解了。


2 BeanFactoryPostProcessor修改BeanDefinition来干预bean的创建

如1中图所示,BeanDefinition对象的属性其实有很多,本文只讲解BeanDefinition对象的class、autowireMode和constructorArgumentValues三个属性。


2.1 测试类

  • InstA
package com.nrsc.springstudy.C075_bean_life_cycle_BeanDefinition.beans;

import org.springframework.stereotype.Component;

/***
 * @author : Sun Chuan
 * @date : 2019/12/29
 * Description:
 */
@Component
public class InstA {

    //@Autowired
    private InstB instB;

    public InstA() {
        System.out.println("InstA的无参构造方法");
    }

    public InstA(InstB instB) {
        this.instB = instB;
        System.out.println("InstA通过构造函数注入InstB");
    }

    public InstB getInstB() {
        return instB;
    }

    public void setInstB(InstB instB) {
        this.instB = instB;
    }

    @Override
    public String toString() {
        return "InstA{instB=" + instB + "}";
    }
}

  • InstB 类
package com.nrsc.springstudy.C075_bean_life_cycle_BeanDefinition.beans;
import org.springframework.stereotype.Component;
@Component
public class InstB {
}
  • InstC 类
package com.nrsc.springstudy.C075_bean_life_cycle_BeanDefinition.beans;
import org.springframework.stereotype.Component;
@Component
public class InstC {
}
  • 启动类
import com.nrsc.springstudy.C075_bean_life_cycle_BeanDefinition.beans.InstA;
import com.nrsc.springstudy.C075_bean_life_cycle_BeanDefinition.config.C075Config;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Test075_BeanDefinition {
    @Test
    public void test01() {
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(C075Config.class);
        InstA instA = (InstA) ac.getBean("instA");

        System.out.println(instA);
    }
}

2.2 BeanDefinition的class属性

  • 利用BeanFactoryPostProcessor修改InstA定义对象的class属性,代码如下:
package com.nrsc.springstudy.C075_bean_life_cycle_BeanDefinition.beans;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.stereotype.Component;
@Component
public class NrscBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

        GenericBeanDefinition instABeanDefinition =
                (GenericBeanDefinition)beanFactory.getBeanDefinition("instA");
        //修改InstA的class属性为InstC.class
        instABeanDefinition.setBeanClass(InstC.class);
    }
}

启动项目,会报如下错误,即启动类中的InstA instA = (InstA) ac.getBean("instA");语句报类型转换异常:
在这里插入图片描述

由此可知:BeanDefinition对象的class属性用来控制Bean实例化到底实例化成哪种类型的对象。


2.2 BeanDefinition的autowireMode属性

autowiedMode属性的取值有如下5个:
所在类:AutowireCapableBeanFactory

//默认值,表示如果A对象里要注入B对象必须显示的在B对象上标注@Autowired注解
int AUTOWIRE_NO = 0;

//通过bean的名字注入对象
int AUTOWIRE_BY_NAME = 1;

//通过类型注入对象
int AUTOWIRE_BY_TYPE = 2;

//通过构造函数注入
int AUTOWIRE_CONSTRUCTOR = 3;

/** //废弃掉的
 * Constant that indicates determining an appropriate autowire strategy
 * through introspection of the bean class.
 * @see #createBean
 * @see #autowire
 * @deprecated as of Spring 3.0: If you are using mixed autowiring strategies,
 * prefer annotation-based autowiring for clearer demarcation of autowiring needs.
 */
@Deprecated
int AUTOWIRE_AUTODETECT = 4;

开发中要想在InstA中注入InstB,我们肯定会在InstA中的InstB属性上加上@Autowired注解,如2.1中的InstA类所示。
但能不能不加呢??? 其实是可以的,即用BeanFactoryPostProcessor来修改InstA定义对象的自动注入模型属性 — autowireMode。

  • 利用BeanFactoryPostProcessor修改InstA定义对象的autowireMode属性,代码如下:
@Component
@Component
public class NrscBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

        GenericBeanDefinition instABeanDefinition =
                (GenericBeanDefinition)beanFactory.getBeanDefinition("instA");
        //class属性:用来控制Bean实例化到底实例化成哪种类型的对象。
        //instABeanDefinition.setBeanClass(InstC.class);

        //autowireMode属性:控制Bean属性的注入方式
        //instABeanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_NAME);
        //instABeanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
        instABeanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
    }
}

启动项目,可以看到即使在InstA中的InstB属性上不加@Autowired属性,InstB也可以注入到InstA对象里:
在这里插入图片描述

由此可知: BeanDefinition对象的autowireMode属性会控制Bean属性的注入方式。


2.3 BeanDefinition的constructorArgumentValues属性

有如下测试类 — InstD:

package com.nrsc.springstudy.C075_bean_life_cycle_BeanDefinition.beans;
import org.springframework.stereotype.Component;
@Component
public class InstD {

    private String username;

    private Integer age;

    public InstD() {
        System.out.println("调用D的无参构造创建D对象");
    }

    public InstD(String username) {
        this.username = username;
        System.out.println("调用D的有参构造创建D对象====username");
    }

    public InstD(Integer age) {
        this.age = age;
        System.out.println("调用D的有参构造创建D对象====age");
    }

    public InstD(String username, Integer age) {
        this.username = username;
        this.age = age;
        System.out.println("调用D的有参构造创建D对象====username+age");
    }

    public InstD(Integer age, String username) {
        this.age = age;
        this.username = username;
        System.out.println("调用D的有参构造创建D对象====age+username");
    }
}

在一般情况下,spring肯定会调用InstD对象的无参构造方法来创建该对象,那有没有可能不调用其构造方法,而调用上面任意一个构造方法来实现D对象的创建呢?其实是可以的,即用BeanFactoryPostProcessor来修改InstA定义对象的constructorArgumentValues属性。

  • 利用BeanFactoryPostProcessor修改InstD定义对象的constructorArgumentValues属性的代码如下:
@Component
public class NrscBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

        //GenericBeanDefinition instABeanDefinition =
        //        (GenericBeanDefinition)beanFactory.getBeanDefinition("instA");
        //class属性:用来控制Bean实例化到底实例化成哪种类型的对象。
        //instABeanDefinition.setBeanClass(InstC.class);

        //autowireMode属性:控制Bean属性的注入方式
        //instABeanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_NAME);
        //instABeanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
        //instABeanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);

        GenericBeanDefinition instDBeanDefinition =
                (GenericBeanDefinition)beanFactory.getBeanDefinition("instD");
        ConstructorArgumentValues constructorArgumentValues = new ConstructorArgumentValues();
        //表示构造函数的第一个参数的类型为12的类型 ---> 即int型 ---》spring会进行类型推断
        constructorArgumentValues.addIndexedArgumentValue(0,12);
        //表示构造函数的第二个参数的类型为"haha"的类型 ---> 即String类型
        constructorArgumentValues.addIndexedArgumentValue(1,"haha");
		//将instD的定义对象的constructorArgumentValues属性设置为自己定义的类型
        instDBeanDefinition.setConstructorArgumentValues(constructorArgumentValues);
    }
}

启动项目,可以看到创建InstD对象时调用的并不是无参构造函数,而是我指定的第一个参数类型为int/Integrt型,第二个为String类型的构造函数。
在这里插入图片描述

由此可知: BeanDefinition对象的constructorArgumentValues属性会控制Bean创建时使用的构造函数。

3 bean的生命周期总结

结合前面的文章加上本篇的内容对bean的生命周期再做一次总结,总结内容如下图所示:
在这里插入图片描述

发布了189 篇原创文章 · 获赞 187 · 访问量 39万+

猜你喜欢

转载自blog.csdn.net/nrsc272420199/article/details/103751143