概述
经常会在业务中遇到需要在项目启动后刷新/预热一些数据的要求。
 常见可以监听ApplicationReadyEvent和ContextRefreshedEvent.
但是因为常见的springboot项目都依赖的springmvc,所以实际上有2个容器,spring的ioc容器是springmvc的父容器。
而且ContextRefreshedEvent实际中会发布多次,如果业务只需要执行一次的情况话是不太适合监听这个的。
发布多次的原因是每加载完一次context,就会执行一次ContextRefreshedEvent而且每次执行,都会再执行一次parent的ContextRefreshedEvent.上面提到父子容器都会触发这个事件
org.springframework.context.support.AbstractApplicationContext#publishEvent(java.lang.Object, org.springframework.core.ResolvableType)
 
只想在启动后执行一次demo
可以监听ApplicationReadyEvent事件
package com.xxx.xxx.xxx.xxx.xxx;
import cn.hutool.extra.spring.SpringUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.Objects;
@Slf4j
@Component
public class xxxRefreshEventListener implements ApplicationListener<ApplicationReadyEvent> {
    @Override
    public void onApplicationEvent(ApplicationReadyEvent contextRefreshedEvent) {
        //刷新配置
        refreshTargetProperties();
    }
    private <T> T refreshObjProperties(Class<T> objClass){
        try {
            T bean = SpringUtil.getBean(objClass);
            BusinessProperties annotation = AnnotationUtils.findAnnotation(objClass, BusinessProperties.class);
            if(Objects.isNull(annotation)){
                return bean;
            }
            //配置前缀
            String prefix = annotation.prefix();
            BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass());
            for (PropertyDescriptor propertyDescriptor : beanInfo.getPropertyDescriptors()) {
                String name = propertyDescriptor.getName();
                Class<?> propertyType = propertyDescriptor.getPropertyType();
                Method writeMethod = propertyDescriptor.getWriteMethod();
                //完成的config key
                String configKey = String.join(".", prefix, name);
                //获取数据
                xxx info = xxx.xxx(configKey);
                if(Objects.isNull(info )){
                    continue;
                }
                //db中存放的值
                String configValue = info.getConfigValue();
                //给属性填充值
                if(Objects.equals(propertyType, String.class)){
                    writeMethod.invoke(bean, configValue);
                }else {
                    Object propertyValue = objectMapper.readValue(configValue, propertyType);
                    writeMethod.invoke(bean, propertyValue);
                }
            }
            return bean;
        } catch (Exception e) {
            log.error("刷新配置异常,class:{}", objClass, e);
            return null;
        }
    }
}
spring关键的几个事件
- ContextClosedEvent- spring容器关闭事件
- ContextRefreshedEvent- spring容器的初始化后或者刷新完成事件;
- ContextStoppedEvent- spring容器停止事件
- ContextStartedEvent- spring容器初始化开始事件
3和4其实是spring生命周期相关的事件,1是整个spring容器销毁的事件。
springboot对spring容器周期事件的扩展
springboot对于spring的事件又有自己的扩展.
- ApplicationEnvironmentPreparedEvent容器环境对象初始化后的事件
- ApplicationPreparedEvent容器初始化前的事件,主要是在做- refresh动作之前做触发的事件
- ApplicationStartedEvent容器已经完成 refresh 动作后所触发的事件
- ApplicationReadyEvent容器已经运行中的事件
- ApplicationFailedEvent容器初始化失败所触发的事件
- ApplicationStartingEvent容器开始时所触发的事件
触发顺序如下
ApplicationStartingEvent ->
ApplicationEnvironmentPreparedEvent -> 
ApplicationPreparedEvent ->
ContextStartedEvent -> 
ContextRefreshedEvent ->
ApplicationStartedEvent	->
ApplicationReadyEvent


















