@RefreshScope和@Scheduled
@RefreshScope
功能:刷新配置信息,下例可以自动从配置文件中刷新到最新的startTask值
TaskConfig,java
/**
* @author yaLong
* @date 2022/2/12
*/
@RefreshScope
@Configuration
public class TaskConfig {
/**
* 自动任务调度的开关
*/
@Value("${auto-task.start:false}")
private Boolean startTask;
public Boolean getStartTask() {
return startTask;
}
}
@Scheduled
定时任务,下例可以每秒打印一下当前时间
ScheduleTask.java
/**
* @author yaLong
* @date 2022/1/13
*/
@Component
@Slf4j
public class ScheduleTask {
@Scheduled(cron = "*/1 * * * * ?")
public void test(){
System.out.println(System.currentTimeMillis());
}
}
问题
当一个类中同时使用@RefreshScope和@Scheduled时,定时任务失效,原因如下
- 需要动态刷新的类标注@RefreshScope注解
- @RefreshScope 注解标注了@Scope 注解,并默认了ScopedProxyMode.TARGET_CLASS属性,此属性的功能就是在创建一个代理,在每次调用的时候都用它来调用GenericScope get 方法来获取对象.
- 当改属性发生变更时,ContextRefresher refresh() ->RefreshScope refreshAll() 进行缓存清理方法调用,并发送刷新事件通知 -> GenericScope 真正的 清理方法destroy() 实现清理缓存
- 在下一次使用对象的时候,会调用GenericScope get(String name, ObjectFactory<?> objectFactory) 方法创建一个新的对象,并存入缓存中,此时新对象因为Spring 的装配机制就是新的属性了
- 调用了destroy()后,定时任务的缓存被清理了,又没有去生成这个对象,所以定时功能失效
解决方案
1.RefreshScopeRefreshedEvent,在收到消息之后从容器里获取一次当前bean,触发定时任务对象创建,就可以继续定时任务
test.java
/**
* @author yaLong
* @date 2022/2/12
*/
@EnableScheduling
@Configuration
@RefreshScope
public class test implements ApplicationListener<RefreshScopeRefreshedEvent> {
@Value("${auto-task.start:false}")
private Boolean startTask;
@Scheduled(cron = "*/1 * * * * ?")
public void test(){
System.out.println(startTask+":"+System.currentTimeMillis());
}
@Override
public void onApplicationEvent(RefreshScopeRefreshedEvent event) {
}
}
2.配置和定时任务放在不同class
因为destroy()卸载的是当前类的实例,所以定时任务和配置分开写就可以
TaskConfig.java
/**
* @author yaLong
* @date 2022/2/12
*/
@RefreshScope
@Configuration
public class TaskConfig {
/**
* 自动任务调度的开关
*/
@Value("${auto-task.start:false}")
private Boolean startTask;
public Boolean getStartTask() {
return startTask;
}
public void setStartTask(Boolean startTask) {
this.startTask = startTask;
}
}
test.java
/**
* @author yaLong
* @date 2022/2/12
*/
@EnableScheduling
@Configuration
public class test{
@Autowired
private TaskConfig config;
@Scheduled(cron = "*/1 * * * * ?")
public void test(){
System.out.println(config.getStartTask()+":"+System.currentTimeMillis());
}
}
总结
- 当改属性发生变更时,ContextRefresher refresh() ->RefreshScope refreshAll() 进行缓存清理方法调用,并发送刷新事件通知 -> GenericScope 真正的 清理方法destroy() 实现清理缓存
- 此时缓存被清除干净
- 在下一次使用对象的时候,会调用GenericScope get(String name, ObjectFactory<?> objectFactory) 方法创建一个新的对象,并存入缓存中,此时新对象因为Spring 的装配机制就是新的属性
- 配置刷新后会卸载类实例,并重新实例化类(如果类中存在计数等情况需要注意)
- @RefreshScope 不能修饰在 @Scheduled、listener、Timmer等类中
你要是觉得写的还不错,就点个关注,可以评论区留下足迹,以后方便查看.
你要是觉得写的很辣鸡,评论区欢迎来对线!
欢迎转载!