@EnableScheduling
注解和@Scheduled注解实现定时任务,也可以通过SchedulingConfigurer接口来实现定时任务。但是这两种方式不能动态添加、删除、启动、停止任务。要实现动态增删启停定时任务功能,比较广泛的做法是集成Quartz框架。但是本人的开发原则是:在满足项目需求的情况下,尽量少的依赖其它框架,避免项目过于臃肿和复杂。
查看spring-context这个jar包中org.springframework.scheduling.ScheduledTaskRegistrar
这个类的源代码,发现可以通过改造这个类就能实现动态增删启停定时任务功能。
添加执行定时任务的线程池配置类
@Configuration
publicclassSchedulingConfig{
@Bean
publicTaskSchedulertaskScheduler(){
ThreadPoolTaskSchedulertaskScheduler=newThreadPoolTaskScheduler();
//定时任务执行线程池核心线程数
taskScheduler.setPoolSize(4);
taskScheduler.setRemoveOnCancelPolicy(true);
taskScheduler.setThreadNamePrefix("TaskSchedulerThreadPool-");
returntaskScheduler;
}
}
添加ScheduledFuture的包装类。ScheduledFuture是ScheduledExecutorService定时任务线程池的执行结果。
publicfinalclassScheduledTask{
volatileScheduledFuture>future;
/**
*取消定时任务
*/
publicvoidcancel(){
ScheduledFuture>future=this.future;
if(future!=null){
future.cancel(true);
}
}
}
添加Runnable接口实现类,被定时任务线程池调用,用来执行指定bean里面的方法。
publicclassSchedulingRunnableimplementsRunnable{
privatestaticfinalLoggerlogger=LoggerFactory.getLogger(SchedulingRunnable.class);
privateStringbeanName;
privateStringmethodName;
privateStringparams;
publicSchedulingRunnable(StringbeanName,StringmethodName){
this(beanName,methodName,null);
}
publicSchedulingRunnable(StringbeanName,StringmethodName,Stringparams){
this.beanName=beanName;
this.methodName=methodName;
this.params=params;
}
@Override
publicvoidrun(){
logger.info("定时任务开始执行- bean:{},方法:{},参数:{}",beanName,methodName,params);
longstartTime=System.currentTimeMillis();
try{
Objecttarget=SpringContextUtils.getBean(beanName);
Methodmethod=null;
if(StringUtils.isNotEmpty(params)){
method=target.getClass().getDeclaredMethod(methodName,String.class);
}else{
method=target.getClass().getDeclaredMethod(methodName);
}
ReflectionUtils.makeAccessible(method);
if(StringUtils.isNotEmpty(params)){
method.invoke(target,params);
}else{
method.invoke(target);
}
}catch(Exceptionex){
logger.error(String.format("定时任务执行异常- bean:%s,方法:%s,参数:%s ",beanName,methodName,params),ex);
}
longtimes=System.currentTimeMillis()-startTime;
logger.info("定时任务执行结束- bean:{},方法:{},参数:{},耗时:{}毫秒",beanName,methodName,params,times);
}
@Override
publicbooleanequals(Objecto){
if(this==o)returntrue;
if(o==null||getClass()!=o.getClass())returnfalse;
SchedulingRunnablethat=(SchedulingRunnable)o;
if(params==null){
returnbeanName.equals(that.beanName)&&
methodName.equals(that.methodName)&&
that.params==null;
}
returnbeanName.equals(that.beanName)&&
methodName.equals(that.methodName)&&
params.equals(that.params);
}
@Override
publicinthashCode(){
if(params==null){
returnObjects.hash(beanName,methodName);
}
returnObjects.hash(beanName,methodName,params);
}
}
添加定时任务注册类,用来增加、删除定时任务。
@Component
publicclassCronTaskRegistrarimplementsDisposableBean{
privatefinalMapscheduledTasks=newConcurrentHashMap<>(16);
@Autowired
privateTaskSchedulertaskScheduler;
publicTaskSchedulergetScheduler(){
returnthis.taskScheduler;
}
publicvoidaddCronTask(Runnabletask,StringcronExpression){
addCronTask(newCronTask(task,cronExpression));
}
publicvoidaddCronTask(CronTaskcronTask){
if(cronTask!=null){
Runnabletask=cronTask.getRunnable();
if(this.scheduledTasks.containsKey(task)){
removeCronTask(task);
}
this.scheduledTasks.put(task,scheduleCronTask(cronTask));
}
}
publicvoidremoveCronTask(Runnabletask){
ScheduledTaskscheduledTask=this.scheduledTasks.remove(task);
if(scheduledTask!=null)
scheduledTask.cancel();
}
publicScheduledTaskscheduleCronTask(CronTaskcronTask){
ScheduledTaskscheduledTask=newScheduledTask();
scheduledTask.future=this.taskScheduler.schedule(cronTask.getRunnable(),cronTask.getTrigger());
returnscheduledTask;
}
@Override
publicvoiddestroy(){
for(ScheduledTasktask:this.scheduledTasks.values()){
task.cancel();
}
this.scheduledTasks.clear();
}
}
添加定时任务示例类
@Component("demoTask")
publicclassDemoTask{
publicvoidtaskWithParams(Stringparams){
System.out.println("执行有参示例任务:"+params);
}
publicvoidtaskNoParams(){
System.out.println("执行无参示例任务");
}
}
定时任务数据库表设计
添加定时任务实体类
publicclassSysJobPO{
/**
*任务ID
*/
privateIntegerjobId;
/**
*bean名称
*/
privateStringbeanName;
/**
*方法名称
*/
privateStringmethodName;
/**
*方法参数
*/
privateStringmethodParams;
/**
*cron表达式
*/
privateStringcronExpression;
/**
*状态(1正常0暂停)
*/
privateIntegerjobStatus;
/**
*备注
*/
privateStringremark;
/**
*创建时间
*/
privateDatecreateTime;
/**
*更新时间
*/
privateDateupdateTime;
publicIntegergetJobId(){
returnjobId;
}
publicvoidsetJobId(IntegerjobId){
this.jobId=jobId;
}
publicStringgetBeanName(){
returnbeanName;
}
publicvoidsetBeanName(StringbeanName){
this.beanName=beanName;
}
publicStringgetMethodName(){
returnmethodName;
}
publicvoidsetMethodName(StringmethodName){
this.methodName=methodName;
}
publicStringgetMethodParams(){
returnmethodParams;
}
publicvoidsetMethodParams(StringmethodParams){
this.methodParams=methodParams;
}
publicStringgetCronExpression(){
returncronExpression;
}
publicvoidsetCronExpression(StringcronExpression){
this.cronExpression=cronExpression;
}
publicIntegergetJobStatus(){
returnjobStatus;
}
publicvoidsetJobStatus(IntegerjobStatus){
this.jobStatus=jobStatus;
}
publicStringgetRemark(){
returnremark;
}
publicvoidsetRemark(Stringremark){
this.remark=remark;
}
publicDategetCreateTime(){
returncreateTime;
}
publicvoidsetCreateTime(DatecreateTime){
this.createTime=createTime;
}
publicDategetUpdateTime(){
returnupdateTime;
}
publicvoidsetUpdateTime(DateupdateTime){
this.updateTime=updateTime;
}
}
booleansuccess=sysJobRepository.addSysJob(sysJob);
if(!success)
returnOperationResUtils.fail("新增失败");
else{
if(sysJob.getJobStatus().equals(SysJobStatus.NORMAL.ordinal())){
SchedulingRunnabletask=newSchedulingRunnable(sysJob.getBeanName(),sysJob.getMethodName(),sysJob.getMethodParams());
cronTaskRegistrar.addCronTask(task,sysJob.getCronExpression());
}
}
returnOperationResUtils.success();
修改定时任务,先移除原来的任务,再启动新任务
booleansuccess=sysJobRepository.editSysJob(sysJob);
if(!success)
returnOperationResUtils.fail("编辑失败");
else{
//先移除再添加
if(existedSysJob.getJobStatus().equals(SysJobStatus.NORMAL.ordinal())){
SchedulingRunnabletask=newSchedulingRunnable(existedSysJob.getBeanName(),existedSysJob.getMethodName(),existedSysJob.getMethodParams());
cronTaskRegistrar.removeCronTask(task);
}
if(sysJob.getJobStatus().equals(SysJobStatus.NORMAL.ordinal())){
SchedulingRunnabletask=newSchedulingRunnable(sysJob.getBeanName(),sysJob.getMethodName(),sysJob.getMethodParams());
cronTaskRegistrar.addCronTask(task,sysJob.getCronExpression());
}
}
returnOperationResUtils.success();
删除定时任务
booleansuccess=sysJobRepository.deleteSysJobById(req.getJobId());
if(!success)
returnOperationResUtils.fail("删除失败");
else{
if(existedSysJob.getJobStatus().equals(SysJobStatus.NORMAL.ordinal())){
SchedulingRunnabletask=newSchedulingRunnable(existedSysJob.getBeanName(),existedSysJob.getMethodName(),existedSysJob.getMethodParams());
cronTaskRegistrar.removeCronTask(task);
}
}
returnOperationResUtils.success();
定时任务启动/停止状态切换
if(existedSysJob.getJobStatus().equals(SysJobStatus.NORMAL.ordinal())){
SchedulingRunnabletask=newSchedulingRunnable(existedSysJob.getBeanName(),existedSysJob.getMethodName(),existedSysJob.getMethodParams());
cronTaskRegistrar.addCronTask(task,existedSysJob.getCronExpression());
}else{
SchedulingRunnabletask=newSchedulingRunnable(existedSysJob.getBeanName(),existedSysJob.getMethodName(),existedSysJob.getMethodParams());
cronTaskRegistrar.removeCronTask(task);
}
添加实现了CommandLineRunner接口的SysJobRunner类,当spring boot项目启动完成后,加载数据库里状态为正常的定时任务。
@Service
publicclassSysJobRunnerimplementsCommandLineRunner{
privatestaticfinalLoggerlogger=LoggerFactory.getLogger(SysJobRunner.class);
@Autowired
privateISysJobRepositorysysJobRepository;
@Autowired
privateCronTaskRegistrarcronTaskRegistrar;
@Override
publicvoidrun(String...args){
//初始加载数据库里状态为正常的定时任务
ListjobList=sysJobRepository.getSysJobListByStatus(SysJobStatus.NORMAL.ordinal());
if(CollectionUtils.isNotEmpty(jobList)){
for(SysJobPOjob:jobList){
SchedulingRunnabletask=newSchedulingRunnable(job.getBeanName(),job.getMethodName(),job.getMethodParams());
cronTaskRegistrar.addCronTask(task,job.getCronExpression());
}
logger.info("定时任务已加载完毕...");
}
}
}
工具类SpringContextUtils,用来从spring容器里获取bean
@Component
publicclassSpringContextUtilsimplementsApplicationContextAware{
privatestaticApplicationContextapplicationContext;
@Override
publicvoidsetApplicationContext(ApplicationContextapplicationContext)
throwsBeansException{
SpringContextUtils.applicationContext=applicationContext;
}
publicstaticObjectgetBean(Stringname){
returnapplicationContext.getBean(name);
}
publicstaticTgetBean(ClassrequiredType) {
returnapplicationContext.getBean(requiredType);
}
publicstaticTgetBean(Stringname,ClassrequiredType) {
returnapplicationContext.getBean(name,requiredType);
}
publicstaticbooleancontainsBean(Stringname){
returnapplicationContext.containsBean(name);
}
publicstaticbooleanisSingleton(Stringname){
returnapplicationContext.isSingleton(name);
}
publicstaticClass extends Object>getType(Stringname){
returnapplicationContext.getType(name);
}
}
本文完,参考本文代码可成功运行,亲测!
(感谢阅读,希望对你所有帮助)来源:www.jianshu.com/p/0f68936393fd-
源代码
+关注
关注
96文章
2945浏览量
66743 -
spring
+关注
关注
0文章
340浏览量
14341 -
Boot
+关注
关注
0文章
149浏览量
35835 -
SpringBoot
+关注
关注
0文章
173浏览量
177
原文标题:告别硬编码,SpringBoot实现动态增删启停定时任务
文章出处:【微信号:AndroidPush,微信公众号:Android编程精选】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
相关推荐
评论