- 一、背景
- 二、后台功能代码展示
- 1. 反射实现方法工具类
- 2. 远程调用接口工具类
- 3. 定时器线程池配置
- 4. 定时器实体类
- 5. 定时实际工作类
- 6. 定时器api类
- 7. 定时器容器初始化启动类
- 8. 容器demo调用类
- 三、前台cron组件功能代码展示
需要实现springboot定时调用项目类和第三方接口,并且可以通过页面进行动态配置。这里只展示后台部分,因为前台都是表单,不做展示,核心功能都在后台。代码中存在相关的冗余参数,主要是为相关业务的拓展保留参数。
二、后台功能代码展示原理:创建map集合,将在spring容器实例化的定时器记录下来。每次新增定时器,一方面存数据库(没做,自行实现),另一方面存在集合中。删除同理。
1. 反射实现方法工具类package serve.peam.schedule.reflect;
/**
* spring容器的相关方法,如根据名称获取spring实例
*
* @author 天真热
* @create 2022-04-28 9:55
* @desc
**/
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> T getBean(Class<T> requiredType) {
return applicationContext.getBean(requiredType);
}
public static <T> T getBean(String name, Class<T> 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;
/**
* 线程类,主要是实现反射方法
*
* @author 天真热
* @create 2022-04-28 9:52
* @desc
**/
public class ScheduleClassReflect implements Runnable {
private static final Logger logger = LoggerFactory.getLogger(ScheduleClassReflect.class);
/**
* 实例化到spring容器的实例名,一般是类名首字母小写
*/
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;
/**
* 线程类,主要是实现反射方法
*
* @author 天真热
* @create 2022-04-28 9:52
* @desc
**/
public class ScheduleRequest implements Runnable {
private static final Logger logger = LoggerFactory.getLogger(ScheduleRequest.class);
/**
* 接口
*/
private String requestInterface;
/**
* 接口类型,POST/GET
*/
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;
/**
* 请求接口
*
* @author 天真热
* @create 2022-04-28 10:11
* @desc
*/
public class ScheduleHttpRequest {
/**
* 请求接口
*
* @param httpUrl 地址
* @param requestType 请求方式
* @param param 参数
* @return
*/
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("\r\n");
}
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;
/**
* 开启线程池
*
* @author 天真热
* @create 2022-04-28 10:11
* @desc
**/
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;
/**
* 定时器管理对象 schedule_task
*
* @author 天真热
* @create 2022-04-28 10:11
* @desc
*/
@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;
/**
* 接口类型,GET/POST
*/
private String cronInterfaceType;
/**
* service类
*/
private String cronServiceName;
/**
* service方法
*/
private String cronServiceMethod;
}
5. 定时实际工作类
package serve.peam.schedule;
import java.util.concurrent.ScheduledFuture;
/**
* 定时器实际工作类
*
* @author 天真热
* @create 2022-04-28 9:43
* @desc
**/
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;
/**
* 定时器新增、删除Api
*
* @author 天真热
* @create 2022-04-28 10:11
* @desc
*/
@Component
public class ScheduleApi implements DisposableBean {
//线程、定时器任务
private Map<Long, Map<String, Object>> taskMaps = new ConcurrentHashMap<>(16);
//定时器实现类
@Autowired
private TaskScheduler taskScheduler;
public TaskScheduler getScheduler() {
return this.taskScheduler;
}
/**
* 添加并启动定时器
*
* @param task
* @param cronExpression
*/
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<String, Object> map = new HashMap();
map.put("runnable", task);
map.put("scheduledRealTask", scheduledRealTask);
//添加定时器
this.taskMaps.put(taskId, map);
}
/**
* 移除并关闭定时器
*
* @param taskId
*/
public void removeCronTask(Long taskId) {
//移除定时器
Map<String, Object> 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<String, Object> 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;
/**
* 项目启动加载定时器任务
*
* @author 天真热
* @create 2022-04-28 9:39
* @desc
**/
@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<ScheduleTask> 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;
/**
* @author 天真热
* @create 2022-04-30 12:43
* @desc
**/
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
- 下载:npm install czr-vue-cron
- vue组件
<template>
<div id="app">
<input v-model="cronData"/>
<CzrVueCron :cron.sync="cronData" :recent="[5, 5]"/>
</div>
</template>
<script>
import CzrVueCron from 'czr-vue-cron'
export default {
components: {
CzrVueCron
},
data() {
return {
cronData: ''
}
}
}
</script>
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)