前言
最近通过阅读 小傅哥 的手撕Spring系列文章,跟着源码敲了一遍。
此文通过Bean生命周期对简化版Spring的执行流程进行总结。
源码仓库 :https://gitee.com/ithuameng/small-spring
Bean 生命周期
一、Xml 配置文件与 Refresh 方法
- 准备 xml 配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context">>
<!--目标对象-->
<bean id="userService" class="com.ithuameng.springframework.test.bean.UserService3"/>
<!--注册 BeanPostProcessor-->
<bean class="com.ithuameng.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>
<!--自定义环绕拦截-->
<bean id="beforeAdvice" class="com.ithuameng.springframework.test.advice.UserServiceBeforeAdvice"/>
<!--方法拦截器-->
<bean id="methodInterceptor"
class="com.ithuameng.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor">
<property name="advice" ref="beforeAdvice"/>
</bean>
<!--pointcutAdvisor 切面配置-->
<bean id="pointcutAdvisor" class="com.ithuameng.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor">
<property name="expression" value="execution(* com.ithuameng.springframework.test.bean.IUserService.*(..))"/>
<property name="advice" ref="methodInterceptor"/>
</bean>
<context:component-scan base-package="com.ithuameng.springframework.test.bean"/>
</beans>
可以 自定义Bean
,配置AOP代理
,也可以 使用注解 @Component
,通过 component-scan 来扫描。
- 测试方法使用 ClassPathXmlApplicationContext 加载 xml配置文件:
- 通过 ClassPathXmlApplicationContext 构造方法执行
Spring 核心方法 refresh
:
注:下文将通过这个 refresh 方法来解析各方面的执行流程。
二、BeanDefinition
一个 BeanDefinition 描述了一个 Bean 实例,实例包含属性值、构造方法参数值以及更多实现信息。主要目的是允许修改属性值和其他 Bean 元数据。
BeanDefinition 一些属性信息:
通过 refresh 方法第一行代码 refreshBeanFactory()
来加载 xml 中定义的 Bean 信息。
@Component 注解扫描
xml 使用了如下的组件扫描配置:
Java 代码判断是否使用了 component-scan:
通过Java代码对 xml 中配置的包路径进行扫描,判断是否有使用了 @Component 注解的类,若有则将其定义为 BeanDefinition 注册进 beanDefinitionMap 中:
findCandidateComponents 方法如下:
解析 xml 自定义
xml 配置文件中,可以自定义各种各样的 Bean,使用 Java进行解析处理,将其注册到 beanDefinitionMap 中:
三、BeanFactoryPostProcessor
Spring IoC 容器允许 BeanFactoryPostProcessor 在容器实例化任何 bean之前读取bean的定义(配置元数据),通过beanFactory可以获取bean的定义信息,并 可以修改bean的定义信息
。
refresh方法中第四行代码执行 BeanFactoryPostProcessor 对 BeanDefinition 进行修改:
方法中使用 BeanFactory 获取所有实现 BeanFactoryPostProcessor 接口的 Bean,执行其中的 postProcessBeanFactory 方法:
private void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
Map<String, BeanFactoryPostProcessor> beanFactoryPostProcessorMap = beanFactory.getBeansOfType(BeanFactoryPostProcessor.class);
for (BeanFactoryPostProcessor beanFactoryPostProcessor : beanFactoryPostProcessorMap.values()) {
beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
}
}
可以通知自定义 BeanFactoryPostProcessor 接口的实现类,进行对某个 BeanDefinition 进行修改,例如下面的实现类,对 userService 这个 Bean定义信息进行修改:
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
BeanDefinition beanDefinition = beanFactory.getBeanDefinition("userService");
PropertyValues propertyValues = beanDefinition.getPropertyValues();
propertyValues.addPropertyValue(new PropertyValue("company", "改为:字节跳动"));
}
}
将其定义在 xml 配置文件中,交由 Spring IOC 管理:
四、BeanPostProcessor
BeanPostProcessor也称为Bean后置处理器,它是Spring中定义的接口,在Spring容器的创建过程中(具体为Bean初始化前后)会回调BeanPostProcessor中定义的两个方法。BeanPostProcessor的源码如下:
其中 postProcessBeforeInitialization 方法会在每一个bean对象的初始化方法调用之前回调;postProcessAfterInitialization 方法会在每个bean对象的初始化方法调用之后被回调。
BeanPostProcessor 与 AOP 的实现有着密切的关系。
五、Bean实例化
Bean 实例化是整个 Bean生命周期中最为重要的地方,也是整个 IOC 与 AOP 体现所在。
创建 Bean 实例
protected Object createBeanInstance(BeanDefinition beanDefinition, String beanName, Object[] args) {
Constructor constructorToUse = null;
Class<?> beanClass = beanDefinition.getBeanClass();
Constructor<?>[] declaredConstructors = beanClass.getDeclaredConstructors();
for (Constructor ctor : declaredConstructors) {
if (null != args && ctor.getParameterTypes().length == args.length) {
constructorToUse = ctor;
break;
}
}
return getInstantiationStrategy().instantiate(beanDefinition, beanName, constructorToUse, args);
}
提供了两种实例化 Bean 的方法,使用了策略模式进行算法选择:
Bean 三级缓存
循环依赖问题
- 循环依赖主要分为这三种,自身依赖于自身、互相循环依赖、多组循环依赖。
- 无论循环依赖的数量有多少,循环依赖的本质是一样的。就是你的完整创建依赖于我,而我的完整创建也依赖于你,但我们互相没法解耦,最终导致依赖创建失败。
三级缓存就为了解决 Bean之间相互注入收发的循环依赖问题,一级缓存存放的是完整的 Bean 实例,二级缓存存放的是代理之后的 Bean 实例,三级缓存存放的是代理对象方法
。
实例一个 Bean 首先需要向缓存中获取,如果缓存中已经有这个 Bean 则直接返回,没有则需要创建 Bean:
上面使用策略算法实例化 Bean之后,将没有完全实例化的 Bean存放到第三级缓存中:
// 创建 Bean实例
bean = createBeanInstance(beanDefinition, beanName, args);
// 处理循环依赖,将实例化后的 Bean 对象提前放入缓存中暴露出来
if (beanDefinition.isSingleton()) {
Object finalBean = bean;
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, beanDefinition, finalBean));
}
从三级缓存中获取数据的时候,会使用 AOP 对实例的 Bean 对象进行代理,如果 AOP 没有进行配置,则会返回之前实例的 Bean 保存到 第二级缓存中,将第三级缓存中的数据删除。
Bean 填充属性
之前在介绍解析 xml 配置文件时,已经将 Bean的属性信息解析配置到 PropertyValue 中,不过还有一种是使用了注解的方式进行属性内容注入,例如 @Value
,@Autowired
这两个注解,这个时候我们就需要手动的对使用了注解的属性进行解析:
解析完之后存放到 PropertyValues 中,就可以给 Bean 进行属性填充了:
Aware 感知
Aware 感知指的是 Bean 实现某些接口,可以获取应用程序上下文中的一些对象信息,比如 BeanFactory、ClassLoader 等。
例如定义一个 BeanFactoryAware 的接口,标识需要获取 BeanFactory 对象信息:
spring 中的 AutowiredAnnotationBeanPostProcessor 类实现这个接口:
执行 Bean的初始化方法时获取这些感知对象:
这样就可以使用这些感知对象了。
Bean 对象的初始化方法
Bean 初始化方法来源于实现了 InitializingBean 接口或者是在 xml 中配置了 init-method 信息,调用 invokeInitMethods 执行 Bean的初始化方法:
private void invokeInitMethods(String beanName, Object bean, BeanDefinition beanDefinition) throws Exception {
// 1.Bean 实现了 InitializingBean接口
if (bean instanceof InitializingBean) {
((InitializingBean) bean).afterPropertiesSet();
}
// 2.配置信息 init-method
String initMethodName = beanDefinition.getInitMethodName();
if (StrUtil.isNotEmpty(initMethodName)) {
Method initMethod = beanDefinition.getBeanClass().getMethod(initMethodName);
if (null == initMethod) {
throw new BeansException("Could not find an init method named '" + initMethodName + "' on bean with name '" + beanName + "'");
}
initMethod.invoke(bean);
}
}
在执行这个初始化方法之前,将会执行 BeanPostProcessor Before 处理:
@Override
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException {
Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
Object current = processor.postProcessBeforeInitialization(result, beanName);
if (null == current) {
return result;
}
result = current;
}
return result;
}
在执行这个初始化方法之后,将会执行 BeanPostProcessor After 处理:
@Override
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException {
Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
Object current = processor.postProcessAfterInitialization(result, beanName);
if (null == current) {
return result;
}
result = current;
}
return result;
}
Bean 对象的销毁方法
Bean 销毁方法来源于实现了 DisposableBean 接口或者是在 xml 中配置了 destroy-method 信息。我们需要在应用上下文中注册虚拟机钩子:
Runtime.getRuntime().addShutdownHook(new Thread(this::close));
当程序关闭时,执行程序上下文中的 close 事件,调用 Bean 的 destory方法:
@Override
public void destroy() throws Exception {
// 1.bean实现接口 DisposableBean
if (bean instanceof DisposableBean) {
((DisposableBean) bean).destroy();
}
// 2.配置信息 destroy-method {判断是为了避免二次执行销毁}
if (StrUtil.isNotEmpty(destroyMethodName) && !(bean instanceof DisposableBean && "destroy".equals(this.destroyMethodName))) {
Method initMethod = bean.getClass().getMethod(destroyMethodName);
if (null == initMethod) {
throw new BeansException("Could not find an destroy method named '" + destroyMethodName + "' on bean with name '" + beanName + "'");
}
initMethod.invoke(bean);
}
}
六、总结
这个简化版 spring 还实现了容器事件和事件监听器,类型转换器等功能,具体的实现不一一演示。
此文用于记录 spring 中 Bean 的生命周期是如何进行的,用于巩固已学知识。
学海无涯,作者:浪子花梦,写于:2022 / 8 / 27