关于springboot:SpringBean的生命周期

1次阅读

共计 12457 个字符,预计需要花费 32 分钟才能阅读完成。

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

正文完
 0