
需要实现springboot定时调用项目类和第三方接口,并且可以通过页面进行动态配置。这里只展示后台部分,因为前台都是表单,不做展示,核心功能都在后台。代码中存在相关的冗余参数,主要是为相关业务的拓展保留参数。
二、后台功能代码展示原理:创建map集合,将在spring容器实例化的定时器记录下来。每次新增定时器,一方面存数据库(没做,自行实现),另一方面存在集合中。删除同理。
1. 反射实现方法工具类package serve.peam.schedule.reflect;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class SpringContextUtils implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringContextUtils.applicationContext = applicationContext;
}
public static Object getBean(String name) {
return applicationContext.getBean(name);
}
public static T getBean(Class requiredType) {
return applicationContext.getBean(requiredType);
}
public static T getBean(String name, Class requiredType) {
return applicationContext.getBean(name, requiredType);
}
public static boolean containsBean(String name) {
return applicationContext.containsBean(name);
}
public static boolean isSingleton(String name) {
return applicationContext.isSingleton(name);
}
public static Class extends Object> getType(String name) {
return applicationContext.getType(name);
}
}
package serve.peam.schedule.reflect;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ReflectionUtils;
import java.lang.reflect.Method;
import java.util.Date;
public class ScheduleClassReflect implements Runnable {
private static final Logger logger = LoggerFactory.getLogger(ScheduleClassReflect.class);
private String beanName;
private String methodName;
private Long taskId;
private String params;
public ScheduleClassReflect(String beanName, String methodName, Long taskId) {
this(beanName, methodName, taskId, null);
}
public ScheduleClassReflect(String beanName, String methodName, Long taskId, String params) {
this.beanName = beanName;
this.methodName = methodName;
this.taskId = taskId;
this.params = params;
}
@Override
public void run() {
logger.info("定时任务开始执行 - bean:{},方法:{},参数:{}", beanName, methodName, params);
long startTime = System.currentTimeMillis();
try {
//从spring容器获取实例
Object target = SpringContextUtils.getBean(beanName);
Method method = 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 (Exception ex) {
logger.error(String.format("定时任务执行异常 - bean:%s,方法:%s,参数:%s ", beanName, methodName, params), ex);
}
long times = System.currentTimeMillis() - startTime;
logger.info("定时任务执行结束 - bean:{},方法:{},参数:{},耗时:{} 毫秒", beanName, methodName, params, times);
}
}
2. 远程调用接口工具类
package serve.peam.schedule.request;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Date;
public class ScheduleRequest implements Runnable {
private static final Logger logger = LoggerFactory.getLogger(ScheduleRequest.class);
private String requestInterface;
String requestType;
private Long taskId;
private String params;
public ScheduleRequest(String requestInterface, String requestType, Long taskId) {
this(requestInterface, requestType, taskId, null);
}
public ScheduleRequest(String requestInterface, String requestType, Long taskId, String params) {
this.requestInterface = requestInterface;
this.requestType = requestType;
this.params = params;
this.taskId = taskId;
}
@Override
public void run() {
logger.info("定时任务开始执行 - 接口:{},接口类型:{},参数:{}", requestInterface, requestType, params);
long startTime = System.currentTimeMillis();
try {
//远程调用接口逻辑
ScheduleHttpRequest.request(requestInterface, requestType, requestType);
} catch (Exception ex) {
logger.error(String.format("定时任务执行异常 - 接口:%s,接口类型:%s,参数:%s ", requestInterface, requestType, params), ex);
}
long times = System.currentTimeMillis() - startTime;
logger.info("定时任务执行结束 - 接口:{},接口类型:{},参数:{},耗时:{} 毫秒", requestInterface, requestType, params, times);
}
}
package serve.peam.schedule.request;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
public class ScheduleHttpRequest {
public static Boolean request(String httpUrl, String requestType, String param) {
boolean isSuccess = false;
HttpURLConnection connection = null;
InputStream is = null;
OutputStream os = null;
BufferedReader br = null;
String result = null;
try {
URL url = new URL(httpUrl);
// 通过远程url连接对象打开连接
connection = (HttpURLConnection) url.openConnection();
// 设置连接请求方式
connection.setRequestMethod(requestType);
// 设置连接主机服务器超时时间:15000毫秒
connection.setConnectTimeout(15000);
// 设置读取主机服务器返回数据超时时间:60000毫秒
connection.setReadTimeout(60000);
// 默认值为:false,当向远程服务器传送数据/写数据时,需要设置为true
connection.setDoOutput(true);
// 默认值为:true,当前向远程服务读取数据时,设置为true,该参数可有可无
connection.setDoInput(true);
connection.setRequestProperty("Content-Type", "application/x-www-formurlencoded");
connection.setRequestProperty("Accept", "application/json;charset=UTF-8");
// 通过连接对象获取一个输出流
os = connection.getOutputStream();
// 通过输出流对象将参数写出去/传输出去,它是通过字节数组写出的
os.write(param.getBytes());
// 通过连接对象获取一个输入流,向远程读取
int code = connection.getResponseCode();
if (connection.getResponseCode() == 200) {
is = connection.getInputStream();
// 对输入流对象进行包装:charset根据工作项目组的要求来设置
br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
StringBuffer sbf = new StringBuffer();
String temp = null;
// 循环遍历一行一行读取数据
while ((temp = br.readLine()) != null) {
sbf.append(temp);
sbf.append("rn");
}
result = sbf.toString();
}
isSuccess = true;
} catch (MalformedURLException e) {
isSuccess = false;
e.printStackTrace();
} catch (IOException e) {
isSuccess = false;
e.printStackTrace();
} finally {
// 关闭资源
if (null != br) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (null != os) {
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (null != is) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
// 断开与远程地址url的连接
connection.disconnect();
}
return isSuccess;
}
}
3. 定时器线程池配置
package serve.peam.schedule;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
public class SchedulingThreadPoolConfig {
@Bean
public TaskScheduler taskScheduler() {
ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler(); // 定时任务执行线程池核心线程数
taskScheduler.setPoolSize(10);
taskScheduler.setRemoveOnCancelPolicy(true);
taskScheduler.setThreadNamePrefix("TaskSchedulerThreadPool-");
return taskScheduler;
}
}
4. 定时器实体类
package serve.peam.schedule;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
@Data
public class ScheduleTask implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
private String taskName;
private String taskStatus;
private String taskType;
private String cronExpression;
private String cronInterface;
private String cronInterfaceType;
private String cronServiceName;
private String cronServiceMethod;
}
5. 定时实际工作类
package serve.peam.schedule;
import java.util.concurrent.ScheduledFuture;
public class ScheduledRealTask {
volatile ScheduledFuture> future;
public void cancel() {
ScheduledFuture> future = this.future;
if (future != null) {
future.cancel(true);
}
}
}
6. 定时器api类
package serve.peam.schedule;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.config.CronTask;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Component
public class ScheduleApi implements DisposableBean {
//线程、定时器任务
private Map> taskMaps = new ConcurrentHashMap<>(16);
//定时器实现类
@Autowired
private TaskScheduler taskScheduler;
public TaskScheduler getScheduler() {
return this.taskScheduler;
}
public void addCronTask(Long taskId, Runnable task, String cronExpression) {
//先移除
if (this.taskMaps.containsKey(taskId)) {
removeCronTask(taskId);
}
//创建定时器
CronTask cronTask = new CronTask(task, cronExpression);
//启动定时器
ScheduledRealTask scheduledRealTask = new ScheduledRealTask();
scheduledRealTask.future = this.taskScheduler.schedule(cronTask.getRunnable(), cronTask.getTrigger());
//构造任务map
Map map = new HashMap();
map.put("runnable", task);
map.put("scheduledRealTask", scheduledRealTask);
//添加定时器
this.taskMaps.put(taskId, map);
}
public void removeCronTask(Long taskId) {
//移除定时器
Map map = this.taskMaps.remove(taskId);
if (map != null) {
ScheduledRealTask scheduledRealTask = (ScheduledRealTask) map.get("scheduledRealTask");
//关闭定时器
if (scheduledRealTask != null) {
scheduledRealTask.cancel();
}
}
}
@Override
public void destroy() {
for (Map map : this.taskMaps.values()) {
ScheduledRealTask scheduledRealTask = (ScheduledRealTask) map.get("scheduledRealTask");
if (scheduledRealTask != null) {
scheduledRealTask.cancel();
}
}
this.taskMaps.clear();
}
}
7. 定时器容器初始化启动类
package serve.peam.schedule;
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import serve.peam.schedule.reflect.ScheduleClassReflect;
import serve.peam.schedule.request.ScheduleRequest;
import java.util.ArrayList;
import java.util.List;
@Component
public class ScheduleInit implements CommandLineRunner {
private static final Logger logger = LoggerFactory.getLogger(ScheduleInit.class);
@Autowired
private ScheduleApi scheduleApi;
@Override
public void run(String... args) {
// 初始加载数据库里状态为正常的定时任务,这里暂时取空,具体根据业务获取
List ScheduleTaskList = new ArrayList<>();
//遍历
for (ScheduleTask scheduleTask : ScheduleTaskList) {
//启动状态的才注入spring
if (scheduleTask.getTaskStatus().equals("开启")) {
if (scheduleTask.getTaskType().equals("类调用")) {
//类调用
ScheduleClassReflect task = new ScheduleClassReflect(scheduleTask.getCronServiceName(), scheduleTask.getCronServiceMethod(), scheduleTask.getId());
scheduleApi.addCronTask(scheduleTask.getId(), task, scheduleTask.getCronExpression());
} else if (scheduleTask.getTaskType().equals("接口调用")) {
//接口调用
ScheduleRequest task = new ScheduleRequest(scheduleTask.getCronInterface(), scheduleTask.getCronInterfaceType(), scheduleTask.getId());
scheduleApi.addCronTask(scheduleTask.getId(), task, scheduleTask.getCronExpression());
}
}
}
logger.info("定时任务已加载完毕...");
}
}
8. 容器demo调用类
package serve.peam.schedule;
import org.springframework.beans.factory.annotation.Autowired;
import serve.peam.schedule.reflect.ScheduleClassReflect;
import serve.peam.schedule.request.ScheduleRequest;
public class Demo {
@Autowired
private ScheduleApi scheduleApi;
public void demo() {
//获取ScheduleTask
ScheduleTask scheduleTask = new ScheduleTask();
//新增类定时调用
ScheduleClassReflect task = new ScheduleClassReflect(scheduleTask.getCronServiceName(), scheduleTask.getCronServiceMethod(), scheduleTask.getId());
scheduleApi.addCronTask(scheduleTask.getId(), task, scheduleTask.getCronExpression());
//新增接口定时调用
ScheduleRequest task1 = new ScheduleRequest(scheduleTask.getCronInterface(), scheduleTask.getCronInterfaceType(), scheduleTask.getId());
scheduleApi.addCronTask(scheduleTask.getId(), task1, scheduleTask.getCronExpression());
//移除定时调用
scheduleApi.removeCronTask(scheduleTask.getId());
}
}
三、前台cron组件功能代码展示
前端功能基本是表单啥的,这里不做演示。值得一提的是vue有提供cron表达式插件:czr-vue-cron