开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第1天
Spring Bean生命周期,从入门到进阶:
【Spring Bean生命周期】小白也能看懂的入门篇
【Spring Bean生命周期】高手如何解读源码(一) - 资源的扫描和注册
【Spring Bean生命周期】高手如何解读源码(二) - Bean的生命周期
1 Spring Bean的生命周期
理解Spring的生命周期,千万不要一上来就去看源码,抠细节。Spring发展这么多年,已经增加了越来越多的特性,很容易就迷失在代码细节里。越复杂的东西,就越要学会抓大放小,比如:Spring的生命周期通俗点理解就是:
Spring初始化一个IOC容器,然后加载XML中描述的 或 含有指定注解的Java类,将其统一用Resource表示,并扫描为BeanDefinition,然后注册到IOC容器中,最后完成实例化和属性注入。如图所示:
整个过程可以分解成下面几个核心动作。
2 核心动作及组件
2.1 首先构造一个IOC容器
BeanFactory 接口定义了IOC容器最基本的形式。主要方法是getBean()
,其中DefaultListableBeanFactory
这个是核心实现类,实现了大部分方法。
ApplicationContext Spring提供的高级形态的IOC容器。在BeanFactory的基础上提供了其他功能:
- 支持不同的信息源。扩展了MessageSource接口
- 访问资源。实现ResourceLoader 和 Resource等
- 支持应用事件
2.2 获取资源
Spring通过扫描Xml文件 或 被注解的类,将这些统一用Resource表示。其中,核心组件包括:
Resource 表示的是各种资源。包括了各种Bean描述文件,如java类中@Bean @Service和@Controller的定义,又比如xml中标签的定义,groovy中bean的定义等等。这些够可以统称为资源。
ResourceLoader这些资源都是在classpath下面的,只要通过ClassLoader我们可以扫描到和加载到的。
Reader/Scanner Reader解析不同的文件,如xmlReader,groovyReader等,Scanner解析通过注解定义的Bean,并且解析识别。
2.3 Bean的元数据注册到容器中
BeanDefinition 表示了其实就是我们交给对Spring容器管理对象,也就是Bean的描述和定义,比如这个Bean是否是单例、是否有依赖其他Bean、依赖了那些Bean、Bean的名称、别名等等。与class的区别是,class描述的是类的信息,而BeanDefinition描述的是实例的信息。针对不同的资源,有不同的实现类:
接口:
● BeanDefinition: Bean的实例的基础定义。
● AnnotatedBeanDefinition:该接口扩展了 BeanDefinition 的功能,其用来操作注解元数据
实现类:
● AbstractBeanDefinition:抽象类,接口BeanDefinition的主要实现类
● RootBeanDefinition:该类继承自 AbstractBeanDefinition,它可以单独作为一个 BeanDefinition,也可以作为其他 BeanDefinition 的父类。
● ChildBeanDefinition:不可以单独存在,必须依赖一个父 BeanDetintion,构造 ChildBeanDefinition 时,通过构造方法传入父 BeanDetintion 的名称或通过 setParentName 设置父名称。
● GenericBeanDefinition:是 Spring 2.5 以后新引入的 BeanDefinition,是 ChildBeanDefinition 更好的替代者,它同样可以通过 setParentName 方法设置父 BeanDefinition,既可以单独使用也可以作为子BeanDefinition使用。
● ConfigurationClassBeanDefinition:该类继承自 RootBeanDefinition ,并实现了 AnnotatedBeanDefinition 接口。这个BeanDefinition用来描述加了@Bean 注解的Bean
● AnnotatedGenericBeanDefinition:该类继承自 GenericBeanDefinition ,并实现了 AnnotatedBeanDefinition 接口。这个 BeanDefinition 用来描述标注 @Configuration 注解的 Bean。
● ScannedGenericBeanDefinition:该类继承自 GenericBeanDefinition ,并实现了 AnnotatedBeanDefinition 接口。这个 BeanDefinition 用来描述标注 @Component 注解的 Bean,其派生注解如 @Service、@Controller 也同理。
BeanDefinitionRegistry 封装了对BeanDefinition常见操作的接口,容器默认实现了这个接口,所以一般它也代表了容器。容器BeanFactory实现了它,可以通过实现的方法,维护List。
InternalBean 其实是指Spring自己注入的相关Bean,用来实现SpringBoot的核心功能的。
2.4 Bean的实例化及前后扩展
Spring提供了很多的扩展点,可以满足第三方框架的自定义需求,何为扩展点?具体点就是:只预留接口,不做具体的功能,子类去实现。比如:
public abstract class CustomIoc {
public void getBean(){
// 获取Bean前的扩展点,只定义不实现,由子类实现
beforeGetBean();
doGetBean();
}
// do nothing
public abstract void beforeGetBean();
private void doGetBean() {}
}
我可以针对获取Bean的方式不同,分别写自己的实现类,比如定义一个XmlCustomIoc类 和 AnnotationCustomIoc类,
class XmlCustomIoc extends CustomIoc{
@Override
public void beforeGetBean() {
// Xml扫描,获取资源
}
}
class AnnotationCustomIoc extends CustomIoc{
@Override
public void beforeGetBean() {
// 注解扫描,获取资源
}
}
这样在调用CustomIoc的getBean()方法时,实例子类的beforeGetBean()就会被执行到。
同样的,Spring也预留了扩展点,其中比较核心的两个是BeanFactorypostProcess
和 BeanpostProcess
。可以在子类中去实现这些方法。
BeanFactorypostProcess接口
实现该接口,可以在spring的bean创建之前,修改bean的定义属性。也就是说,Spring允许BeanFactoryPostProcessor在容器实例化任何其它bean之前读取配置元数据,并可以根据需要进行修改,例如:
- 可以把bean的scope从singleton改为prototype,
- 也可以把property的值给修改掉。
- 还可以增加或者修改BeanDefinition信息。
注意:BeanFactoryPostProcessor是在spring容器加载了bean的定义文件之后,在bean实例化之前执行的。
接口方法的入参是ConfigurableListableBeanFactory,使用该参数,可以获取到相关bean的定义信息。
@Component
public class FactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("******调用了BeanFactoryPostProcessor");
String[] beanStr = beanFactory.getBeanDefinitionNames();
for (String beanName : beanStr) {
if ("person".equals(beanName)) {
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
MutablePropertyValues m = beanDefinition.getPropertyValues();
if (m.contains("name")) {
m.addPropertyValue("name", "赵四");
System.out.println("修改了name属性初始值了");
}
}
}
}
}
BeanPostProcessor接口
这个接口是单独成类的,它的作用范围是Spring容器,一旦在容器中配置了这个类,那么该容器中所有bean在初始化 的前后都会调用这个接口对应的方法。
@Component
public class PostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("后置处理器处理bean=【"+beanName+"】开始");
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("后置处理器处理bean=【"+beanName+"】完毕!");
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
return bean;
}
}
经过以上四步之后,基本实现了最简单的Spring的IOC容器中BeanDefinition注册,具体可参考下图: