diff --git a/redis-component/src/main/java/com/gcc/container/components/redis/ApplicationConfiguration.java b/redis-component/src/main/java/com/gcc/container/components/redis/ApplicationConfiguration.java index c181c760d57a899a6ae9e013db0edbeaff30fdd7..98c4ba07c5d5139b40c18853ae47c36d9fc4a6cf 100644 --- a/redis-component/src/main/java/com/gcc/container/components/redis/ApplicationConfiguration.java +++ b/redis-component/src/main/java/com/gcc/container/components/redis/ApplicationConfiguration.java @@ -2,8 +2,9 @@ package com.gcc.container.components.redis; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; - +import org.springframework.cache.annotation.EnableCaching; @Configuration +@EnableCaching @ComponentScan(basePackages = "com.gcc.container.components.redis.*") public class ApplicationConfiguration { } diff --git a/task-component/README.md b/task-component/README.md index eb4143d9abd2493095ede6e62959695c16037a00..9f65f243c3a6f1ffbc3d5f69e0808f48eca13a25 100644 --- a/task-component/README.md +++ b/task-component/README.md @@ -2,7 +2,7 @@ > 相比于SpringBoot本身自带的定时任务注解的死板(程序一旦运行,无法更改定时任务执行周期、相关配置等问题)和xxl-Job的笨重,该组件从代码级提供了轻量级的定时任务管理解决方案 > -> 引入该组件,只需要实现定时任务基类,即可完成自动注册定时任务,通过定时任务接口来动态控制任务的状态,使用接口配合页面,可扩展到页面功能,做到随时启停、修改定时任务 +> 引入该组件,即可完成自动注册定时任务,通过定时任务接口来动态控制任务的状态,使用接口配合页面,可扩展到页面功能,做到随时启停、修改定时任务 ## 组件依赖说明 @@ -47,9 +47,11 @@ gcc-task: ### 新建定时任务 -引入依赖后,需要新增一个定时任务逻辑,则需要实现基类`AbstractBaseCronTask` ,并加入注解 `@TaskJob` +#### 类级定时任务 -**TaskJob的参数如下**: +引入依赖后,需要新增一个定时任务逻辑,则需要实现基类`AbstractBaseCronTask` ,并加入注解 `@ClassJob` + +**ClassJob的参数如下**: | 参数 | 说明 | 样例 | | ------ | --------------------------------------------- | -------------- | @@ -85,8 +87,6 @@ public class TaskMysqlOne extends AbstractBaseCronTask { 继承`AbstractBaseCronTask` 必须要实现携带TaskEntity参数的`构造函数`、`beforeJob()`、`startJob()` 、`afterJob()` 三个方法即可。**原则上这三个方法是规范定时任务的执行,实际使用,只需要把核心逻辑放在三个方法中任何一个即可**。 - - 因定时任务类是非SpringBean管理的类,所以**在自定义的定时任务类内无法使用任何Spring相关的注解(如@Autowired)**,**但是却可以通过自带的**`getServer(Class className)`**方法来获取任何Spring上下文中的Bean** 例如,你有一个UserService的接口及其Impl的实现类 @@ -117,6 +117,75 @@ public class TaskMysqlOne extends AbstractBaseCronTask { } ``` +#### 方法级别定时任务 + +如果不想使用繁琐的类任务,想像xxl-job一样通过注解指定某个方法为定时执行,则可以使用方法级别的定时任务定义,具体使用方式为`@MethodJob`注解对Bean上的方法进行标注,则可以进行定时任务注册: + +**MethodJob的参数如下**: + +| 参数 | 说明 | 样例 | +| ------ | -------------------------------- | -------------- | +| cron | 定时任务默认的执行周期,仅在首次初始化该任务使用(**必填**) | 10 0/2 * * * ? | +| desc | 任务描述,非必填 | 这是测试任务 | +| bootup | 是否开机自启动,**缺省值为 false** | false | + +*`cron` 属性仅在第一次新增该任务时提供一个默认的执行周期,必须填写,后续任务加载后,定时任务相关数据会被存放在文件或数据库中,此时则以文件或数据库中该任务的cron为主 + +```java +//正常定义的Service接口 +public interface AsyncTestService { + void taskJob(); + +} + +//Service接口实现类 +@Service +public class AsyncTestServiceImpl implements AsyncTestService { + + @MethodJob(cron = "11 0/1 * * * ?",desc = "这是个方法级任务") + @Override + public void taskJob() { + log.info("方法级别任务查询关键字为企业的数据"); + QueryWrapper query = new QueryWrapper<>(); + query.like("art_name","企业"); + List data = articleMapper.selectList(query); + log.info("查出条数为{}",data.size()); + } +} +``` + +**该注解仅支持SpringBoot中标注为`@Component`、`@Service`、`@Repository`的Bean** + + + +运行效果: + +```shell +[main] com.web.test.Application : Started Application in 3.567 seconds (JVM running for 4.561) +[main] .g.c.c.t.c.InitTaskSchedulingApplication : 【定时任务初始化】 容器初始化 +[main] o.s.s.c.ThreadPoolTaskScheduler : Initializing ExecutorService +[main] .g.c.c.t.c.InitTaskSchedulingApplication : 【定时任务初始化】定时任务初始化任务开始 +[main] c.g.c.c.task.compont.TaskScheduleImpl : 【定时任务初始化】装填任务:TaskTwoPerson [ 任务执行周期:15 0/2 * * * ? ] [ bootup:0] +[main] c.g.c.c.task.compont.TaskScheduleImpl : 【定时任务初始化】装填任务:TaskMysqlOne [ 任务执行周期:10 0/2 * * * ? ] [ bootup:1] +[main] c.g.c.c.task.compont.TaskScheduleImpl : 【定时任务初始化】装填任务:taskJob [ 任务执行周期:11 0/1 * * * ? ] [ bootup:0] +[main] .g.c.c.t.c.InitTaskSchedulingApplication : 【定时任务初始化】定时任务初始化任务完成 +[main] c.g.c.c.task.service.AfterAppStarted : 【定时任务自运行】运行开机自启动任务 +[main] TaskMysqlOne : ---------------------任务 TaskMysqlOne 开始执行----------------------- +[main] TaskMysqlOne : 任务描述:这是一个测试任务 +[main] TaskMysqlOne : 我是张三 +[main] TaskMysqlOne : 任务耗时:约 0.0 s +[main] TaskMysqlOne : ---------------------任务 TaskMysqlOne 结束执行----------------------- + +[task-thread-1] taskJob : ---------------------任务 taskJob 开始执行----------------------- +[task-thread-1] taskJob : 任务描述:这是个方法级任务 +[task-thread-1] c.w.t.service.impl.AsyncTestServiceImpl : 方法级别任务查询关键字为企业的数据 +[task-thread-1] c.w.t.service.impl.AsyncTestServiceImpl : 查出条数为1 +[task-thread-1] taskJob : 任务耗时:约 8.45 s +[task-thread-1] taskJob : ---------------------任务 taskJob 结束执行----------------------- + + +``` + ### 服务接口说明 定时任务相关的管理操作均封装在`TaskScheduleManagerService`接口中,可直接在外部项目中注入使用即可: @@ -146,7 +215,6 @@ public class TaskSchedulingController { public Response openTask(String taskId){ return Response.success(taskScheduleManagerService.openTask(taskId)); } - ``` `TaskScheduleManagerService`的接口定义如下: @@ -199,50 +267,73 @@ public interface TaskScheduleManagerService { @Data public class TaskEntity implements Serializable { + /** + * 任务ID(唯一) + */ private String taskId; - + + /** + * 任务名称 + */ private String taskName; - + + /** + * 任务描述 + */ private String taskDesc; + /** + * 遵循cron 表达式 + */ private String taskCron; - private String taskClass; - /** - * 任务相关配置 + * 类路径 */ - private String taskOutConfig; - + private String taskClass; + + /** + * 任务级别 CLASS_LEVEL 类级别,METHOD_LEVEL 方法级别 + */ + private TaskLevel taskLevel; + /** * 任务注册时间 */ private String taskCreateTime; - /** - * 是否启用 + * 是否启用,1启用,0不启用 */ private Integer taskIsUse; - + /** - * 是否开启自启动 + * 是否系统启动后立刻运行 1是。0否 */ private Integer taskBootUp; - + /** - * 上次运行状态 + * 上次运行状态 1:成功,0:失败 */ private Integer taskLastRun; - /** - * 任务是否在内存中 + * 任务是否在内存中 1:是,0:否 */ private Integer taskRamStatus; + + /** + * 外部配置 (扩展待使用字段) + */ + private String taskOutConfig; + + /** + * 加载配置 + */ + private String loadConfigure; ``` task-component会将内存中的TaskEntity进行持久化,下次再启动项目时,则直接使用持久化的TaskEntity来加载定时任务,task-component默认是以JSON文件进行持久化,实际使用中为了能带来更好的体验,建议扩展为数据库存储,扩展方式如下: -1、新建数据库表(以Mysql为例) +1、基于**TaskEntity类** 新建数据库表(以Mysql为例) ```SQL DROP TABLE IF EXISTS `tb_task_info`; @@ -252,59 +343,107 @@ CREATE TABLE `tb_task_info` ( `task_desc` text, `task_cron` varchar(20) DEFAULT NULL, `task_class` varchar(100) DEFAULT NULL COMMENT '定时任务类路径', + `task_level` varchar(50) DEFAULT NULL COMMENT '任务级别:类级别(CLASS_LEVEL)、方法级别(METHOD_LEVEL)', `task_is_use` tinyint DEFAULT NULL COMMENT '是否启用该任务,1:启用,0禁用', `task_boot_up` tinyint DEFAULT NULL COMMENT '是否为开机即运行,1:初始化即运行,0,初始化不运行', `task_out_config` text CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci COMMENT '定时任务额外配置项,采用json结构存放', `task_create_time` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '定时任务追加时间', `task_last_run` tinyint DEFAULT NULL COMMENT '任务上次执行状态;1正常,0执行失败,null未知', `task_ram_status` tinyint DEFAULT NULL COMMENT '任务当前状态;1内存运行中,0内存移除', - PRIMARY KEY (`task_id`) USING BTREE + `loadConfigure` text  COMMENT '加载相关配置', +PRIMARY KEY (`task_id`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 ROW_FORMAT=DYNAMIC; ``` 2、项目中实现`TaskRepository`接口,并使用@Primary注解: +TaskRepository接口如下: + ```java +public interface TaskRepository { + /** + * 新增数据,必须保证entity每个字段都插入 + * @param entity + * @return int + */ + int save(TaskEntity entity); + /** + * 根据TaskId删除整条数据 + * @param id id + * @return int + */ + int removeByTaskId(String id); + /** + * 更新整个任务(除taskId外,其他字段数据均根据实体更新) + * @param entity 实体 + * @return int + */ + int update(TaskEntity entity); + /** + * 查询全部任务 + * @return List + */ + List queryAllTask(); + + /** + * 查询数据:根据实体中的字段进行查询 + * @param entity 查询 + * @return TaskEntity + */ + List queryData(TaskEntity entity); + + /** + * 查询具体的任务实体 + * @param taskId 任务id + * @return TaskEntity + */ + TaskEntity queryData(String taskId); +} +``` + + + + + +3.基于Mybatis-Plus的实现Demo + +```java +@Mapper +public interface TaskDataMapper extends BaseMapper { +} + @Repository @Primary public class TaskRepositoryImpl implements TaskRepository { - @Autowired private TaskDataMapper taskDataMapper; - @Override public int save(TaskEntity entity) { entity.setTaskCreateTime(DateUtil.now()); return taskDataMapper.insert(entity); } - @Override public int update(TaskEntity entity) { UpdateWrapper update = new UpdateWrapper<>(); update.eq("task_id",entity.getTaskId()); return taskDataMapper.update(entity,update); } - - @Override public int removeByTaskId(String id) { QueryWrapper query = new QueryWrapper<>(); query.eq("task_id",id); return taskDataMapper.delete(query); } - @Override public List queryAllTask() { return taskDataMapper.selectList(new QueryWrapper<>()); } - @Override public TaskEntity queryData(String id) { QueryWrapper query = new QueryWrapper<>(); query.eq("task_id",id); return taskDataMapper.selectOne(query); } - @Override public List queryData(TaskEntity entity) { QueryWrapper query = new QueryWrapper<>(); @@ -323,11 +462,6 @@ public class TaskRepositoryImpl implements TaskRepository { return taskDataMapper.selectList(query); } } - ``` - - 只需要实现其中指定的几个持久化和查询方法即可替换component中的JSON文件持久化 - - diff --git a/task-component/src/main/java/com/gcc/container/components/task/annotation/TaskJob.java b/task-component/src/main/java/com/gcc/container/components/task/annotation/ClassJob.java similarity index 93% rename from task-component/src/main/java/com/gcc/container/components/task/annotation/TaskJob.java rename to task-component/src/main/java/com/gcc/container/components/task/annotation/ClassJob.java index 39a90f8c98affd1e0db025a2bf1fe713a52e2a40..cabefceba4f846d4b4a6460a0eba0145a23cdf99 100644 --- a/task-component/src/main/java/com/gcc/container/components/task/annotation/TaskJob.java +++ b/task-component/src/main/java/com/gcc/container/components/task/annotation/ClassJob.java @@ -8,7 +8,7 @@ import java.lang.annotation.Target; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) -public @interface TaskJob { +public @interface ClassJob { String desc() default "暂无描述"; diff --git a/task-component/src/main/java/com/gcc/container/components/task/annotation/MethodJob.java b/task-component/src/main/java/com/gcc/container/components/task/annotation/MethodJob.java new file mode 100644 index 0000000000000000000000000000000000000000..b04e463a05e857c3e171d2e2dacd8422090288dd --- /dev/null +++ b/task-component/src/main/java/com/gcc/container/components/task/annotation/MethodJob.java @@ -0,0 +1,21 @@ +package com.gcc.container.components.task.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 定时任务方法 + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface MethodJob { + + String desc() default "暂无描述"; + + String cron() default "00 00 00 * * ?"; + + boolean bootup() default false; + +} diff --git a/task-component/src/main/java/com/gcc/container/components/task/compont/AbstractBaseCronTask.java b/task-component/src/main/java/com/gcc/container/components/task/compont/AbstractBaseCronTask.java index 7264f39f76578f7a295def55e34939ec4b2d043b..06f6e3828a90e59d5beb4a12abc7d448fef2be33 100644 --- a/task-component/src/main/java/com/gcc/container/components/task/compont/AbstractBaseCronTask.java +++ b/task-component/src/main/java/com/gcc/container/components/task/compont/AbstractBaseCronTask.java @@ -66,6 +66,7 @@ public abstract class AbstractBaseCronTask implements Runnable{ taskEntity.setTaskLastRun(1); }catch (Exception e){ log.error("任务{}计算出错!请及时排查问题!", taskEntity.getTaskName(),e); + e.printStackTrace(); taskEntity.setTaskLastRun(0); } getServer(TaskRepository.class).update(taskEntity); diff --git a/task-component/src/main/java/com/gcc/container/components/task/compont/BaseMethodLevelTask.java b/task-component/src/main/java/com/gcc/container/components/task/compont/BaseMethodLevelTask.java new file mode 100644 index 0000000000000000000000000000000000000000..0d56a45232a0c988abba5097050ca31d765dcf2c --- /dev/null +++ b/task-component/src/main/java/com/gcc/container/components/task/compont/BaseMethodLevelTask.java @@ -0,0 +1,52 @@ +package com.gcc.container.components.task.compont; + +import com.gcc.container.components.task.model.entity.TaskEntity; + +import java.lang.reflect.Method; + +/** + * 方法级别定时任务基类 + * @author GCC + */ +class BaseMethodLevelTask extends AbstractBaseCronTask{ + + + private Class beanPath; + + private String methodName; + + + private Method method = null; + + + public BaseMethodLevelTask(TaskEntity taskEntity, Class className, String methodName) { + super(taskEntity); + this.beanPath = className; + this.methodName = methodName; + } + + @Override + public void beforeJob() { + try { + method = Class.forName(beanPath.getName()).getMethod(methodName); + }catch (Exception e){ + e.printStackTrace(); + } + } + + @Override + public void startJob(){ + if(null != method){ + try { + method.invoke(getServer(beanPath)); + }catch (Exception e){ + e.printStackTrace(); + } + } + } + + @Override + public void afterJob() { + + } +} diff --git a/task-component/src/main/java/com/gcc/container/components/task/compont/InitTaskSchedulingApplication.java b/task-component/src/main/java/com/gcc/container/components/task/compont/InitTaskSchedulingApplication.java index 6085c3ad9cf69822452d2ff48b44d3387af88d1c..f9199ef98cf6c6071c934ac62a9713a90bf6bc6f 100644 --- a/task-component/src/main/java/com/gcc/container/components/task/compont/InitTaskSchedulingApplication.java +++ b/task-component/src/main/java/com/gcc/container/components/task/compont/InitTaskSchedulingApplication.java @@ -20,7 +20,7 @@ public class InitTaskSchedulingApplication implements ApplicationRunner { private TaskSchedule taskSchedule; - private TaskTempFactory taskTempFactory; + private TaskFactory taskFactory; @Autowired @@ -29,14 +29,14 @@ public class InitTaskSchedulingApplication implements ApplicationRunner { } @Autowired - public void setTaskTempFactory(TaskTempFactory taskTempFactory) { - this.taskTempFactory = taskTempFactory; + public void setTaskTempFactory(TaskFactory taskFactory) { + this.taskFactory = taskFactory; } @Override public void run(ApplicationArguments args) throws Exception { //开启扫描 - taskTempFactory.scanTaskInfo(); + taskFactory.scanTaskInfo(); log.info("【定时任务初始化】 容器初始化"); taskSchedule.initScheduling(); log.info("【定时任务初始化】定时任务初始化任务开始"); diff --git a/task-component/src/main/java/com/gcc/container/components/task/compont/TaskTempFactory.java b/task-component/src/main/java/com/gcc/container/components/task/compont/TaskFactory.java similarity index 31% rename from task-component/src/main/java/com/gcc/container/components/task/compont/TaskTempFactory.java rename to task-component/src/main/java/com/gcc/container/components/task/compont/TaskFactory.java index 1f0eca59f23bde5d74ff036a0789c0d178033d6f..1cabd5ec3b46d5ffb6776dd5f0838b5121323997 100644 --- a/task-component/src/main/java/com/gcc/container/components/task/compont/TaskTempFactory.java +++ b/task-component/src/main/java/com/gcc/container/components/task/compont/TaskFactory.java @@ -4,90 +4,186 @@ package com.gcc.container.components.task.compont; import cn.hutool.core.date.DateUtil; import cn.hutool.crypto.digest.DigestUtil; -import com.gcc.container.components.task.annotation.TaskJob; +import cn.hutool.json.JSONObject; +import com.gcc.container.components.task.annotation.MethodJob; +import com.gcc.container.components.task.annotation.ClassJob; import com.gcc.container.components.task.dao.TaskRepository; import com.gcc.container.components.task.model.entity.TaskEntity; +import com.gcc.container.components.task.model.enums.TaskLevel; import com.gcc.container.components.task.property.TaskDataProperties; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; import org.springframework.core.type.filter.AnnotationTypeFilter; import org.springframework.stereotype.Component; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Set; +import org.springframework.stereotype.Repository; +import org.springframework.stereotype.Service; +import java.lang.reflect.Method; +import java.util.*; import java.util.stream.Collectors; @Component -class TaskTempFactory { +class TaskFactory { + + private ApplicationContext context; + - @Autowired private TaskRepository taskRepository; - @Autowired - TaskDataProperties taskDataProperties; + + private TaskDataProperties taskDataProperties; + + + public final String CLASS_PATH = "com.gcc.container.components.task.compont.BaseMethodLevelTask"; + private List tempContainer = new ArrayList<>(); + @Autowired + public void setContext(ApplicationContext context) { + this.context = context; + } + + @Autowired + public void setTaskRepository(TaskRepository taskRepository) { + this.taskRepository = taskRepository; + } + + @Autowired + public void setTaskDataProperties(TaskDataProperties taskDataProperties) { + this.taskDataProperties = taskDataProperties; + } + + public void scanTaskInfo() throws Exception { + //扫描类级任务 ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false); - provider.addIncludeFilter(new AnnotationTypeFilter(TaskJob.class)); + provider.addIncludeFilter(new AnnotationTypeFilter(ClassJob.class)); Set beanDefinitions = provider.findCandidateComponents(taskDataProperties.getTaskClassPackage()); for (BeanDefinition beanDefinition : beanDefinitions) { Class clazz = Class.forName(beanDefinition.getBeanClassName()); - TaskJob taskJob = clazz.getAnnotation(TaskJob.class); + ClassJob classJob = clazz.getAnnotation(ClassJob.class); if (AbstractBaseCronTask.class.isAssignableFrom(clazz)) { - tempContainer.add(builderTaskEntity(clazz,taskJob)); + tempContainer.add(buildTaskEntity(clazz, classJob)); } else { throw new IllegalArgumentException("Class " + clazz.getName() + " must extend AbstractBaseCronTask"); } } + //扫描方法级任务 + //仅支持在 Component、Service、Repository 注解上的定时任务 + Map allBeans = context.getBeansWithAnnotation(Component.class); + allBeans.putAll(context.getBeansWithAnnotation(Service.class)); + allBeans.putAll(context.getBeansWithAnnotation(Repository.class)); + for (Object bean : allBeans.values()) { + Class beanClass = bean.getClass(); + for (Method method : beanClass.getDeclaredMethods()) { + if (method.isAnnotationPresent(MethodJob.class)) { + MethodJob annotation = method.getAnnotation(MethodJob.class); + String className = beanClass.getName(); + String methodName = method.getName(); + tempContainer.add(buildMethodTaskEntity(className,methodName,annotation)); + } + } + } } public List mergeTaskEntities() { - List db = getIsUseTask(); - Map map = db.stream() + Map> db = getIsUseTask(); + + List classLevelJob = db.get("ClassLevel"); + + List methodLevelJob =db.get("MethodLevel"); + + Map ClassLevelMap = classLevelJob.stream() .collect(Collectors.toMap( TaskEntity::getTaskClass, task -> task )); + Map methodLevelMap = methodLevelJob.stream() + .collect(Collectors.toMap( + TaskEntity::getTaskId, + task -> task + )); + for(TaskEntity entity:tempContainer){ - if(!map.containsKey(entity.getTaskClass())){ - taskRepository.save(entity); - db.add(entity); + //类级别任务 + if(TaskLevel.CLASS_LEVEL == entity.getTaskLevel()){ + if(!ClassLevelMap.containsKey(entity.getTaskClass())){ + taskRepository.save(entity); + classLevelJob.add(entity); + } + } + //方法级别任务 + if(TaskLevel.METHOD_LEVEL == entity.getTaskLevel()){ + if(!methodLevelMap.containsKey(entity.getTaskId())){ + taskRepository.save(entity); + methodLevelJob.add(entity); + } } } - return db; + //合并两处任务 + classLevelJob.addAll(methodLevelJob); + return classLevelJob; } - private TaskEntity builderTaskEntity(Class cronTaskClass, TaskJob taskJob) { - String path = cronTaskClass.getName(); TaskEntity entity = new TaskEntity(); + private TaskEntity buildTaskEntity(Class cronTaskClass, ClassJob classJob) { + String path = cronTaskClass.getName(); + TaskEntity entity = new TaskEntity(); entity.setTaskId(DigestUtil.md5Hex(path)); entity.setTaskClass(path); entity.setTaskIsUse(1); entity.setTaskName(cronTaskClass.getSimpleName()); - entity.setTaskDesc(taskJob.desc()); - entity.setTaskBootUp(converBootUp(taskJob.bootup())); - entity.setTaskCron(taskJob.cron()); + entity.setTaskDesc(classJob.desc()); + entity.setTaskBootUp(converBootUp(classJob.bootup())); + entity.setTaskCron(classJob.cron()); + entity.setTaskCreateTime(DateUtil.now()); + entity.setTaskLevel(TaskLevel.CLASS_LEVEL); + return entity; + } + + private TaskEntity buildMethodTaskEntity(String beanClass,String method, MethodJob methodJob) { + String key = beanClass+"."+method+"()"; + TaskEntity entity = new TaskEntity(); + entity.setTaskId(DigestUtil.md5Hex(key)); + entity.setTaskClass(CLASS_PATH); + entity.setTaskIsUse(1); + entity.setTaskName(method); + entity.setTaskDesc(methodJob.desc()); + entity.setTaskBootUp(converBootUp(methodJob.bootup())); + entity.setTaskCron(methodJob.cron()); entity.setTaskCreateTime(DateUtil.now()); - //todo 外部配置 + entity.setTaskLevel(TaskLevel.METHOD_LEVEL); + entity.setLoadConfigure(new JSONObject() + .set("beanClass",beanClass) + .set("methodName",method).toJSONString(0)); return entity; } - private List getIsUseTask(){ + private Map> getIsUseTask(){ + Map> resultMap = new HashMap<>(); List list = taskRepository.queryAllTask(); - List result = new ArrayList<>(); + List methodLevel = new ArrayList<>(); + List classLevel = new ArrayList<>(); for(TaskEntity entity:list){ if(entity.isUse()){ - result.add(entity); + + if(TaskLevel.CLASS_LEVEL == entity.getTaskLevel()){ + classLevel.add(entity); + } + + if(TaskLevel.METHOD_LEVEL == entity.getTaskLevel()){ + methodLevel.add(entity); + } } } - return result; + resultMap.put("ClassLevel",classLevel); + resultMap.put("MethodLevel",methodLevel); + return resultMap; } private Integer converBootUp(Boolean flag){ diff --git a/task-component/src/main/java/com/gcc/container/components/task/compont/TaskScheduleImpl.java b/task-component/src/main/java/com/gcc/container/components/task/compont/TaskScheduleImpl.java index d9f58a75f65f8451169f782308d56d07deed942d..12725008094522ec7dbf97f218035308f8795cdb 100644 --- a/task-component/src/main/java/com/gcc/container/components/task/compont/TaskScheduleImpl.java +++ b/task-component/src/main/java/com/gcc/container/components/task/compont/TaskScheduleImpl.java @@ -1,9 +1,9 @@ package com.gcc.container.components.task.compont; -import cn.hutool.core.util.NumberUtil; import com.gcc.container.components.task.dao.TaskRepository; import com.gcc.container.components.task.model.entity.TaskEntity; +import com.gcc.container.components.task.model.enums.TaskLevel; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; @@ -37,7 +37,7 @@ public class TaskScheduleImpl implements TaskSchedule { private TaskRepository taskRepository; - private TaskTempFactory taskFactory; + private TaskFactory taskFactory; @Autowired @@ -51,7 +51,7 @@ public class TaskScheduleImpl implements TaskSchedule { } @Autowired - public void setTaskFactory(TaskTempFactory taskFactory) { + public void setTaskFactory(TaskFactory taskFactory) { this.taskFactory = taskFactory; } @@ -79,8 +79,19 @@ public class TaskScheduleImpl implements TaskSchedule { if(!runningTasks.containsKey(task.getTaskId())){ try{ Class clazz = Class.forName(task.getTaskClass()); - Constructor c = clazz.getConstructor(TaskEntity.class); - AbstractBaseCronTask runnable = (AbstractBaseCronTask) c.newInstance(task); + AbstractBaseCronTask runnable = null; + //类级别任务 + if(TaskLevel.CLASS_LEVEL == task.getTaskLevel()) { + Constructor c = clazz.getConstructor(TaskEntity.class); + runnable = (AbstractBaseCronTask) c.newInstance(task); + } + //方法级别任务 + if(TaskLevel.METHOD_LEVEL == task.getTaskLevel()){ + String className = task.getLoadConfiguration().getStr("beanClass"); + String methodName = task.getLoadConfiguration().getStr("methodName"); + Constructor c = clazz.getConstructor(TaskEntity.class,Class.class,String.class); + runnable = (AbstractBaseCronTask) c.newInstance(task, context.getBean(Class.forName(className)).getClass(),methodName); + } //反射方式生成对象不属于Spring容器管控,对于Spring的bean使用需要手动注入 runnable.setApplicationContext(context); CronTrigger cron = new CronTrigger(task.getTaskCron()); @@ -92,7 +103,8 @@ public class TaskScheduleImpl implements TaskSchedule { taskRepository.update(task); return true; }catch (Exception e){ - log.error("定时任务加载失败..."+e); + log.error("定时任务{}加载失败..."+e,task.getTaskName()); + taskRepository.removeByTaskId(task.getTaskId()); } } return false; diff --git a/task-component/src/main/java/com/gcc/container/components/task/dao/impl/FileOpTaskRepositoryImpl.java b/task-component/src/main/java/com/gcc/container/components/task/dao/impl/FileOpTaskRepositoryImpl.java index 50fefe69b605be1a9b0f311beab9bf5d889c7b16..198aba155a0134a0e46ba0927983ae4b6253132c 100644 --- a/task-component/src/main/java/com/gcc/container/components/task/dao/impl/FileOpTaskRepositoryImpl.java +++ b/task-component/src/main/java/com/gcc/container/components/task/dao/impl/FileOpTaskRepositoryImpl.java @@ -117,7 +117,7 @@ public class FileOpTaskRepositoryImpl implements TaskRepository { // 确保文件存在 File file = FileUtil.file(getDefaultPath()); if (!file.exists()) { - log.error("FILE is not exists"); + log.warn("FILE is not exists"); return new ArrayList<>(); } // 读取 JSON 文件内容 diff --git a/task-component/src/main/java/com/gcc/container/components/task/model/entity/TaskEntity.java b/task-component/src/main/java/com/gcc/container/components/task/model/entity/TaskEntity.java index cfb9bcc78015fd744d8dd208fe577e46995c25b1..6b34ee277e0a012bd84f7054de6060489336972f 100644 --- a/task-component/src/main/java/com/gcc/container/components/task/model/entity/TaskEntity.java +++ b/task-component/src/main/java/com/gcc/container/components/task/model/entity/TaskEntity.java @@ -1,6 +1,9 @@ package com.gcc.container.components.task.model.entity; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.gcc.container.components.task.model.enums.TaskLevel; import lombok.Data; import java.io.Serializable; import java.util.HashMap; @@ -13,8 +16,6 @@ import java.util.Map; @Data public class TaskEntity implements Serializable { - //todo 解决外部创建表的操作 - private String taskId; private String taskName; @@ -26,9 +27,9 @@ public class TaskEntity implements Serializable { private String taskClass; /** - * 任务相关配置 + * 任务级别 */ - private String taskOutConfig; + private TaskLevel taskLevel; /** * 任务注册时间 @@ -56,6 +57,18 @@ public class TaskEntity implements Serializable { private Integer taskRamStatus; + /** + * 外部配置 + */ + private String taskOutConfig; + + + /** + * 加载配置 + */ + private String loadConfigure; + + public TaskEntity taskIsUse(Boolean isUse) { if(isUse) { this.taskIsUse = 1; @@ -84,6 +97,10 @@ public class TaskEntity implements Serializable { return this; } + public TaskEntity level(TaskLevel level){ + this.taskLevel = level; + return this; + } public Boolean isUse(){ @@ -112,4 +129,8 @@ public class TaskEntity implements Serializable { } + public JSONObject getLoadConfiguration(){ + return JSONUtil.parseObj(this.loadConfigure); + } + } diff --git a/task-component/src/main/java/com/gcc/container/components/task/model/enums/TaskLevel.java b/task-component/src/main/java/com/gcc/container/components/task/model/enums/TaskLevel.java new file mode 100644 index 0000000000000000000000000000000000000000..d344c81d9ddc1d87b99ff62feb8c1d3c4e676f2e --- /dev/null +++ b/task-component/src/main/java/com/gcc/container/components/task/model/enums/TaskLevel.java @@ -0,0 +1,19 @@ +package com.gcc.container.components.task.model.enums; + +/** + * 任务级别枚举 + * @author GCC + */ +public enum TaskLevel { + + /** + * 类级别 + */ + CLASS_LEVEL, + + /** + * 方法级别 + */ + METHOD_LEVEL; + +}