Spring-Bean的生命周期

KerryWu

1. 前言

最初接触java的接口服务是servlet,最基础要掌握的就是servlet的生命周期。现在都是基于springboot开发后台接口,spring框架的复杂度远远高于原始的servlet,弄清楚spring中bean的生命周期,有利于我们对其框架的熟练掌握。

1.1、servlet生命周期

我们先回顾一下servlet的生命周期吧。

  1. constructpopulate:Servlet容器负责加载和实例化Servlet。当Servlet容器启动时,或者在容器检测到需要这个Servlet来响应第一个请求时,创建Servlet实例。因为容器是通过Java的反射API来创建Servlet实例,调用的是Servlet的默认构造方法(即不带参数的构造方法)。
  2. init:在servlet实例化之后,容器将调用Servlet的init()方法初始化这个对象。初始化的目的是为了让Servlet对象在处理客户端请求前完成一些初始化的工作,如建立数据库的连接,获取配置信息等。对于每一个Servlet实例,init()方法只被调用一次。
  3. service:它是servlet的核心,每当一个客户请求一个HttpServlet对象,该对象的Service()方法就要调用,而且传递给这个方法一个“请求”(ServletRequest)对象和一个“响应”(ServletResponse)对象作为参数。在HttpServlet中已存在Service()方法。默认的服务功能是调用与HTTP请求的方法相应的do功能。
  4. destroy:当容器检测到一个servlet实例应该从服务中被移除的时候,容器就会调用实例的destroy()方法,以便让该实例可以释放它所使用的资源,保存数据到持久存储设备中。当需要释放内存或者容器关闭时,容器就会调用Servlet实例的destroy()方法。在destroy()方法调用之后,容器会释放这个Servlet实例,该实例随后会被Java的垃圾收集器所回收。如果再次需要这个Servlet处理请求,Servlet容器会创建一个新的Servlet实例。对于每一个Servlet实例,destroy()方法也只被调用一次。

2. spring容器加载

springboot的启动类中,都会执行SpringApplication.run方法。在创建上下文环境之后,最核心的方法就是refreshContext,对上下文环境进行初始化操作,本段就着重说明这一过程。

2.1. refreshContext全过程

    @Override
    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {

            /**
             * 准备环境
             * 1. 记录容器的启动时间startupDate,标记容器为激活
             * 2. 初始化上下文环境如文件路径信息
             * 3. 验证必填属性是否填写
             */
            prepareRefresh();

            /**
             * 告诉子类去刷新beanFactory
             * 这步完成后配置文件就解析成一个个beanDefination,注册到BeanFactory(但是未被初始化,仅将信息写到了beanDefination的map中)
             */
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            /**
             * 设置beanFactory类加载器,添加多个beanPostProcesser
             */
            prepareBeanFactory(beanFactory);

            try {
                /**
                 * 允许子类上下文中对beanFactory做后期处理
                 */
                postProcessBeanFactory(beanFactory);

                /**
                 * 执行 context 中注册的 BeanFactoryPostProcessor中的postProcessBeanFactory() 方法
                 * 1. BeanFactoryPostProcessor 是 bean 属性处理容器。即管理 bean工厂中的BeanDefinition
                 * 2. BeanDefinition在 spring mvc中就是xml文件中对应的bean标签(注意,是标签,而不是真实的 java bean)
                 */
                invokeBeanFactoryPostProcessors(beanFactory);

                /**
                 * 注册 BeanPostProcessor 的实现类
                 * 1. BeanPostProcessor接口包含两个方法:postProcessBeforeInitialization 和 postProcessAfterInitialization
                 * 2. 这里只是注册,两个方法实际分别在 Bean初始化之前和初始化之后得到执行
                 */
                registerBeanPostProcessors(beanFactory);

                /**
                 * 初始化ApplicationContext的MessageSource,MessageSource是国际化相关的接口
                 */
                initMessageSource();

                /**
                 * 初始化ApplicationContext事件广播器
                 */
                initApplicationEventMulticaster();

                // 初始化子类特殊bean(钩子方法)
                onRefresh();

                /**
                 * 注册事件监听器
                 */
                registerListeners();

                /**
                 * 初始化所有的单例java bean(不包含懒加载的类)
                 */
                finishBeanFactoryInitialization(beanFactory);

                /**
                 * 广播事件,ApplicationContext初始化完成
                 */
                finishRefresh();
            } catch (BeansException ex) {
                ....................
            }

        }
    }

2.2. BeanDefinition

BeanDefinition 是 spring bean 的建模对象,即把一个bean实例化出来的模型对象。有人会问把一个bean实例化出来有Class就行了啊,Class也就是我们通常说的类对象,就是一个普通对象的建模对象,那么为什么spring不能用Class来建立bean呢?很简单,因为Class无法完成bean的抽象,比如bean的作用域,bean的注入模型,bean是否是懒加载等等信息,Class是无法抽象出来的,故而需要一个BeanDefinition类来抽象这些信息,以便于spring能够完美的实例化一个bean。

spring扫描到某个bean的类时,除了要存储类名、构造器等Class的基础信息以外,还要存储scope,lazy,dependsOn等spring的属性。BeanDefintion是一个类,专门用来存储上述bean中的各种属性。因此当spring扫描到一个符合规则的类时,首先是实例化一个BeanDefinition的对象,并且把根据类的类名生成一个bean的名字,继而以beanName为key,BeanDefinition对象为value,存放在一个map中。

值得注意的是,这里都是讲在扫描bean后,BeanDefinition的实例化,以及放入BeanDefinition的map中。还没有讲到Bean的实例化和装载。

BeanDefinition 的属性方法如下:

  • String: getBeanClassName: 返回当前bean definition定义的类名
  • ConstructorArgumentValues: getConstructorArgumentValues:返回bean的构造函数参数
  • String[]: getDependsOn:返回当前bean所依赖的其他bean的名称
  • String: getFactoryBeanName: 返回factory bean的名称
  • String: getFactoryMethodName: 返回工厂方法的名称
  • BeanDefinition: getOriginatingBeanDefinition: 返回原始的BeanDefinition,如果不存在返回null
  • String: getParentName: 返回当前bean definition的父definition的名字
  • MutablePropertyValues: getPropertyValues: 返回一个用于新的bean实例上的属性值
  • String: getScope: 返回当前bean的目标范围
  • boolean: isAbstract: 当前bean是否是abstract,意味着不能被实例化
  • boolean: isLazyInit: bean是否是延迟初始化
  • boolean: isPrimary: bean是否为自动装配的主要候选bean
  • boolean: isPrototype: bean是否是多实例
  • boolean: isSingleton: bean是否是单例
  • void: setAutowiredCandidate(boolean): 设置bean是否对其他bean是自动装配的候选bean
  • void: setBeanClassName(String): 指定bean definition的类名
  • void: setDependsOn(String ...): 设置当前bean初始化所依赖的beans的名称
  • void: setFactoryBeanName(String): 如果factory bean的名称
  • void: setFactoryMethodName(String): 设置工厂的方法名
  • void: setLazyInit(boolean lazyInit): 设置是否延迟初始化
  • void: setParentName(String): 设置父definition的名称
  • void: setPrimary(boolean): 设置是否主要的候选bean
  • void: setScope(String): 设置bean的范围,如:单例,多实例

2.3. BeanFactoryPostProcessor

BeanFactoryPostProcessor是实现spring容器功能扩展的重要接口,例如修改BeanDefinition,实现bean动态代理等。很多框架都是通过此接口实现对spring容器的扩展,例如mybatis与spring集成时,只定义了mapper接口,无实现类,接口也没有添加@Component等注解,但spring却可以完成自动注入,也是基于BeanFactoryPostProcessor接口来实现的。

BeanFactoryPostProcessor.java

@FunctionalInterface
public interface BeanFactoryPostProcessor {
    void postProcessBeanFactory(ConfigurableListableBeanFactory var1) throws BeansException;
}

前文说到BeanDefinition在实例化之后,被放入BeanDefinition map中,而BeanFactoryPostProcessor就是在BeanDefinition map初始化完成后,Bean实例构造方法执行之前执行的。

BeanFactoryPostProcessor的主要作用是,开发者可以修改容器中所有BeanDefinition的信息,接口方法的入参是ConfigurrableListableBeanFactory,使用该参数,可以获取到相关bean的定义信息。但 绝对不允许进行bean实例化相关的操作!因为中spring加载机制中,BeanFactoryPostProcessor是在Bean构造方法之前执行的,如果这个时候提前实例化bean,很可能会一连串的问题。

如:在接口用通过configurableListableBeanFactory.getBean(“user”),获取User类的bean,实际上即实例化了User的bean。会导致在后续其他节点代码中,无法注入User的bean实例。

2.4. BeanPostProcessor

如果说BeanFactoryPostProcessor是对BeanDefinition的扩展处理,那么BeanPostProcessor更多的是对Bean的扩展处理。BeanPostProcessor的触发时间是在 Bean的实例化之后(执行了构造方法,并且对属性进行了赋值),在Bean的init方法执行之前(@PostConstruct注解方法、InitializeBean接口方法、@Bean中initMethod方法)

BeanPostProcessor.java

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;
    }
}

BeanPostProcessor接口中定义两个方法,根据名字可以推测,postProcessBeforeInitialization在Bean的init方法之前执行,postProcessAfterInitialization在Bean的init方法之后执行。这点很好的体现了spring的AOP思想,可以理解为BeanPostProcessor是对Bean的init方法执行前后,做了一层切面拦截。

有一个实现Spring aop 的BeanPostProcessor 叫 AnnotationAwareAspectJAutoProxyCreator。在postProcessBeforeInitialization或者postProcessAfterInitialization方法中,对对象进行判断,看他需不需要织入切面逻辑,如果需要,那我就根据这个对象,生成一个代理对象,然后返回这个代理对象,那么最终注入容器的,自然就是代理对象了。

2.5. finishBeanFactoryInitialization

该方法会实例化所有剩余的非懒加载单例 bean。值得注意的是“所有剩余的”,就是有一些特殊的bean,这该方法之前就已经加载了,如:

  1. 一些spring容器内部的bean;
  2. 实现了 BeanFactoryPostProcessor 接口的bean;
  3. 实现了 BeanPostProcessor 接口的 bean(但是这些bean会在这个方法中触发)。

其他的非懒加载单例 bean 都会在这个方法中被实例化,并且 BeanPostProcessor 的触发也是在这个方法中。

3. Bean的生命履历

基于spring的refresh过程,我们把关于bean的部分拎出来,就形成了下面的bean的加载过程:

  1. spring基于@ComponentScan等注解扫描到bean,并将每个bean实例化成包含bean各种属性的BeanDefinition的对象,放入beanDefinitionMap中。
  2. spring 中的 bean 简单分为:特殊bean和普通bean,beanFactory实际会先实例化特殊bean。
  3. 实现BeanFactoryPostProcessor接口的就是一种特殊bean,在beanDefinitionMap加载后触发,可以自定义实现类,对其中的BeanDefinition进行修改。
  4. construct过程。BeanFactory 执行getBean方法生产其他的普通bean(调用类的构造方法,或FactoryBean的getObject方法,以及@Bean注解的方法)。
  5. 此时bean获取会受三级缓存(singletonFactories、earlySingletonObjects、singletonObjects)影响,如earlySingletonObjects会提前曝光尚未populate属性数据的单例对象,可解决循环依赖问题。
  6. populate过程。设置bean的依赖关系(基于属性注入)以及属性值。
  7. 对于实现BeanPostProcessor接口的类,执行接口中的postProcessBeforeInitialization 方法。
  8. initialze过程。执行bean的各种初始化方法,initialze方法的优先级如下:(1)@PostConstruct指定的方法 -> (2)InitializingBean接口的afterPropertiesSet()方法 -> (3)@Bean中initMethod指定的方法
  9. 对于实现BeanPostProcessor接口的类,执行接口中的postProcessAfterInitialization 方法。
  10. destroy过程,容器销毁,执行bean的各种销毁方法,destroy方法的优先级如下:(1)@PostDestroy指定的方法 -> (2)DisposableBean接口的destroy()方法 -> (3)@Bean中destroyMethod指定的方法

在spring bean生命周期中,网上有人用Bean的“初始化”、“实例化”等名字,很容易搞混,所以我使用了下列几个英文单词,感觉更能表达这些过程:

  • construct:是对象创建的过程。比如使用构造方法new对象。
  • populate:是为对象中的属性赋值的过程,包括@Autowired等依赖注入,@Value的赋值等。
  • initialize:执行初始化方法。

其实并非只有在populate时才会注入依赖,还是要取决于属性的注入方式:基于属性注入、setter注入和构造器注入等。如果是基于构造器注入,在construct过程中,就会导入依赖。

另外我们中使用@Value、@ConfigurationProperties等注解赋值,也是属于populate过程的操作。

4、bean相关方法

对于上述关于bean生命周期的介绍,我们写个demo,一一实现相关的方法,亲眼看看他们的加载过程。

4.1、BeanFactoryPostProcessor

@Slf4j
@Component
public class BeanFactoryPostProcessorImpl implements BeanFactoryPostProcessor {

    /**
     * getBean 是获取Object对象,getBeanDefinition 是获取Class类
     * 可以修改BeanDefinition
     * 但getBean方法不要用,会导致提前加载bean,bean实例化紊乱
     * @param beanFactory
     */
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        log.warn("实现 BeanFactoryPostProcessor 接口:开始了!");
        Arrays.stream(beanFactory.getBeanDefinitionNames())
                .filter(beanName -> User.USER_BEAN_NAME.equals(beanName))
                .forEach(beanName -> {
                    //BeanDefinition beanDefinition=beanFactory.getBeanDefinition(beanName);
                    //beanDefinition.getPropertyValues().add("name","Kerry");
                    log.warn("BeanFactoryPostProcessor:Bean name is " + beanName);
                });
    }
}

主要是对 BeanDefinitionName 的修改,这里将对象的属性name的值设置为Kerry。

4.2、BeanPostProcessor

@Slf4j
@Component
public class BeanPostProcessorImpl implements BeanPostProcessor {

    /**
     * Bean 初始化方法 执行前 调用
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if(User.USER_BEAN_NAME.equals(beanName)) {
            log.warn("BeanPostProcessor-name:"+((User)bean).getName());
            log.warn("实现 BeanPostProcessor 接口:postProcessBeforeInitialization(Object bean, String beanName),beanName=" + beanName);
        }
        return bean;
    }

    /**
     * Bean 初始化方法 执行后 调用
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if(User.USER_BEAN_NAME.equals(beanName)) {
            log.warn("实现 BeanPostProcessor 接口:postProcessAfterInitialization(Object bean, String beanName),beanName=" + beanName);
        }
        return bean;
    }
}

4.3、初始化和销毁方法

初始化和销毁两种方法往往是配套的,有下面三种,并且按照下列顺序执行:

  1. @PostConstruct 和 @PreDestroy 注解修饰的方法,分别在初始化和销毁时执行。
  2. 实现 InitializeBean 接口的 afterPropertiesSet()方法,和实现 DisposableBean 接口的 destroy()方法,分别在初始化和销毁时执行。
  3. @Bean 中initMethod属性指定的方法,和destroyMethod属性指定的方法,分别在初始化和销毁时执行。

我们在User类中定义了这些初始化和销毁方法,由于还要测试依赖注入顺序,也定义了Type类的方法。

User.java

@Slf4j
public class User implements InitializingBean, DisposableBean {
    public final static String USER_BEAN_NAME="user";
    //配置文件中,注入的值为 demoName
    @Value("${params.user.name}")
    private String name;
    private Integer age=1;

    @Autowired
    private Type type;


    @Override
    public String toString(){
        return "My name is "+name+",I'm "+age+"years old.";
    }

    /**
     * 构造方法
     * 1、默认构造方法
     * 2、重载构造方法
     */
    public User(){
        log.warn("构造方法:public User()");
    }

    /**
     * 初始化
     * 1、注解:@PostConstruct 的方法
     * 2、实现 InitializeBean 接口:afterPropertiesSet()
     * 3、@Bean 中指定的 initMethod
     */
    @PostConstruct
    public void postConstruct(){
        log.warn("注解:@PostConstruct 的方法");
    }

    @Override
    public void afterPropertiesSet(){
        log.warn("实现 InitializeBean 接口:afterPropertiesSet()");
    }

    public void initMethod(){
        log.warn("@Bean 中指定的 initMethod ");
    }

    /**
     * 销毁
     * 1、注解:@PreDestroy 的方法
     * 2、实现 DisposableBean 接口:destroy()
     * 3、@Bean 中指定的 destroyMethod
     */
    @PreDestroy
    public void preDestroy(){
        log.warn("注解:@PreDestroy 的方法");
    }

    @Override
    public void destroy(){
        log.warn("实现 DisposableBean 接口:destroy()");
    }

    public void destroyMethod(){
        log.warn("@Bean 中指定的 destroyMethod");
    }
    
    public void setName(String name) {
        log.warn("User.setName方法:"+name);
        this.name = name;
    }
}

Type.java

@Component
@Data
@Slf4j
public class Type implements InitializingBean {
    private String typeCode;

    public Type(){
       log.warn("构造方法:public Type()");
    }

    @Override
    public void afterPropertiesSet(){
        log.warn("Type:实现 InitializeBean 接口:afterPropertiesSet()");
    }
}

BeanConfig.java

@Configuration
public class BeanConfig {
    
    @Bean(initMethod = "initMethod",destroyMethod = "destroyMethod")
    public User user(){
        return new User();
    }
}

4.4. 结果验证

还记得我们在BeanFactoryPostProcessorImpl中修改了name的值吗?我们先把修改的代码注释掉,执行之后的日志如下,的确按照前面的Bean生命周期加载的:

p.k.e.s.c.BeanFactoryPostProcessorImpl   : 实现 BeanFactoryPostProcessor 接口:开始了!
p.k.e.s.c.BeanFactoryPostProcessorImpl   : BeanFactoryPostProcessor:Bean name is user
p.k.exercise.springexercise.pojo.User    : 构造方法:public User()
p.k.exercise.springexercise.pojo.Type    : 构造方法:public Type()
p.k.exercise.springexercise.pojo.Type    : Type:实现 InitializeBean 接口:afterPropertiesSet()
p.k.e.s.config.BeanPostProcessorImpl     : BeanPostProcessor-name:demoName
p.k.e.s.config.BeanPostProcessorImpl     : 实现 BeanPostProcessor 接口:postProcessBeforeInitialization(Object bean, String beanName),beanName=user
p.k.exercise.springexercise.pojo.User    : 注解:@PostConstruct 的方法
p.k.exercise.springexercise.pojo.User    : 实现 InitializeBean 接口:afterPropertiesSet()
p.k.exercise.springexercise.pojo.User    : @Bean 中指定的 initMethod 
p.k.e.s.config.BeanPostProcessorImpl     : 实现 BeanPostProcessor 接口:postProcessAfterInitialization(Object bean, String beanName),beanName=user
p.k.exercise.springexercise.pojo.User    : 注解:@PreDestroy 的方法
p.k.exercise.springexercise.pojo.User    : 实现 DisposableBean 接口:destroy()
p.k.exercise.springexercise.pojo.User    : @Bean 中指定的 destroyMethod

现在把BeanFactoryPostProcessorImpl中的注释放开,日志变了:

p.k.e.s.c.BeanFactoryPostProcessorImpl   : 实现 BeanFactoryPostProcessor 接口:开始了!
p.k.e.s.c.BeanFactoryPostProcessorImpl   : BeanFactoryPostProcessor:Bean name is user
p.k.exercise.springexercise.pojo.User    : 构造方法:public User()
p.k.exercise.springexercise.pojo.Type    : 构造方法:public Type()
p.k.exercise.springexercise.pojo.Type    : Type:实现 InitializeBean 接口:afterPropertiesSet()
p.k.exercise.springexercise.pojo.User    : User.setName方法:Kerry
p.k.e.s.config.BeanPostProcessorImpl     : BeanPostProcessor-name:Kerry
p.k.e.s.config.BeanPostProcessorImpl     : 实现 BeanPostProcessor 接口:postProcessBeforeInitialization(Object bean, String beanName),beanName=user
p.k.exercise.springexercise.pojo.User    : 注解:@PostConstruct 的方法
p.k.exercise.springexercise.pojo.User    : 实现 InitializeBean 接口:afterPropertiesSet()
p.k.exercise.springexercise.pojo.User    : @Bean 中指定的 initMethod 
p.k.e.s.config.BeanPostProcessorImpl     : 实现 BeanPostProcessor 接口:postProcessAfterInitialization(Object bean, String beanName),beanName=user
p.k.exercise.springexercise.pojo.User    : 注解:@PreDestroy 的方法
p.k.exercise.springexercise.pojo.User    : 实现 DisposableBean 接口:destroy()
p.k.exercise.springexercise.pojo.User    : @Bean 中指定的 destroyMethod

可以看到如果我们在BeanFactoryPostProcessor接口中通过BeanDefinition 的PropertyValue 设置属性值,其实只是修改了该属性的setter方法,而真正执行该方法,还是在populate阶段,而非BeanFactoryPostProcessor执行阶段。

阅读 1.9k

保持饥饿

471 声望
133 粉丝
0 条评论

保持饥饿

471 声望
133 粉丝
文章目录
宣传栏