Spring4新特性——泛型限定式依赖注入javascript
Spring4新特性——集成Bean Validation 1.1(JSR-349)到SpringMVC git
Spring4新特性——Groovy Bean定义DSLgithub
Spring4新特性——更好的Java泛型操做API web
Spring4新特性——JSR310日期API的支持spring
Spring4新特性——注解、脚本、任务、MVC等其余特性改进 编程
在以前的《跟我学SpringMVC》中的《第七章 注解式控制器的数据验证、类型转换及格式化》中已经介绍过SpringMVC集成Bean Validation 1.0(JSR-303),目前Bean Validation最新版本是Bean Validation 1.1(JSR-349),新特性能够到官网查看,笔者最喜欢的两个特性是:跨参数验证(好比密码和确认密码的验证)和支持在消息中使用EL表达式,其余的还有如方法参数/返回值验证、CDI和依赖注入、分组转换等。对于方法参数/返回值验证,你们能够参阅《Spring3.1 对Bean Validation规范的新支持(方法级别验证) 》。api
Bean Validation 1.1当前实现是Hibernate validator 5,且spring4才支持。接下来咱们从如下几个方法讲解Bean Validation 1.1,固然不必定是新特性:浏览器
集成Bean Validation 1.1到SpringMVC
分组验证、分组顺序及级联验证
消息中使用EL表达式
方法参数/返回值验证
自定义验证规则
类级别验证器
脚本验证器
cross-parameter,跨参数验证
混合类级别验证器和跨参数验证器
组合多个验证注解
本地化
由于大多数时候验证都配合web框架使用,并且不少朋友都咨询过如分组/跨参数验证,因此本文介绍下这些,且是和SpringMVC框架集成的例子,其余使用方式(好比集成到JPA中)能够参考其官方文档:
规范:http://beanvalidation.org/1.1/spec/
hibernate validator文档:http://hibernate.org/validator/
1.一、项目搭建
首先添加hibernate validator 5依赖:
<dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <version>5.0.2.Final</version> </dependency>
若是想在消息中使用EL表达式,请确保EL表达式版本是 2.2或以上,如使用Tomcat6,请到Tomcat7中拷贝相应的EL jar包到Tomcat6中。
<dependency> <groupId>javax.el</groupId> <artifactId>javax.el-api</artifactId> <version>2.2.4</version> <scope>provided</scope> </dependency>
请确保您使用的Web容器有相应版本的el jar包。
对于其余POM依赖请下载附件中的项目参考。
1.二、Spring MVC配置文件(spring-mvc.xml):
<!-- 指定本身定义的validator --> <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</value> <value>classpath:org/hibernate/validator/ValidationMessages</value> </list> </property> <property name="useCodeAsDefaultMessage" value="false"/> <property name="defaultEncoding" value="UTF-8"/> <property name="cacheSeconds" value="60"/> </bean>
此处主要把bean validation的消息查找委托给spring的messageSource。
1.三、实体验证注解:
public class User implements Serializable { @NotNull(message = "{user.id.null}") private Long id; @NotEmpty(message = "{user.name.null}") @Length(min = 5, max = 20, message = "{user.name.length.illegal}") @Pattern(regexp = "[a-zA-Z]{5,20}", message = "{user.name.illegal}") private String name; @NotNull(message = "{user.password.null}") private String password; }
对于验证规则能够参考官方文档,或者《第七章 注解式控制器的数据验证、类型转换及格式化》。
1.四、错误消息文件messages.properties:
user.id.null=用户编号不能为空 user.name.null=用户名不能为空 user.name.length.illegal=用户名长度必须在5到20之间 user.name.illegal=用户名必须是字母 user.password.null=密码不能为空
1.五、控制器
@Controller public class UserController { @RequestMapping("/save") public String save(@Valid User user, BindingResult result) { if(result.hasErrors()) { return "error"; } return "success"; } }
1.六、错误页面:
<spring:hasBindErrors name="user"> <c:if test="${errors.fieldErrorCount > 0}"> 字段错误:<br/> <c:forEach items="${errors.fieldErrors}" var="error"> <spring:message var="message" code="${error.code}" arguments="${error.arguments}" text="${error.defaultMessage}"/> ${error.field}------${message}<br/> </c:forEach> </c:if> <c:if test="${errors.globalErrorCount > 0}"> 全局错误:<br/> <c:forEach items="${errors.globalErrors}" var="error"> <spring:message var="message" code="${error.code}" arguments="${error.arguments}" text="${error.defaultMessage}"/> <c:if test="${not empty message}"> ${message}<br/> </c:if> </c:forEach> </c:if> </spring:hasBindErrors>
你们之后能够根据这个作通用的错误消息显示规则。好比我前端页面使用validationEngine显示错误消息,那么我能够定义一个tag来通用化错误消息的显示:showFieldError.tag。
1.七、测试
输入如:http://localhost:9080/spring4/save?name=123 , 咱们获得以下错误:
name------用户名必须是字母 name------用户名长度必须在5到20之间 password------密码不能为空 id------用户编号不能为空
基本的集成就完成了。
如上测试有几个小问题:
一、错误消息顺序,你们能够看到name的错误消息顺序不是按照书写顺序的,即不肯定;
二、我想显示如:用户名【zhangsan】必须在5到20之间;其中咱们想动态显示:用户名、min,max;而不是写死了;
三、我想在修改的时候只验证用户名,其余的不验证怎么办。
接下来咱们挨着试试吧。
若是咱们想在新增的状况验证id和name,而修改的状况验证name和password,怎么办? 那么就须要分组了。
首先定义分组接口:
public interface First { } public interface Second { }
分组接口就是两个普通的接口,用于标识,相似于java.io.Serializable。
接着咱们使用分组接口标识实体:
public class User implements Serializable { @NotNull(message = "{user.id.null}", groups = {First.class}) private Long id; @Length(min = 5, max = 20, message = "{user.name.length.illegal}", groups = {Second.class}) @Pattern(regexp = "[a-zA-Z]{5,20}", message = "{user.name.illegal}", groups = {Second.class}) private String name; @NotNull(message = "{user.password.null}", groups = {First.class, Second.class}) private String password; }
验证时使用如:
@RequestMapping("/save") public String save(@Validated({Second.class}) User user, BindingResult result) { if(result.hasErrors()) { return "error"; } return "success"; }
即经过@Validate注解标识要验证的分组;若是要验证两个的话,能够这样@Validated({First.class, Second.class})。
接下来咱们来看看经过分组来指定顺序;还记得以前的错误消息吗? user.name会显示两个错误消息,并且顺序不肯定;若是咱们先验证一个消息;若是不经过再验证另外一个怎么办?能够经过@GroupSequence指定分组验证顺序:
@GroupSequence({First.class, Second.class, User.class}) public class User implements Serializable { private Long id; @Length(min = 5, max = 20, message = "{user.name.length.illegal}", groups = {First.class}) @Pattern(regexp = "[a-zA-Z]{5,20}", message = "{user.name.illegal}", groups = {Second.class}) private String name; private String password; }
经过@GroupSequence指定验证顺序:先验证First分组,若是有错误当即返回而不会验证Second分组,接着若是First分组验证经过了,那么才去验证Second分组,最后指定User.class表示那些没有分组的在最后。这样咱们就能够实现按顺序验证分组了。
另外一个比较常见的就是级联验证:
如:
public class User { @Valid @ConvertGroup(from=First.class, to=Second.class) private Organization o; }
一、级联验证只要在相应的字段上加@Valid便可,会进行级联验证;@ConvertGroup的做用是当验证o的分组是First时,那么验证o的分组是Second,即分组验证的转换。
假设咱们须要显示如:用户名[NAME]长度必须在[MIN]到[MAX]之间,此处你们能够看到,咱们不想把一些数据写死,如NAME、MIN、MAX;此时咱们可使用EL表达式。
如:
@Length(min = 5, max = 20, message = "{user.name.length.illegal}", groups = {First.class})
错误消息:
user.name.length.illegal=用户名长度必须在{min}到{max}之间
其中咱们可使用{验证注解的属性}获得这些值;如{min}获得@Length中的min值;其余的也是相似的。
到此,咱们仍是没法获得出错的那个输入值,如name=zhangsan。此时就须要EL表达式的支持,首先肯定引入EL jar包且版本正确。而后使用如:
user.name.length.illegal=用户名[${validatedValue}]长度必须在5到20之间
使用如EL表达式:${validatedValue}获得输入的值,如zhangsan。固然咱们还可使用如${min > 1 ? '大于1' : '小于等于1'},及在EL表达式中也能拿到如@Length的min等数据。
另外咱们还能够拿到一个java.util.Formatter类型的formatter变量进行格式化:
${formatter.format("%04d", min)}
这个能够参考《Spring3.1 对Bean Validation规范的新支持(方法级别验证) 》,概念是相似的,具体能够参考Bean Validation 文档。
有时候默认的规则可能还不够,有时候还须要自定义规则,好比屏蔽关键词验证是很是常见的一个功能,好比在发帖时帖子中不容许出现admin等关键词。
一、定义验证注解
package com.sishuok.spring4.validator; import javax.validation.Constraint; import javax.validation.Payload; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.Target; import static java.lang.annotation.ElementType.*; import static java.lang.annotation.RetentionPolicy.*; /** * <p>User: Zhang Kaitao * <p>Date: 13-12-15 * <p>Version: 1.0 */ @Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE }) @Retention(RUNTIME) //指定验证器 @Constraint(validatedBy = ForbiddenValidator.class) @Documented public @interface Forbidden { //默认错误消息 String message() default "{forbidden.word}"; //分组 Class<?>[] groups() default { }; //负载 Class<? extends Payload>[] payload() default { }; //指定多个时使用 @Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE }) @Retention(RUNTIME) @Documented @interface List { Forbidden[] value(); } }
二、 定义验证器
package com.sishuok.spring4.validator; import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorContextImpl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.util.StringUtils; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; import java.io.Serializable; /** * <p>User: Zhang Kaitao * <p>Date: 13-12-15 * <p>Version: 1.0 */ public class ForbiddenValidator implements ConstraintValidator<Forbidden, String> { private String[] forbiddenWords = {"admin"}; @Override public void initialize(Forbidden constraintAnnotation) { //初始化,获得注解数据 } @Override public boolean isValid(String value, ConstraintValidatorContext context) { if(StringUtils.isEmpty(value)) { return true; } for(String word : forbiddenWords) { if(value.contains(word)) { return false;//验证失败 } } return true; } }
验证器中可使用spring的依赖注入,如注入:@Autowired private ApplicationContext ctx;
三、使用
public class User implements Serializable { @Forbidden() private String name; }
四、当咱们在提交name中含有admin的时候会输出错误消息:
forbidden.word=您输入的数据中有非法关键词
问题来了,哪一个词是非法的呢?bean validation 和 hibernate validator都没有提供相应的api提供这个数据,怎么办呢?经过跟踪代码,发现一种不是特别好的方法:咱们能够覆盖org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl实现(即复制一份代码放到咱们的src中),而后覆盖buildAnnotationParameterMap方法;
private Map<String, Object> buildAnnotationParameterMap(Annotation annotation) { …… //将Collections.unmodifiableMap( parameters );替换为以下语句 return parameters; }
即容许这个数据能够修改;而后在ForbiddenValidator中:
for(String word : forbiddenWords) { if(value.contains(word)) { ((ConstraintValidatorContextImpl)context).getConstraintDescriptor().getAttributes().put("word", word); return false;//验证失败 } }
经过((ConstraintValidatorContextImpl)context).getConstraintDescriptor().getAttributes().put("word", word);添加本身的属性;放到attributes中的数据能够经过${} 获取。而后消息就能够变成:
forbidden.word=您输入的数据中有非法关键词【{word}】
这种方式不是很友好,可是能够解决咱们的问题。
典型的如密码、确认密码的场景,很是经常使用;若是没有这个功能咱们须要本身写代码来完成;并且常常重复本身。接下来看看bean validation 1.1如何实现的。
6.一、定义验证注解
package com.sishuok.spring4.validator; import javax.validation.Constraint; import javax.validation.Payload; import javax.validation.constraints.NotNull; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.Target; import static java.lang.annotation.ElementType.*; import static java.lang.annotation.RetentionPolicy.*; /** * <p>User: Zhang Kaitao * <p>Date: 13-12-15 * <p>Version: 1.0 */ @Target({ TYPE, ANNOTATION_TYPE}) @Retention(RUNTIME) //指定验证器 @Constraint(validatedBy = CheckPasswordValidator.class) @Documented public @interface CheckPassword { //默认错误消息 String message() default ""; //分组 Class<?>[] groups() default { }; //负载 Class<? extends Payload>[] payload() default { }; //指定多个时使用 @Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE }) @Retention(RUNTIME) @Documented @interface List { CheckPassword[] value(); } }
6.二、 定义验证器
package com.sishuok.spring4.validator; import com.sishuok.spring4.entity.User; import org.springframework.util.StringUtils; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; /** * <p>User: Zhang Kaitao * <p>Date: 13-12-15 * <p>Version: 1.0 */ public class CheckPasswordValidator implements ConstraintValidator<CheckPassword, User> { @Override public void initialize(CheckPassword constraintAnnotation) { } @Override public boolean isValid(User user, ConstraintValidatorContext context) { if(user == null) { return true; } //没有填密码 if(!StringUtils.hasText(user.getPassword())) { context.disableDefaultConstraintViolation(); context.buildConstraintViolationWithTemplate("{password.null}") .addPropertyNode("password") .addConstraintViolation(); return false; } if(!StringUtils.hasText(user.getConfirmation())) { context.disableDefaultConstraintViolation(); context.buildConstraintViolationWithTemplate("{password.confirmation.null}") .addPropertyNode("confirmation") .addConstraintViolation(); return false; } //两次密码不同 if (!user.getPassword().trim().equals(user.getConfirmation().trim())) { context.disableDefaultConstraintViolation(); context.buildConstraintViolationWithTemplate("{password.confirmation.error}") .addPropertyNode("confirmation") .addConstraintViolation(); return false; } return true; } }
其中咱们经过disableDefaultConstraintViolation禁用默认的约束;而后经过buildConstraintViolationWithTemplate(消息模板)/addPropertyNode(所属属性)/addConstraintViolation定义咱们本身的约束。
6.三、使用
@CheckPassword() public class User implements Serializable { }
放到类头上便可。
@ScriptAssert(script = "_this.password==_this.confirmation", lang = "javascript", alias = "_this", message = "{password.confirmation.error}") public class User implements Serializable { }
经过脚本验证是很是简单并且强大的,lang指定脚本语言(请参考javax.script.ScriptEngineManager JSR-223),alias是在脚本验证中User对象的名字,可是你们会发现一个问题:错误消息怎么显示呢? 在springmvc 中会添加到全局错误消息中,这确定不是咱们想要的,咱们改造下吧。
7.一、定义验证注解
package com.sishuok.spring4.validator; import org.hibernate.validator.internal.constraintvalidators.ScriptAssertValidator; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.Target; import javax.validation.Constraint; import javax.validation.Payload; import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.RetentionPolicy.RUNTIME; @Target({ TYPE }) @Retention(RUNTIME) @Constraint(validatedBy = {PropertyScriptAssertValidator.class}) @Documented public @interface PropertyScriptAssert { String message() default "{org.hibernate.validator.constraints.ScriptAssert.message}"; Class<?>[] groups() default { }; Class<? extends Payload>[] payload() default { }; String lang(); String script(); String alias() default "_this"; String property(); @Target({ TYPE }) @Retention(RUNTIME) @Documented public @interface List { PropertyScriptAssert[] value(); } }
和ScriptAssert没什么区别,只是多了个property用来指定出错后给实体的哪一个属性。
7.二、验证器
package com.sishuok.spring4.validator; import javax.script.ScriptException; import javax.validation.ConstraintDeclarationException; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; import com.sishuok.spring4.validator.PropertyScriptAssert; import org.hibernate.validator.constraints.ScriptAssert; import org.hibernate.validator.internal.util.Contracts; import org.hibernate.validator.internal.util.logging.Log; import org.hibernate.validator.internal.util.logging.LoggerFactory; import org.hibernate.validator.internal.util.scriptengine.ScriptEvaluator; import org.hibernate.validator.internal.util.scriptengine.ScriptEvaluatorFactory; import static org.hibernate.validator.internal.util.logging.Messages.MESSAGES; public class PropertyScriptAssertValidator implements ConstraintValidator<PropertyScriptAssert, Object> { private static final Log log = LoggerFactory.make(); private String script; private String languageName; private String alias; private String property; private String message; public void initialize(PropertyScriptAssert constraintAnnotation) { validateParameters( constraintAnnotation ); this.script = constraintAnnotation.script(); this.languageName = constraintAnnotation.lang(); this.alias = constraintAnnotation.alias(); this.property = constraintAnnotation.property(); this.message = constraintAnnotation.message(); } public boolean isValid(Object value, ConstraintValidatorContext constraintValidatorContext) { Object evaluationResult; ScriptEvaluator scriptEvaluator; try { ScriptEvaluatorFactory evaluatorFactory = ScriptEvaluatorFactory.getInstance(); scriptEvaluator = evaluatorFactory.getScriptEvaluatorByLanguageName( languageName ); } catch ( ScriptException e ) { throw new ConstraintDeclarationException( e ); } try { evaluationResult = scriptEvaluator.evaluate( script, value, alias ); } catch ( ScriptException e ) { throw log.getErrorDuringScriptExecutionException( script, e ); } if ( evaluationResult == null ) { throw log.getScriptMustReturnTrueOrFalseException( script ); } if ( !( evaluationResult instanceof Boolean ) ) { throw log.getScriptMustReturnTrueOrFalseException( script, evaluationResult, evaluationResult.getClass().getCanonicalName() ); } if(Boolean.FALSE.equals(evaluationResult)) { constraintValidatorContext.disableDefaultConstraintViolation(); constraintValidatorContext .buildConstraintViolationWithTemplate(message) .addPropertyNode(property) .addConstraintViolation(); } return Boolean.TRUE.equals( evaluationResult ); } private void validateParameters(PropertyScriptAssert constraintAnnotation) { Contracts.assertNotEmpty( constraintAnnotation.script(), MESSAGES.parameterMustNotBeEmpty( "script" ) ); Contracts.assertNotEmpty( constraintAnnotation.lang(), MESSAGES.parameterMustNotBeEmpty( "lang" ) ); Contracts.assertNotEmpty( constraintAnnotation.alias(), MESSAGES.parameterMustNotBeEmpty( "alias" ) ); Contracts.assertNotEmpty( constraintAnnotation.property(), MESSAGES.parameterMustNotBeEmpty( "property" ) ); Contracts.assertNotEmpty( constraintAnnotation.message(), MESSAGES.parameterMustNotBeEmpty( "message" ) ); } }
和以前的类级别验证器相似,就很少解释了,其余代码所有拷贝自org.hibernate.validator.internal.constraintvalidators.ScriptAssertValidator。
7.三、使用
@PropertyScriptAssert(property = "confirmation", script = "_this.password==_this.confirmation", lang = "javascript", alias = "_this", message = "{password.confirmation.error}")
和以前的区别就是多了个property,用来指定出错时给哪一个字段。 这个相对以前的类级别验证器更通用一点。
直接看示例;
8.一、首先注册MethodValidationPostProcessor,起做用请参考《Spring3.1 对Bean Validation规范的新支持(方法级别验证) 》
<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor"> <property name="validator" ref="validator"/> </bean>
8.二、Service
@Validated @Service public class UserService { @CrossParameter public void changePassword(String password, String confirmation) { } }
经过@Validated注解UserService表示该类中有须要进行方法参数/返回值验证; @CrossParameter注解方法表示要进行跨参数验证;即验证password和confirmation是否相等。
8.三、验证注解
package com.sishuok.spring4.validator; //省略import @Constraint(validatedBy = CrossParameterValidator.class) @Target({ METHOD, CONSTRUCTOR, ANNOTATION_TYPE }) @Retention(RUNTIME) @Documented public @interface CrossParameter { String message() default "{password.confirmation.error}"; Class<?>[] groups() default { }; Class<? extends Payload>[] payload() default { }; }
8.四、验证器
package com.sishuok.spring4.validator; //省略import @SupportedValidationTarget(ValidationTarget.PARAMETERS) public class CrossParameterValidator implements ConstraintValidator<CrossParameter, Object[]> { @Override public void initialize(CrossParameter constraintAnnotation) { } @Override public boolean isValid(Object[] value, ConstraintValidatorContext context) { if(value == null || value.length != 2) { throw new IllegalArgumentException("must have two args"); } if(value[0] == null || value[1] == null) { return true; } if(value[0].equals(value[1])) { return true; } return false; } }
其中@SupportedValidationTarget(ValidationTarget.PARAMETERS)表示验证参数; value将是参数列表。
8.五、使用
@RequestMapping("/changePassword") public String changePassword( @RequestParam("password") String password, @RequestParam("confirmation") String confirmation, Model model) { try { userService.changePassword(password, confirmation); } catch (ConstraintViolationException e) { for(ConstraintViolation violation : e.getConstraintViolations()) { System.out.println(violation.getMessage()); } } return "success"; }
调用userService.changePassword方法,若是验证失败将抛出ConstraintViolationException异常,而后获得ConstraintViolation,调用getMessage便可获得错误消息;而后到前台显示便可。
从以上来看,不如以前的使用方便,须要本身对错误消息进行处理。 下一节咱们也写个脚本方式的跨参数验证器。
9.一、验证注解
package com.sishuok.spring4.validator; //省略import @Constraint(validatedBy = { CrossParameterScriptAssertClassValidator.class, CrossParameterScriptAssertParameterValidator.class }) @Target({ TYPE, FIELD, PARAMETER, METHOD, CONSTRUCTOR, ANNOTATION_TYPE }) @Retention(RUNTIME) @Documented public @interface CrossParameterScriptAssert { String message() default "error"; Class<?>[] groups() default { }; Class<? extends Payload>[] payload() default { }; String script(); String lang(); String alias() default "_this"; String property() default ""; ConstraintTarget validationAppliesTo() default ConstraintTarget.IMPLICIT; }
此处咱们经过@Constraint指定了两个验证器,一个类级别的,一个跨参数的。validationAppliesTo指定为ConstraintTarget.IMPLICIT,表示隐式自动判断。
9.二、验证器
请下载源码查看
9.三、使用
9.3.一、类级别使用
@CrossParameterScriptAssert(property = "confirmation", script = "_this.password==_this.confirmation", lang = "javascript", alias = "_this", message = "{password.confirmation.error}")
指定property便可,其余和以前的同样。
9.3.二、跨参数验证
@CrossParameterScriptAssert(script = "args[0] == args[1]", lang = "javascript", alias = "args", message = "{password.confirmation.error}") public void changePassword(String password, String confirmation) { }
经过args[0]==args[1] 来判断是否相等。
这样,咱们的验证注解就自动适应两种验证规则了。
有时候,可能有好几个注解须要一块儿使用,此时就可使用组合验证注解
@Target({ FIELD}) @Retention(RUNTIME) @Documented @NotNull(message = "{user.name.null}") @Length(min = 5, max = 20, message = "{user.name.length.illegal}") @Pattern(regexp = "[a-zA-Z]{5,20}", message = "{user.name.length.illegal}") @Constraint(validatedBy = { }) public @interface Composition { String message() default ""; Class<?>[] groups() default { }; Class<? extends Payload>[] payload() default { }; }
这样咱们验证时只须要:
@Composition() private String name;
简洁多了。
即根据不一样的语言选择不一样的错误消息显示。
一、本地化解析器
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver"> <property name="cookieName" value="locale"/> <property name="cookieMaxAge" value="-1"/> <property name="defaultLocale" value="zh_CN"/> </bean>
此处使用cookie存储本地化信息,固然也能够选择其余的,如Session存储。
二、设置本地化信息的拦截器
<mvc:interceptors> <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"> <property name="paramName" value="language"/> </bean> </mvc:interceptors>
即请求参数中经过language设置语言。
三、消息文件
四、 浏览器输入
http://localhost:9080/spring4/changePassword?password=1&confirmation=2&language=en_US
到此,咱们已经完成大部分Bean Validation的功能实验了。对于如XML配置、编程式验证API的使用等对于咱们使用SpringMVC这种web环境用处不大,因此就很少介绍了,有兴趣能够本身下载官方文档学习。