通常的接收mq消息都会根据topic来确定怎么处理该条消息,还有tag用来细分topic消息。在代码中用if else来判断topic和tag然后进行相应的处理。本文将使用java自定义注解来映射相对应的类,处理mq消息,用来代替难看的if else判断,并整合spring
自定义注解
package cn.cxnxs.mq.annotation;\r\n\r\nimport java.lang.annotation.ElementType;\r\nimport java.lang.annotation.Retention;\r\nimport java.lang.annotation.RetentionPolicy;\r\nimport java.lang.annotation.Target;\r\n\r\n@Target(value=ElementType.TYPE)\r\n@Retention(value = RetentionPolicy.RUNTIME)\r\npublic @interface Topic {\r\n String value();\r\n}\r\n\r\npackage cn.cxnxs.mq.annotation;\r\n\r\nimport java.lang.annotation.ElementType;\r\nimport java.lang.annotation.Retention;\r\nimport java.lang.annotation.RetentionPolicy;\r\nimport java.lang.annotation.Target;\r\n\r\n@Target(value=ElementType.METHOD)\r\n@Retention(value = RetentionPolicy.RUNTIME)\r\npublic @interface Tag {\r\n String value();\r\n}
用这两个注解分别对应mq消息的topic和tag
2.mq消息处理类
package cn.cxnxs.mq.handler;\r\n\r\nimport java.util.List;\r\n\r\nimport org.springframework.beans.factory.annotation.Autowired;\r\nimport org.springframework.stereotype.Service;\r\n\r\nimport cn.cxnxs.entity.Book;\r\nimport cn.cxnxs.mq.annotation.Tag;\r\nimport cn.cxnxs.mq.annotation.Topic;\r\nimport cn.cxnxs.mq.pojo.MQContext;\r\nimport cn.cxnxs.service.BookService;\r\n\r\n@Service\r\n@Topic("h1")\r\npublic class Handler1{\r\n \r\n @Autowired\r\n private BookService bookServiceImpl;\r\n \r\n @Tag("a")\r\n public void queryBook(MQContext context) {\r\n System.out.println("Handler1正在处理");\r\n //List<Book> s= bookServiceImpl.findBookById(1);\r\n //System.out.println(s);\r\n }\r\n}\r\npackage cn.cxnxs.mq.handler;\r\n\r\nimport org.springframework.stereotype.Service;\r\n\r\nimport cn.cxnxs.mq.MQHandler;\r\nimport cn.cxnxs.mq.annotation.Tag;\r\nimport cn.cxnxs.mq.annotation.Topic;\r\nimport cn.cxnxs.mq.pojo.MQContext;\r\n\r\n@Service\r\n@Topic("h1")\r\npublic class Handler2 implements MQHandler{\r\n\r\n @Tag("*")\r\n public void Handler2Message(MQContext context) {\r\n System.out.println("Handler2正在处理");\r\n\r\n }\r\n}
这两个处理类分别映射了topic=h1,tag=a这条mq消息,tag支持模糊匹配,Handler2中@Tag(*)代表匹配所有h1的tag.
3.处理类调用类
package cn.cxnxs.mq;\r\n\r\nimport java.io.File;\r\nimport java.lang.reflect.Method;\r\nimport java.util.ArrayList;\r\nimport java.util.List;\r\nimport java.util.regex.Pattern;\r\n\r\nimport org.apache.log4j.Logger;\r\nimport org.springframework.web.context.ContextLoader;\r\nimport org.springframework.web.context.WebApplicationContext;\r\n\r\nimport cn.cxnxs.mq.annotation.Tag;\r\nimport cn.cxnxs.mq.annotation.Topic;\r\nimport cn.cxnxs.mq.pojo.MQContext;\r\n\r\n/**\r\n * \r\n * \r\n * <p>\r\n * Title: MQHandlerManager\r\n * </p>\r\n * \r\n * <p>\r\n * Description:\r\n * </p>\r\n * \r\n * @author potatomato\r\n * \r\n * @date 2018年12月17日\r\n */\r\npublic class MQHandlerManager {\r\n\r\n private String packageName;\r\n\r\n private boolean isShowContent = false;\r\n\r\n private WebApplicationContext webApplicationContext = ContextLoader.getCurrentWebApplicationContext();;\r\n \r\n private Logger logger=Logger.getLogger(getClass());\r\n /**\r\n * 执行类\r\n */\r\n public void runHandler(MQContext context) {\r\n\r\n logger.info("开始扫描包[" + packageName + "]");\r\n List<String> classNames = getClassName(packageName);\r\n for (String className : classNames) {\r\n logger.info("MQ消息处理类:" + className);\r\n }\r\n if (isShowContent) {\r\n logger.info("消息标签:" + context.toString());\r\n logger.info("消息内容:" + context.getContent());\r\n }\r\n \r\n int count = 0;\r\n for (String className : classNames) {\r\n try {\r\n Class<?> handler = (Class<?>) Class.forName(className);\r\n if (handler.isAnnotationPresent(Topic.class)) {\r\n count++;\r\n Topic topic = (Topic) handler.getAnnotation(Topic.class);\r\n String topicVal=topic.value();\r\n if(topicVal.equals(context.getTopic())){\r\n Method[] methods=handler.getMethods();\r\n for (Method method:methods) {\r\n if(method.isAnnotationPresent(Tag.class)){\r\n Tag tag=method.getAnnotation(Tag.class);\r\n String tagStr=tag.value();\r\n String pattern = formatPattern(tagStr);\r\n boolean isMatch = Pattern.matches(pattern, context.getTag());\r\n if (isMatch) {\r\n String handlerName = bgCap2low(handler.getSimpleName());\r\n //从spring容器中获取对象\r\n method.invoke(webApplicationContext.getBean(handlerName),context);\r\n }\r\n }\r\n }\r\n }\r\n }\r\n \r\n } catch (ClassNotFoundException e) {\r\n // TODO Auto-generated catch block\r\n e.printStackTrace();\r\n } catch (Exception e) {\r\n // TODO Auto-generated catch block\r\n e.printStackTrace();\r\n }\r\n \r\n }\r\n\r\n if (count <= 0) {\r\n logger.info("消息" + context.toString() + "未匹配到处理类");\r\n return;\r\n }\r\n }\r\n\r\n /**\r\n * 根据包名获取包下的所有类\r\n * \r\n * @param packageName\r\n * @return\r\n */\r\n private List<String> getClassName(String packageName) {\r\n String filePath =this.getClass().getResource("/").getPath() + packageName.replace(".", "\\\\");\r\n \r\n List<String> fileNames = getClassName(filePath, null);\r\n return fileNames;\r\n }\r\n\r\n private List<String> getClassName(String filePath, List<String> className) {\r\n List<String> myClassName = new ArrayList<String>();\r\n File file = new File(filePath);\r\n File[] childFiles = file.listFiles();\r\n for (File childFile : childFiles) {\r\n if (childFile.isDirectory()) {\r\n myClassName.addAll(getClassName(childFile.getPath(), myClassName));\r\n } else {\r\n String childFilePath = childFile.getPath();\r\n childFilePath = childFilePath.substring(childFilePath.indexOf("\\\\classes") + 9,\r\n childFilePath.lastIndexOf("."));\r\n childFilePath = childFilePath.replace("\\\\", ".");\r\n myClassName.add(childFilePath);\r\n }\r\n }\r\n return myClassName;\r\n }\r\n\r\n private String formatPattern(String str) {\r\n str = str.replaceAll("\\\\*", "\\\\.\\\\*");\r\n return str;\r\n }\r\n \r\n /** \r\n * 把一个字符串的第一个字母小写\r\n */\r\n private String bgCap2low(String fildeName) throws Exception {\r\n byte[] items = fildeName.getBytes(); \r\n items[0] = (byte) ((char) items[0] - 'A' + 'a');\r\n return new String(items);\r\n }\r\n\r\n /**\r\n * @return the packageName\r\n */\r\n public String getPackageName() {\r\n return packageName;\r\n }\r\n\r\n /**\r\n * @param packageName\r\n * the packageName to set\r\n */\r\n public void setPackageName(String packageName) {\r\n this.packageName = packageName;\r\n }\r\n\r\n /**\r\n * @return the isShowContent\r\n */\r\n public boolean isShowContent() {\r\n return isShowContent;\r\n }\r\n\r\n /**\r\n * @param isShowContent\r\n * the isShowContent to set\r\n */\r\n public void setShowContent(boolean isShowContent) {\r\n this.isShowContent = isShowContent;\r\n }\r\n\r\n}
将mq消息处理类放在固定的包里,扫描这个包中所有的类,循环判断类中是否用了@Topic注解,再执行这个类中@Tag注解的方法进行处理。
MQ消息封装类
package cn.cxnxs.mq.pojo;\r\n\r\npublic class MQContext {\r\n\r\n private String topic;\r\n private String tag;\r\n private String content;\r\n \r\n public MQContext(String topic,String tag,String content) {\r\n this.topic=topic;\r\n this.tag=tag;\r\n this.content=content;\r\n }\r\n \r\n /**\r\n * @return the topic\r\n */\r\n public String getTopic() {\r\n return topic;\r\n }\r\n /**\r\n * @param topic the topic to set\r\n */\r\n public void setTopic(String topic) {\r\n this.topic = topic;\r\n }\r\n /**\r\n * @return the tag\r\n */\r\n public String getTag() {\r\n return tag;\r\n }\r\n /**\r\n * @param tag the tag to set\r\n */\r\n public void setTag(String tag) {\r\n this.tag = tag;\r\n }\r\n \r\n @Override\r\n public String toString(){\r\n return "[topic="+topic+",tag="+tag+"]";\r\n }\r\n /**\r\n * @return the content\r\n */\r\n public String getContent() {\r\n return content;\r\n }\r\n /**\r\n * @param content the content to set\r\n */\r\n public void setContent(String content) {\r\n this.content = content;\r\n }\r\n}
场景类
package cn.cxnxs.controller;\r\n\r\nimport java.util.List;\r\n\r\nimport org.springframework.beans.factory.annotation.Autowired;\r\nimport org.springframework.stereotype.Controller;\r\nimport org.springframework.web.bind.annotation.RequestMapping;\r\nimport org.springframework.web.bind.annotation.ResponseBody;\r\n\r\nimport cn.cxnxs.entity.Book;\r\nimport cn.cxnxs.mq.MQHandlerManager;\r\nimport cn.cxnxs.mq.pojo.MQContext;\r\nimport cn.cxnxs.service.BookService;\r\n\r\n@Controller\r\npublic class BookController {\r\n\r\n @Autowired\r\n private BookService bookServiceImpl;\r\n\r\n @RequestMapping("/handler")\r\n @ResponseBody\r\n public String handler(){\r\n MQHandlerManager manager=new MQHandlerManager();\r\n manager.setPackageName("cn.cxnxs.mq.handler");\r\n manager.setShowContent(true);\r\n String topic="h1";\r\n String tag="a";\r\n String content="{name:'张三',age:20}";\r\n MQContext context=new MQContext(topic, tag, content);\r\n manager.runHandler(context);\r\n return "OK";\r\n } \r\n}因为反射调用方法的时候用webApplicationContext获取的对象,所以要使用这套代码事先得搭建好springmvc,场景类这里就写了一个controller