在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