在spring容器启动后监听事件ApplicationReadyEvent

概述

经常会在业务中遇到需要在项目启动后刷新/预热一些数据的要求。
常见可以监听ApplicationReadyEventContextRefreshedEvent.

但是因为常见的springboot项目都依赖的springmvc,所以实际上有2个容器,springioc容器是springmvc的父容器。

而且ContextRefreshedEvent实际中会发布多次,如果业务只需要执行一次的情况话是不太适合监听这个的。

发布多次的原因是每加载完一次context,就会执行一次ContextRefreshedEvent而且每次执行,都会再执行一次parentContextRefreshedEvent.上面提到父子容器都会触发这个事件

org.springframework.context.support.AbstractApplicationContext#publishEvent(java.lang.Object, org.springframework.core.ResolvableType)
在spring容器启动后监听事件ApplicationReadyEvent_第1张图片

只想在启动后执行一次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关键的几个事件

  1. ContextClosedEvent spring 容器关闭事件
  2. ContextRefreshedEvent spring 容器的初始化后或者刷新完成事件;
  3. ContextStoppedEvent spring 容器停止事件
  4. ContextStartedEvent spring 容器初始化开始事件

34其实是spring生命周期相关的事件,1是整个spring容器销毁的事件。

springboot对spring容器周期事件的扩展

springboot对于spring的事件又有自己的扩展.

  1. ApplicationEnvironmentPreparedEvent 容器环境对象初始化后的事件
  2. ApplicationPreparedEvent 容器初始化前的事件,主要是在做 refresh 动作之前做触发的事件
  3. ApplicationStartedEvent 容器已经完成 refresh 动作后所触发的事件
  4. ApplicationReadyEvent 容器已经运行中的事件
  5. ApplicationFailedEvent 容器初始化失败所触发的事件
  6. ApplicationStartingEvent 容器开始时所触发的事件

触发顺序如下

//容器开始运行,在所有操作之前发送
ApplicationStartingEvent ->

//容器的环境准备就绪,在创建上下文之前发送
ApplicationEnvironmentPreparedEvent -> 

//在容器的上下文(context)被刷新之前,并且bean的定义都加载完之后发送
ApplicationPreparedEvent ->

//容器初始化或者刷新后发送,与spring中相同
ContextRefreshedEvent ->

//在容器上下文刷新完成后发送,此时spring容易已经完全启动
ApplicationStartedEvent	->

//spring容器已经完全启动,可以对完接收请求
ApplicationReadyEvent

你可能感兴趣的:(springboot,JAVA,spring,spring,java,spring,boot)