• Spring中使用自定义的注解校验器的实现


      

         首先先学习一下注解,注解为我们在代码中添加信息提供了一种形式化的方法,使得我们在稍后的某个时刻可以方便地使用这些数据。

        在日常的编码中我们一直都在使用注解,只是没有特别关注过,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>
     
  • 相关阅读:
    pytest 失败重试
    mysql 增删改查
    jenkins分布式启动slave发现ip不对应实际ip
    测试架构师必读:编码规范、开发技巧、架构画图
    mysql limit和offset用法
    pytest 使用fixture给测试函数传参
    mysql union和union all
    pytest setup和teardown用法
    pytest的Hook函数详解
    pytest 用例依赖
  • 原文地址:https://www.cnblogs.com/wei-zw/p/8797776.html
Copyright © 2020-2023  润新知