【注解】03.自定义注解案例

来源:互联网 发布:linux ping 测试 编辑:程序博客网 时间:2024/06/08 15:54

自定义注解案例


学习完自定义注解,那就该去尝试使用它,下面将以一个真实场景案例来演示注解的使用。

需求:对于FlowNode接口的扩展,当业务有变化时,需要扩展FlowNode,扩展时还要根据FlowNode的类型和标签名生成XML,可以理解为每个扩展的FlowNode对应一个标签。

未使用注解时,采用的是静态工厂模式,代码如下:

package com.qxl.flow;import java.util.HashMap;import java.util.Map;/**  * 根据类型实例化FlowNode节点,当添加结点类型时,只需在static中put即可。 * @author qxl * @date 2016年11月14日 下午6:05:12  * @version 1.0.0 */public class FlowNodeFactory {/** 开始 */public static final String TYPE_START = "start";/** 动作 */public static final String TYPE_TASK = "task";/** 分支 */public static final String TYPE_FORK = "fork";/** 聚合  */public static final String TYPE_JOIN = "join";/** 结束 */public static final String TYPE_END = "end";/** 正确*/public static final String TYPE_TRUE = "T";/** 错误*/public static final String TYPE_FALSE = "F";/** 模型分支*/public static final String TYPE_FORKTEXT = "forkTxt";public static final String TYPE_ROOTELE_TAG = "rootElement";public static final String TYPE_VAR_TAG = "property";public static final String TYPE_PROCESS_TAG = "process";public static final String TYPE_SEQUENCEFLOW_TAG = "sequenceFlow";/** 决策树中生成的drl规则的groupid前缀 */public static final String ruleFlowGroupHeader = "group_";/** 决策树中生成的drl规则的id前缀 */public static final String ruleFlowRuleHeader = "rule_";private static Map<String,String> flowMap = new HashMap<String,String>();private static Map<String,String> flowTagMap = new HashMap<String,String>();static{//类型与FlowNode类对应的关系flowMap.put(TYPE_START, "com.qxl.flow.basicflow.impl.StartFlowNode");flowMap.put(TYPE_TASK, "com.qxl.flow.basicflow.ActionFlowNode");flowMap.put(TYPE_FORK, "com.qxl.flow.basicflow.impl.DivergeFlowNode");flowMap.put(TYPE_JOIN, "com.qxl.flow.basicflow.impl.ConvergeFlowNode");flowMap.put(TYPE_END, "com.qxl.flow.basicflow.impl.EndFlowNode");flowMap.put(TYPE_TRUE, "com.qxl.flow.basicflow.impl.TrueFlowNode");flowMap.put(TYPE_FALSE, "com.qxl.flow.basicflow.impl.FalseFlowNode");flowMap.put(TYPE_FORKTEXT, "com.qxl.flow.basicflow.impl.ForkTxtFlowNode");//类型与标签对应关系flowTagMap.put(TYPE_START, "startEvent");flowTagMap.put(TYPE_TASK, "businessRuleTask");flowTagMap.put(TYPE_FORK, "inclusiveGateway");flowTagMap.put(TYPE_JOIN, "parallelGateway");flowTagMap.put(TYPE_END, "endEvent");flowTagMap.put(TYPE_ROOTELE_TAG, "definitions");flowTagMap.put(TYPE_VAR_TAG, "property");flowTagMap.put(TYPE_PROCESS_TAG,"process");flowTagMap.put(TYPE_SEQUENCEFLOW_TAG, "sequenceFlow");}/** * 根据节点类型,实例化FlowNode对象 * @param type * @return * @throws Exception */public static FlowNode getFlowNode(String type) throws Exception{if(!flowMap.containsKey(type)){throw new Exception("This type is illegal.");}String classStr = flowMap.get(type);FlowNode flowNode = (FlowNode) Class.forName(classStr).newInstance();return flowNode;}public static String getFlowTag(String type) throws Exception{if(!flowTagMap.containsKey(type)){throw new Exception("This type is illegal.");}return flowTagMap.get(type);}}

将他们的对应关系存在Map中,需要时从Map中取即可,这样,当扩展一个FlowNode时,需要修改此工厂方法,不够完美,当使用注解时,就可以解决此问题,将静态工厂变成动态工厂。


使用注解思路:

  1)自定义类注解,包括类型和标签名两个参数

  2)扩展FlowNode时,加上此注解

  3)实现类似spring的扫描,扫描指定包下所有包含指定注解的所有类,将他们添加到Map中,使用从Map中取出。

示例代码:

FlowNodeAnno.java--注解

package com.framework.rule.resources.flow.annotation;import java.lang.annotation.Documented;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;/**  * FlowNode的注解,当扩展标签时,在类的前面加上此注解即可 *   type为必填项,与前端扩展的节点的类型相对应; *   tagName为选填,若想生成的bpmn中有此标签,则在此变量上指定标签名 * @author qxl * @date 2016年11月23日 下午4:59:53  * @version 1.0.0 */@Documented@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)public @interface FlowNodeAnno {/** * 对应前端形状的类型 * @return */String type();/** * 对应bpmn中标签的名字 * @return */String tagName() default "";}
LoadFlowNodeClasses.java--扫描工具类

package com.framework.rule.resources.flow.annotation;import java.io.IOException;import java.lang.annotation.Annotation;import java.util.HashSet;import java.util.LinkedList;import java.util.List;import java.util.Set;import org.apache.commons.logging.Log;  import org.apache.commons.logging.LogFactory;  import org.springframework.core.io.Resource;  import org.springframework.core.io.support.PathMatchingResourcePatternResolver;  import org.springframework.core.io.support.ResourcePatternResolver;  import org.springframework.core.type.classreading.CachingMetadataReaderFactory;  import org.springframework.core.type.classreading.MetadataReader;  import org.springframework.core.type.classreading.MetadataReaderFactory;  import org.springframework.core.type.filter.AnnotationTypeFilter;  import org.springframework.core.type.filter.TypeFilter;  import org.springframework.util.ClassUtils; /**  * 此类会扫描指定路径下所有标注了指定的注解的类, * 通过getClassSet方法可获取所有符合条件的class的集合 * @author qxl * @date 2016年11月23日 下午3:00:23  * @version 1.0.0 */public class LoadFlowNodeClasses {protected final Log logger = LogFactory.getLog(getClass());          private static final String RESOURCE_PATTERN = "/**/*.class";            private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();            private List<String> packagesList= new LinkedList<String>();            private List<TypeFilter> typeFilters = new LinkedList<TypeFilter>();            private Set<Class<?>> classSet= new HashSet<Class<?>>();            /**      * 构造函数      * @param packagesToScan 指定哪些包需要被扫描,支持多个包"package.a,package.b"并对每个包都会递归搜索      * @param annotationFilter 指定扫描包中含有特定注解标记的bean,支持多个注解      */      public LoadFlowNodeClasses(String[] packagesToScan, Class<? extends Annotation>... annotationFilter){          if (packagesToScan != null) {              for (String packagePath : packagesToScan) {                  this.packagesList.add(packagePath);              }          }          if (annotationFilter != null){              for (Class<? extends Annotation> annotation : annotationFilter) {                  typeFilters.add(new AnnotationTypeFilter(annotation, false));              }          }      }            /**      * 将符合条件的Bean以Class集合的形式返回      * @return      * @throws IOException      * @throws ClassNotFoundException      */      public Set<Class<?>> getClassSet() throws IOException, ClassNotFoundException {          this.classSet.clear();          if (!this.packagesList.isEmpty()) {                  for (String pkg : this.packagesList) {                      String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +                              ClassUtils.convertClassNameToResourcePath(pkg) + RESOURCE_PATTERN;                      Resource[] resources = this.resourcePatternResolver.getResources(pattern);                      MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(this.resourcePatternResolver);                      for (Resource resource : resources) {                          if (resource.isReadable()) {                              MetadataReader reader = readerFactory.getMetadataReader(resource);                              String className = reader.getClassMetadata().getClassName();                            if (matchesEntityTypeFilter(reader, readerFactory)) {                                  this.classSet.add(Class.forName(className));                              }                          }                      }                  }          }          //输出日志          if (logger.isInfoEnabled()){              for (Class<?> clazz : this.classSet) {                  logger.info(String.format("Found class:%s", clazz.getName()));              }          }          return this.classSet;      }                    /**      * 检查当前扫描到的Bean含有任何一个指定的注解标记      * @param reader      * @param readerFactory      * @return      * @throws IOException      */      private boolean matchesEntityTypeFilter(MetadataReader reader, MetadataReaderFactory readerFactory) throws IOException {          if (!this.typeFilters.isEmpty()) {              for (TypeFilter filter : this.typeFilters) {                  if (filter.match(reader, readerFactory)) {                    return true;                  }              }          }          return false;      }    }

FlowNodeFactory.java -- FlowNode工厂类

package com.framework.rule.resources.flow;import java.io.IOException;import java.util.HashMap;import java.util.Map;import java.util.Set;import com.framework.rule.resources.flow.annotation.FlowNodeAnno;import com.framework.rule.resources.flow.annotation.LoadFlowNodeClasses;import com.framework.util.FrameLogUtil;import com.framework.util.SpringUtils;/**  * 根据类型实例化FlowNode节点,当添加结点类型时,只需在static中put即可。 * @author qxl * @date 2016年11月14日 下午6:05:12  * @version 1.0.0 */public class FlowNodeFactory {/** 开始 */public static final String TYPE_START = "start";/** 结束 */public static final String TYPE_END = "end";/** 分支 */public static final String TYPE_FORK = "fork";public static final String TYPE_ROOTELE_TAG = "rootElement";public static final String TYPE_VAR_TAG = "property";public static final String TYPE_PROCESS_TAG = "process";public static final String TYPE_SEQUENCEFLOW_TAG = "sequenceFlow";/** 决策树中生成的drl规则的groupid前缀 */public static final String ruleFlowGroupHeader = "group_";/** 决策树中生成的drl规则的id前缀 */public static final String ruleFlowRuleHeader = "rule_";private static Map<String,String> flowMap = new HashMap<String,String>();private static Map<String,String> flowTagMap = new HashMap<String,String>();static {LoadFlowNodeClasses loadFlowNodeClasses = (LoadFlowNodeClasses)(SpringUtils.getBean("loadFlowNodeClasses"));Set<Class<?>> classSet = null;try {classSet = loadFlowNodeClasses.getClassSet();for (Class<?> clazz : classSet) {  System.out.println(clazz.getName());FlowNodeAnno flowNode = (FlowNodeAnno)clazz.getAnnotation(FlowNodeAnno.class);            if(flowNode != null){            String className = clazz.getName();//获取类名            String type = flowNode.type();//获取FlowNode类型            String tagName = flowNode.tagName();//获取对应标签名            flowMap.put(type, className);            if(tagName!=null && !tagName.isEmpty()){            flowTagMap.put(type, tagName);            }            }        }} catch (ClassNotFoundException e) {FrameLogUtil.error(FlowNodeFactory.class, e.getMessage());} catch (IOException e) {FrameLogUtil.error(FlowNodeFactory.class, e.getMessage());}flowTagMap.put(TYPE_ROOTELE_TAG, "definitions");flowTagMap.put(TYPE_VAR_TAG, "property");flowTagMap.put(TYPE_PROCESS_TAG,"process");flowTagMap.put(TYPE_SEQUENCEFLOW_TAG, "sequenceFlow");}/** * 根据节点类型,实例化FlowNode对象 * @param type * @return * @throws Exception */public static FlowNode getFlowNode(String type) throws Exception{if(!flowMap.containsKey(type)){throw new Exception("The type of ["+type+"] is illegal.");}String classStr = flowMap.get(type);FlowNode flowNode = (FlowNode) Class.forName(classStr).newInstance();return flowNode;}/** * 根据节点类型,获取对应标签名 * @param type * @return * @throws Exception */public static String getFlowTag(String type) throws Exception{if(!flowTagMap.containsKey(type)){throw new Exception("The type of ["+type+"] is illegal.");}return flowTagMap.get(type);}}

applicationContext.xml中扫描类的配置

<!-- 加载所有标注@FlowNodeAnno注解 --><bean id="loadFlowNodeClasses" class="com.framework.rule.resources.flow.annotation.LoadFlowNodeClasses">      <constructor-arg value="com.framework.rule.resources.flow.basicflow" />      <constructor-arg >      <value>com.framework.rule.resources.flow.annotation.FlowNodeAnno</value>      </constructor-arg>  </bean> 

ActionFlowNode.java -- 注解使用示例类

package com.framework.rule.resources.flow.basicflow;import java.util.Map;import org.dom4j.Element;import com.framework.rule.resources.ExpressRuleResource;import com.framework.rule.resources.RulePackage;import com.framework.rule.resources.flow.FlowNodeFactory;import com.framework.rule.resources.flow.FlowNodeParse;import com.framework.rule.resources.flow.annotation.FlowNodeAnno;import com.framework.util.FrameLogUtil;import com.framework.web.vo.ExpRuleContentVo;/**  * * @author qxl * @date 2016年11月14日 上午10:10:43  * @version 1.0.0 */@FlowNodeAnno(type="task",tagName="businessRuleTask")public class ActionFlowNode extends AbstractFlowNode implements FlowNodeParse{public static final String conditionKey = "condition";private final String actionKey = "action";private String condition;private String action;public String getCondition() {return condition;}public void setCondition(String condition) {this.condition = condition;}public String getAction() {return action;}public void setAction(String action) {this.action = action;}@Overridepublic void serialize(Map<String, String> data) {if(data.containsKey(conditionKey)){this.condition = data.get(conditionKey);}if(data.containsKey(actionKey)){this.action = data.get(actionKey);}}@Overridepublic Element parseToXml(Element ele,Map<String,String> data) {try {Element e = ele.addElement(FlowNodeFactory.getFlowTag(this.getNodeType()));e.addAttribute("id", this.getNodeId());e.addAttribute("name", this.getNodeType());e.addAttribute("g:ruleFlowGroup", FlowNodeFactory.ruleFlowGroupHeader+data.get("ruleId")+"_"+this.getNodeId());Element e1 = e.addElement("ioSpecification");e1.addElement("inputSet");e1.addElement("outputSet");} catch (Exception e) {FrameLogUtil.error(getClass(), e.getMessage());}return ele;}@Overridepublic ExpressRuleResource parseToDrl(RulePackage rulePackage,Map<String,String> data) {ExpRuleContentVo expRuleVo = new ExpRuleContentVo();expRuleVo.setCdtTxtCode(null);expRuleVo.setMeetTxtCode(this.getAction());ExpressRuleResource expRule = new ExpressRuleResource();expRule.setGroup(true);expRule.setGroupName(FlowNodeFactory.ruleFlowGroupHeader+data.get("ruleId")+"_"+this.getNodeId());expRule.setRuleId(FlowNodeFactory.ruleFlowRuleHeader+data.get("ruleId")+"_"+this.getNodeId());expRule.setRuleName(FlowNodeFactory.ruleFlowRuleHeader+data.get("ruleId")+"_"+this.getNodeId());expRule.setRulePackage(rulePackage);expRule.initFromExpRule(expRuleVo);return expRule;}}


至此,此案例的全部示例代码已贴出来,其中扫描部分是参考网上大神写的,稍作了些修改,但是据此文章的撰写已经很久,导致以及忘记原文链接,故希望原博主不要怪罪。扫描部分基于spring的配置,后期有时间我将研究不基于spring的扫描工具类。

当经过这样修改后,如果需要扩展FlowNode时,直接在指定包下写一个新的实现,然后加上注解,不需要修改其他代码,系统将会自动识别此新扩展的节点。这样就把静态工厂变成了动态工厂。




0 0
原创粉丝点击