【SpringBoot|Java】玩具意义的另类@Async实现方式(下)

书接上文 继续编码

在之前我们已经完成处理任务的处理器 以及任务的发现步骤 现在是时候让他们真正的发挥作用了

定义上下文

为了更好的利用之前保存的信息 我们需要定义不同的上下文分级 让他们分工更明确 和之前展示的流程图一样 我们需要两层上下文 一层是处理器上下文 一层则是处理其他模块初始化工作的上下文
这样在之后拓展的时候 我们也可以更好的进行拓展

处理器的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如下

处理器上下文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进行动态代理的任务处理

不需要动态调用的方法转换(MethodTransferToTaskUtil类)

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;
    }

}

相信读者注意到有些新朋友:TaskInstanceHolderCglibProxyFactory
他们的作用我在注释中已经写明 现在再说明一下

TaskInstanceHolder

这个相对来说比较简单 因为我们最终还是需要实例来调用这些方法 从而达到执行方法 模拟任务的效果 如果一个类中有多个任务 这样则会多次创建一个类 因此为了节约这里的成本 我们将复用这些实例
代码如下

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为指定类创建代理实例的工厂 具体解释见注释

CglibProxyFactory

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;
    }

}

测试

首先要编写各个任务类

cglib任务测试

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可以让用户自定义) 以及持久化,配置文件等 可以留给读者自行拓展 之后可能会更新这些模块的设计和实现

最后 这是我第一次写一个较长的文章 可能阅读体验没有那么好 但是还是希望读者读完之后能有所收获 谢谢你的阅读 喜欢的话点个关注吧! 未来会更新更多的实用的大小知识

  • 26
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
@Async和@PostConstruct是Spring框架中的两个注解,分别用于实现异步方法和在Bean初始化后执行某些逻辑。 @Async注解用于标记一个方法为异步方法,当该方法被调用时,Spring会将其放入一个线程池中进行异步执行,而不会阻塞主线程。这样可以提高系统的并发性能和响应速度。 @PostConstruct注解用于标记一个方法为Bean的初始化方法,在Bean实例化后,Spring会调用被@PostConstruct标记的方法进行一些初始化操作。这个注解通常用于需要在Bean创建之后执行一些逻辑的场景。 在Spring Boot中,你可以同时使用@Async和@PostConstruct注解。例如,你可以在一个Bean的初始化方法上使用@PostConstruct注解,在该方法中调用一个异步方法,以便在系统启动时执行某些耗时操作。 举个例子,假设你有一个名为UserService的Bean,在它的初始化方法中调用一个异步方法来加载用户数据: ```java @Service public class UserService { @Async @PostConstruct public void init() { // 异步加载用户数据 loadData(); } public void loadData() { // 加载用户数据的逻辑 // ... } } ``` 上述代码中,当UserService的Bean被实例化后,Spring会调用init方法,在init方法中调用loadData方法,由于loadData方法被@Async注解标记,它将异步执行,不会阻塞主线程。 注意,为了让@Async注解生效,你需要在Spring Boot的配置类中添加@EnableAsync注解,以启用异步支持。 总结:@Async和@PostConstruct是Spring框架中的两个注解,分别用于实现异步方法和在Bean初始化后执行某些逻辑。在Spring Boot中,你可以同时使用这两个注解来实现异步初始化操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值