转自: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>

参考资料

在系统中使用Bean Validation验证参数的更多相关文章

  1. Java Bean Validation(参数校验) 最佳实践

    转载来自:http://www.cnblogs.com 参数校验是我们程序开发中必不可少的过程.用户在前端页面上填写表单时,前端js程序会校验参数的合法性,当数据到了后端,为了防止恶意操作,保持程序的 ...

  2. SpringMVC中实现Bean Validation(JSR 303 JSR 349 JSR 380)

    JSR 303是针对bean数据校验提出的一个规范.使用注解方式实现数据校验. 每个注解的用法这里就不多介绍,请移步JSR 303 - Bean Validation 介绍及最佳实践 笔者上面提到的J ...

  3. 利用 Bean Validation 来简化接口请求参数校验

    团队新来了个校招实习生静静,相互交流后发现竟然是我母校同实验室的小学妹,小学妹很热情地认下了我这个失散多年的大湿哥,后来... 小学妹:大湿哥,咱们项目里的 Controller 怎么都看不到参数校验 ...

  4. JSR 303 - Bean Validation 介绍及最佳实践

    JSR 303 - Bean Validation 介绍及最佳实践 JSR 303 – Bean Validation 是一个数据验证的规范,2009 年 11 月确定最终方案.2009 年 12 月 ...

  5. JSR教程1——JSR 303 - Bean Validation介绍

    1.Bean Validation 在任何时候,当你要处理一个应用程序的业务逻辑,数据校验是你必须要考虑和面对的事情.应用程序必须通过某种手段来确保输入进来的数据从语义上来讲是正确的.在通常的情况下, ...

  6. JSR 303 - Bean Validation 介绍及最佳实践(转)

    JSR 303 – Bean Validation 是一个数据验证的规范,2009 年 11 月确定最终方案.2009 年 12 月 Java EE 6 发布,Bean Validation 作为一个 ...

  7. Bean Validation规范

    以下内容转载自:https://www.ibm.com/developerworks/cn/java/j-lo-beanvalid/ Bean Validation规范介绍 JSR303 规范(Bea ...

  8. 一个新人如何学习在大型系统中添加新功能和Debug

    文章背景: 今年七月份正式入职,公司主营ERP软件,楼主所在的组主要负责二次开发,使用的语言是Java. 什么叫二次开发呢?ERP软件的客户都是企业.而这些企业之间的情况都有所不同,一套标准版本的企业 ...

  9. 系统中异常公共处理模块 in spring boot

    最近在用spring boot 做微服务,所以对于异常信息的 [友好展示]有要求,我设计了两点: 一. 在业务逻辑代码中,异常的抛出 我做了限定,一般只会是三种: 1. OmcException // ...

随机推荐

  1. Spring+MyBatis实现数据库读写分离方案

    推荐第四种:https://github.com/shawntime/shawn-rwdb 方案1 通过MyBatis配置文件创建读写分离两个DataSource,每个SqlSessionFactor ...

  2. github----awesome-typescript-projects

    https://github.com/brookshi/awesome-typescript-projects

  3. jms和activemq简介

    一.JMS简介 JMS即Java消息服务(Java Message Service)应用程序接口,是一个Java平台中关于面向消息中间件(MOM)的API,用于在两个应用程序之间,或分布式系统中发送消 ...

  4. jscs sublime 插件配置 .jscsrc 文件

    { "disallowEmptyBlocks": true, "disallowKeywordsOnNewLine": ["else", & ...

  5. MySql存储过程与函数详解

    存储过程和函数是在数据库中定义一些SQL语句的集合,然后直接调用这些存储过程和函数来执行已经定义好的SQL语句.存储过程和函数可以避免开发人员重复的编写相同的SQL语句.而且,存储过程和函数是在MyS ...

  6. solr如何让全词匹配结果在最前面

    在全文搜索中默认排序是按照匹配度权值score排序的,权值越大位置越靠前,那为什么有很多时候全词匹配反而不在最前面那,其实很简单因为全词匹配权值也就是100,但是还有很多权值大于100的排在了前面. ...

  7. Python源码文件中带有中文时,输出乱码

    Python源码文件中带有中文时,文件头应加注释: #!/usr/bin/env python # -*- coding: utf-8 -*- 第一行注释是为了告诉Linux/OS X系统,这是一个P ...

  8. spring得到所有Action类

    applicationContext对象.getBeansOfType(BaseAction.class)

  9. python学习之----BuildSoup和正则表达式

    在抓取网页的时候,BeautifulSoup 和正则表达式总是配合使用的.其实,大多数支 持字符串参数的函数(比如,find(id="aTagIdHere"))都可以用正则表达式实 ...

  10. WPF datagrid 设置表头线与颜色、透明度

    <!--数据网格的列标题样式属性--> <DataGrid.ColumnHeaderStyle> <!--样式类型:DataGridColumnHeader(数据网格列标 ...