美文网首页随笔-生活工作点滴IT@程序员猿媛
【Spring 笔记】Bean 的生命周期相关整理

【Spring 笔记】Bean 的生命周期相关整理

作者: 58bc06151329 | 来源:发表于2019-10-14 09:57 被阅读0次

    文前说明

    作为码农中的一员,需要不断的学习,我工作之余将一些分析总结和学习笔记写成博客与大家一起交流,也希望采用这种方式记录自己的学习之旅。

    本文仅供学习交流使用,侵权必删。
    不用于商业目的,转载请注明出处。

    1. 概述

    • Spring 并不是一启动容器就开启 bean 的实例化进程,只有当客户端通过显示或者隐式的方式调用 BeanFactorygetBean() 方法来请求某个实例对象的时候,才会触发相应 bean 的实例化进程。
      • 如果选择直接使用 ApplicationContext 容器,该容器启动时会立刻调用注册到该容器所有 bean 定义的实例化方法。
      • 对于 BeanFactory 容器而言,也不是所有的 getBean() 方法都会触发实例化进程,比如 singleton 类型的 bean,该类型的 bean 只会在第一次调用 getBean() 的时候才会触发,而后续的调用则会直接返回容器缓存中的实例对象。
    • getBean() 方法,是 bean 实例化进程的入口,真正的实现逻辑是在 AbstractAutowireCapableBeanFactorydoCreateBean() 中实现。
    Bean 的生命周期
    • Spring 容器将会对其所有管理的 Bean 对象全部给予一个统一的 生命周期管理,同时在这个阶段也可以对其进行干涉(比如对 bean 进行增强处理,对 bean 进行篡改)。

    2. 原理

    bean 的实例化

    • doCreateBean() 方法中,首先进行 bean 实例化工作,主要由 createBeanInstance() 方法实现,该方法返回一个 BeanWrapper 对象。
      • 这个时候的 Bean 还不能够被使用,最基本的属性都没有设置。实际开发过程中,一般都不会直接使用该类,而是通过 BeanFactory 隐式使用。
      • BeanWrapper 接口有一个默认实现类 BeanWrapperImpl,其主要作用是对 Bean 进行包裹,然后对这个包裹的 Bean 进行操作,比如后续注入 Bean 属性。
      • 在实例化 Bean 过程中,Spring 采用 策略模式 决定采用哪种方式实例化 Bean,一般有 反射CGLIB 动态字节码 两种方式。
    • InstantiationStrategy 定义了 Bean 实例化策略的抽象接口。
      • 其子类 SimpleInstantiationStrategy 提供了基于反射来实例化对象的功能,但是不支持 方法注入方式的对象实例化
      • CglibSubclassingInstantiationStrategy 继承 SimpleInstantiationStrategy,除了拥有反射实例化对象的功能外,还提供了 通过 CGLIB 的动态字节码的功能进而支持方法注入所需的对象实例化 需求。
      • 默认情况下,Spring 采用 CglibSubclassingInstantiationStrategy
    • 具体可查看 【Spring 笔记】创建 Bean 相关整理(上)【Spring 笔记】创建 Bean 相关整理(下)

    激活 Aware

    • 当 Spring 完成 bean 对象实例化并且设置完相关属性和依赖后,则开始 bean 的初始化进程(initializeBean()),初始化第一个阶段是检查当前 bean 对象是否实现了一系列以 Aware 结尾的的接口。
      • Aware 接口为 Spring 容器的核心接口,具有标识作用,实现了该接口的 bean 是具有被 Spring 容器通知的能力,通知的方式是采用 回调 的方式。
      • 在初始化阶段主要是感知 BeanNameAwareBeanClassLoaderAwareBeanFactoryAware
    // AbstractAutowireCapableBeanFactory.java
    private void invokeAwareMethods(final String beanName, final Object bean) {
        if (bean instanceof Aware) {
            // BeanNameAware
            if (bean instanceof BeanNameAware) {
                ((BeanNameAware) bean).setBeanName(beanName);
            }
            // BeanClassLoaderAware
            if (bean instanceof BeanClassLoaderAware) {
                ClassLoader bcl = getBeanClassLoader();
                if (bcl != null) {
                    ((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
                }
            }
            // BeanFactoryAware
            if (bean instanceof BeanFactoryAware) {
                ((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
            }
        }
    }
    
    Aware 接口 说明
    BeanNameAware 对该 bean 对象定义的 beanName 设置到当前对象实例中
    BeanClassLoaderAware 将当前 bean 对象相应的 ClassLoader 注入到当前对象实例中
    BeanFactoryAware BeanFactory 容器会将自身注入到当前对象实例中,这样当前对象就会拥有一个 BeanFactory 容器的引用。
    LoadTimeWeaverAware 加载 Spring Bean 时织入第三方模块,如 AspectJ。
    BootstrapContextAware 资源适配器 BootstrapContext,如 JCA,CCI。
    ResourceLoaderAware 底层访问资源的加载器。
    PortletConfigAware PortletConfig。
    PortletContextAware PortletContext。
    ServletConfigAware ServletConfig。
    ServletContextAware ServletContext。
    MessageSourceAware 国际化。
    ApplicationEventPublisherAware 应用事件。
    NotificationPublisherAware JMX 通知。

    BeanPostProcessor

    • 初始化第二个阶段则是 BeanPostProcessor 增强处理,在该阶段 BeanPostProcessor 会处理当前容器内所有符合条件的实例化后的 bean 对象。
      • 它主要是对 Spring 容器提供的 bean 实例对象进行有效的扩展,允许 Spring 在初始化 bean 阶段对其进行定制化修改,如处理标记接口或者为其提供代理实现。
    • BeanPostProcessor 接口提供了两个方法,在不同的时机执行,分别是 前置处理后置处理
    public interface BeanPostProcessor {
        @Nullable
        default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            return bean;
        }
        @Nullable
        default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            return bean;
        }
    }
    

    InitializingBean 和 init-method

    • InitializingBean 是一个接口,为 Spring Bean 的初始化提供了一种方式,有一个 afterPropertiesSet() 方法,在 bean 的初始化进程中会判断当前 bean 是否实现了 InitializingBean,如果实现则调用 afterPropertiesSet() 方法,进行初始化工作。
    • 然后再检查是否也指定了 init-method ,如果指定了则通过反射机制调用指定的 init-method 方法。
    // AbstractAutowireCapableBeanFactory.java
    protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
            throws Throwable {
        // 首先会检查是否是 InitializingBean ,如果是的话需要调用 afterPropertiesSet()
        boolean isInitializingBean = (bean instanceof InitializingBean);
        if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
            if (logger.isTraceEnabled()) {
                logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
            }
            if (System.getSecurityManager() != null) { // 安全模式
                try {
                    AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
                        // 属性初始化的处理
                        ((InitializingBean) bean).afterPropertiesSet();
                        return null;
                    }, getAccessControlContext());
                } catch (PrivilegedActionException pae) {
                    throw pae.getException();
                }
            } else {
                // 属性初始化的处理
                ((InitializingBean) bean).afterPropertiesSet();
            }
        }
    
        if (mbd != null && bean.getClass() != NullBean.class) {
            // 判断是否指定了 init-method(),
            // 如果指定了 init-method(),则再调用制定的init-method
            String initMethodName = mbd.getInitMethodName();
            if (StringUtils.hasLength(initMethodName) &&
                    !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
                    !mbd.isExternallyManagedInitMethod(initMethodName)) {
                // 激活用户自定义的初始化方法
                // 利用反射机制执行
                invokeCustomInitMethod(beanName, bean, mbd);
            }
        }
    }
    
    • 虽然两种方式都可以实现初始化定制化,但是更加推崇 init-method 方式,因为对于 InitializingBean 接口而言,需要 bean 去实现接口,这样会显得 Spring 具有一定的侵入性。
    • init-method 采用反射的方式,所以执行效率上相对于 InitializingBean 接口回调的方式可能会低一些。
    • 具体可查看 【Spring 笔记】InitializingBean 和 init-method 相关整理

    DisposableBean 和 destroy-method

    • DisposableBean 和 destroy-method 用于对象的自定义销毁工作。
      • 当一个 bean 对象经历了实例化、设置属性、初始化阶段,那么该 bean 对象就可以供容器使用(调用的过程)。当完成调用后,如果是 singleton 类型的 bean ,则会看当前 bean 是否应实现了 DisposableBean 接口或者配置了 destroy-method 属性,如果是则会为该实例注册一个用于对象销毁的回调方法,便于在这些 singleton 类型的 bean 对象销毁之前执行销毁逻辑。
      • 并不是对象完成调用后就会立刻执行销毁方法,因为这个时候 Spring 容器还处于运行阶段,只有当 Spring 容器关闭的时候才会去调用。
      • 对于 BeanFactory 容器而言,需要主动调用 destroySingletons() 方法,通知 BeanFactory 容器去执行相应的销毁方法。
      • 对于 ApplicationContext 容器而言,调用 registerShutdownHook() 方法。

    2.1 用例验证

    • 配置
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
        <bean id="test" class="Test" init-method="initMethod" destroy-method="destroyMethdo">
            <property name="test" value="test"/>
        </bean>
    </beans>
    
    • 用例
    public class Test implements BeanNameAware, BeanFactoryAware, BeanClassLoaderAware, BeanPostProcessor,
            InitializingBean, DisposableBean {
    
        private String test;
    
        public void setTest(String test) {
            System.out.println("setTest 属性注入....");
            this.test = test;
        }
    
        public Test() { // 构造方法
            System.out.println("调用构造函数...");
        }
    
        public void run() {
            System.out.println("run 方法调用...");
        }
    
        @Override
        public void setBeanClassLoader(ClassLoader classLoader) {
            System.out.println("BeanClassLoaderAware 被调用...");
        }
    
        @Override
        public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
            System.out.println("BeanFactoryAware 被调用...");
        }
    
        @Override
        public void setBeanName(String s) {
            System.out.println("BeanNameAware 被调用...");
        }
    
        @Override
        public void destroy() throws Exception {
            System.out.println("DisposableBean destroy 被调用...");
        }
    
        @Override
        public void afterPropertiesSet() throws Exception {
            System.out.println("InitializingBean afterPropertiesSet 被调用...");
        }
    
        @Override
        public Object postProcessBeforeInitialization(Object o, String s) throws BeansException {
            System.out.println("BeanPostProcessor postProcessBeforeInitialization 被调用...");
            return o;
        }
    
        @Override
        public Object postProcessAfterInitialization(Object o, String s) throws BeansException {
            System.out.println("BeanPostProcessor postProcessAfterInitialization 被调用...");
            return o;
        }
    
        public void initMethod() {
            System.out.println("init-method 被调用...");
        }
    
        public void destroyMethdo() {
            System.out.println("destroy-method 被调用...");
        }
    
        public static void main(String[] args) {
            ClassPathResource resource = new ClassPathResource("spring.xml");
            DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
            XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
            reader.loadBeanDefinitions(resource);
            factory.addBeanPostProcessor(new Test());
            Test test = (Test) factory.getBean("test");
            test.run();
            System.out.println("方法调用完成,容器开始关闭....");
            // 关闭容器
            factory.destroySingletons();
        }
    
    }
    
    /**
    print 
    调用构造函数...
    调用构造函数...
    setTest 属性注入....
    BeanNameAware 被调用...
    BeanClassLoaderAware 被调用...
    BeanFactoryAware 被调用...
    BeanPostProcessor postProcessBeforeInitialization 被调用...
    InitializingBean afterPropertiesSet 被调用...
    init-method 被调用...
    BeanPostProcessor postProcessAfterInitialization 被调用...
    run 方法调用...
    方法调用完成,容器开始关闭....
    DisposableBean destroy 被调用...
    destroy-method 被调用...
    **/
    
    • Spring Bean 的生命周期过程。
      1. Spring 容器根据 实例化策略 对 Bean 进行实例化。
      2. 实例化完成后,如果该 bean 设置了一些属性的话,则利用 set 方法设置一些属性。
      3. Bean 实现了 BeanNameAware 接口,则调用 setBeanName() 方法。
      4. Bean 实现了 BeanClassLoaderAware 接口,则调用 setBeanClassLoader() 方法。
      5. Bean 实现了 BeanFactoryAware 接口,则调用 setBeanFactory() 方法。
      6. 容器注册了 BeanPostProcessor,则调用 postProcessBeforeInitialization() 方法,完成 bean 的前置处理。
      7. Bean 实现了 InitializingBean 接口,则调用 afterPropertiesSet() 方法。
      8. Bean 配置了 init-method 方法,则调用其指定的方法。
      9. 容器注册了 BeanPostProcessor 则调用 postProcessAfterInitialization() 方法,完成 bean 的后置处理。
      10. 对象完成初始化,开始方法调用。
      11. 容器进行关闭之前,如果该 bean 实现了 DisposableBean 接口,则调用 destroy() 方法。
      12. 容器进行关闭之前,如果该 bean 配置了 destroy-method,则调用其指定的方法。

    相关文章

      网友评论

        本文标题:【Spring 笔记】Bean 的生命周期相关整理

        本文链接:https://www.haomeiwen.com/subject/nortmctx.html