关于springboot:SpringBean的生命周期

1. 前言

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

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执行阶段。

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理