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


    在这里插入图片描述

    前言

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

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

    Bean 生命周期

    请添加图片描述

    一、Xml 配置文件与 Refresh 方法

    1. 准备 xml 配置文件:
    
    <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"/>
        
        <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>
        
        <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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    可以 自定义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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    可以通知自定义 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", "改为:字节跳动"));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    将其定义在 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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    提供了两种实例化 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));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    从三级缓存中获取数据的时候,会使用 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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    在执行这个初始化方法之前,将会执行 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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在执行这个初始化方法之后,将会执行 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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    Bean 对象的销毁方法

    Bean 销毁方法来源于实现了 DisposableBean 接口或者是在 xml 中配置了 destroy-method 信息。我们需要在应用上下文中注册虚拟机钩子:

    Runtime.getRuntime().addShutdownHook(new Thread(this::close));
    
    • 1

    当程序关闭时,执行程序上下文中的 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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    六、总结

    这个简化版 spring 还实现了容器事件和事件监听器,类型转换器等功能,具体的实现不一一演示。

    此文用于记录 spring 中 Bean 的生命周期是如何进行的,用于巩固已学知识。

    学海无涯,作者:浪子花梦,写于:2022 / 8 / 27

  • 相关阅读:
    Django中序列化器or模型单独使用
    Linux在线安装MySQL8.0.24安装、MySQL数据备份和恢复
    23 Python的shutil模块
    easy_login通关思路
    kafka安装
    1843. 可疑银行账户
    【数据结构基础_字符串】Leetcode 415.字符串相加
    Ubuntu20.24安装记录——安装VM-Tools
    centos7安装mysql急速版
    零基础学Linux内核之设备驱动篇(11)_获取设备节点信息
  • 原文地址:https://blog.csdn.net/weixin_42100963/article/details/126535801