首先先学习一下注解,注解为我们在代码中添加信息提供了一种形式化的方法,使得我们在稍后的某个时刻可以方便地使用这些数据。
在日常的编码中我们一直都在使用注解,只是没有特别关注过,Java中内置了三种注解:@Override,@SuppressWarnings @Deprecated。相信只要学习过Java的同学一定是见过这些主角的 。
如果我们要写一个自定义的注解应该怎么呢?
首先需要定义一个注解标注出是自定义的注解
/** * * @author zhangwei_david * @version $Id: CustomerRule.java, v 0.1 2015年5月29日 下午10:12:16 zhangwei_david Exp $ */ @Documented @Target(ElementType.ANNOTATION_TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface CustomerValidator { }这个注解中没有任何内容,属于标记注解
自定义 日期类型校验器的注解
/** * * @author zhangwei_david * @version $Id: Date.java, v 0.1 2015年5月29日 下午10:00:20 zhangwei_david Exp $ */ @Documented @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @CustomerValidator public @interface DateString { String pattern() default "yyyy-MM-dd HH:mm:ss"; String errorCode() default "must date"; String message() default "must be date pattern"; }
, @Target是用来定义该注解将可以应用在什么地方,FIELD表示该注解应用在一个属性上,@Rectetion 用来定义该注解在哪一个级别可以使用 RUNTIME表示运行时。
String pattern() default "yyyy-MM-dd HH:mm:ss" 表示如果不指定pattern这个值的时候将返回默认值“yyyy-MM-dd HH:mm:ss” 。
有了自己的注解,那么就需要一个注解的处理器,定义一个处理器接
/** *自定义注解处理器接口 * * @author zhangwei_david * @version $Id: CustomerValidatorRule.java, v 0.1 2015年5月30日 上午8:51:52 zhangwei_david Exp $ */ public interface CustomerValidatorRule { /** * 判断是否支持该注解 * * @param annotation * @param property * @return */ public boolean support(Annotation annotation); /** * 校验处理 * * * @param annotation * @param field * @param errors */ public void valid(Annotation annotation, Object object, Field field, Errors errors) throws Exception; }
/** * * @author zhangwei_david * @version $Id: AbastractCustomerValidatorRule.java, v 0.1 2015年5月30日 上午11:22:19 zhangwei_david Exp $ */ public abstract class AbastractCustomerValidatorRule implements CustomerValidatorRule { /** * @see com.cathy.core.service.annotation.rule.CustomerValidatorRule#support(java.lang.annotation.Annotation) */ public abstract boolean support(Annotation annotation); /** * @param <T> * @see com.cathy.core.service.annotation.rule.CustomerValidatorRule#valid(java.lang.annotation.Annotation, java.lang.reflect.Field, org.springframework.validation.Errors) */ public void valid(Annotation annotation, Object target, final Field field, final Errors errors) throws Exception { preHandle(annotation, target, field, errors); PropertyDescriptor propertyDescriptor = BeanUtils.getPropertyDescriptor(target.getClass(), field.getName()); Method reader = propertyDescriptor.getReadMethod(); Object property = reader.invoke(target); validProperty(annotation, property, new PostHandler() { public void postHanle(String errorCode, String message) { errors.rejectValue(field.getName(), errorCode, message); } }); } public static interface PostHandler { public void postHanle(String errorCode, String message); } /** * */ private void preHandle(Annotation annotation, Object target, Field field, Errors errors) { Assert.notNull(target); Assert.notNull(annotation); Assert.notNull(errors); Assert.notNull(field); } public abstract void validProperty(Annotation annotation, Object property, PostHandler postHandler); }
/** * * @author zhangwei_david * @version $Id: DateValidatorRule.java, v 0.1 2015年5月30日 上午11:17:09 zhangwei_david Exp $ */ @CustomerRule public class DateValidatorRule extends AbastractCustomerValidatorRule { /** * @see com.cathy.core.service.annotation.rule.CustomerValidatorRule#support(java.lang.annotation.Annotation, java.lang.Object) */ @Override public boolean support(Annotation annotation) { return annotation instanceof DateString; } /** * @see com.cathy.core.service.annotation.rule.AbastractCustomerValidatorRule#validProperty(java.lang.annotation.Annotation, java.lang.Object) */ @Override public void validProperty(Annotation annotation, Object property, PostHandler postHandler) { DateString ds = (DateString) annotation; if (parse(ds.pattern(), (String) property) == null) { postHandler.postHanle(ds.errorCode(), ds.message()); } } private Date parse(String pattern, String property) { try { SimpleDateFormat sdf = new SimpleDateFormat(pattern); return sdf.parse(property); } catch (ParseException e) { //do noting } return null; } }
这样我们就有了一个注解处理器,为了方便扩展,该处使用注解的方式加载定义的注解处理器,这就需要定义一个标注是自定义的注解处理器的注解。
/** * * @author zhangwei_david * @version $Id: CustomerValidatorRule.java, v 0.1 2015年5月30日 下午12:51:20 zhangwei_david Exp $ */ @Documented @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Component public @interface CustomerRule { }
/** * * @author zhangwei_david * @version $Id: CustomerValidatorProcesser.java, v 0.1 2015年5月30日 下午12:38:33 zhangwei_david Exp $ */ public class CustomerValidatorConfig implements ApplicationContextAware { private Map<Annotation, CustomerValidatorRule> rules = new ConcurrentHashMap<Annotation, CustomerValidatorRule>(); Map<String, Object> customerValidationRules = null; /** * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext) */ public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { customerValidationRules = applicationContext .getBeansWithAnnotation(CustomerRule.class); System.out.println(customerValidationRules); } private CustomerValidatorRule findFormMap(Annotation annotation) { for (Entry<String, Object> entry : customerValidationRules.entrySet()) { if (entry.getValue() != null && ((CustomerValidatorRule) entry.getValue()).support(annotation)) { return (CustomerValidatorRule) entry.getValue(); } } return null; } public CustomerValidatorRule findRule(Annotation annotation) { CustomerValidatorRule customerValidatorRule = null; if (!rules.containsKey(annotation)) { CustomerValidatorRule cvr = findFormMap(annotation); if (cvr != null) { rules.put(annotation, cvr); } customerValidatorRule = cvr; } customerValidatorRule = rules.get(annotation); return customerValidatorRule; } }通过实现ApplicationContextAware接口,从上下文中自动加载处理器。
/** * * @author zhangwei_david * @version $Id: CustomerValidatorFactory.java, v 0.1 2015年5月30日 下午1:03:56 zhangwei_david Exp $ */ @Component public class CustomerValidatorFactory implements Validator { @Autowired private CustomerValidatorConfig customerValidatorConfig; /** * @see org.springframework.validation.Validator#supports(java.lang.Class) */ public boolean supports(Class<?> clazz) { return true; } /** * @see org.springframework.validation.Validator#validate(java.lang.Object, org.springframework.validation.Errors) */ public void validate(Object target, Errors errors) { Assert.notNull(target); Assert.notNull(errors); List<Field> fileds = getFields(target.getClass()); for (Field field : fileds) { Annotation[] annotations = field.getAnnotations(); for (Annotation annotation : annotations) { if (annotation.annotationType().getAnnotation(CustomerValidator.class) != null) { try { CustomerValidatorRule customerValidatorRule = customerValidatorConfig .findRule(annotation); if (customerValidatorRule != null) { customerValidatorRule.valid(annotation, target, field, errors); } } catch (Exception e) { e.printStackTrace(); } } } } } /** * 获取class的fields。 * * @param clazz bean所在的class * @return */ private List<Field> getFields(Class<? extends Object> clazz) { // 声明Field数组 List<Field> fields = new ArrayList<Field>(); // 如果class类型不为空 while (clazz != null) { // 添加属性到属性数组 Collections.addAll(fields, clazz.getDeclaredFields()); clazz = clazz.getSuperclass(); } return fields; } }
使用自定义校验处理器:
/** * * @author zhangwei_david * @version $Id: MyTest.java, v 0.1 2014年12月31日 下午9:25:49 zhangwei_david Exp $ */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:spring.xml") public class DemoTest { @Autowired private Validator customerValidatorFactory; @Test public void helloTest() { Form form = new Form(); form.setCurrent("2015 11 11"); BindException errors = new BindException(form, "target"); customerValidatorFactory.validate(form, errors); System.out.println(errors.getFieldErrors()); } }
/** * * @author zhangwei_david * @version $Id: Form.java, v 0.1 2015年5月30日 下午4:04:06 zhangwei_david Exp $ */ public class Form { @DateString private String current; /** * Getter method for property <tt>current</tt>. * * @return property value of current */ public String getCurrent() { return current; } /** * Setter method for property <tt>current</tt>. * * @param current value to be assigned to property current */ public void setCurrent(String current) { this.current = current; } }运行的结果是:
五月 30, 2015 8:21:35 下午 org.springframework.test.context.TestContextManager retrieveTestExecutionListeners 信息: @TestExecutionListeners is not present for class [class com.cathy.core.service.annotation.HelloServiceTest]: using defaults. 五月 30, 2015 8:21:36 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions 信息: Loading XML bean definitions from class path resource [spring.xml] 五月 30, 2015 8:21:36 下午 org.springframework.context.support.GenericApplicationContext prepareRefresh 信息: Refreshing org.springframework.context.support.GenericApplicationContext@f7aae2: startup date [Sat May 30 20:21:36 CST 2015]; root of context hierarchy 五月 30, 2015 8:21:36 下午 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons 信息: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@19627bc: defining beans [customerValidatorFactory,dateValidatorRule,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,customerValidatorConfig,org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor]; root of factory hierarchy {dateValidatorRule=com.cathy.core.service.annotation.rule.DateValidatorRule@1758f2a} [Field error in object 'target' on field 'current': rejected value [2015 11 11]; codes [must date.target.current,must date.current,must date.java.lang.String,must date]; arguments []; default message [must be date pattern]]PS: spring的配置文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:task="http://www.springframework.org/schema/task" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.1.xsd "> <context:component-scan base-package="com.cathy.core.service"/> <bean id="customerValidatorConfig" class="com.cathy.core.service.annotation.handle.CustomerValidatorConfig"/> </beans>