在与仓库系统的对接过程中,我们使用了阿里巴巴的奇门规范。该规范中根据不同的method方法参数来确定不同的业务,比如:
# 入库单创建 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技术文档中,大概有50多个不同的业务接口method,这也就意味着我们至少要case 50次以上。你觉得一个switch中case 50次合理吗?答案当然是不合理的。
在这了再分享一句话:
任何一个傻瓜都能写出计算机能理解的程序,而优秀的程序员却能写出别人能读得懂的程序。—— Martin Fowler
解决思路:
每次接受请求之后,根据method的不同,来执行不同的业务逻辑。那么我们能不能将请求的method和需要执行的业务逻辑方法做一个映射,这样我们根据method就能直接找到具体的业务逻辑处理方法。
那么我们的method怎么和我们的业务方法映射绑定呢?解决方法是在每个业务方法上面增加一个注解(比如@Name)。那么问题来了,我们什么时候生成这样的映射关系呢?
我们可以在容器启动的时候,就去生成这样的映射关系。那么我们怎么知道哪些类包含了具有@Name注解的方法呢?为了能快速获取到包含@Name的类,我们增加一个类注解@MethodHandler,在方法上使用了@Name注解的类上我们加上一个@MethodHandler注解。这样我们就能快速找到这样的类了。
@Target(ElementType.METHOD) @Documented @Retention(RetentionPolicy.RUNTIME) public @interface Name { String[] value() default {}; }
@Target(ElementType.METHOD)表示@Name是个方法注解。同时里面的value是个数组,是因为可能存在多个method执行相同业务逻辑的情况
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface MethodHandler { }
@Target({ElementType.TYPE})表示@MethodHandler是个类或者接口注解,次注解的作用是让我们能快速找到包含@Name注解的方法。
/** * 方法映射 */ public class MethodMapping { //方法注解对应的名字 public String[] names; //具体的执行方法 public Method method; public MethodMapping(String[] names, Method method) { this.names = names; this.method = method; } }
这个类主要存储奇门method和具体执行的方法的映射
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(此处只展示部分)
@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的作用如下:
@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=)
@Data public class Returner<T> implements Serializable { private String code; private String desc; private T body; } 此对象主要为了统一返回值,方便解析
首先要先明白解决方案思路才能理解代码,其实就是把类-method-业务逻辑做一个映射,这样就能直接通过接口中传递的method来找到具体的业务逻辑代码。如果有不明白的地方可以在下面留言。