在系统中使用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>

参考资料

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值