Spring的SchedulingConfigurer实现定时任务
前提:在做业务平台的时候我们经常会遇到,某些跟时间打交道的需要修改状态,比如说在时间区间之前,属于未生效状态,区间之内属于有效期,区间之后,属于过期,或者需要每天 每周 每月,甚至是年为单位的做一些固定的操作。通过定时任务可以通过开启定时任务来完成这些需求。
我做合同管理模块,合同有未生效,已生效,已过期,三个状态,不可能每次用户登录的时候去判断这个状态,然后修改,这样做会在登录的逻辑里边耦合了合同业务逻辑,同时消耗了登录时间,不太可取。
下便是代码:
依赖:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.xc</groupId>
<artifactId>timetask</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>timetask</name>
<description>Demo project for Spring Boot</description> <properties>
<java.version>1.8</java.version>
</properties> <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies> <build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build> </project>
pom.xml
主启动类开启定时任务注解
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.web.bind.annotation.RestController; @SpringBootApplication
@RestController
@EnableScheduling
public class TimetaskApplication { public static void main(String[] args) {
SpringApplication.run(TimetaskApplication.class, args);
} }
SpringUtil工具类:需要ApplicationContextAware接口

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component; @Component
public class SpringUtil implements ApplicationContextAware { private Logger logger= LoggerFactory.getLogger(SpringUtil.class); private static ApplicationContext applicationContext; @Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if(SpringUtil.applicationContext == null) {
SpringUtil.applicationContext = applicationContext;
}
logger.info("========ApplicationContext配置成功,在普通类可以通过调用SpringUtils.getAppContext()获取applicationContext对象,applicationContext="+SpringUtil.applicationContext+"========");
} //获取applicationContext
public static ApplicationContext getApplicationContext() {
return applicationContext;
} //通过name获取 Bean.
public static Object getBean(String name){
Class cla = null;
try {
cla=Class.forName(name);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return getApplicationContext().getBean(cla);
} //通过class获取Bean.
public static <T> T getBean(Class<T> clazz){
return getApplicationContext().getBean(clazz);
} //通过name,以及Clazz返回指定的Bean
public static <T> T getBean(String name,Class<T> clazz){
return getApplicationContext().getBean(name, clazz);
} }
SpringUtil.java
SysTaskController实现SchedulingConfigurer接口,配置定时任务以及开启定时任务

import com.xc.timetask.entity.Task;
import com.xc.timetask.service.TaskService;
import com.xc.timetask.util.SpringUtil;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.TriggerContext;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component; import javax.annotation.Resource;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledFuture; @Lazy(value = false)
@Component
public class SysTaskController implements SchedulingConfigurer { protected static Logger logger = LoggerFactory.getLogger(SysTaskController.class); private SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); private static ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler(); private static Map<String,ScheduledFuture<?>> scheduledFutureMap = new HashMap<>(); @Resource
private TaskService taskService; //从数据库里取得所有要执行的定时任务
private List<Task> getAllTasks() throws Exception {
List<Task> list=taskService.selectyunx();
return list;
}
static {
threadPoolTaskScheduler.initialize();
}
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
List<Task> tasks= null;
try {
tasks = getAllTasks();
} catch (Exception e) {
e.printStackTrace();
}
logger.info("定时任务启动,预计启动任务数量="+tasks.size()+"; time="+sdf.format(new Date())); //校验数据(这个步骤主要是为了打印日志,可以省略)
checkDataList(tasks); //通过校验的数据执行定时任务
int count = 0;
if(tasks.size()>0) {
for (Task task:tasks) {
try {
//taskRegistrar.addTriggerTask(getRunnable(task), getTrigger(task));
start(task);
count++;
} catch (Exception e) {
logger.error("定时任务启动错误:" + task.getBean_name() + ";" + task.getMethod_name() + ";" + e.getMessage());
}
}
}
logger.info("定时任务实际启动数量="+count+"; time="+sdf.format(new Date()));
}
private static Runnable getRunnable(Task task){
return new Runnable() {
@Override
public void run() {
try {
Object obj = SpringUtil.getBean(task.getBean_name());
Method method = obj.getClass().getMethod(task.getMethod_name());
method.invoke(obj);
} catch (InvocationTargetException e) {
logger.error("定时任务启动错误,反射异常:"+task.getBean_name()+";"+task.getMethod_name()+";"+ e.getMessage());
} catch (Exception e) {
logger.error(e.getMessage());
}
}
};
} private static Trigger getTrigger(Task task){
return new Trigger() {
@Override
public Date nextExecutionTime(TriggerContext triggerContext) {
//将Cron 0/1 * * * * ? 输入取得下一次执行的时间
CronTrigger trigger = new CronTrigger(task.getCron());
Date nextExec = trigger.nextExecutionTime(triggerContext);
return nextExec;
}
}; } private List<Task> checkDataList(List<Task> list) {
String errMsg="";
for(int i=0;i<list.size();i++){
if(!checkOneData(list.get(i)).equalsIgnoreCase("success")){
errMsg+=list.get(i).getName()+";";
list.remove(list.get(i));
i--;
};
}
if(!StringUtils.isBlank(errMsg)){
errMsg="未启动的任务:"+errMsg;
logger.error(errMsg);
}
return list;
} public static String checkOneData(Task task){
String result="success";
Class cal= null;
try {
cal = Class.forName(task.getBean_name()); Object obj = SpringUtil.getBean(cal);
Method method = obj.getClass().getMethod(task.getMethod_name());
String cron=task.getCron();
if(StringUtils.isBlank(cron)){
result="定时任务启动错误,无cron:"+task.getName();
logger.error(result);
}
} catch (ClassNotFoundException e) {
result="定时任务启动错误,找不到类:"+task.getBean_name()+ e.getMessage();
logger.error(result);
} catch (NoSuchMethodException e) {
result="定时任务启动错误,找不到方法,方法必须是public:"+task.getBean_name()+";"+task.getMethod_name()+";"+ e.getMessage();
logger.error(result);
} catch (Exception e) {
logger.error(e.getMessage());
}
return result;
}
/**
* 启动定时任务
* @param task
* @param
*/
public static void start(Task task){ ScheduledFuture<?> scheduledFuture = threadPoolTaskScheduler.schedule(getRunnable(task),getTrigger(task));
scheduledFutureMap.put(task.getId(),scheduledFuture);
logger.info("启动定时任务" + task.getId() ); } /**
* 取消定时任务
* @param task
*/
public static void cancel(Task task){ ScheduledFuture<?> scheduledFuture = scheduledFutureMap.get(task.getId()); if(scheduledFuture != null && !scheduledFuture.isCancelled()){
scheduledFuture.cancel(Boolean.FALSE);
} scheduledFutureMap.remove(task.getId());
logger.info("取消定时任务" + task.getId() ); } /**
* 编辑
* @param task
* @param
*/
public static void reset(Task task){
logger.info("修改定时任务开始" + task.getId() );
cancel(task);
start(task);
logger.info("修改定时任务结束" + task.getId());
} }
SysTaskController.java
TaskDemo:定时任务要操作的类

import org.springframework.stereotype.Component; import java.text.SimpleDateFormat;
import java.util.Date; @Component
public class TaskDemo {
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //注入需要的service
//private ContractService contractService; public String run(){ try {
/*
使用service做一些修改的业务操作
获取所有的合同列表
List<Contract> list = contractService.getAllContract();
修改合同的状态
contractService.updateContractList(list); 下边用打印***来代替业务进行测试定时任务
*/
System.out.println("*********"); System.out.println("TaskContract is running"+sdf.format(new Date()));
} catch (Exception e) {
e.printStackTrace();
}
return "/main";
}
}
TaskDemo.java
其他的controller service dao类就省略了,后边会给出github地址,有demo可执行
演示及说明
访问/timetask/list可以看到所有的定时任务列表 可以进行启动 停止 以及常规的增删改查

特别说明 新增的时候类名必须是全路径类名,因为是通过Class.forName("com.xc.timetask········") 反射来加载要执行的类
表达式 是cron表达式 就是定时任务的执行单位 不知道的可以 戳这里 去看看
方法 是要执行的方法的名字
新增和修改页面 方法哪里填写 run 就可以 和 TaskDemo 里的那个方法对应

这里忘记加上时间区间的datepicker日历控件 来进行开始时间 结束时间填充, 直接去数据库表里修改开始 结束时间 轻点喷我!!!!!
另外demo使用了LayUI 真的是惨不忍睹 弹出层会重复 -!- 不过后台代码都是好用的
结果 因为上边图中列表 只有一个定时任务开启的

需要demo 源码的 请戳这里
SysTaskController
Spring的SchedulingConfigurer实现定时任务的更多相关文章
- Spring基于SchedulingConfigurer实现定时任务
Spring 基于 SchedulingConfigurer 实现定时任务,代码如下: import org.springframework.scheduling.annotation.Schedul ...
- spring的Scheduled(定时任务)和多线程
一.前言 在我们日常的开发中,经常用到数据同步的更新,这时我们采用的是spring的定时任务和java的多线程进行数据的更新,进行时实的服务调用. 二.实现思路 1.创建线程类 ...
- spring注解scheduled实现定时任务
只想说,spring注解scheduled实现定时任务使用真的非常简单. 一.配置spring.xml文件 1.在beans加入xmlns:task="http://www.springfr ...
- Spring Boot 中实现定时任务的两种方式
在 Spring + SpringMVC 环境中,一般来说,要实现定时任务,我们有两中方案,一种是使用 Spring 自带的定时任务处理器 @Scheduled 注解,另一种就是使用第三方框架 Qua ...
- Spring Boot(九):定时任务
Spring Boot(九):定时任务 一.pom包配置 pom包里面只需要引入springboot starter包即可 <dependencies> <dependency> ...
- spring boot 学习(八)定时任务 @Scheduled
SpringBoot 定时任务 @Scheduled 前言 有时候,我们有这样的需求,需要在每天的某个固定时间或者每隔一段时间让应用去执行某一个任务.一般情况下,可以使用多线程来实现这个功能:在 Sp ...
- 【spring boot】spring boot中使用定时任务配置
spring boot中使用定时任务配置 =============================================================================== ...
- spring boot 基础篇 -- 定时任务
在日常项目中,常常会碰到定时监控项目中某个业务的变化,下面是spring boot 集成的定时任务具体配置: @Component public class IndexWarningScheduled ...
- 品Spring:关于@Scheduled定时任务的思考与探索,结果尴尬了
非Spring风格的代码与Spring的结合 现在的开发都是基于Spring的,所有的依赖都有Spring管理,这没有问题. 但是要突然写一些非Spring风格的代码时,可能会很不习惯,如果还要和Sp ...
随机推荐
- 入门大数据---Python基础
前言 由于AI的发展,包括Python集成了很多计算库,所以淡入了人们的视野,成为一个极力追捧的语言. 首先概括下Python中文含义是蟒蛇,它是一个胶水语言和一个脚本语言,胶水的意思是能和多种语言集 ...
- Python实用笔记 (25)面向对象高级编程——多重继承
class Dog(Mammal, Runnable): pass 多重继承,继承了不同大类的所有功能,这种设计称之为Mixln,其目的就是给一个类增加多个功能,这样,在设计类的时候,我们优先考虑通过 ...
- 线程的创建方式以及synchronize的使用
线程的两种创建方式 继承 Thread class Thr extends Thread { @Override public void run() { Thread.cur ...
- Android 错误异常之Error:Unable to resolve dependency for ':app@debug/compileClasspath': Could。。。。
这个错误一般出现在导入别人的项目的时候出现的, 我出错原因是,as版本3.5.2用了几个月感觉不如3.0.1的带劲,so 该到了3.0.1 ,出现了这个错, 之前也遇到过,基本都是gradle版本的错 ...
- Android 伤敌一千自损八百之萤石摄像头集成(三)
说一下萤石原生播放 先上代码 private MyOrientationDetector mOrientationDetector; @Override protected void onCreate ...
- 2.在linux安装ssh_免密连接
Linux开启ssh服务 首先更新源 sudo apt-get update 安装ssh服务 sudo apt-get install openssh-server 检测是否已启动 ps -e | g ...
- 一次运维-堡垒机多次跳转导出及导入mysql数据库
1. 场景描述 记录一次运维,朋友公司要从线上环境同步数据到测试环境,因为公司监管问题,导致数据无法从线上获取,需要通过vpn,堡垒机,3次跳转到目标主机,通过命令导出mysql数据文件,然后再将数据 ...
- 每日一题 - 剑指 Offer 41. 数据流中的中位数
题目信息 时间: 2019-06-30 题目链接:Leetcode tag: 大根堆 小根堆 难易程度:中等 题目描述: 如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有 ...
- 使用原生js来控制、修改CSS伪元素的方法总汇, 例如:before和:after
在网页中,如果需要使用辅助性/装饰性的内容的时候,我们不应该直接写在HTML中,这样会影响真正的内容,这就需要使用伪元素了,这是由于css的纯粹语义化是没有意义的.在使用伪元素的时候,会发现js并不真 ...
- CSS(二)- 选择器 - 一定要搞懂的选择器优先级和权重问题
css的优先级之前一直没怎么注意没当回事,总以为对同一元素靠后的渲染会覆盖前面的渲染,要是覆盖不了那就来个!important嘛.直到我那在学前端基础的后端伙伴拿一个问题问住了我,我才意识到这是重点中 ...