遇到了Spring中一个由于初始化顺序不同导致的问题。
和同学一起Spring Scheduler
写了一个包含定时任务的Component
,简化如下:
package hello;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
public class Component1 {
// SpringApplicationContext是一个查找Bean的工具类
SomeBean someBean = SpringApplicationContext.getBean(SomeBean.class);
@Scheduled(fixedRate = 1000L)
public void schedule() {
// 执行业务逻辑
someBean.doSomething();
System.out.println("Scheduled");
}
}
由于SomeBean和Component1的实例化顺序不确定,SomeBean晚于Component1初始化的情况下,SpringApplicationContext.getBean(SomeBean.class)会失败,这样就不得不考虑让这一步骤延后,以避免初始化失败。
于是想到了使用Lazy注解延迟Component1的实例化。使用Lazy注解后,Component1将不会在ApplicationContext启动时提前被实例化,而是第一次向容器通过getBean索取bean时实例化的。
@Lazy
@Component
public class Component1 {
以为这样就解决了应用启动失败的问题,谁知这样导致schedule定时任务不生效了。
仔细看了@Scheduled注解的工作原理后,发现它的核心类是ScheduledAnnotationBeanPostProcessor。其中的核心方法是postProcessAfterInitialization,负责@Schedule注解的扫描,构建ScheduleTask。而这个类实现的是BeanPostProcessor接口,仅在类被实例化之后才被调用。
原因缕清楚了,使用了@Lazy注解的Component1没有在任何其他地方被显式实例化,因为无法被扫描到其内部的@Scheduled注解,观察到的结果就是:定时任务不生效。
有了这些分析后就好办了:既让Component1实例化,使得@Scheduled生效,又让SomeBean能够被找到。
最终使用了如下写法:
package hello;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
public class Component1 {
@Scheduled(fixedRate = 1000L)
public void schedule() {
// SpringApplicationContext是一个查找Bean的工具类
SomeBean someBean = SpringApplicationContext.getBean(SomeBean.class);
// 执行业务逻辑
someBean.doSomething();
System.out.println("Scheduled");
}
}