SpringBoot中@Async不使用AOP的玩具意义的实现方式 下
书接上文 继续编码
在之前我们已经完成处理任务的处理器 以及任务的发现步骤 现在是时候让他们真正的发挥作用了
定义上下文
为了更好的利用之前保存的信息 我们需要定义不同的上下文分级 让他们分工更明确 和之前展示的流程图一样 我们需要两层上下文 一层是处理器上下文 一层则是处理其他模块初始化工作的上下文
这样在之后拓展的时候 我们也可以更好的进行拓展
处理器的builder
为了方便用户自定义 以及之后的拓展传递的参数 我们需要利用builder来将这些需要自定义或者拓展的参数和真正的上下文隔离开 防止过多的构造函数 以及方便的设置默认值 提高代码可读性
依然是从下到上的给出这些构建器
package core.builder;
import core.handler.CommonHandler;
import constants.type.CoreSizeConstants;
/**
* @description: 普通任务处理器构建器
* @date: 2024/5/19 16:48
* @version: 1.0
*/
public class CommonHandlerBuilder {
//使用启动钩子保证和容器化管理保证只会出现一个处理器
private int coreSize=CoreSizeConstants.CORE_TYPE_MIXED;
public CommonHandlerBuilder coreSize(int size){
this.coreSize=size;
return this;
}
public CommonHandler build(){
return new CommonHandler(coreSize);
}
}
package core.builder;
import core.handler.ScheduledHandler;
import constants.type.CoreSizeConstants;
/**
* @description: 定时任务处理器构建器
* @date: 2024/5/19 16:58
* @version: 1.0
*/
public class ScheduledHandlerBuilder {
private int coreSize=CoreSizeConstants.CORE_TYPE_MIXED;
public ScheduledHandlerBuilder coreSize(int size)
{
this.coreSize = size;
return this;
}
public ScheduledHandler build()
{
return new ScheduledHandler(coreSize);
}
}
这两个目前只有coreSize 也就是线程核心数量这一个属性 之后拓展直接按照这些格式进行拓展即可
接下来 我们可以编写它们上一层的处理器上下文构建器了
package core.builder;
import core.context.HandlerContext;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
/**
* @description: 处理器上下文构建器
* @date: 2024/6/7 10:48
* @version: 1.0
*/
public class HandlerContextBuilder {
private CommonHandlerBuilder commonHandlerBuilder;
private ScheduledHandlerBuilder scheduledHandlerBuilder;
//用于设置用户自定义的存放异步任务结果的map
private Map<String, CompletableFuture<?>> futureMap;
public HandlerContextBuilder setCommonHandlerBuilder(CommonHandlerBuilder handlerBuilder){
this.commonHandlerBuilder=handlerBuilder;
return this;
}
public HandlerContextBuilder setScheduledHandlerBuilder(ScheduledHandlerBuilder handlerBuilder){
this.scheduledHandlerBuilder=handlerBuilder;
return this;
}
public HandlerContextBuilder setFutureMap(Map<String, CompletableFuture<?>> futureMap){
this.futureMap=futureMap;
return this;
}
public HandlerContext build(){
if(commonHandlerBuilder==null){
commonHandlerBuilder=new CommonHandlerBuilder();
}
if(scheduledHandlerBuilder==null){
scheduledHandlerBuilder=new ScheduledHandlerBuilder();
}
if(futureMap==null){
futureMap=new ConcurrentHashMap<>();
}
return new HandlerContext(commonHandlerBuilder.build(),scheduledHandlerBuilder.build(),futureMap);
}
}
定义处理器上下文和builder
处理器上下文一定是需要保存两个处理器 以及辅助它们进行工作的一些东西 具体定义如下
package core.context;
import common.Pair;
import core.handler.CommonHandler;
import core.handler.ScheduledHandler;
import exception.handle.HandleException;
import exception.handle.HandlerException;
import lombok.extern.slf4j.Slf4j;
import task.type.ScheduledTask;
import task.Task;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledFuture;
/**
* @description: 处理器上下文 用于传递一些用户配置
* @date: 2024/5/19 16:13
* @version: 1.0
*/
@Slf4j
public class HandlerContext {
private CommonHandler commonHandler;
private ScheduledHandler scheduledHandler;
//普通任务列表
private List<Task> commonTasks=new ArrayList<>();
//定时任务列表
private Map<Task,String> scheduledTasks=new ConcurrentHashMap<>();
//需要返回值的任务列表
private List<Task> callableTasks=new ArrayList<>();
//保存需要返回结果任务的future
private Map<String, CompletableFuture<?>> futureMap;
public CommonHandler getCommonHandler() {
return this.commonHandler;
}
public ScheduledHandler getScheduledHandler(){
return this.scheduledHandler;
}
//如果用户没有传递任何参数 按照默认配置
public HandlerContext(CommonHandler commonHandler,ScheduledHandler scheduledHandler
,Map<String,CompletableFuture<?>> futureMap){
this.commonHandler = commonHandler;
this.scheduledHandler = scheduledHandler;
this.futureMap=futureMap;
this.commonHandler.setFutureMap(this.futureMap);
}
public Map<String,CompletableFuture<?>> getFutureMap(){
return this.futureMap;
}
public void addCommonTask(Task task){
this.commonTasks.add(task);
}
public void addScheduledTask(Task task,String cron){
this.scheduledTasks.put(task,cron);
}
public void removeCommonTask(Task task){
this.commonTasks.remove(task);
}
public void removeScheduledTask(Task task){
this.scheduledTasks.remove(task);
}
public void doHandle(){
List<Task> runnableTasks=new ArrayList<>();
List<Task> callableTasks=new ArrayList<>();
for (Task commonTask : commonTasks) {
if(commonTask.transferToRunnable()!=null){
runnableTasks.add(commonTask);
}else if(commonTask.transferToCallable()!=null){
callableTasks.add(commonTask);
}else{
log.error("任务类型错误 无法处理非Runnable和Callable类型的任务 此任务类型为:"+commonTask.getClass().getName());
}
}
try {
if(runnableTasks.size()!=0){
commonHandler.handleTasks(runnableTasks);
}
if(scheduledTasks.size()!=0){
scheduledHandler.handleTasks(scheduledTasks);
}
if(callableTasks.size()!=0){
commonHandler.handleHasResultTasks(callableTasks);
}
} catch (HandlerException e) {
e.printStackTrace();
}catch (HandleException e){
log.error("处理链中发生了异常 具体情况为:"+e.getMsg());
e.printStackTrace();
}
}
public void addCommonTasks(List<Task> tasks) {
this.commonTasks.addAll(tasks);
}
public void addScheduledTasks(List<Pair<Task,String>> tasks) {
for (Pair<Task,String> task : tasks) {
this.scheduledTasks.put(task.getLeft(),task.getRight());
}
}
}
其builder如下
package core.builder;
import core.context.HandlerContext;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
/**
* @description: 处理器上下文构建器
* @date: 2024/6/7 10:48
* @version: 1.0
*/
public class HandlerContextBuilder {
private CommonHandlerBuilder commonHandlerBuilder;
private ScheduledHandlerBuilder scheduledHandlerBuilder;
private Map<String, CompletableFuture<?>> futureMap;
public HandlerContextBuilder setCommonHandlerBuilder(CommonHandlerBuilder handlerBuilder){
this.commonHandlerBuilder=handlerBuilder;
return this;
}
public HandlerContextBuilder setScheduledHandlerBuilder(ScheduledHandlerBuilder handlerBuilder){
this.scheduledHandlerBuilder=handlerBuilder;
return this;
}
public HandlerContextBuilder setFutureMap(Map<String, CompletableFuture<?>> futureMap){
this.futureMap=futureMap;
return this;
}
//构建
public HandlerContext build(){
//为空的地方设置默认值
if(commonHandlerBuilder==null){
commonHandlerBuilder=new CommonHandlerBuilder();
}
if(scheduledHandlerBuilder==null){
scheduledHandlerBuilder=new ScheduledHandlerBuilder();
}
if(futureMap==null){
futureMap=new ConcurrentHashMap<>();
}
return new HandlerContext(commonHandlerBuilder.build(),scheduledHandlerBuilder.build(),futureMap);
}
}
编写总上下文和其builder
在完成了处理器上下文的构建之后 我们可以着手进行更上一层的上下文的开发 也就是咱们的总上下文 负责各个模块的初始化和启动工作 这样可以保证启动时执行的顺序 防止需要时其他模块没准备好 掉链子的情况 之后拓展的模块也需要在这里进行初始化
在之前我们将任务定义是获取了 但是获取任务定义这个操作本身需要一个执行时机 也就是在这里执行
为了保证功能唯一 我们依旧将这个操作封装成一个类
编写任务定义获取类
package core.finder;
import org.objectweb.asm.ClassReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
/**
* @description: 任务注入者
* @date: 2024/5/21 16:05
* @version: 1.0
*/
public class ComponentScanner {
private static TaskClassVisitor taskClassVisitor=new TaskClassVisitor();
public static void scan(List<Path> classPaths) throws IOException {
//参数的路径由上下文传递 这里只负责扫描指定文件路径
//已经获取到每一个类的路径了 现在需要进行的是使用ASM来进行解析
for (Path classPath : classPaths) {
ClassReader classReader=new ClassReader(Files.readAllBytes(classPath));
//设置自定义扫描器
classReader.accept(taskClassVisitor,0);
}
}
}
编写任务转换类
可想而知 我们之前一直提到的将任务定义转换为实际可以执行的Runnable和Callable也在总上下文中进行 是通过一些其他类辅助实现 在这里调用进行转换的模式
首先 我们先编写不需要使用Cglib进行动态代理的任务处理
package utils;
import common.Pair;
import common.factory.CglibProxyFactory;
import constants.type.TaskTypeConstants;
import core.handler.Handler;
import core.holder.TaskInstanceHolder;
import exception.handle.HandleException;
import exception.task.TaskTypeException;
import exception.task.VerifyException;
import lombok.extern.slf4j.Slf4j;
import task.*;
import task.type.CommonTask;
import task.type.ScheduledTask;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Callable;
/**g
* @description: 方法转任务工具类
* @date: 2024/5/21 17:06
* @version: 1.0
*/
@Slf4j
public class MethodTransferToTaskUtil {
/**
* @param taskDefinitions: 方法定义
* @return List<Task>
* @description 将方法转换为普通不带返回值的任务
* @date 2024/5/21 17:07
*/
public static List<Task> toCommonRunnableTask(List<TaskDefinition> taskDefinitions, Handler handler) throws Exception {
List<Task> result=new ArrayList<>();
for (TaskDefinition taskDefinition : taskDefinitions) {
if(taskDefinition.getType()!=TaskTypeConstants.COMMON_NO_RESULT){
throw new TaskTypeException("任务类型错误");
}
Object instance = getInstance(taskDefinition);
//之后如果拓展失败策略等 可以在此处进行 获取策略实例
if(taskDefinition.getEnableProxy()){
continue;
}
//构造Runnable 获取失败策略 并添加到里面
Method method = getMethodByName(instance, taskDefinition.getMethodName());
final Object executor=instance;
Task task=new CommonTask();
Runnable runnable=()->{
try {
method.invoke(executor);
} catch (Exception e) {
try {
//在这里执行失败策略实例对应的动作
e.printStackTrace();
} catch (Exception ex) {
//这里执行失败策略失败的处理
ex.printStackTrace();
}
}
};
task.setTask(runnable);
String taskName = taskDefinition.getClassName() + "." + taskDefinition.getMethodName();
task.setTaskName(taskName);
result.add(task);
}
return result;
}
private static Object getInstance(TaskDefinition taskDefinition) throws Exception {
String className = taskDefinition.getClassName();
//从实例持有中获取实例 因为调用方法还是需要它的类实例调用的 如果一个类中存在多个任务 创建多个实例就有些浪费 又因为初始化时是单线程执行的 因此可以直接使用map来保存这些实例 定义会在下方给出
Object instance = TaskInstanceHolder.getInstance(className);
//如果不需要使用代理 则直接使用反射创建实例
if(taskDefinition.getEnableProxy()==null||!taskDefinition.getEnableProxy()) {
if (instance == null) {
instance = Class.forName(className).newInstance();
TaskInstanceHolder.putInstance(className, instance);
}
}else{
//否则说明需要代理 不能传递原始类型 而是传递cglib代理之后的类型
if(instance==null){
//获取代理实例
instance=CglibProxyFactory.getInstance().getProxy(Class.forName(className));
if(instance==null){
throw new VerifyException("无法找到"+className+"的类作为任务的容器 将停止运行!");
}
TaskInstanceHolder.putInstance(className, instance);
}
}
//返回实例
return instance;
}
//作用和之前的转换函数差不多 也是转换任务
public static List<Task> toCallableTask(List<TaskDefinition> taskDefinitions,Handler handler) throws Exception {
List<Task> result=new ArrayList<>();
for (TaskDefinition taskDefinition : taskDefinitions) {
if(taskDefinition.getType()!=TaskTypeConstants.COMMON_HAS_RESULT){
throw new TaskTypeException("任务类型错误");
}
Object instance=getInstance(taskDefinition);
//构造Callable
Method method = getMethodByName(instance, taskDefinition.getMethodName());
final Object executor=instance;
Task task=new CommonTask();
Callable<Object> callable=()->{
try {
final Object invoke = method.invoke(executor);
return invoke;
} catch (Exception e) {
e.printStackTrace();
return null;
}
};
task.setTask(callable);
String taskName = taskDefinition.getClassName() + "." + taskDefinition.getMethodName();
task.setTaskName(taskName);
result.add(task);
}
return result;
}
//转换定时任务为runnable 转换为callable不支持
public static List<Pair<Task,String>> toScheduledRunnable(List<TaskDefinition> taskDefinitions,Handler handler) throws Exception {
List<Pair<Task,String>> tasks=new ArrayList<>();
for (TaskDefinition taskDefinition : taskDefinitions) {
if(taskDefinition.getType()!= TaskTypeConstants.SCHEDULED){
throw new TaskTypeException("任务类型错误");
}
Object instance=getInstance(taskDefinition);
Method method = instance.getClass().getMethod(taskDefinition.getMethodName());
final Object executor=instance;
//之后如果拓展失败策略等 可以在此处进行 获取策略实例
Task task=new ScheduledTask();
Runnable runnable =()->{
try {
method.invoke(executor);
} catch (Exception e) {
try {
//在这里执行实例对应的动作
e.printStackTrace();
} catch (Exception ex) {
ex.printStackTrace();
}
}
};
task.setTask(runnable);
//设置任务名称 方便之后获取结果(如果任务类型支持的话)
String taskName = taskDefinition.getClassName() + "." + taskDefinition.getMethodName();
task.setTaskName(taskName);
Pair<Task,String> pair=new Pair<>();
pair.setLeft(task);
pair.setRight(taskDefinition.getCron());
tasks.add(pair);
}
return tasks;
}
//根据名称获取方法实例
private static Method getMethodByName(Object instance,String methodName){
Class<?> clazz = instance.getClass();
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
if(declaredMethod.getName().equals(methodName)){
return declaredMethod;
}
}
return null;
}
}
相信读者注意到有些新朋友:TaskInstanceHolder和CglibProxyFactory
他们的作用我在注释中已经写明 现在再说明一下
这个相对来说比较简单 因为我们最终还是需要实例来调用这些方法 从而达到执行方法 模拟任务的效果 如果一个类中有多个任务 这样则会多次创建一个类 因此为了节约这里的成本 我们将复用这些实例
代码如下
package core.holder;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* @description: 任务调用实例持有者
* @date: 2024/5/21 17:11
* @version: 1.0
*/
public class TaskInstanceHolder {
private static Map<String,Object> taskInstanceMap=new ConcurrentHashMap<>();
public static void putInstance(String className,Object instance){
taskInstanceMap.put(className,instance);
}
public static Object getInstance(String className){
return taskInstanceMap.get(className);
}
}
而CglibProxyFactory是一个Cglib为指定类创建代理实例的工厂 具体解释见注释
package common.factory;
import constants.type.TaskTypeConstants;
import core.holder.TaskDefinitionHolder;
import exception.handle.HandlerException;
import lombok.extern.slf4j.Slf4j;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import task.*;
import task.type.CommonTask;
import java.lang.reflect.Method;
import java.util.concurrent.Callable;
/**
* @description: 设置动态代理工厂
* @date: 2024/5/31 12:00
* @version: 1.0
*/
@Slf4j
public class CglibProxyFactory implements MethodInterceptor {
private static CglibProxyFactory instance=new CglibProxyFactory();
private static CommonHandler commonHandler;
public Object getProxy(Class<?> clazz) {
// 创建Enhancer对象,类似于JDK动态代理的Proxy类,下一步将通过它来创建目标类的代理对象
Enhancer enhancer = new Enhancer();
// 设置enhancer的父类为需要代理的类
enhancer.setSuperclass(clazz);
// 定义代理逻辑对象为当前类,要求当前类实现MethodInterceptor方法
enhancer.setCallback(this);
// 创建并返回代理对象
return enhancer.create();
}
//此方法是代表着实例被代理之后的额外操作
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
String name = method.getName();
//获取需要被代理的任务
TaskDefinition taskDefinition = TaskDefinitionHolder.proxyTaskMap.get(name);
if(taskDefinition !=null){
int type = taskDefinition.getType();
Task task;
//根据不同的任务类型进行不同的处理
if(type== TaskTypeConstants.COMMON_NO_RESULT){
task=new CommonTask();
Runnable runnable=()->{
try{
methodProxy.invokeSuper(o,objects);
}catch (Exception e){
//可以在这里进行失败策略的处理
} catch (Throwable throwable) {
log.error("CGLIB内部问题 请尝试使用更新的版本!");
throwable.printStackTrace();
}
};
task.setTaskName(taskDefinition.getClassName()+"."+taskDefinition.getMethodName());
task.setTask(runnable);
}else if(type== TaskTypeConstants.COMMON_HAS_RESULT){
task=new CommonTask();
Callable<Object> callable=()->{
try{
return methodProxy.invokeSuper(o,objects);
}catch (Throwable throwable){
throwable.printStackTrace();
return null;
}
};
}else{
throw new HandlerException("无法设置带有参数的任务为定时任务!");
}
}else{
return methodProxy.invokeSuper(o,objects);
}
return null;
}
public static CglibProxyFactory getInstance(){
return instance;
}
}
编写总上下文
有了这些虎将 我们可以进行总上下文的编写了
package core.context;
import common.Pair;
import common.factory.CglibProxyFactory;
import constants.type.TaskTypeConstants;
import core.builder.HandlerContextBuilder;
import core.finder.ComponentScanner;
import core.holder.TaskDefinitionHolder;
import lombok.extern.slf4j.Slf4j;
import task.Task;
import task.TaskDefinition;
import utils.FileUtil;
import utils.MethodTransferToTaskUtil;
import java.io.File;
import java.nio.file.Path;
import java.util.*;
import java.util.stream.Collectors;
/**
* @description: 所有的容器 包括任务的容器 执行器的容器
* @date: 2024/5/21 16:05
* @version: 1.0
*/
@Slf4j
public class Context {
private HandlerContext handlerContext;
public Context(){
}
public Context(HandlerContext handlerContext){
this.handlerContext=handlerContext;
}
public HandlerContext getHandlerContext(){
return handlerContext;
}
public void init(Class<?> clazz){
//扫描的步骤 首先获取这个启动类所在的目录下的全部包
File diskPath = FileUtil.getDiskPath(clazz);
if(diskPath==null){
throw new RuntimeException("找不到启动类所在的磁盘路径!");
}
Path path = diskPath.toPath();
try {
//获取文件路径
final List<Path> paths = FileUtil.packagesHandle(path);
//扫描文件路径
ComponentScanner.scan(paths);
//如果用户没有传递处理器上下文builder 则给默认值
if(handlerContext==null) {
handlerContext = new HandlerContextBuilder().build();
}
//进行任务转换
initProxyMethods();
transferCommonTask();
transferScheduledTask();
//等待上述的任务全部转换完毕 任务上下文开始执行任务
handlerContext.doHandle();
} catch (Exception e) {
e.printStackTrace();
}
}
//初始化需要代理处理的任务
private void initProxyMethods(){
Collection<List<TaskDefinition>> values = TaskDefinitionHolder.taskDefinitionMap.values();
for (List<TaskDefinition> value : values) {
for (TaskDefinition taskDefinition : value) {
if(taskDefinition.getEnableProxy()!=null&&taskDefinition.getEnableProxy()){
TaskDefinitionHolder.proxyTaskMap.put(taskDefinition.getMethodName(),taskDefinition);
}
}
}
}
//转换定时任务
private void transferScheduledTask() throws Exception{
//处理被@Scheduled注解的方法 并解析其中的cron表达式 然后放入到Map映射中
Set<String> list = TaskDefinitionHolder.typeTaskMapKey.get(TaskTypeConstants.SCHEDULED);
//这里就需要一些小小的修改了 因为这里不仅要转换方法为Runnable 而且需要进行cron的解析
if (list!=null) {
Set<String> collect = list.stream().map(s -> {
int i = s.lastIndexOf(".");
if(i!=-1){
return s.substring(0,i);
}
return s;
}).collect(Collectors.toSet());
for (String s : collect) {
//现在获取的任务 包括cron和任务体
List<TaskDefinition> taskDefinitions = TaskDefinitionHolder.taskDefinitionMap.get(s);
List<Pair<Task,String>> scheduledTasks=MethodTransferToTaskUtil.toScheduledRunnable(taskDefinitions,handlerContext.getScheduledHandler());
handlerContext.addScheduledTasks(scheduledTasks);
}
}
}
//转换普通任务
private void transferCommonTask() throws Exception {
Set<String> list = TaskDefinitionHolder.typeTaskMapKey.get(TaskTypeConstants.COMMON_NO_RESULT);
if(list!=null) {
Set<String> collect = list.stream()
.map(s -> {
int i = s.lastIndexOf(".");
if(i!=-1){
return s.substring(0,i);
}
return s;
}).collect(Collectors.toSet());
for (String s : collect) {
List<TaskDefinition> taskDefinitions = TaskDefinitionHolder.taskDefinitionMap.get(s);
List<Task> tasks = MethodTransferToTaskUtil.toCommonRunnableTask(taskDefinitions,handlerContext.getCommonHandler());
handlerContext.addCommonTasks(tasks);
}
}
Set<String> callList = TaskDefinitionHolder.typeTaskMapKey.get(TaskTypeConstants.COMMON_HAS_RESULT);
if (callList!=null) {
Set<String> collect = callList.stream().map(s -> {
int i = s.lastIndexOf(".");
if(i!=-1){
return s.substring(0,i);
}
return s;
}).collect(Collectors.toSet());
for (String s : collect) {
List<TaskDefinition> taskDefinitions = TaskDefinitionHolder.taskDefinitionMap.get(s);
List<Task> tasks = MethodTransferToTaskUtil.toCallableTask(taskDefinitions, handlerContext.getCommonHandler());
handlerContext.addCommonTasks(tasks);
}
}
}
}
最后 我们编写总上下文的builder
总上下文builder
package core.builder;
import core.context.Context;
import core.context.HandlerContext;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
/**
* @description: 统一上下文构建器
* @date: 2024/6/7 10:47
* @version: 1.0
*/
public class ContextBuilder {
private HandlerContextBuilder handlerContextBuilder;
private Map<String, CompletableFuture<?>> futureMap;
public ContextBuilder setHandlerContextBuilder(HandlerContextBuilder handlerContextBuilder){
this.handlerContextBuilder=handlerContextBuilder;
return this;
}
public ContextBuilder setFutureMap(Map<String, CompletableFuture<?>> futureMap){
this.futureMap=futureMap;
return this;
}
public Context build(){
if(handlerContextBuilder==null){
handlerContextBuilder=new HandlerContextBuilder();
}
if(futureMap==null){
futureMap=new ConcurrentHashMap<>();
}
handlerContextBuilder.setFutureMap(futureMap);
return new Context(handlerContextBuilder.build());
}
}
定义启动类
最终我们走到了这一步 也就是完成启动类 正式跑起来测试它 其实 每写完一部分都要进行单元测试的 但是把这个加进来 篇幅有些过于长了 所以还是算了
启动类中包含了总上下文 话不多说 直接上代码
package core;
import core.builder.ContextBuilder;
import core.context.Context;
import java.io.IOException;
/**
* @description: 程序入口 类似于SpringBoot的启动方式
* @date: 2024/5/21 16:36
* @version: 1.0
*/
public class TaskManger {
public static Context init(Class<?> clazz, String ...args) {
Context context=new Context();
context.init(clazz);
return context;
}
public static Context init(ContextBuilder builder, Class<?> clazz, String ...args) {
if(builder==null){
return init(clazz,args);
}
Context context=builder.build();
context.init(clazz);
return context;
}
}
测试
首先要编写各个任务类
package test.task;
import annotation.task.NoResultTask;
import lombok.extern.slf4j.Slf4j;
/**
* @description: cglib测试
* @date: 2024/6/5 12:23
* @version: 1.0
*/
@Slf4j
public class CGLIBTaskTest {
@NoResultTask(hasRuntimeParam = "true")
public void cglibTest(String msg){
log.info("我是cglib的任务"+msg);
}
public void cglibTest2(String msg){
log.info("我是cglib的普通方法");
}
}
package test.task;
import annotation.task.NoResultTask;
import lombok.extern.slf4j.Slf4j;
/**
* @description: 测试任务1
* @date: 2024/5/21 16:54
* @version: 1.0
*/
@Slf4j
public class LogTaskTest {
@NoResultTask(hasRuntimeParam = "false")
public void test()
{
log.info("test1");
}
@NoResultTask(hasRuntimeParam = "false")
public void test1(){
log.info("author test");
}
}
package test.task;
import annotation.task.Scheduled;
import lombok.extern.slf4j.Slf4j;
/**
* @description: 定时任务测试
* @date: 2024/5/22 10:47
* @version: 1.0
*/
@Slf4j
public class ScheduleLogTaskTest {
//每到每分钟0秒时执行
@Scheduled(cron = "0 * * * * ?")
public void scheduleLog(){
log.info("scheduleLog ------");
}
}
测试之前 先保证你的全部包和类与Main启动类在同一目录下 大概看起来be like:
然后在Main中编写这样的代码
import core.builder.ContextBuilder;
import core.context.Context;
import core.TaskManger;
import core.holder.TaskInstanceHolder;
import lombok.extern.slf4j.Slf4j;
import test.task.CGLIBTaskTest;
import test.task.ResultTaskTest;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
/**
* @description: 测试
* @date: 2024/5/19 11:36
* @version: 1.0
*/
@Slf4j
public class Main {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ContextBuilder contextBuilder=new ContextBuilder();
Map<String,CompletableFuture<?>> futureMap = new ConcurrentHashMap<>();
contextBuilder.setFutureMap(futureMap);
Context init = TaskManger.init(contextBuilder,Main.class, args);
CGLIBTaskTest cglibTaskTest=(CGLIBTaskTest) TaskInstanceHolder.getInstance(CGLIBTaskTest.class.getName());
//模拟动态调用过程
cglibTaskTest.cglibTest("代理测试");
CompletableFuture<?> completableFuture = futureMap.get(ResultTaskTest.class.getName() + "." + "resultTest");
final Object o = completableFuture.get();
log.info(o.toString());
}
}
可见控制台打印
总结
到这里为止 我们就完成了我们开头提到的内容 其实如果想要拓展 可以将其拓展成一个较小的任务管理模块 完善任务失败策略(策略模式) cpu内存占用报警(利用ASM可以让用户自定义) 以及持久化,配置文件等 可以留给读者自行拓展 之后可能会更新这些模块的设计和实现
最后 这是我第一次写一个较长的文章 可能阅读体验没有那么好 但是还是希望读者读完之后能有所收获 谢谢你的阅读 喜欢的话点个关注吧! 未来会更新更多的实用的大小知识