springmvc集成JSR-303的解析消息文件的默认实现浅析
来源:互联网 发布:数控车床电脑自动编程 编辑:程序博客网 时间:2024/06/05 01:46
springmvc集成JSR-303的解析消息文件的默认实现浅析
- 博客分类:
- springmvc杂谈
- java开发常见问题分析
springmvc如何集成JSR-303进行数据验证在之前的如下文章中已经介绍过了:
SpringMVC数据验证——第七章 注解式控制器的数据验证、类型转换及格式化——跟着开涛学SpringMVC
举个例子:
比如我的验证
- @Length(min = 5, max = 200, message = "{message.title.length.not.valid}")
- @Column(name = "title")
- private String title;
有朋友想得到min、max及此时的title值,可以在消息文件中通过:
当然也可以使用{value} 获取此时的title值
这到底是怎么工作的呢?
在JSR-303中,使用javax.validation.MessageInterpolator来解析消息,而如果:
- <!-- 以下 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>
即此时使用的hibernate实现,注入了spring的messageSource来解析消息时:
- public void setValidationMessageSource(MessageSource messageSource) {
- this.messageInterpolator = HibernateValidatorDelegate.buildMessageInterpolator(messageSource);
- }
- /**
- * Inner class to avoid a hard-coded Hibernate Validator 4.1+ dependency.
- */
- private static class HibernateValidatorDelegate {
- public static MessageInterpolator buildMessageInterpolator(MessageSource messageSource) {
- return new ResourceBundleMessageInterpolator(new MessageSourceResourceBundleLocator(messageSource));
- }
- }
即内部委托给了org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator#ResourceBundleMessageInterpolator:
并使用如下代码解析消息:
- public String interpolate(String message, Context context) {
- // probably no need for caching, but it could be done by parameters since the map
- // is immutable and uniquely built per Validation definition, the comparison has to be based on == and not equals though
- return interpolateMessage( message, context.getConstraintDescriptor().getAttributes(), defaultLocale );
- }
此处可以看到context.getConstraintDescriptor().getAttributes(),其作用是获取到注解如@Length上的所有数据,具体代码实现如下:
- private Map<String, Object> buildAnnotationParameterMap(Annotation annotation) {
- final Method[] declaredMethods = ReflectionHelper.getDeclaredMethods( annotation.annotationType() );
- Map<String, Object> parameters = new HashMap<String, Object>( declaredMethods.length );
- for ( Method m : declaredMethods ) {
- try {
- parameters.put( m.getName(), m.invoke( annotation ) );
- }
- catch ( IllegalAccessException e ) {
- throw log.getUnableToReadAnnotationAttributesException( annotation.getClass(), e );
- }
- catch ( InvocationTargetException e ) {
- throw log.getUnableToReadAnnotationAttributesException( annotation.getClass(), e );
- }
- }
- return Collections.unmodifiableMap( parameters );
- }
循环每一个方法 并获取值放入map,接着进入方法:
- private String interpolateMessage(String message, Map<String, Object> annotationParameters, Locale locale)
具体实现思路如下:
1、首先查询缓存中是否存在,如果存在直接获取缓存中解析的消息:
- if ( cacheMessages ) {
- resolvedMessage = resolvedMessages.get( localisedMessage );
- }
2、如果没有,按照JSR-303规定的使用三步获取:
首先委托给ResourceBundle获取消息值:
- ResourceBundle userResourceBundle = userResourceBundleLocator
- .getResourceBundle( locale );
- ResourceBundle defaultResourceBundle = defaultResourceBundleLocator
- .getResourceBundle( locale );
2.1、委托给用户定义的resourceBundle进行解析(即我们之前指定的messageSource),递归的查找消息并替换那些转义的:
- // search the user bundle recursive (step1)
- userBundleResolvedMessage = replaceVariables(
- resolvedMessage, userResourceBundle, locale, true
- );
转义的包括:
\\{、\\}、\\\\。
所谓递归的查找意思就是如:
a=hello {b}
b=123
会在解析a时再递归解析b,如果{b}就是一个字符串,而不想被解析,可以通过\\{b\\}转移完成;
替换完转义字符后,还是会再递归的查找下去。
2.2、使用默认的resourceBundle(即默认找org.hibernate.validator.ValidationMessages.properties)按照和2.1一样的步骤执行:
- // search the default bundle non recursive (step2)
- resolvedMessage = replaceVariables( userBundleResolvedMessage, defaultResourceBundle, locale, false );
- evaluatedDefaultBundleOnce = true;
2.3、解析完成后,接着替换注解变量值:
- // resolve annotation attributes (step 4)
- resolvedMessage = replaceAnnotationAttributes( resolvedMessage, annotationParameters );
- // last but not least we have to take care of escaped literals
- resolvedMessage = resolvedMessage.replace( "\\{", "{" );
- resolvedMessage = resolvedMessage.replace( "\\}", "}" );
- resolvedMessage = resolvedMessage.replace( "\\\\", "\\" );
- return resolvedMessage;
如之前说的
@Length(min = 5, max = 200, message = "{message.title.length.not.valid}")
消息:
标题长度必须在{min}到{max}个字符之间
那么,如果没有在之前的resourceBundle中得到替换,那么会被注解的值替换掉。
即得到标题长度必须在5到200个字符之间。
此处有一个小问题:
如果你的messageSource添加了:
- <property name="useCodeAsDefaultMessage" value="true"/>
意思就是如果找不到key对应的消息,则使用code作为默认消息;这样会引发一个问题就是,根据code找消息,永远能找到,即不可能成功执行【2.3】。
如“标题长度必须在{min}到{max}个字符之间”,如果消息文件中没有min 和 max,实际得到的是:
”标题长度必须在min到max个字符之间“,不是我们期望的;
如“标题长度必须在\\{min\\}到max个字符之间”,实际也会获取到:
”标题长度必须在min到max个字符之间“,也不是我们期望的。
所以实际使用时useCodeAsDefaultMessage应该为false。
- springmvc集成JSR-303的解析消息文件的默认实现浅析
- 基于JSR-303与springMVC 4的自定义后台校验
- SpringMVC 数据的格式化、JSR 303数据校验和国际化
- SpringMVC-10 JSR 303
- 基于JSR-356实现的Tyrus WebSocket框架的消息传递机制初步了解
- 实现SpringMVC与MyBatis的集成
- spring集成JSR-303验证框架--Hibernate-validator实现
- springmvc使用JSR-303进行表单验证不生效的问题
- SpringMVC 使用JSR-303进行校验[转自-自行车上的程序员]
- Java WebSocket的Tomcat实现(JSR-356)
- JSR 303 springmvc 数据校验
- SpringMVC数据校验-JSR 303
- SpringMVC使用JSR 303校验
- springmvc实现文件的上传
- SpringMvc实现文件的上传
- springMVC实现文件的上传下载
- SpringMvc的消息验证
- SpringMvc的消息验证
- sql语句基础要点(以sql server 2008 为例)
- 选择排序和插入排序
- POJ 1734 Sightseeing trip【floyd求最小环+记录路径】
- Android 自定义view设置xml属性
- 虚拟机下破解CentOS root密码
- springmvc集成JSR-303的解析消息文件的默认实现浅析
- poj 3278 bfs 标记数组
- RxJava开发精要2-为什么是Observables?
- Android控件之RecyclerView的基本使用
- 【Github教程】史上最全github使用方法:github入门到精通
- python下根据csv将数据集按照类别分文件夹放置
- 关于expdp在服务器上同时只能有一个进程在执行的问题
- [易飞]事务进程被另一进程锁死 解决办法
- Django学习笔记--后台管理界面