zoukankan      html  css  js  c++  java
  • 在系统中使用Bean Validation验证参数

    转自:http://www.importnew.com/18561.html

    为什么要使用Bean Validation?

     当我们实现某个接口时,都需要对入参数进行校验。例如下面的代码
    1
    2
    3
    4
    5
    public String queryValueByKey(String parmTemplateCode, String conditionName, String conditionKey, String resultName) {
            checkNotNull(parmTemplateCode, "parmTemplateCode not null");
            checkNotNull(conditionName, "conditionName not null");
            checkNotNull(conditionKey, "conditionKey not null");
            checkNotNull(resultName, "resultName not null");

    该方法输入的四个参数都是必填项。用代码进行参数验证带来几个问题

    • 需要写大量的代码来进行参数验证。
    • 需要通过注释来直到每个入参的约束是什么。
    • 每个程序员做参数验证的方式不一样,参数验证不通过抛出的异常也不一样。

    什么是Bean Validation?

    Bean Validation是一个通过配置注解来验证参数的框架,它包含两部分Bean Validation API和Hibernate Validator。

    • Bean Validation API是Java定义的一个验证参数的规范。
    • Hibernate Validator是Bean Validation API的一个实现。

    快速开始

    引入POM

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    <!-- Bean Validation start -->
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-validator</artifactId>
        <version>5.1.1.Final</version>
    </dependency>
    <dependency>
        <groupId>javax.validation</groupId>
        <artifactId>validation-api</artifactId>
        <version>1.1.0.Final</version>
    </dependency>
    <dependency>
        <groupId>javax.el</groupId>
        <artifactId>el-api</artifactId>
        <version>2.2</version>
    </dependency>
    <dependency>
        <groupId>org.glassfish.web</groupId>
        <artifactId>javax.el</artifactId>
        <version>2.2.4</version>
    </dependency>
    <dependency>
        <groupId>org.jboss.logging</groupId>
        <artifactId>jboss-logging</artifactId>
        <version>3.1.3.GA</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml</groupId>
        <artifactId>classmate</artifactId>
        <version>1.0.0</version>
    </dependency>
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.13</version>
    </dependency>
    <!-- Bean Validation end -->

    实例代码如下,可以验证Bean,也可以验证方法参数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    import java.lang.reflect.Method;
    import java.util.Set;
     
    import javax.validation.ConstraintViolation;
    import javax.validation.Validation;
    import javax.validation.Validator;
    import javax.validation.constraints.Max;
    import javax.validation.constraints.NotNull;
    import javax.validation.executable.ExecutableValidator;
     
    public class BeanValidatorTest {
     
        public static void main(String[] args) {
            Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
            //验证Bean参数,并返回验证结果信息
            Car car = new Car();
            Set<ConstraintViolation<Car>> validators = validator.validate(car);
            for (ConstraintViolation<Car> constraintViolation : validators) {
                System.out.println(constraintViolation.getMessage());
            }
     
            // 验证方法参数
            Method method = null;
            try {
                method = Car.class.getMethod("drive", int.class);
            } catch (SecurityException e) {
            } catch (NoSuchMethodException e) {
            }
            Object[] parameterValues = { 80 };
            ExecutableValidator executableValidator = validator.forExecutables();
            Set<ConstraintViolation<Car>> methodValidators = executableValidator.validateParameters(car,
                method, parameterValues);
            for (ConstraintViolation<Car> constraintViolation : methodValidators) {
                System.out.println(constraintViolation.getMessage());
            }
        }
     
        public static class Car {
     
            private String name;
     
            @NotNull(message = "车主不能为空")
            public String getRentalStation() {
                return name;
            }
     
            public void drive(@Max(75) int speedInMph) {
     
            }
     
        }
    }

    执行代码后,输出如下:

     
    1
    2
    车主不能为空
    最大不能超过75

    使用代码验证方法参数

    Validation验证不成功可能返回多个验证错误信息,我们可以包装下,当有错误时直接返回第一个错误的异常。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    import static com.google.common.collect.Iterables.getFirst;
     
    import java.util.Set;
     
    import javax.validation.ConstraintViolation;
    import javax.validation.Validation;
    import javax.validation.Validator;
     
    /**
     * 对象验证器
     *
     * @author tengfei.fangtf
     * @version $Id: BeanValidator.java, v 0.1 Dec 30, 2015 11:33:40 PM tengfei.fangtf Exp $
     */
    public class BeanValidator {
     
        /**
         * 验证某个bean的参数
         *
         * @param object 被校验的参数
         * @throws ValidationException 如果参数校验不成功则抛出此异常
         */
        public static <T> void validate(T object) {
            //获得验证器
            Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
            //执行验证
            Set<ConstraintViolation<T>> constraintViolations = validator.validate(object);
            //如果有验证信息,则将第一个取出来包装成异常返回
            ConstraintViolation<T> constraintViolation = getFirst(constraintViolations, null);
            if (constraintViolation != null) {
                throw new ValidationException(constraintViolation);
            }
        }
     
    }

    我们可以在每个方法的第一行调用BeanValidator.validate来验证参数,测试代码如下,

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    import static junit.framework.Assert.assertEquals;
     
    import javax.validation.constraints.Max;
    import javax.validation.constraints.NotNull;
     
    import org.junit.Test;
     
    /**
     *
     * @author tengfei.fangtf
     * @version $Id: BeanValidatorTest.java, v 0.1 Dec 30, 2015 11:33:56 PM tengfei.fangtf Exp $
     */
    public class BeanValidatorTest {
     
        @Test
        public void test() {
            try {
                BeanValidator.validate(new Car());
            } catch (Exception e) {
                assertEquals("rentalStation 车主不能为空", e.getMessage());
            }
        }
     
        public static class Car {
     
            private String name;
     
            @NotNull(message = "车主不能为空")
            public String getRentalStation() {
                return name;
            }
     
            public void drive(@Max(75) int speedInMph) {
     
            }
     
        }
     
    }

    使用拦截器验证方法参数

    我们在对外暴露的接口的入参中使用Bean Validation API配置参数约束,如下XXXService接口

    1
    2
    3
    4
    5
    public interface XXXService {
     
    GetObjectResponse getObject(GetObjectRequest getObjectRequest);
     
    }

    在getObject的GetObjectRequest参数中配置注解来约束参数。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    public class GetObjectRequest {
     
        @Valid
        @NotNull
        private ObjectKey      objectKey;
     
        @Size(max = 9)
        private Map&lt;String, Object&gt; parameters;
     
        @AssertTrue
        public boolean isEntityNameOrCodeAtLeastOneIsNotBlank() {
            return isNotBlank(entityName) || isNotBlank(entityCode);
        }
    //代码省略
    }

    编写参数验证拦截器,当方法被调用时,触发Validator验证器执行验证,如果不通过则抛出ParameterValidationException。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    import static com.google.common.collect.Iterables.getFirst;
     
    import java.util.Set;
     
    import javax.validation.ConstraintViolation;
    import javax.validation.Validation;
    import javax.validation.Validator;
     
    import org.aopalliance.intercept.MethodInterceptor;
    import org.aopalliance.intercept.MethodInvocation;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
     
    import com.xx.ParameterValidationException;
     
    /**
     * 参数验证拦截器,基于JSR-303 BeanValidation
     *
     * @author tengfei.fangtf
     *
     * @version $Id: TitanValidateInterceptor.java, v 0.1 Nov 23, 2015 11:13:55 PM tengfei.fangtf Exp $
     */
    public class TitanValidateInterceptor implements MethodInterceptor {
     
        private static final Logger LOGGER = LoggerFactory.getLogger(TitanValidateInterceptor.class);
     
        private final Validator     validator;
     
        public TitanValidateInterceptor() {
            validator = Validation.buildDefaultValidatorFactory().getValidator();
        }
     
        @Override
        public Object invoke(MethodInvocation invocation) throws Throwable {
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Validate arguments");
            }
            //获取参数,并检查是否应该验证
            Object[] arguments = invocation.getArguments();
            for (Object argument : arguments) {
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Validate argument: {}", argument);
                }
                Set<ConstraintViolation<Object>> constraintViolations = validator.validate(argument);
                ConstraintViolation<Object> constraintViolation = getFirst(constraintViolations, null);
                if (constraintViolation == null) {
                    continue;
                }
                if (LOGGER.isInfoEnabled()) {
                    LOGGER.info("ConstraintViolation: {}", constraintViolation);
                }
                throw new ParameterValidationException(constraintViolation.getPropertyPath() + " " + constraintViolation.getMessage());
            }
            return invocation.proceed();
        }
     
    }

    配置拦截器core-service.xml,拦截XXXService的所有方法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:webflow="http://www.springframework.org/schema/webflow-config"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
             http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
             http://www.springframework.org/schema/webflow-config http://www.springframework.org/schema/webflow-config/spring-webflow-config-2.0.xsd"
        default-autowire="byName">
     
        <bean id="XXXService" class="org.springframework.aop.framework.ProxyFactoryBean">
            <property name="target">
                <bean class="com.XXXService" />
            </property>
            <property name="interceptorNames">
                <list>
                    <value>validateInterceptor</value>
                </list>
            </property>
        </bean>
     
        <bean id="validateInterceptor"
            class="com.mybank.bkloanapply.common.validator.ValidateInterceptor" />
    </beans>

    参考资料

  • 相关阅读:
    前端与算法 leetcode 387. 字符串中的第一个唯一字符
    前端与算法 leetcode 283. 移动零
    前端与算法 leetcode 125. 验证回文串
    前端与算法 leetcode 7. 整数反转
    vue-create 报错 command failed: yarn --registry=https://registry.npm.taobao.org --disturl=https://npm.taobao.org/dist 完美解决方案
    前端与算法 leetcode 242. 有效的字母异位词
    javascript 解决默认取整的坑(目前已知的最佳解决方案)
    Excel中countif函数的使用方法
    excel的函数基础——if条件函数该怎么用?
    excel 这也许是史上最好最全的VLOOKUP函数教程
  • 原文地址:https://www.cnblogs.com/toSeeMyDream/p/9414450.html
Copyright © 2011-2022 走看看