本文讲解基于jsr303和jsr349标准的 Bean Validation规范。经过如下部分讲解:java
Bean Validation 是一个运行时的数据验证框架,在验证以后验证的错误信息会被立刻返回。数据校验是任何一个应用程序都会用到的功能,不管是显示层仍是持久层. 一般,相同的校验逻辑会分散在各个层中, 这样,不只浪费了时间还会致使重复代码(以下图). 为了不重复, 开发人员常常会把这些校验逻辑直接写在领域模型里面, 可是这样又把领域模型代码和校验代码混杂在了一块儿, 而这些校验逻辑更应该是描述领域模型的元数据. JSR 303,349 - Bean Validation - 为实体验证定义了元数据模型和API. 默认的元数据模型是经过Annotations来描述的,可是也可使用XML来重载或者扩展. Bean Validation API 并不局限于应用程序的某一层或者哪一种编程模型, 例如,以下图所示, Bean Validation 能够被用在任何一层, 或者是像相似Swing的富客户端程序中.
一个 constraint 一般由 annotation 和相应的 constraint validator 组成,它们是一对多的关系。也就是说能够有多个 constraint validator 对应一个 annotation。在运行时,Bean Validation 框架自己会根据被注释元素的类型来选择合适的 constraint validator 对数据进行验证。git
在 Java Bean 中,对某一方法、字段、属性或其组合形式等进行约束的注解,即为约束注解web
@NotNull(message = "The id of employee can not be null") private Integer id;
对于每个约束注解,在实际使用前必须有相关定义。JSR303 规范默认提供了几种约束注解的定义,咱们也能够扩展规范提供的 API,实现符合自身业务需求的约束注解(下文有示例)。 表 1. Bean Validation 中内置的 constraint正则表达式
Constraint | 详细信息 |
---|---|
@Null | 被注释的元素必须为 null |
@NotNull | 被注释的元素必须不为 null |
@AssertTrue | 被注释的元素必须为 true |
@AssertFalse | 被注释的元素必须为 false |
@Min(value) | 被注释的元素必须是一个数字,其值必须大于等于指定的最小值 |
@Max(value) | 被注释的元素必须是一个数字,其值必须小于等于指定的最大值 |
@DecimalMin(value) | 被注释的元素必须是一个数字,其值必须大于等于指定的最小值 |
@DecimalMax(value) | 被注释的元素必须是一个数字,其值必须小于等于指定的最大值 |
@Size(max, min) | 被注释的元素的大小必须在指定的范围内 |
@Digits (integer, fraction) | 被注释的元素必须是一个数字,其值必须在可接受的范围内 |
@Past | 被注释的元素必须是一个过去的日期 |
@Future | 被注释的元素必须是一个未来的日期 |
@Pattern(value) | 被注释的元素必须符合指定的正则表达式 |
标准实现者如hibernate-validater提供的约束以下: |
Constraint | 详细信息 |
---|---|
被注释的元素必须是电子邮箱地址 | |
@Length | 被注释的字符串的大小必须在指定的范围内 |
@NotEmpty | 被注释的字符串的必须非空 |
@Range | 被注释的元素必须在合适的范围内 |
约束注解和普通的注解同样,一个典型的约束注解的定义应该至少包括以下内容:spring
约束注解应用的目标元素类型包括 METHOD, FIELD, TYPE, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER。METHOD 约束相关的 getter 方法;FIELD 约束相关的属性;TYPE 约束具体的 Java Bean;ANNOTATION_TYPE 用在组合约束中;该规范一样也支持对参数(PARAMETER)和构造器(CONSTRUCTOR)的约束。数据库
@Target({ }) // 约束注解应用的目标元素类型 @Retention() // 约束注解应用的时机 @Constraint(validatedBy ={}) // 与约束注解关联的验证器 public @interface ConstraintName{ String message() default " "; // 约束注解验证时的输出消息 Class<?>[] groups() default { }; // 约束注解在验证时所属的组别 Class<? extends Payload>[] payload() default { }; // 约束注解的有效负载 }
默认验证组别为:javax.validation.groups.Default编程
有效负载一般用来将一些元数据信息与该约束注解相关联,经常使用的一种状况是用负载表示验证结果的严重程度。api
最后经过一段示例代码描述如何定义约束注解:服务器
@Target( { METHOD, FIELD, ANNOTATION_TYPE }) @Retention(RUNTIME) @Constraint(validatedBy = MobliePhoneValidator.class) @Documented public @interface MobliePhone { String message() default "{com.xdja.constraints.MobliePhone.message}"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; // String value(); }
约束必须是11号手机号码。mvc
约束验证规则的定义实现ConstraintValidator接口。 以下示例代码为定义一个简单的玉树验证规则:
public class MobliePhoneValidator implements ConstraintValidator<MobliePhone, String> { final String regExp = "^[1]([3][0-9]{1}|59|58|88|89)[0-9]{8}$"; public void initialize(MobliePhone constraintAnnotation) {//初始化 } public boolean isValid(String object, ConstraintValidatorContext constraintContext) {//验证方法 boolean isValid = false; if (object == null){ return isValid; } if (Pattern.compile(regExp).matcher(object).matches()) { isValid = true; } if (isValid) { constraintContext.disableDefaultConstraintViolation(); constraintContext.buildConstraintViolationWithTemplate("{com.xdja.constraints.MobliePhone.message}") .addConstraintViolation(); } return isValid; } }
虽然能够经过Bean Validation 提供的@Pattern(value) 注解约束可是这里只是提供一个示例参考。
约束注解的声明及为使用定义好的和已有的约束注解、参考以下示例代码:
class User{ @MobliePhone//约束注解的声明 private String phone; public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; } }
在使用jsr303 or jsr 349时须要分别导入:
validation-api-1.0.0.GA.jar JSR-303规范API包 hibernate-validator-4.3.0.Final.jar Hibernate 参考实现 对JavaBean进行验证,如方法级别(方法参数/返回值)
validation-api-1.1.0.Final.jar hibernate-validator-5.2.4.Final.jar 跨参数验证(好比密码和确认密码的验证)和支持在消息中使用EL表达式
介绍一下SPI技术
一个服务(service)一般指的是已知的接口或者抽象类,服务提供方就是对这个接口或者抽象类的实现,而后按spi标准存放到资源路径META-INF/services目录下,文件的命名为该服务接口的全限定名。若有一个服务接口com.test.Service,其服务实现类为com.test.ChildService,那此时须要在META-INF/services中放置文件com.test.Service,其中的内容就为该实现类的全限定名com.test.ChildService,有多个服务实现,每一行写一个服务实现,#后面的内容为注释,而且该文件只可以是以UTF-8编码。 也能够参考以下网址:Java中的SPI(Service Provider Interface)介绍及示例 那么hibernate-validator是如何提供默认实现呢?以下图: 因而可知是基于Java SPI技术的标准实现。
首先从得到Validator 对象开始:
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
源码分析javax.validation.Validation类的buildDefaultValidatorFactory方法为:
public static ValidatorFactory buildDefaultValidatorFactory() { return byDefaultProvider().configure().buildValidatorFactory(); }
得到Configuration对象信息为建立ValidatorFactory作准备。继续查看javax.validation.Validation类的byDefaultProvider方法:
public static GenericBootstrap byDefaultProvider() { return new GenericBootstrapImpl(); }
javax.validation.Validation.GenericBootstrapImpl 类是javax.validation.Validation类的 内部私有静态类,方法configure是核心方法:
public Configuration<?> configure() { ValidationProviderResolver resolver = this.resolver == null ? getDefaultValidationProviderResolver() ://注意此处调用 this.resolver; List<ValidationProvider<?>> validationProviders; try { validationProviders = resolver.getValidationProviders();//得到ValidationProvider } // don't wrap existing ValidationExceptions in another ValidationException catch ( ValidationException e ) { throw e; } // if any other exception occurs wrap it in a ValidationException catch ( RuntimeException re ) { throw new ValidationException( "Unable to get available provider resolvers.", re ); } if ( validationProviders.size() == 0 ) { String msg = "Unable to create a Configuration, because no Bean Validation provider could be found." + " Add a provider like Hibernate Validator (RI) to your classpath."; throw new ValidationException( msg ); } Configuration<?> config; try {//根据spi服务获取到的 Configuration 对象。该对象已是Hibernate-validater对象实例了 config = resolver.getValidationProviders().get( 0 ).createGenericConfiguration( this ); } catch ( RuntimeException re ) { throw new ValidationException( "Unable to instantiate Configuration.", re ); } return config; } 查看javax.validation.Validation.GenericBootstrapImpl 类另外一个方法源码: public ValidationProviderResolver getDefaultValidationProviderResolver() { if ( defaultResolver == null ) { defaultResolver = new DefaultValidationProviderResolver(); } return defaultResolver; }
查看静态私有类javax.validation.Validation.DefaultValidationProviderResolver源码为:
private static class DefaultValidationProviderResolver implements ValidationProviderResolver { public List<ValidationProvider<?>> getValidationProviders() { // class loading and ServiceLoader methods should happen in a PrivilegedAction return GetValidationProviderListAction.getValidationProviderList(); } }
得到ValidationProvider对象。 查看静态私有类javax.validation.Validation.GetValidationProviderListAction中的私有方法loadProviders:
private List<ValidationProvider<?>> loadProviders(ClassLoader classloader) { ServiceLoader<ValidationProvider> loader = ServiceLoader.load( ValidationProvider.class, classloader );//调用该方法已经获取到 services中spi接口实现类 Iterator<ValidationProvider> providerIterator = loader.iterator(); List<ValidationProvider<?>> validationProviderList = new ArrayList<ValidationProvider<?>>(); while ( providerIterator.hasNext() ) { try { validationProviderList.add( providerIterator.next() ); } catch ( ServiceConfigurationError e ) { // ignore, because it can happen when multiple // providers are present and some of them are not class loader // compatible with our API. } } return validationProviderList; }
以上方法为使用Java SPI发现hibernate-validater的实现方式。
ValidatorFactory对象的建立使用org.hibernate.validator.internal.engine.ConfigurationImpl类的buildValidatorFactory()方法、源码以下:
public final ValidatorFactory buildValidatorFactory() { parseValidationXml();//解析xml配置 ValidatorFactory factory = null; try { if ( isSpecificProvider() ) { factory = validationBootstrapParameters.getProvider().buildValidatorFactory( this ); } else { final Class<? extends ValidationProvider<?>> providerClass = validationBootstrapParameters.getProviderClass(); if ( providerClass != null ) { for ( ValidationProvider<?> provider : providerResolver.getValidationProviders() ) { if ( providerClass.isAssignableFrom( provider.getClass() ) ) { factory = provider.buildValidatorFactory( this ); break; } } if ( factory == null ) { throw log.getUnableToFindProviderException( providerClass ); } } else { List<ValidationProvider<?>> providers = providerResolver.getValidationProviders(); assert providers.size() != 0; // I run therefore I am factory = providers.get( 0 ).buildValidatorFactory( this ); } } } finally { // close all input streams opened by this configuration for ( InputStream in : configurationStreams ) { try { in.close(); } catch ( IOException io ) { log.unableToCloseInputStream(); } } } return factory; }
经过ValidatorFactory 就能够得到Validator对象了。
有些时候,在用户的应用中须要一些更复杂的 constraint。Bean Validation 提供扩展 constraint 的机制。能够经过两种方法去实现,一种是组合现有的 constraint 来生成一个更复杂的 constraint,另一种是开发一个全新的 constraint。
经过以下代码示例:
// @Max 和 @Min 都是内置的 constraint @Max(10000) @Min(8000) @Constraint(validatedBy = {}) @Documented @Target( { ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD }) @Retention(RetentionPolicy.RUNTIME) public @interface Price { String message() default "错误的价格"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; }
使用注释约束:
@Price private Integer price;
校验:
User u = new User(); u.setPhone("13100000000"); u.setPrice(1); ValidationResult vr = ValidationUtils.validateEntity(u);//验证 System.out.println(vr.isHasErrors()); if (vr.isHasErrors()) { for (String key : vr.getErrorMsg().keySet()) { System.out.println("key:"+key+",value:"+vr.getErrorMsg().get(key)); } }
输出内容为: key:price,value:最小不能小于8000
上述中已经有定义。
使用方式以下代码:
伪代码以下: public class PersonAppTimeRank{ @NotEmpty(message="{appId.null}") private String appId;//应用ID【必填】 @NotEmpty(message="{personId.null}") private String personId;//用户ID【必填】 }
控制层方法定义:
public ResponseBean personAppCountRankingList0( @Valid PersonAppTimeRank appTimeRank, BindingResult result){ //验证后的错误信息都在 result中 List<ObjectError> listOe = result.getAllErrors(); 包含全部未验证经过的对象集合信息 }
注意xml文件配置以下:
<mvc:annotation-driven validator="validator" /> <!-- 如下 validator ConversionService 在使用 mvc:annotation-driven 会 自动注册 --> <bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"> <property name="providerClass" value="org.hibernate.validator.HibernateValidator" /> <!-- 若是不加默认到 使用classpath下的 ValidationMessages.properties --> <property name="validationMessageSource" ref="messageSource" /> </bean> <!-- 国际化的消息资源文件(本系统中主要用于显示/错误消息定制) --> <bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource"> <property name="basenames"> <list> <!-- 在web环境中必定要定位到classpath 不然默认到当前web应用下找 --> <value>classpath:messages/messages</value> <value>classpath:org/hibernate/validator/ValidationMessages</value> </list> </property> <property name="useCodeAsDefaultMessage" value="true" /> <property name="defaultEncoding" value="UTF-8" /> <property name="cacheSeconds" value="60" /> </bean>
查看org.springframework.validation.beanvalidation.LocalValidatorFactoryBean工厂bean类的方法:
@Override public void afterPropertiesSet() { @SuppressWarnings({"rawtypes", "unchecked"})//和上边hibernate validater雷同 Configuration<?> configuration = (this.providerClass != null ? Validation.byProvider(this.providerClass).configure() : Validation.byDefaultProvider().configure()); // Try Hibernate Validator 5.2's externalClassLoader(ClassLoader) method if (this.applicationContext != null) { try { Method eclMethod = configuration.getClass().getMethod("externalClassLoader", ClassLoader.class); ReflectionUtils.invokeMethod(eclMethod, configuration, this.applicationContext.getClassLoader()); } catch (NoSuchMethodException ex) { // Ignore - no Hibernate Validator 5.2+ or similar provider } } MessageInterpolator targetInterpolator = this.messageInterpolator; if (targetInterpolator == null) { targetInterpolator = configuration.getDefaultMessageInterpolator(); } configuration.messageInterpolator(new LocaleContextMessageInterpolator(targetInterpolator)); if (this.traversableResolver != null) { configuration.traversableResolver(this.traversableResolver); } ConstraintValidatorFactory targetConstraintValidatorFactory = this.constraintValidatorFactory; if (targetConstraintValidatorFactory == null && this.applicationContext != null) { targetConstraintValidatorFactory = new SpringConstraintValidatorFactory(this.applicationContext.getAutowireCapableBeanFactory()); } if (targetConstraintValidatorFactory != null) { configuration.constraintValidatorFactory(targetConstraintValidatorFactory); } if (this.parameterNameDiscoverer != null) { configureParameterNameProviderIfPossible(configuration); } if (this.mappingLocations != null) { for (Resource location : this.mappingLocations) { try { configuration.addMapping(location.getInputStream()); } catch (IOException ex) { throw new IllegalStateException("Cannot read mapping resource: " + location); } } } for (Map.Entry<String, String> entry : this.validationPropertyMap.entrySet()) { configuration.addProperty(entry.getKey(), entry.getValue()); } // Allow for custom post-processing before we actually build the ValidatorFactory. postProcessConfiguration(configuration); this.validatorFactory = configuration.buildValidatorFactory(); setTargetValidator(this.validatorFactory.getValidator()); }
也能够经过定义工具类直接显示的调用、其中工具类定义以下:
@Component public class ValidationUtilBean { //在setter方法中 注入 private static Validator validator /*= Validation.buildDefaultValidatorFactory().getValidator()*/; /** * 校验对象 * @param obj * @return */ public static <T> ValidationResult validateEntity(T obj) { ValidationResult result = new ValidationResult(); Set<ConstraintViolation<T>> set = validator.validate(obj, Default.class); if (set != null && set.size() > 0) { result.setHasErrors(true); Map<String, String> errorMsg = new HashMap<String, String>(); for (ConstraintViolation<T> cv : set) { errorMsg.put(cv.getPropertyPath().toString(), cv.getMessage()); } result.setErrorMsg(errorMsg); } return result; } /** * 校验属性 * * @param obj * @param propertyName * @return */ public static <T> ValidationResult validateProperty(T obj, String propertyName) { ValidationResult result = new ValidationResult(); Set<ConstraintViolation<T>> set = validator.validateProperty(obj, propertyName, Default.class); if (set != null && set.size() > 0) { result.setHasErrors(true); Map<String, String> errorMsg = new HashMap<String, String>(); for (ConstraintViolation<T> cv : set) { errorMsg.put(propertyName, cv.getMessage()); } result.setErrorMsg(errorMsg); } return result; } public Validator getValidator() { return validator; } @Autowired public void setValidator(Validator validator) { ValidationUtilBean.validator = validator; } }
注意 @Autowired public void setValidator(Validator validator) {} 须要一个配置好的validator bean。能够参考 6 中spring的定义。
在控制层显示使用代码调用以下:
ValidationResult result = ValidationUtils.validateEntity(appTimeRank);
ValidationUtils类就是上边定义的工具类、能够验证明体、实体中属性均可以.
没有MethodValidationPostProcessor以前咱们可能这样验证:
public UserModel get(Integer uuid) { //前置条件 Assert.notNull(uuid); Assert.isTrue(uuid > 0, "uuid must lt 0"); //获取 User Model UserModel user = new UserModel(); //此处应该从数据库获取 //后置条件 Assert.notNull(user); return user; }
有了MethodValidationPostProcessor以后咱们能够这样验证:
public @NotNull UserModel get2(@NotNull @Size(min = 1) Integer uuid) { //获取 User Model UserModel user = new UserModel(); //此处应该从数据库获取 return user; }
示例:
@Validated //① 告诉MethodValidationPostProcessor此Bean须要开启方法级别验证支持 public class UserService { public @NotNull UserModel get2(@NotNull @Min(value = 1) Integer uuid) { //②声明前置条件/后置条件 //获取 User Model UserModel user = new UserModel(); //此处应该从数据库获取 if(uuid > 100) {//方便后置添加的判断(此处假设传入的uuid>100 则返回null) return null; } return user; } }
<!--注册方法验证的后处理器--> <bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor"/>
若是出现异常就抛出ConstraintViolationException,核心代码类org.springframework.validation.beanvalidation.MethodValidationInterceptor实现上述功能:
public Object invoke(MethodInvocation invocation) throws Throwable { Class<?>[] groups = determineValidationGroups(invocation);// if (forExecutablesMethod != null) {//=============== Bean Validation 1.1 // Standard Bean Validation 1.1 API Object execVal = ReflectionUtils.invokeMethod(forExecutablesMethod, this.validator); Method methodToValidate = invocation.getMethod(); Set<ConstraintViolation<?>> result; try {//方法参数校验 result = (Set<ConstraintViolation<?>>) ReflectionUtils.invokeMethod(validateParametersMethod, execVal, invocation.getThis(), methodToValidate, invocation.getArguments(), groups); } catch (IllegalArgumentException ex) { // Probably a generic type mismatch between interface and impl as reported in SPR-12237 / HV-1011 // Let's try to find the bridged method on the implementation class... methodToValidate = BridgeMethodResolver.findBridgedMethod( ClassUtils.getMostSpecificMethod(invocation.getMethod(), invocation.getThis().getClass())); result = (Set<ConstraintViolation<?>>) ReflectionUtils.invokeMethod(validateParametersMethod, execVal, invocation.getThis(), methodToValidate, invocation.getArguments(), groups); }//若是有直接抛出异常 if (!result.isEmpty()) { throw new ConstraintViolationException(result); } //执行方法调用 返回结果集 Object returnValue = invocation.proceed(); //方法返回结果校验开始 result = (Set<ConstraintViolation<?>>) ReflectionUtils.invokeMethod(validateReturnValueMethod, execVal, invocation.getThis(), methodToValidate, returnValue, groups); if (!result.isEmpty()) {//若是有直接抛出异常 throw new ConstraintViolationException(result); } return returnValue; } else {//================ 默认使用 hibernate Validator 303 // Hibernate Validator 4.3's native API return HibernateValidatorDelegate.invokeWithinValidation(invocation, this.validator, groups); } }
方法级别验证会出现一下问题: 代码以下:
@Validated @Transactional public interface UserService { UserModel login(@NotBlank String account, @NotEmpty String password); }
问题描述: 在接口应用MethodValidationPostProcessor和Spring注解式事务后发现: 验证参数以前就先开启了事务(得到了DB链接),应该先验证输入,验证失败后直接抛出异常,验证成功再开启事务,这样对业务层的性能才更好。
能够修改它们的order来完成;不能使用tx:ann…… 注册 ,手工注册相应的bean指定order便可.若是使用注解bean就懵逼了.还好Spring 4.2 利用@Order控制配置类的加载顺序.能够参考以下:http://wiselyman.iteye.com/blog/2217192
<!-- 这个标签上能够写order,默认是Integer.MAX_VALUE --> <tx:annotation-driven transaction-manager="transactionManager" /> <!-- 其余配置项配置 --> <bean id="validator" class="com.gmail.dohongdayi.ssh.common.validation.ValidatorFactoryBean"> <property name="providerClass" value="org.hibernate.validator.HibernateValidator" /> <!-- 若是不加默认到 使用classpath下的 ValidationMessages.properties <property name="validationMessageSource" ref="messageSource" /> --> </bean> <!-- 定义切入点 --> <bean id="validationPointcut" class="org.springframework.aop.support.annotation.AnnotationMatchingPointcut"> <constructor-arg index="0" value="org.springframework.validation.annotation.Validated" /> <constructor-arg index="1" value="true" /> </bean> <!-- 定义通知--> <bean id="validationAdvice" class="org.springframework.validation.beanvalidation.MethodValidationInterceptor"> <constructor-arg index="0" ref="validator" /> </bean> <!-- 替代MethodValidationPostProcessor,让验证切面的advisor加入到Spring AOP的AspectJAwareAdvisorAutoProxyCreator的advised --> <aop:config> <!-- 经过order指定验证优先于事务(100 < Integer.MAX_VALUE) --> <aop:advisor pointcut-ref="validationPointcut" advice-ref="validationAdvice" order="100" /> </aop:config>
在客户端验证参数:
<dubbo:reference id="validationService" interface="com.alibaba.dubbo.examples.validation.api.ValidationService" validation="true" />
在服务器端验证参数:
<dubbo:service interface="com.alibaba.dubbo.examples.validation.api.ValidationService" ref="validationService" validation="true" />
参考如下网址: double中使用jsr303 出现的异常为:RpcException
ConstraintViolationException ve = (ConstraintViolationException) e.getCause(); // 里面嵌了一个ConstraintViolationException Set<ConstraintViolation<?>> violations = ve.getConstraintViolations(); // 能够拿到一个验证错误详细信息的集合 System.out.println(violations); 就得到了原生的异常信息。
从源代码分析实现原始和支持的功能,查看类com.alibaba.dubbo.validation.filter.ValidationFilter的源码核心方法invoke:
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException { if (validation != null && ! invocation.getMethodName().startsWith("$") && ConfigUtils.isNotEmpty(invoker.getUrl().getMethodParameter(invocation.getMethodName(), Constants.VALIDATION_KEY))) {//判断你的 <dubbo:service <dubbo:reference 是否配置有 validation配置项若是有进行 jsr303验证、若是没有不验证 try {// validation 稍后介绍 得到 validator对象 Validator validator = validation.getValidator(invoker.getUrl()); if (validator != null) {// 若是validator 对象部位空 开始进行验证。验证的核心代码就是 validation要介绍的。爬出的RpcException 直接抛出 抛出的其它异常包装为 RpcException异常 就是开头介绍的:验证出现异常得到异常的形式 validator.validate(invocation.getMethodName(), invocation.getParameterTypes(), invocation.getArguments()); } } catch (RpcException e) { throw e; } catch (Throwable t) { throw new RpcException(t.getMessage(), t); } } return invoker.invoke(invocation); }
查看以下连接了解Validation的扩展点:http://dubbo.io/Developer+Guide-zh.htm#DeveloperGuide-zh-Validation如今dubbox框架支持jsr303标准、后期须要能够继续扩展支持349标准.重点介绍com.alibaba.dubbo.validation.support.jvalidation.JValidation对象。 该类的源码以下:
public class JValidation extends AbstractValidation { @Override protected Validator createValidator(URL url) { return new JValidator(url);//使用 JValidator建立Validator }}
查看com.alibaba.dubbo.validation.support.jvalidation.JValidator以下图: 发现只提供了一个公共方法提供校验、所以不支持方法返回值校验等高级特性
经过@GroupSequence指定验证顺序:
@GroupSequence({First.class, Second.class, User.class})
先验证First分组,若是有错误当即返回而不会验证Second分组,接着若是First分组验证经过了,那么才去验证Second分组,最后指定User.class表示那些没有分组的在最后。这样咱们就能够实现按顺序验证分组了。
首先肯定引入EL jar包且版本正确。而后使用如: user.name.length.illegal=用户名[${validatedValue}]长度必须在5到20之间
先使用分组@Validated注解 即经过@Validate注解标识要验证的分组;若是要验证两个的话,能够这样@Validated({First.class, Second.class})。