【bean的生命周期】--- BeanDefinition和BeanFactoryPostProcessor简介
文章目录
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的生命周期再做一次总结,总结内容如下图所示:
推荐阅读
-
【bean的生命周期】--- BeanDefinition和BeanFactoryPostProcessor简介
-
详解Spring中Bean的生命周期和作用域及实现方式
-
详解Spring中Bean的生命周期和作用域及实现方式
-
spring bean 的生命周期和配置源信息
-
详解SpringIOC容器中bean的作用范围和生命周期
-
Java Bean的作用域,生命周期和注解
-
bean的作用域、初始化和销毁方法及生命周期
-
Spring中Bean的生命周期自定义销毁和初始化方法实现详解
-
spring注册组件——@Bean的生命周期(指定初始化和销毁方法)示例
-
【Spring】- Bean的生命周期的init和destroy方法的三种方式