`
jinnianshilongnian
  • 浏览: 21499354 次
  • 性别: Icon_minigender_1
博客专栏
5c8dac6a-21dc-3466-8abb-057664ab39c7
跟我学spring3
浏览量:2417769
D659df3e-4ad7-3b12-8b9a-1e94abd75ac3
Spring杂谈
浏览量:3008061
43989fe4-8b6b-3109-aaec-379d27dd4090
跟开涛学SpringMVC...
浏览量:5638956
1df97887-a9e1-3328-b6da-091f51f886a1
Servlet3.1规范翻...
浏览量:259806
4f347843-a078-36c1-977f-797c7fc123fc
springmvc杂谈
浏览量:1597033
22722232-95c1-34f2-b8e1-d059493d3d98
hibernate杂谈
浏览量:250107
45b32b6f-7468-3077-be40-00a5853c9a48
跟我学Shiro
浏览量:5858146
Group-logo
跟我学Nginx+Lua开...
浏览量:701782
5041f67a-12b2-30ba-814d-b55f466529d5
亿级流量网站架构核心技术
浏览量:784897
社区版块
存档分类
最新评论

springmvc集成JSR-303的解析消息文件的默认实现浅析

 
阅读更多

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值,可以在消息文件中通过:

写道
message.content.length.not.valid=内容长度必须在{min}到{max}个字符之间

当然也可以使用{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。

 

4
4
分享到:
评论
8 楼 Janle 2017-03-05  
<!-- 国际化的消息资源文件 -->
    <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>
    <mvc:interceptors>
        <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
            <property name="paramName" value="language"/>
        </bean>
    </mvc:interceptors>
    <bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver">
        <property name="cookieName" value="language"/>
        <property name="cookieMaxAge" value="3600"/>
        <property name="defaultLocale" value="zh_CN"/>
    </bean>

    <bean id="validateMessageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
        <property name="basename" value="classpath:messages/validationMessages"></property>
        <property name="useCodeAsDefaultMessage" value="false"/>
        <property name="defaultEncoding" value="UTF-8"/>
        <property name="cacheSeconds" value="60"/>
    </bean>

具体validationMessages_zh_CN.properties配置
user.username.illegal=用户名格式不正确
password.length.illegal=密码[${validatedValue}]长度必须为{min}到{max}个字符

在运行时候直接弹出错误的是{user.username.illegal},并没有解析出message中指定的配置错误信息
大神帮忙看看什么情况
7 楼 jinnianshilongnian 2013-10-08  
jluwang 写道
jinnianshilongnian 写道
jluwang 写道
你好,关于validationEngine有个问题想请教一下
如果一个表单的输入绕过了前台验证,但是没通过spring mvc的JSR303校验,并且将错误信息记录到BindingResult对象中,那么如何用validationEngine的方式提示BindingResult里面的信息?


你可以参考
https://github.com/zhangkaitao/es/blob/master/web/src/main/webapp/WEB-INF/tags/showFieldError.tag



https://github.com/zhangkaitao/es/blob/master/web/src/main/webapp/WEB-INF/tags/showGlobalError.tag


不好意思,现在才看到,代码很好用,非常感谢!!

不过还是遇到了一点小问题,validationEngine其中有个showOneMessage不起作用(好像官方demo就不起作用)

另外由于jquery.validationEngine-zh_CN.js文件里面无法用el获取jsp的变量,导致进行ajax校验的时候没有方便的办法去传递额外的参数
我想到的办法是要么把这里面的js代码放到一个jsp文件里,用<%@include%>包含进去.要么就不用validationEngine,还是沿用jquery的ajax..不知道您有没有更方便的途径处理ajax校验

我使用@include包含的,通过ajax没必要 ajax的话 你可以看看es, 里边可以直接返回错误消息的
6 楼 jluwang 2013-09-29  
jinnianshilongnian 写道
jluwang 写道
你好,关于validationEngine有个问题想请教一下
如果一个表单的输入绕过了前台验证,但是没通过spring mvc的JSR303校验,并且将错误信息记录到BindingResult对象中,那么如何用validationEngine的方式提示BindingResult里面的信息?


你可以参考
https://github.com/zhangkaitao/es/blob/master/web/src/main/webapp/WEB-INF/tags/showFieldError.tag



https://github.com/zhangkaitao/es/blob/master/web/src/main/webapp/WEB-INF/tags/showGlobalError.tag


不好意思,现在才看到,代码很好用,非常感谢!!

不过还是遇到了一点小问题,validationEngine其中有个showOneMessage不起作用(好像官方demo就不起作用)

另外由于jquery.validationEngine-zh_CN.js文件里面无法用el获取jsp的变量,导致进行ajax校验的时候没有方便的办法去传递额外的参数
我想到的办法是要么把这里面的js代码放到一个jsp文件里,用<%@include%>包含进去.要么就不用validationEngine,还是沿用jquery的ajax..不知道您有没有更方便的途径处理ajax校验
5 楼 jinnianshilongnian 2013-09-16  
jluwang 写道
你好,关于validationEngine有个问题想请教一下
如果一个表单的输入绕过了前台验证,但是没通过spring mvc的JSR303校验,并且将错误信息记录到BindingResult对象中,那么如何用validationEngine的方式提示BindingResult里面的信息?


你可以参考
https://github.com/zhangkaitao/es/blob/master/web/src/main/webapp/WEB-INF/tags/showFieldError.tag



https://github.com/zhangkaitao/es/blob/master/web/src/main/webapp/WEB-INF/tags/showGlobalError.tag
4 楼 jluwang 2013-09-16  
你好,关于validationEngine有个问题想请教一下
如果一个表单的输入绕过了前台验证,但是没通过spring mvc的JSR303校验,并且将错误信息记录到BindingResult对象中,那么如何用validationEngine的方式提示BindingResult里面的信息?
3 楼 EricLiang 2013-09-02  
jinnianshilongnian 写道

我前台验证使用validationEngine; jsr303只提供后台验证

validationEngine这个很好,后来想了一下,前后台校验还是要分开的,一个后台可能会有多个前台(web、android)
2 楼 jinnianshilongnian 2013-08-29  
EricLiang 写道
关于JSR303校验,采用spring mvc后有个遗憾一直未能解决:如何统一前后台的校验,后台使用JSR303很好,如果需要前台校验岂不是再重新实现一遍,在springmvc 框架下面能不能实现前后台统一校验?并且可以配置校验模式:前台、后台、前后台。
初步有个想法:页面上使用ajax请求提交,在校验时想通过后台controller来处理(使用JSR303校验),问题是前台如何写?以前使用JSR303校验都是页面使用spring form标签同步提交的.
看了你的很多系列文章很有收获,所以向你请教一下

我前台验证使用validationEngine; jsr303只提供后台验证
1 楼 EricLiang 2013-08-28  
关于JSR303校验,采用spring mvc后有个遗憾一直未能解决:如何统一前后台的校验,后台使用JSR303很好,如果需要前台校验岂不是再重新实现一遍,在springmvc 框架下面能不能实现前后台统一校验?并且可以配置校验模式:前台、后台、前后台。
初步有个想法:页面上使用ajax请求提交,在校验时想通过后台controller来处理(使用JSR303校验),问题是前台如何写?以前使用JSR303校验都是页面使用spring form标签同步提交的.
看了你的很多系列文章很有收获,所以向你请教一下

相关推荐

    SpringMVC使用JSR-303进行验证Bean.docx

    总的来说,SpringMVC与JSR-303的集成使得我们可以方便地在Bean级别进行数据验证,减少了手动检查输入数据的工作,提高了代码的可读性和维护性。同时,它提供了丰富的验证注解,满足了各种常见的数据校验需求。通过...

    springMVC集成spring-data-redis

    在SpringMVC中集成Spring Data Redis,可以利用Redis的高效特性来提升应用程序的数据处理能力,例如作为session共享的存储、缓存数据或者实现发布/订阅(Pub/Sub)功能。发布/订阅是一种通信模式,允许发送者(pub)将...

    SpringMVC ---- HelloWorld ---- 代码

    SpringMVC ---- HelloWorld ---- 代码 SpringMVC ---- HelloWorld ---- 代码 SpringMVC ---- HelloWorld ---- 代码 SpringMVC ---- HelloWorld ---- 代码 SpringMVC ---- HelloWorld ---- 代码 SpringMVC ---- Hello...

    SpringMVC 使用JSR-303进行校验 @Valid示例

    使用SpringMVC结合JSR-303进行数据校验的流程一般包括以下几个步骤: 1. 准备校验时使用的JAR包:在项目中添加validation-api和具体的实现库(如hibernate-validator),用于执行校验规则。 2. 编写需要校验的Java...

    SpringMVC-Mybatis-Shiro-redis

    《SpringMVC-Mybatis-Shiro-Redis:构建安全高效的Web应用》 在现代Web开发中,构建一个高效且安全的后端系统是至关重要的。本文将深入探讨一个基于SpringMVC、Mybatis、Shiro和Redis的Web应用架构,这四个组件共同...

    springmvc使用JSR-303进行数据校验实例

    2. 配置Spring:在Spring配置文件中需要声明一个MessageSource的Bean,用于读取校验时使用的错误消息,同时定义一个LocalValidatorFactoryBean,它是一个实现了JSR-303校验器接口的Bean。通过配置这个校验器,可以...

    SpringMVC-Mybatis-Shiro-redis-master

    【标题】"SpringMVC-Mybatis-Shiro-redis-master" 涉及的是一个集成框架项目,这个项目集成了四个关键的技术组件:SpringMVC、MyBatis、Shiro和Redis。这些技术在现代Java Web开发中扮演着重要角色。 **SpringMVC**...

    springmvc_jsr303

    项目中所需的jar包在压缩包中已提供,项目中用到了JSR303和hibernate-validator的技术,大量采用了注解@NotNull,@Email,@Length,@Max,@Pattern,@Size等,此外还采用了自定义注解,验证信息全部配置在属性文件中.

    springmvc表单验证JSR-303验证框架jar包

    hibernate-validator-5.2.4.Final.jar hibernate-validator-annotation-processor-5.2.4.Final.jar hibernate-validator-cdi-5.2.4.Final.jar validation-api-1.1.0.Final.jar jboss-logging-3.2.1.Final.jar ...

    springmvc-jackson-jar包集合

    默认情况下,Spring MVC使用了Jackson库来实现这一功能,因为Jackson具有高效且灵活的特性。 二、Jackson库解析 1. `jackson-databind-2.2.1.jar`:这是Jackson的核心模块,提供了对复杂JSON结构映射到Java对象的...

    SpringMVC精品资源--基于springMVC实现的解决方案系统.zip

    3. **模型绑定与数据验证**:SpringMVC支持模型绑定,可以把HTTP请求参数自动绑定到Java对象,同时提供数据验证功能,通过在模型对象上添加JSR-303/JSR-349注解进行验证。 4. **视图技术**:SpringMVC可以配合多种...

    SpringMVC精品资源--深入解析SpringMVC核心原理:从手写简易版MVC框架开始(SmartMvc).zip

    本资源包"SpringMVC精品资源--深入解析SpringMVC核心原理:从手写简易版MVC框架开始(SmartMvc).zip"旨在帮助开发者深入理解SpringMVC的工作机制,并通过构建一个简单的SmartMvc框架来实践这一过程。 1. **MVC模式...

    SpringMVC精品资源--JAX-RS &amp; SpringMVC supported maven buil.zip

    SpringMVC和JAX-RS是两种在Java世界中广泛使用的Web开发框架,它们都用于构建RESTful服务,但有着不同的设计哲学和技术实现。这个压缩包"SpringMVC精品资源--JAX-RS & SpringMVC supported maven build.zip"显然是一...

    springmvc-high-other-05.rar

    本资料"springmvc-high-other-05.rar"着重探讨了SpringMVC的一些高级特性和用法,特别是那些不太常见但对提升应用性能和可维护性至关重要的特性。 1. **POM.xml配置解析**:在"pom.xml"文件中,我们可以看到项目...

    springmvc-data-object

    此外,Spring MVC还支持数据验证,通过实现Validator接口或使用JSR-303/JSR-349 Bean Validation,可以对用户输入进行验证,确保数据的正确性和完整性。 总结来说,Spring MVC通过一系列机制实现了从前端页面到后端...

    SpringMVC-demo - examples

    `@Valid` 注解配合 `JSR-303` 验证规则,可以实现数据的校验。 7. **异常处理** 通过全局异常处理器,如 `@ControllerAdvice` 和 `@ExceptionHandler` 注解,我们可以统一处理应用程序中抛出的异常,提供友好的...

    SpringMVC4教程-.pptx

    SpringMVC4教程-.pptx SpringMVC4教程-.pptx SpringMVC4教程-.pptx SpringMVC4教程-.pptx SpringMVC4教程-.pptx

    SpringMVC精品资源--JAX-RS &amp; SpringMVC supported gradle bui.zip

    【标题】"SpringMVC精品资源--JAX-RS & SpringMVC supported gradle bui.zip" 提供的是一份关于使用Gradle构建支持JAX-RS和SpringMVC的项目资源。这涉及到两个关键的技术栈:SpringMVC,一个用于构建Web应用程序的...

    springmvc_exception-1.0-SNAPSHOT.war

    SpringMVC文件上传war包

    springMVC-3.0-file-upload.rar_SpringMVC3.0-api _springmvc_spring

    "springMVC-3.0-file-upload.rar" 包含了一个示例,展示了如何在Spring MVC应用中集成文件上传功能,这个例子的描述表明它是基于注解驱动的,这意味着我们将使用如`@Controller`、`@RequestMapping`等注解来定义控制...

Global site tag (gtag.js) - Google Analytics