一、Bean 实例化的基本流程
1、Bean 实例化的基本流程
Spring容器在进行初始化时,会将xml配置的的信息封装成一个BeanDefinition对象,所有的 BeanDefinition存储到一个名为beanDefinitionMap的Map集合中去,Spring框架再对该Map进行遍 历,使用反射创建Bean实例对象,创建好的Bean对象存储在一个名为singletonObjects的Map集合中,当调用 getBean方法 时则最终从该Map集合中取出Bean实例对象返回。
2、Bean信息定义对象-BeanDefinition
BeanDefinition存储到一个名为beanDefinitionMap
对象存储在一个名为singletonObjects的Map
3、Bean 实例化的基本流程
- 加载xml配置文件,解析获取配置中的每个的信息封装成一个个的BeanDefinition对象;
- 将BeanDefinition存储在一个名为beanDefinitionMap的Map中;
- ApplicationContext底层遍历beanDefinitionMap,创建Bean实例对象;
- 创建好的Bean实例对象,被存储到一个名为singletonObjects的Map中;
- 当执行applicationContext.getBean(beanName)时,从singletonObjects去匹配Bean实例返回
二、Spring的后处理器
Spring的后处理器是Spring对外开发的重要扩展点,允许我们介入到Bean的整个实例化流程中来,以达到动态注册BeanDefinition,动态修改BeanDefinition,以及动态修改Bean的作用。Spring主要有两种后处理器:
- BeanFactoryPostProcessor:Bean工厂后处理器,在BeanDefinitionMap填充完毕,Bean实例 化之前执行;
- BeanPostProcessor:Bean后处理器,一般在Bean实例化之后,填充到单例池singletonObjects 之前执行。
1、BeanFactoryPostProcessor
Bean工厂后处理器 – BeanFactoryPostProcessor
BeanFactoryPostProcessor是一个接口规范,实现了该接口的类只要交由Spring容器管理的话,那么Spring就会回调该接口的方法,用于对BeanDefinition注册和修改的功能。
演示修改:
1. 创建BeanFactoryPostProcessor实现类并重写方法
2. 注入实现类
演示注册:
1. 创建BeanFactoryPostProcessor实现类并重写方法
2. 注入实现类
Bean工厂后处理器 – BeanDefinitionRegistryPostProcessor
Spring 提供了一个BeanFactoryPostProcessor的子接口BeanDefinitionRegistryPostProcessor专门用 于注册BeanDefinition操作
实例代码(1):
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("MyBeanFactoryPostProcessor的postProcessBeanFactory");
// 修改演示
// BeanDefinition beanDefinition = beanFactory.getBeanDefinition("user");
// beanDefinition.setBeanClassName("com.apesource.pojo.Student");
// 注册演示
BeanDefinition beanDefinition = new RootBeanDefinition();
beanDefinition.setBeanClassName("com.apesource.pojo.Student");
// 强转成DefaultListableBeanFactory
DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) beanFactory;
defaultListableBeanFactory.registerBeanDefinition("stu",beanDefinition);
}
}
这个MyBeanFactoryPostProcessor类实现了BeanFactoryPostProcessor接口,允许在Spring容器实例化任何Bean之前,对应用上下文中的Bean定义进行自定义修改。
postProcessBeanFactory这是一个主要方法,其中进行Bean定义的自定义处理,Spring容器在加载完所有Bean定义后但在实例化任何Bean之前会调用这个方法。
ConfigurableListableBeanFactory代表可配置的 BeanFactory 实例,可以用来获取和修改 BeanDefinition。
首先是修饰演示,获取到已有的BeanDefinition并对其修改配置,这里是获取了id为user的bean并将bean的实现类改为com.apesource.pojo.Student,从而实现了修改。
其次是注册演示,创建一个BeanDefinition对象,对这个对象使用set方法将bean的实现类设置为com.apesource.pojo.Student。然后把当前的ConfigurableListableBeanFactory创建的对象强为DefaultListableBeanFactory,这是 Spring 中一种常见的 BeanFactory 实现类,它允许进行 Bean 的注册和删除。再调用DefaultListableBeanFactory的registerBeanDefinition方法,将刚才的BeanDefinition对象注册到 BeanFactory 中,并起名为stu。
applicationContext.xml中配置:
<bean class="com.apesource.processor.MyBeanFactoryPostProcessor"></bean>
测试代码:
public class Test01 {
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
Object user = applicationContext.getBean("user");
System.out.println(user);
}
}
修饰演示的代码结果为:com.apesource.pojo.Student
(2)Bean工厂后处理器 – BeanDefinitionRegistryPostProcessor
Spring 提供了一个BeanFactoryPostProcessor的子接口BeanDefinitionRegistryPostProcessor专门用于注册BeanDefinition操作。
实例代码(2):
public class MyBeanFactoryPostProcessor2 implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
BeanDefinition beanDefinition = new RootBeanDefinition();
beanDefinition.setBeanClassName("com.apesource.pojo.Student");
beanDefinitionRegistry.registerBeanDefinition("stu2",beanDefinition);
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
}
}
这里首先是继承了BeanDefinitionRegistryPostProcessor接口,那么我们就可以进行修改 BeanFactory 的配置,还可以直接注册新的 Bean 定义。
重写了postProcessBeanDefinitionRegistry方法,该方法在 Spring 容器初始化期间调用,并传递一个 BeanDefinitionRegistry 实例,允许直接操作 Bean 定义注册表。
首先先创建了一个RootBeanDefinition对象,用来描述一个新的 Bean。然后向该对象设置这个bean的实现类为com.apesource.pojo.Student,最后使用registerBeanDefinition方法注册一个实现类为com.apesource.pojo.Student名叫stu的bean。
它和上面的BeanFactoryPostProcessor的区别就是BeanDefinitionRegistryPostProcessor可以直接注册新的bean,而BeanFactoryPostProcessor需要强制转换后才能注册新的bean。
applicationContext.xml中配置:
<bean class="com.apesource.processor.MyBeanFactoryPostProcessor2"></bean>
测试代码:
public class Test01 {
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
Object stu = applicationContext.getBean("stu2");
System.out.println(stu);
}
}
测试结果:com.apesource.pojo.Student@3108bc
2、BeanPostProcessor
Bean后处理器 – BeanPostProcessor
Bean被实例化后,到最终缓存到名为singletonObjects单例池之前,中间会经过Bean的初始化过程, 例如:属性的填充、初始方法init的执行等,其中有一个对外进行扩展的点BeanPostProcessor,我们称为Bean后处理。跟上面的 Bean工厂后处理器相似,它也是一个接口,实现了该接口并被容器管理的BeanPostProcessor, 会在流程节点上被Spring自动调用。
、
// 接口允许开发者在 Spring 容器的 Bean 初始化前后,插入自定义的逻辑。
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("BeanPostProcessor的before方法...");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("BeanPostProcessor的after方法...");
return bean;
}
}
首先这个类实现了BeanPostProcessor接口,呢我们就可以在 Spring 容器中的 Bean 初始化之前和之后执行自定义逻辑。
第一个重写的方法postProcessBeforeInitialization,这个方法会在 Bean 初始化方法(如 @PostConstruct 注解的方法或自定义的初始化方法)调用之前执行。两个参数分别为当前正在处理的 Bean 实例、当前 Bean 的名称。在处理完逻辑后,必须返回一个 Bean 对象。这里返回的是传入的原始 Bean,没有进行任何修改。
第二个重写的方法是postProcessAfterInitialization,这个方法会在 Bean 初始化方法调用之后执行。参数和上面的方法参数一致。同样返回传入的原始 Bean,没有进行任何修改。
applicationContext.xml中配置:
<bean class="com.apesource.processor.MyBeanPostProcessor"></bean>
我们在刚才的代码进行优化,对实体类User进行构造方法等方法的写入:
public class User implements InitializingBean {
public User() {
System.out.println("===》无参构造方法S");
}
private String uname;
public void setUname(String uname) {
this.uname = uname;
System.out.println("===》执行set方法");
}
@Override
public void afterPropertiesSet() throws Exception {
// 这个接口定义了一个回调方法 afterPropertiesSet()。当 Spring 完成所有属性的注入后,容器会调用这个方法,用于进行一些初始化工作。
System.out.println("===》接口初始化");
}
public void doinit(){
System.out.println("===》属性初始化");
}
}
其中实现了了InitializingBean接口用于重写afterPropertiesSet()方法,该方法会在所有属性(包括 uname)被注入后,由 Spring 容器自动调用,或者说是在 Bean 初始化阶段被调用。
doinit方法是自定义的初始化方法,可以通过 Spring 的 XML 配置或注解配置来调用,这个方法在 Spring 容器初始化完成后可以被显式调用,用于执行一些额外的初始化逻辑。
首先是无参构造->执行set方法->然后是BeanPostProcessor的before方法->执行接口初始化->执行属性初始化->BeanPostProcessor的after方法->最后实例化成功,返回user对象地址。
三、Spring Bean的生命周期
1、什么是SpringBean的生命周期
Spring Bean的生命周期是从 Bean 实例化之后,即通过反射创建出对象之后,到Bean成为一个完整对象,最终存储到单例池中,这个过程被称为Spring Bean的生命周期。Spring Bean的生命周期大体上分为三个阶段:
Bean的实例化阶段:Spring框架会取出BeanDefinition的信息进行判断当前Bean的范围是否是 singleton的, 是否不是延迟加载的,是否不是FactoryBean等,最终将一个普通的singleton的 Bean通过反射进行实例化;
Bean的初始化阶段:Bean创建之后还仅仅是个"半成品",还需要对Bean实例的属性进行填充、执 行一些Aware接口方法、执行BeanPostProcessor方法、执行InitializingBean接口的初始化方法、执行自定义初始化init方法等。该阶段是Spring最具技术含量和复杂度的阶段;
Bean的完成阶段:经过初始化阶段,Bean就成为了一个完整的Spring Bean,被存储到单例池 singletonObjects中去了,即完成了Spring Bean的整个生命周期。
2、SpringBean的初始化过程
由于Bean的初始化阶段的步骤比较复杂,所以着重研究Bean的初始化阶段,Spring Bean的初始化过程涉及如下几个过程:
- Bean实例的属性填充
- Aware接口属性注入
- BeanPostProcessor的before()方法回调
- InitializingBean接口的初始化方法回调
- 自定义初始化方法init回调
- BeanPostProcessor的after()方法回调
3、SpringBean的生命周期阶段:
Spring的声明周期总体分为四个阶段:实例化=>属性注入=>初始化=>销毁。
- Spring Bean 的生命周期总体分四个阶段:实例化=>属性注入=>初始化=>销毁
- Step1实例化Bean:根据配置文件中Bean 的定义,利用Java Reflection反射技术创建Bean的实例
- Step2注入对象依赖的属性值(或对象)
- Step3处理各种Aware接口:Spring 会检测该Bean是否实现了xxxAware接口,通过(Aware类型的接口,可以让Spring框架为当前Bean注入相应的内容。
- 如果Bean实现 BeanNameAware接口,会调用它实现的setBeanName(String beanId)方法,注入Bean的名字;
- 如果 Bean实现 BeanclassLoaderAware接口,调用setBeanClassLoader()方法,注入classLoader对象的实例;
- 如果Bean实现BeanFactoryAware 接口,会调用它实现的setBeanFactory()方法,注入的是Spring工厂;
- 如果 Bean实现 ApplicationContextAware接口,会调用setApplicationContext()方法,注入Spring上下文;
- Step4执行BeanPostProcessor前置处理:如果想对Bean进行一些自定义的前置处理,那么可以让Bean 实现了(BeanPostProcessor接口,将会在该阶段调用postProcessBeforeInitialization(Object obj,string s)方法。
- Step5执行InitializingBean初始化方法:如果Bean 实现了InitializingBean接口,执行afeterPropertiesSet()方法。
- Step6执行init-method自定义初始化方法:如果Bean 在 Spring 配置文件中配置了init-method属性,则会自动调用其配置的初始化方法。
- .Step7执行BeanPostProcessor后置处理:如果这个 Bean实现了BeanPostProcessor接口,将会调用postProcessAfterInitialization(0bject obj,string s)方法,由于这个方法是在 Bean初始化结束后调用;
- 以上几个步骤完成后,|Bean已经被正确创建,可以正常使用这个Bean
- Step8执行DisposableBean销毁Bean:当Bean 不再需要时,会经过清理阶段,如果Bean 实现了DisposableBean这个接口,会调用其实现的destroy()方法执行销毁;
- Step9执行destroy-method自定义销毁方法:如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的自定义销毁方法。
3、Bean实例属性填充
BeanDefinition 中有对当前Bean实体的注入信息通过属性propertyValues进行了存储,例如:
总结:Spring在进行属性注入时,会分为如下几种情况:
注入普通属性,String、int或存储基本类型的集合时,直接通过set方法的反射设置进去;
注入单向对象引用属性时,从容器中getBean获取后通过set方法反射设置进去,如果容器中没有,则先创建被注入对象Bean实例(完成整个生命周期)后,在进行注入操作;
注入双向对象引用属性时,就比较复杂了,涉及了循环引用(循环依赖)
多例含义:多个实体之间相互依赖并形成闭环的情况就叫做"循环依赖",也叫做"循环引用";
public class DefaultSingletonBeanRegistry ... {
//1、最终存储单例Bean成品的容器,即实例化和初始化都完成的Bean,称之为"一级缓存"
Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
//2、早期Bean单例池,缓存半成品对象,且当前对象已经被其他对象引用了,称之为"二级缓存"
Map<String, Object> earlySingletonObjects = new ConcurrentHashMap(16);
//3、单例Bean的工厂池,缓存半成品对象,对象未被引用,使用时在通过工厂创建Bean,称之为"三
级缓存"
Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);
}
注:将对象保存至三级缓存的时候,会包装成ObjectFactory对象录入,未来通过此接口对应的get方法再
次提取对象
解决方案:
Spring提供了三级缓存存储完整Bean实例和半成品Bean实例,用于解决循环引用问题在DefaultListableBeanFactory的上四级父类DefaultSingletonBeanRegistry中提供如下三个Map:
public class DefaultSingletonBeanRegistry ... {
//1、最终存储单例Bean成品的容器,即实例化和初始化都完成的Bean,称之为"一级缓存"
Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
//2、早期Bean单例池,缓存半成品对象,且当前对象已经被其他对象引用了,称之为"二级缓存"
Map<String, Object> earlySingletonObjects = new ConcurrentHashMap(16);
//3、单例Bean的工厂池,缓存半成品对象,对象未被引用,使用时在通过工厂创建Bean,称之为"三
级缓存"
Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);
}
注:将对象保存至三级缓存的时候,会包装成ObjectFactory对象录入,未来通过此接口对应的get方法再
次提取对象
UserService和UserDao循环依赖的过程结合上述三级缓存描述一下:
- UserService 实例化对象,但尚未初始化,将UserService存储到三级缓存;
- UserService 属性注入,需要UserDao,从缓存中获取,没有UserDao;
- UserDao实例化对象,但尚未初始化,将UserDao存储到到三级缓存;
- UserDao属性注入,需要UserService,从三级缓存获取UserService,UserService从三级缓存移 入二级缓存;
- UserDao执行其他生命周期过程,最终成为一个完成Bean,存储到一级缓存,删除二三级缓存;
- UserService 注入UserDao;
- UserService执行其他生命周期过程,最终成为一个完成Bean,存储到一级缓存,删除二三级缓存。
一级缓存:存储的是完整的、初始化后的单例(Singleton)Bean 实例。
二级缓存:存储的是早期的 Bean 实例,通常是已经实例化但还未进行完全初始化(如未完成属性填充)的 Bean。
三级缓存:存储的是一个 Bean 的工厂对象,用于创建半成品的 Bean 实例(通常是代理对象)。
四、常用的Aware接口
Aware接口是一种框架辅助属性注入的一种思想,其他框架中也可以看到类似的接口。框架具备高度封装性,我们接触到的一般都是业务代码,一个底层功能API不能轻易的获取到,但是这不意味着永远用不到这些对象,如果用到了,就可以使用框架提供的类似Aware的接口,让框架给我们注入该对象。
处理器的作用,为Bean生命周期各个阶段提供扩展。
实例代码:
public class User implements BeanNameAware, ApplicationContextAware, BeanFactoryAware {
// BeanFactoryAware接口
// 当 Spring 容器创建 User Bean 时,会调用这个方法,并传递当前的 BeanFactory 实例作为参数。控制台会输出这个 BeanFactory 的信息。
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println(beanFactory);
}
// BeanNameAware
// 当 Spring 创建并配置 User Bean 时,会调用这个方法,并将 Bean 的名称作为参数传递给它。控制台会输出该名称,即 "s"。
@Override
public void setBeanName(String s) {
System.out.println(s);
}
// ApplicationContextAware
// 当 Spring 容器创建 User Bean 时,会调用这个方法,并传递当前的 ApplicationContext 实例作为参数。控制台会输出这个 ApplicationContext 的信息。
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println(applicationContext);
}
// 顺序setBeanName setBeanFactory setApplicationContext
}
这个类实现了BeanNameAware、ApplicationContextAware和BeanFactoryAware它们提供了一些回调方法,使 Bean 可以在初始化过程中获取 Spring 容器的相关信息。
setBeanFactory方法是当 Spring 容器创建 User Bean 时,会调用此方法,并将当前的 BeanFactory 实例作为参数传递给它。
setBeanName方法是当 Spring 容器创建并配置 User Bean 时,会调用此方法,并将 Bean 的名称作为参数传递给它。
setApplicationContext方法是当 Spring 容器创建 User Bean 时,会调用此方法,并将当前的 ApplicationContext 实例作为参数传递给它。
三者的方法调用顺序为:setBeanName(先设置 Bean 的名称)-> setBeanFactory(然后传递 BeanFactory 实例)-> setApplicationContext(最后传递 ApplicationContext 实例)。
测试代码:
public class Test01 {
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
Object user = applicationContext.getBean("users");
System.out.println(user);
}
}
结果为: