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