在與倉庫系統的對接過程當中,咱們使用了阿里巴巴的奇門規範。該規範中根據不一樣的method參數來肯定不一樣的業務,好比:java
# 入庫單建立 method=taobao.qimen.entryorder.create # 庫存查詢 method=taobao.qimen.inventory.query # 商品同步接口 method=taobao.qimen.singleitem.synchronize
那麼咱們在解析的時候,經常使用的方式就是使用switch或者if來處理,以switch爲例,實現代碼以下:程序員
switch (method) { case "taobao.qimen.entryorder.create": return entryorderCreate(); case ""taobao.qimen.inventory.query: return inventoryQuery(); case "taobao.qimen.singleitem.synchronize": return singleitemSyncronize(); default: return ""; }
經過switch,咱們根據不一樣的method可以返回不一樣的執行邏輯結果。從功能上來講,沒有任何的毛病。可是做爲一個程序員,若是隻是爲了完成功能而寫代碼,那這樣的程序員是沒有靈魂的。api
在奇門api技術文檔中,大概有50多個不一樣的業務接口method,這也就意味着咱們至少要case 50次以上。你以爲一個switch中case 50次合理嗎?答案固然是不合理的。數組
在這了再分享一句話:數據結構
任何一個傻瓜都能寫出計算機能理解的程序,而優秀的程序員卻能寫出別人能讀得懂的程序。—— Martin Fowler
每次接受請求以後,根據method的不一樣,來執行不一樣的業務邏輯。那麼咱們能不能將請求的method和須要執行的業務邏輯方法作一個映射,這樣咱們根據method就能直接找到具體的業務邏輯處理方法。
那麼咱們的method怎麼和咱們的業務方法映射綁定呢?解決方法是在每一個業務方法上面增長一個註解(好比@Name)。那麼問題來了,咱們何時生成這樣的映射關係呢?
咱們能夠在容器啓動的時候,就去生成這樣的映射關係。那麼咱們怎麼知道哪些類包含了具備@Name註解的方法呢?
爲了能快速獲取到包含@Name註解方法的類,咱們增長一個類註解@MethodHandler,在方法上使用了@Name註解的類上咱們加上一個@MethodHandler註解,這樣咱們就能快速找到這樣的類。app
@Name註解ide
@Target(ElementType.METHOD) @Documented @Retention(RetentionPolicy.RUNTIME) public @interface Name { String[] value() default {}; }
@Target(ElementType.METHOD)表示@Name是個方法註解。同時裏面的value是個數組,是由於可能存在多個method執行相同業務邏輯的狀況測試
@MethodHandler註解this
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface MethodHandler { }
@Target({ElementType.TYPE})表示@MethodHandler是個類或者接口註解,此註解的做用是讓咱們能快速找到包含@Name註解的方法。spa
MethodMappering
public class MethodMapping { //方法註解對應的名字 public String[] names; //具體的執行方法 public Method method; public MethodMapping(String[] names, Method method) { this.names = names; this.method = method; } }
這個類主要存儲奇門method和具體執行的方法的映射
MethodNames
public class MethodNames { public static final String deliveryorder_confirm = "deliveryorder.confirm"; public static final String taobao_qimen_deliveryorder_confirm = "taobao.qimen.deliveryorder.confirm"; public static final String deliveryorder_batchconfirm = "deliveryorder.batchconfirm"; public static final String taobao_qimen_deliveryorder_batchconfirm = "taobao.qimen.deliveryorder.batchconfirm"; public static final String stockchange_report = "stockchange.report"; public static final String taobao_qimen_stockchange_report = "taobao.qimen.stockchange.report"; public static final String stockout_confirm = "stockout.confirm"; public static final String taobao_qimen_stockout_confirm = "taobao.qimen.stockout.confirm"; public static final String entryorder_confirm = "entryorder.confirm"; public static final String taobao_qimen_entryorder_confirm = "taobao.qimen.entryorder.confirm"; public static final String itemlack_report = "itemlack.report"; public static final String taobao_qimen_itemlack_report = "taobao.qimen.itemlack.report"; public static final String orderprocess_report = "orderprocess.report"; public static final String taobao_qimen_orderprocess_report = "taobao.qimen.orderprocess.report"; public static final String returnorder_confirm = "returnorder.confirm"; public static final String taobao_qimen_returnorder_confirm = "taobao.qimen.returnorder.confirm"; public static final String returnapply_report = "returnapply.report"; public static final String taobao_qimen_returnapply_report = "taobao.qimen.returnapply.report"; public static final String qimen_taobao_qianniu_cloudkefu_address_self_modify = "qimen.taobao.qianniu.cloudkefu.address.self.modify"; }
MethodNames類主要記錄了奇門中全部的method(此處只展現部分)
註解解析和檢查類DetectMethodAnnotation
@Component public class DetectMethodAnnotation extends AbstractReturner implements ApplicationContextAware, InitializingBean { private ApplicationContext applicationContext; //存儲類-方法 private HashMap<String, List<MethodMapping>> classMethodMap = new HashMap<>(); /** * 初始化容器後解析全部包含MethodHandler註解的類中包含Name註解的方法 * * @throws Exception */ @Override public void afterPropertiesSet() throws Exception { //獲取包含註解MethodHandler的類 Map<String, Object> methodHandlerMap = applicationContext.getBeansWithAnnotation(MethodHandler.class); methodHandlerMap.forEach((k, v) -> { Class<?> clazz = v.getClass(); Method[] methods = clazz.getDeclaredMethods();//獲取全部的方法 List<MethodMapping> methodMappings = new ArrayList<>(); for (Method method : methods) { //只解析@Name註解的,而且返回值爲Returner的方法,方便對結果進行解析 if (method.isAnnotationPresent(Name.class) && (method.getReturnType() == Returner.class)) { Name nameAnnotation = method.getAnnotation(Name.class); methodMappings.add(new MethodMapping(nameAnnotation.value(), method)); } } if (!methodMappings.isEmpty()) { classMethodMap.put(clazz.getName(), methodMappings); } }); } /** * 執行 * * @param name * @return */ public <T> Returner<T> execute(String name, Object... parameters) throws Exception { if (!classMethodMap.containsKey(this.getClass().getName())) { return fail("類[" + this.getClass().getName() + "]未使用註解@MethodHandler註冊或未發現任何使用@Name註解的非繼承方法"); } List<MethodMapping> methodMappings = classMethodMap.get(this.getClass().getName()); for (MethodMapping methodMapping : methodMappings) { String[] names = methodMapping.names; if (Arrays.asList(names).contains(name)) { return (Returner) methodMapping.method.invoke(this, parameters); } } return fail("未發現使用註解 @Name(\"" + name + "\") 爲的方法"); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } }
DetectMethodAnnotation的做用以下:
QimenController
@Controller @MethodHandler public class QimenController extends DetectMethodAnnotation { @Name({MethodNames.deliveryorder_confirm, MethodNames.taobao_qimen_deliveryorder_confirm}) public Returner<String> deliveryorderConfirm(String deliveryOrderCode) { logger.info("execute deliveryorderConfirm method with value " + deliveryOrderCode); return success(""); } @Name(MethodNames.stockchange_report) public Returner<String> stockchangeReport() { return success(""); } }
經過QimenController的接口能夠看到具體的使用方式,類上面使用@MethodHandler註解,方法上使用@Name註解,@Name註解中傳入MethodNames類中定義的名字便可。
測試
public class Run { public static final Logger logger = LoggerFactory.getLogger(Run.class); public static void main(String[] args) throws Exception { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Run.class); QimenController qimenController = applicationContext.getBean(QimenController.class); Returner<String> execute = qimenController.execute(MethodNames.deliveryorder_confirm, "T123456789"); logger.info("deliveryorder_confirm:{}", execute); logger.info("stockchange_report:{}", qimenController.execute(MethodNames.stockchange_report)); applicationContext.close(); } }
執行結果以下
[main] INFO solution.swithCase.QimenController - [18] - execute deliveryorderConfirm method with value T123456789 [main] INFO solution.swithCase.Run - [29] - deliveryorder_confirm:Returner(code=0, desc=null, body=) [main] INFO solution.swithCase.Run - [30] - stockchange_report:Returner(code=0, desc=null, body=)
Returner對象
@Data public class Returner<T> implements Serializable { private String code; private String desc; private T body; }
此對象主要爲了統一返回值,方便解析
首先要先明白解決方案思路才能理解代碼,其實就是把類-method-業務邏輯作一個映射,這樣就能直接經過接口中傳遞的method來找到具體的業務邏輯代碼。若是有不明白的地方能夠在下面留言。