【注解】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
- 【注解】03.自定义注解案例
- spring丶自定义注解案例
- 自定义注解与validation结合使用案例
- 自定义注解与validation结合使用案例
- Java注解-自定义注解
- 【注解】02.自定义注解
- Java注解----自定义注解
- Java注解自定义注解
- 【Java】【注解】自定义注解
- 自定义注解
- 自定义注解
- 自定义注解
- 自定义注解
- 自定义注解
- 自定义注解
- 自定义注解
- @自定义注解
- 自定义注解
- 如何将一个url 地址 转化成json格式的对象
- 串口通信
- 第十四周 实践项目<1>--数组大折腾(2)
- 文章标题
- 润乾报表html代码填报
- 【注解】03.自定义注解案例
- ios 获取webview的高度
- 【eclipse高效开发】——AST的获取与访问
- 13期 12月期刊自荐
- 如何查看IDEA的快捷键设置
- 浏览器加载和渲染html的顺序
- 深度探索c++对象模型之template的具现行为
- ListView的简单自定义
- 4564: [Haoi2016]地图