手撕Spring总结,Bean生命周期从始至终

发布于:2023-01-04 ⋅ 阅读:(461) ⋅ 点赞:(0)

在这里插入图片描述

前言

最近通过阅读 小傅哥 的手撕Spring系列文章,跟着源码敲了一遍。
此文通过Bean生命周期对简化版Spring的执行流程进行总结。

源码仓库https://gitee.com/ithuameng/small-spring

Bean 生命周期

请添加图片描述

一、Xml 配置文件与 Refresh 方法

  1. 准备 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 来扫描。

  1. 测试方法使用 ClassPathXmlApplicationContext 加载 xml配置文件:
    请添加图片描述
  2. 通过 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


网站公告

今日签到

点亮在社区的每一天
去签到