天天看點

使用Spring自定義注解實作任務路由

使用Spring自定義注解實作任務路由

在spring

mvc的開發中,我們可以通過requestmapping來配,目前方法用于處理哪一個url的請求.同樣我們現在有一個需求,有一個任務排程器,可以按照不同的任務類型路由到不同的任務執行器。其本質就是通過外部參數進行一次路由和spring

mvc做的事情類似。簡單看了spring mvc的實作原理之後,決定使用自定義注解的方式來實作以上功能。

自定義taskhandler注解

@target({elementtype.type}) 

@retention(retentionpolicy.runtime) 

@documented 

@component 

public @interface taskhandler { 

    string tasktype() default ""; 

}  

以上定義了任務處理器的注解,其中@component表示在spring 啟動過程中,會掃描到并且注入到容器中。tasktype表示類型。

任務處理器定義

public abstract class abstracttaskhandler { 

    /** 

     * 任務執行器 

     * 

     * @param task 任務 

     * @return 執行結果 

     */ 

     public abstract baseresult execute(task task); 

以上定義了一個任務執行的處理器,其他所有的具體的任務執行器繼承實作這個方法。其中task表示任務的定義,包括任務id,執行任務需要的參數等。

任務處理器實作

接下來,我們可以實作一個具體的任務處理器。

@taskhandler(tasktype = "usernamechanged") 

public class usernamechangedsender extends abstracttaskhandler { 

    @override 

    public baseresult execute(task task) { 

      return new baseresult(); 

    } 

以上我們就實作一個使用者名修改通知的任務處理器,具體的業務邏輯這裡沒有實作。

其中:@taskhandler(tasktype = "usernamechanged"),這裡我們指定這個handler用于處理使用者名變更的任務

任務處理handler注冊

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

    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類,具體的注冊過程如下

spring完成bean的初始化

查找spring的容器中,所有帶有taskhandler注解的bean

校驗bean是否為abstracttaskhandler類型,擷取到tasktype

把該bean放到task_handlers_map容器中,即注冊完成

任務執行

接下來我們來看下任務執行

public class taskexecutor implements job { 

    private static final string task_type = "tasktype"; 

    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,具體過程如下

校驗該任務類型,有沒有在注冊中心注冊相關handler

從任務注冊中心擷取到對應的處理的handelr

執行該handelr

以上過程就完成了,可以實作基于注解的一個任務路由過程。其實作思路來自于spring mvc的requestmapping的設計思路.

作者:wangyan9110

來源:51cto