SpringBoot之一个注解实现开机启动(容器启动之后自动运行某些方法)

在SpringBoot中,我们可以通过ApplicationReadyEvent事件实现开机启动,即当容器启动之后自动运行某些方法。

但是每个业务都实现监听ApplicationReadyEvent事件显得代码有些繁琐,我们可以封装一个注解来实现。

定义@Startup注解

/**
 * 系统启动注解
 * 被该注解标记的方法会在系统启动后即ApplicationReadyEvent事件时调用一次
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Startup {
    /**
     * 启动顺序(从小到大)
     *
     * @return
     */
    int value() default 100;
}

实现启动逻辑

/**
 * 系统启动注解监听器
 */
@Slf4j
@Component
public class StartupListener implements ApplicationListener<ApplicationReadyEvent> {

    @Override
    public void onApplicationEvent(ApplicationReadyEvent event) {
        ConfigurableApplicationContext applicationContext = event.getApplicationContext();
        List<StartupBean> startupBeanList = new ArrayList<>();

        //获取所有spring容器管理的bean并遍历
        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
            //获取bean及其原始类的所有方法并遍历
            Object bean = applicationContext.getBean(beanDefinitionName);
            Class<?> targetClass = AopUtils.getTargetClass(bean);
            Method[] methods = targetClass.getMethods();
            for (Method method : methods) {
                //如果方法被@Startup注解标记,则加入startupBeanList
                if (method.isAnnotationPresent(Startup.class)) {
                    StartupBean startupBean = new StartupBean();
                    startupBean.setBean(bean);
                    startupBean.setMethod(method);
                    startupBean.setName(String.format("%s.%s()", targetClass.getSimpleName(), method.getName()));
                    startupBean.setOrder(method.getAnnotation(Startup.class).value());
                    startupBeanList.add(startupBean);
                }
            }
        }

        //对startupBeanList进行排序,并逐个触发执行
        startupBeanList.stream()
                .sorted(Comparator.comparingInt(StartupBean::getOrder))
                .forEach(StartupBean::invoke);
    }

    /**
     * 被Startup注解的方法的一个包装对象
     */
    @Data
    private static class StartupBean {
        private Integer order;
        private String name;
        private Method method;
        private Object bean;

        public void invoke() {
            StopWatch watch = new StopWatch();
            log.info("Startup {} invoke", this.name);
            watch.start();
            try {
                this.method.invoke(this.bean);
            } catch (Exception e) {
                log.error("Startup {} error {}", this.name, e);
            } finally {
                watch.stop();
            }
            log.info("Startup {} finished time={}ms", this.name, watch.getTotalTimeMillis());
        }
    }

}

原理介绍

1、定义一个Startup注解,并编写一个StartupListener的监听类,通过这个类实现对ApplicationReadyEvent事件的监听;
2、在对应的onApplicationEvent方法中,去扫描容器中的所有bean,并获取bean的方法判断是否有被Startup进行注解;
3、如果有的话则证明这个方法需要被开机启动,将其加入startupBeanList中;
4、由于可能有指定顺序的要求,所以统一在扫描完成之后通过注解的value值对startupBeanList进行一个排序,最终对排序的结果进行遍历并调用执行对应的方法;

代码:https://gitee.com/lqccan/blog-demo/tree/master/SpringBoot/startup


觉得内容还不错?打赏个钢镚鼓励鼓励!!👍