zoukankan      html  css  js  c++  java
  • 使用Spring自定义注解实现任务路由的方法

    在Spring mvc的开发中,我们可以通过RequestMapping来配,当前方法用于处理哪一个URL的请求.同样我们现在有一个需求,有一个任务调度器,可以按照不同的任务类型路由到不同的任务执行器。其本质就是通过外部参数进行一次路由和Spring mvc做的事情类似。简单看了Spring mvc的实现原理之后,决定使用自定义注解的方式来实现以上功能。

    自定义TaskHandler注解

    1
    2
    3
    4
    5
    6
    7
    8
    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Component
    public @interface TaskHandler {
     
      String taskType() default "";
    }

    以上定义了任务处理器的注解,其中@Component表示在spring 启动过程中,会扫描到并且注入到容器中。taskType表示类型。

    任务处理器定义

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public abstract class AbstractTaskHandler {
     
      /**
       * 任务执行器
       *
       * @param task 任务
       * @return 执行结果
       */
       public abstract BaseResult execute(Task task);
    }

    以上定义了一个任务执行的处理器,其他所有的具体的任务执行器继承实现这个方法。其中Task表示任务的定义,包括任务Id,执行任务需要的参数等。

    任务处理器实现

    接下来,我们可以实现一个具体的任务处理器。

    1
    2
    3
    4
    5
    6
    7
    @TaskHandler(taskType = "UserNameChanged")
    public class UserNameChangedSender extends AbstractTaskHandler {
      @Override
      public BaseResult execute(Task task) {
       return new BaseResult();
      }
    }

    以上我们就实现一个用户名修改通知的任务处理器,具体的业务逻辑这里没有实现。

    其中:@TaskHandler(taskType = "UserNameChanged"),这里我们指定这个Handler用于处理用户名变更的任务

    任务处理Handler注册

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    public class TaskHandlerRegister extends ApplicationObjectSupport {
     
      private final static Map<String, AbstractTaskHandler> TASK_HANDLERS_MAP = new HashMap<>();
     
      private static final Logger LOGGER = LoggerFactory.getLogger(TaskHandlerRegister.class);
     
      @Override
      protected void initApplicationContext(ApplicationContext context) throws BeansException {
        super.initApplicationContext(context);
        Map<String, Object> taskBeanMap = context.getBeansWithAnnotation(TaskHandler.class);
        taskBeanMap.keySet().forEach(beanName -> {
          Object bean = taskBeanMap.get(beanName);
          Class clazz = bean.getClass();
          if (bean instanceof AbstractTaskHandler && clazz.getAnnotation(TaskHandler.class) != null) {
            TaskHandler taskHandler = (TaskHandler) clazz.getAnnotation(TaskHandler.class);
            String taskType = taskHandler.taskType();
            if (TASK_HANDLERS_MAP.keySet().contains(taskType)) {
              throw new RuntimeException("TaskType has Exits. TaskType=" + taskType);
            }
            TASK_HANDLERS_MAP.put(taskHandler.taskType(), (AbstractTaskHandler) taskBeanMap.get(beanName));
            LOGGER.info("Task Handler Register. taskType={},beanName={}", taskHandler.taskType(), beanName);
          }
        });
      }
     
      public static AbstractTaskHandler getTaskHandler(String taskType) {
        return TASK_HANDLERS_MAP.get(taskType);
      }
    }

    这里继承了Spring的ApplicationObjectSupport类,具体的注册过程如下

    1. Spring完成bean的初始化
    2. 查找spring的容器中,所有带有TaskHandler注解的bean
    3. 校验bean是否为AbstractTaskHandler类型,获取到taskType
    4. 把该bean放到TASK_HANDLERS_MAP容器中,即注册完成

    任务执行

    接下来我们来看下任务执行

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public class TaskExecutor implements Job {
     
      private static final String TASK_TYPE = "taskType";
     
      @Override
      public BaseResult execute(Task task){
        String taskType=task.getTaskType();
        if (TaskHandlerRegister.getTaskHandler(taskType) == null) {
          throw new RuntimeException("can't find taskHandler,taskType=" + taskType);
        }
        AbstractTaskHandler abstractHandler = TaskHandlerRegister.getTaskHandler(taskType);
        return abstractHandler.execute(task);
      }
    }

    这里发起任务执行的是一个Job,具体过程如下

    1. 校验该任务类型,有没有在注册中心注册相关Handler
    2. 从任务注册中心获取到对应的处理的Handelr
    3. 执行该Handelr

    以上过程就完成了,可以实现基于注解的一个任务路由过程。其实现思路来自于Spring mvc的RequestMapping的设计思路

  • 相关阅读:
    shell脚本获取当前时间
    shell脚本读取用户输入并与之交互
    shell脚本获取客户端IP
    shell脚本中重启tomcat进程
    性能测试经验
    cmdhere的两种方法
    tomcat启动多个应用时报内存溢出
    [kuangbin]带你飞之'连通图'专题
    (模板)解决带负权最短路径 Bellman-ford 与 SPFA(前者的队列优化)
    [kuangbin]带你飞之'最短路练习'专题
  • 原文地址:https://www.cnblogs.com/zdd-java/p/9295105.html
Copyright © 2011-2022 走看看