首页 > 其他分享 >Spring Bean生命周期详解

Spring Bean生命周期详解

时间:2023-06-05 16:25:39浏览次数:34  
标签:调用 Spring Bean 详解 println 方法 public out

  本文结合Spring源码5.1.7.RELEASE,详细分析Spring Bean生命周期,包括主要流程以及Spring一系列的扩展方法,最后通过测试实例演示主要步骤。

  Spring提供的Bean扩展方法大致分为三类,一类是BeanPostProcessor接口,一类是BeanFactoryProcessor接口,还有一类是Aware接口。

  Spring Bean的生命周期概括下来,主要分4个大阶段:实例化-->属性赋值-->初始化-->销毁。主要流程图如下:

  以下部分,按步骤分别解析各个阶段。

一、BeanDefination解析及合并

  首先说明一个概念,BeanDefination是什么?BeanDefination就是Bean的元信息,用来描述Bean的各种信息,包括Bean作用域、是否是Primary、字段值、初始化方法等,Bean的元信息的抽象。在Spring创建Bean之前,会有Bean的元信息解析及合并。

  元信息的解析发生在AbstractApplicationContext#refresh-->obtainFreshBeanFactory()-->AbstractRefreshableApplicationContext#refreshBeanFactory()-->loadBeanDefinitions()方法。loadBeanDefinitions是一个抽象方法,Spring有不同类型的实现,包括xml解析、注解解析等。

  

 

 

 

 

 

 

 

 

 

 

  

  

  元信息之所以需要做合并,就是因为Java的继承,子类的Bean在进行装配之前,必须把其父类的元信息合并到当前子类当中。元信息的合并发生在AbstractBeanFactory#doGetBean方法中。

  getMergedLocalBeanDefinition()方法跟踪到最后一步,分析getMergedBeanDefinition方法源码分析如下:

 二、实例化

2.1 InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation

  元信息准备好之后,进入Bean实例化阶段,来到流程图中的第一个扩展接口InstantiationAwareBeanPostProcessor。

  首先看看AbstractAutowireCapableBeanFactory#createBean的代码:

  跟踪调用点方法resolveBeforeInstantiation,代码如下:

   结合两个方法的代码逻辑可知,在开始实例化之前,如果resolveBeforeInstantiation返回有Bean的实例,后续doCeateBean方法里全部逻辑,即属性赋值、初始化等,都不会再执行。

InstantiationAwareBeanPostProcessor接口的postProcessBeforeInstantiation方法默认实现是返回null,所以可以自行实现该接口,在创建Bean的时候返回特定的Bean实例,用作代理,替换原来的Bean,这里也是Spring AOP编程的体现。 2.2 实例化createBeanInstance

  Bean的实例化、属性赋值和初始化三个阶段的代码都在AbstractAutowireCapableBeanFactory#doCreateBean方法中,代码如下:

    createBeanInstance方法简单来说,就是通过构造器方法实例化对象的过程,代码解析如下:

2.3 InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation

  postProcessAfterInstantiation方法的调用点在属性赋值方法populateBean当中,代码解析如下:

   InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation接口的作用是判断是否需要为Bean进行后续的属性赋值。用户可实现该接口,为指明的Bean跳过属性赋值阶段,但后续的初始化依然会执行。

、属性赋值

3.1 InstantiationAwareBeanPostProcessor#postProcessProperties

  通过populateBean的代码解析可知,postProcessProperties和postProcessPropertyValues都是为了获取PropertyValues。PropertyValues封装的是以K-V形式存储的Bean的属性和值。实现postProcessProperties方法,就可对特定的Bean对象的初始属性值进行自定义设置和修改。

 3.2 属性填充applyPropertyValues

  属性填充的过程很简单,就是直接把PropertyValues填充到BeanWrapper中。BeanWrapper是对Bean的包装,将对Bean属性访问和类型转换相关的操作封装到BeanWrapper。

四、初始化

  初始化的代码解析如下:

4.1 Aware接口回调--第一部分

  Aware是Spring开放出来的,用于对Bean起标识作用的超级接口。Spring一系列Aware接口的执行顺序如流程图所示,分两部分。

  第一部分的BeanNameAware、BeanClassLoaderAware和BeanFactoryAware在initializeBean方法中的invokeAwareMethod方法中调用。代码解析如下:

   声明的Bean可以实现Spring全部的Aware接口,重写相应的set方法,即可对当前的Bean进行相应的增强和描述。

4.2 BeanPostProcessor#postProcessBeforeInitialization

  Aware接口的第二部分部分调用在applyBeanPostProcessorsBeforeInitialization方法中。applyBeanPostProcessorsBeforeInitialization实际调用BeanPostProcessor#postProcessBeforeInitialization。在BeanPostProcessor的实现类ApplicationContextAwareProcessor重写了postProcessBeforeInitialization方法。

  ApplicationContextAwareProcessor#postProcessBeforeInitialization中,有第二部分Aware接口:EnvironmentAware、EmbeddedValueResolverAware、ResourceLoaderAware、ApplicationEventPublisherAware、MessageSourceAware和ApplicationContextAware的调用链。

  值得另外说明的是, BeanPostProcessor还有一个实现类InitDestroyAnnotationBeanPostProcessor,同样重写了postProcessBeforeInitialization方法。这里在调用BeanPostProcessor#postProcessBeforeInitialization方法时,InitDestroyAnnotationBeanPostProcessor也会被触发。InitDestroyAnnotationBeanPostProcessor类用于调用Bean中声明了@PostConstruct和@PreDestroy注解的初始化方法和销毁方法。

4.3 InitializingBean#afterPropertiesSet

  afterPropertiesSet方法是Bean初始化方式的一种,Bean自己可以实现InitializingBean接口,在invokeInitMethods方法达到调用点时触发afterPropertiesSet,进行Bean初始化操作。代码解析如下:

4.4 自定义初始化方法

  声明Bean的时候,还可以指定自定义初始化方法,也就是@Bean注解中initMethod属性。invokeCustomInitMethod会通过反射调用initMethod指明的,Bean中自定义的方法。

  如果Bean同时声明了上面三种初始化方法,那么这里的执行顺序就是:@PostConstruct--->InitializingBean#afterPropertiesSet--->initMethod。

4.5 BeanPostProcessor#postProcessAfterInitialization

  在初始化方法全部执行结束之后,紧接着就会开始调用postProcessAfterInitialization方法。代码解析如下:

  从代码中可以看到,如果postProcessAfterInitialization返回的对象不为空,会用返回的对象替换前面初始化好的对象。

  整个流程执行到这里,代表Bean已经创建好了。后续根据作用域的不同,情况亦不同,单例的Bean由Spring负责销毁,其他由用户自己控制生命周期的终结。

五、销毁

   Bean的销毁阶段的代码逻辑在DisposableBeanAdapter#destroy方法中,代码解析如下:

  这里的第一步需要注意,DestructionAwareBeanPostProcessor是一个接口,也就是说,用户可以自定义接口实现,重写postProcessBeforeDestruction方法实现自己的逻辑。然后Spring本身有一个类CommonAnnotationBeanPostProcessor,实现了DestructionAwareBeanPostProcessor接口,用于处理@Predestory和@PostConstruct注解逻辑,这里也会执行。

  从上面代码解析可以看到,Sping Bean的销毁阶段跟初始化阶段类似,也存在三种销毁方法,其执行步骤是:@Predestory--->DisposableBean#destory--->自定义destory方法。

 六、代码演示

  根据上述二到五部分的源码解析,编写演示代码。

6.1 自定义InstantiationAwareBeanPostProcessor

@Component
public class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {

    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        if ("user".equals(beanName)) {
            System.out.println("1. 调用 InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation() 方法");
        }
        return null;
    }

    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        if ("user".equals(beanName)) {
            System.out.println("3. 调用 InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation() 方法");
        }
        return true;
    }

    @Override
    public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
        if ("user".equals(beanName)) {
            System.out.println("4. 调用 InstantiationAwareBeanPostProcessor.postProcessProperties() 方法");
        }
        return null;
    }
}

6.2 自定义BeanPostProcessor

@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if ("user".equals(beanName)) {
            System.out.println("10. 调用 BeanPostProcessor.postProcessBeforeInitialization() 方法");
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if ("user".equals(beanName)) {
            System.out.println("14. 调用 BeanPostProcessor.postProcessAfterInitialization() 方法");
        }
        return bean;
    }
}

6.3 Bean定义

@Data
public class User implements InitializingBean, BeanNameAware, DisposableBean, ApplicationContextAware, BeanClassLoaderAware,
        BeanFactoryAware, EnvironmentAware {

    private int id;

    private String name;

    public User(int id, String name) {
        this.id = id;
        this.name = name;
        System.out.println("2. 调用构造函数");
    }

    @Override
    public void setBeanName(String name) {
        System.out.println("5. 调用 BeanNameAware.setBeanName() 方法");
    }

    @Override
    public void setBeanClassLoader(ClassLoader classLoader) {
        System.out.println("6. 调用 BeanClassLoaderAware.setBeanClassLoader() 方法");
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println("7. 调用 BeanFactoryAware.setBeanFactory() 方法");
    }

    @Override
    public void setEnvironment(Environment environment) {
        System.out.println("8. 调用 EnvironmentAware.setEnvironment() 方法");
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("9. 调用 ApplicationContextAware.setApplicationContext() 方法");
    }

    @PostConstruct
    public void postInit() {
        System.out.println("11. 调用 PostConstruct 初始化 方法");
    }

    @Override
    public void afterPropertiesSet() {
        System.out.println("12. 调用 InitializingBean.afterPropertiesSet() 方法");
    }

    public void myInit() {
        System.out.println("13. 调用 init-method 方法");
    }

    @PreDestroy
    public void myDestroy() {
        System.out.println("15. 调用 PreDestroy 方法");
    }


    @Override
    public void destroy() {
        System.out.println("16. 调用 DisposableBean.destroy() 方法");
    }

    public void myDestroyMethod() {
        System.out.println("17. 调用 destroy-method 方法");
    }
}

  测试代码运行效果如下:

  测试效果符合流程图所示。

  到此,Spring Bean生命周期全阶段解析完成。

标签:调用,Spring,Bean,详解,println,方法,public,out
From: https://www.cnblogs.com/sunshine-ground-poems/p/17306258.html

相关文章

  • Transformer结构及其应用详解——GPT、BERT、MT-DNN、GPT-2
    前言 本文首先详细介绍Transformer的基本结构,然后再通过GPT、BERT、MT-DNN以及GPT-2等基于Transformer的知名应用工作的介绍并附上GitHub链接,看看Transformer是如何在各个著名的模型中大显神威的。本文转载自新智元仅用于学术分享,若侵权请联系删除欢迎关注公众号CV技术指南,专......
  • spring中默认标签alias、import标签解析
    1、Alias标签在bean标签里边有一个alias属性和name属性,可以指定bean的别名,但是有的场景下,在定义bean的时候就把他的别名都指定好是不适用的。比如这个Bean在组件A中,想把他叫做componentA,但是在组件B中又想把他叫做componetB,所以还有一个单独的标签:<alias>专门解决上述场景的。<......
  • 使用powermock写springboot2.7业务类的测试用例
    1,引入powermock依赖<dependency><groupId>org.powermock</groupId><artifactId>powermock-core</artifactId><version>2.0.9</version><scope>test</......
  • 【Exception】The dependencies of some of the beans in the application context fo
    案发现场***************************APPLICATIONFAILEDTOSTART***************************Description:Thedependenciesofsomeofthebeansintheapplicationcontextformacycle:┌─────┐|asyncConfigdefinedinfile[E:\code\spring-boot-demo\t......
  • 第四十八节:Core7.0中速率中间件详解
    一.        二.        三.         !作       者:Yaopengfei(姚鹏飞)博客地址:http://www.cnblogs.com/yaopengfei/声     明1:如有错误,欢迎讨论,请勿谩骂^_^。声     明2:原创博客请在转载......
  • 【SpringBoot】如何配置静态资源的地址与访问路径
    静态资源,例如HTML文件、JS文件,设计到的SpringBoot配置有两项,一是“spring.mvc.static-path-pattern”,一是“spring.resources.static-locations”,很多人都难以分辨它们之间的差异,所以经常出现的结果就是404错误,无法找到静态资源。1.spring.mvc.static-path-patternspring.mvc.sta......
  • springmvc架构流程
    1、 用户发送请求至前端控制器DispatcherServlet2、 DispatcherServlet收到请求调用HandlerMapping处理器映射器。3、 处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。4、 DispatcherServlet通过HandlerAdap......
  • 为SpringBoot Admin监控的服务端加上登录认证
    <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency>packagecom.ciih.refineinner.config;importlombok.extern.slf4......
  • 为SpringBoot Admin加上登录认证
    依赖<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency>配置server:port:8000spring:security:user:n......
  • springmvc后端接收前端数据的四种方式
    前端登录页面代码<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><title>Title</title></head><body><h1>登录页面</h1><formaction="login.do"......