spring4 mvc 中使用 JSR-303 Validator ,国际化占位符问题至资源配置问题

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring4 mvc 中使用 JSR-303 Validator ,国际化占位符问题至资源配置问题相关的知识,希望对你有一定的参考价值。

spring4 mvc
JSR-303 Validator
org.springframework.validation.beanvalidation.LocalValidatorFactoryBean
中使用拦截器
org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator
在拦截器中构造
org.hibernate.validator.resourceloading.PlatformResourceBundleLocator
该对象构造参数bundleName是什么?求教
----------------------------------------------------------------------------------
对回答无奈

参考技术A Spring配置

  人们经常会在一台以上的主机上配置一种Web应用程序。例如,在生产中,一个网站可能只有一个实例。除了此实例外,开发人员可以在用于开发的机器上配置其他的(开发)实例。也可以在公司(机构)内部的本地开发服务器上维护其他应用程序装置,这将让您受益匪浅。该实例的目的是使Web设计者可以获得有质量保证的材料,并为需要为应用程序提供文件资料的人提供准入。

  大家都知道,即使是最简单的场景,也需要安装、配置和维护三个实例。而对于位于不同地理位置的团队来说,要从事这样的项目便更加困难。对于任何不是特别简单的Web应用程序项目,都需要多名开发人员来安装项目装置和本地设置以及运行单元测试的装置等。

  很多组织都将自己开发的产品作为Web应用程序。我们可以在很多产品中发现这种情况,例如电子商务系统、内容管理系统(CMS),以及博客发布平台等。这类产品可在多个服务器中进行部署。对于成功的多用途Web应用程序来说,他们的开发人员必须要保证他们的应用程序便于安装,并且能够与其他Web应用程序完美集成。经过上述讨论之后,我们应该明了,作为本文主题的应用程序配置是通用Web应用程序项目开发人员所需要解决的重要问题之一。

  诸如CVS或Subversion之类的版本控制系统是开发组织使用的一种标准工具。这种工具代表了一些组织的中心源代码版本库,它们被用于保持源代码的有序。用户可以跟踪应用程序源代码的变化,显示不同版本的区别,并可以确定项目分支。而且,它们使得在应用程序部署中进行部分更新成为可能。

  很明显,版本控制系统软件是跟踪源代码所必需的,它对于解决应用程序配置问题有非常大的帮助。在本文中,我们将不会把重点放在版本控制系统上,因为这方面已经有很多相关的材料了。在此,我们将关注版本控制问题中的一个小话题:如何使Web应用程序的配置更加便捷(尤其是使用Spring
MVC框架编写的Web应用程序)。

  问题是:我们在此讨论的是一种什么样的配置?任何Web应用程序都需要一些资源,这些资源通常都是其所运行的服务器所特有的,例如数据库URL、发送电子邮件的SMTP服务器,以及包含专用软件文件的文件夹等。这样的设置应该集中,从而使应用程序配置更加简单。

  但是,这只是这个问题最简单的一种版本。有时候,在应用程序开发中需要更加复杂的配置。这意味着,必须将各次部署中的不同Bean连接起来,而这会使问题更加复杂。

  这些应用程序配置问题的解决方案有诸多优势,包括:简化应用程序的安装和配置过程,使源代码版本控制更加简便,减少源代码版本库中的冲突现象。下面,我们将通过示例详细讨论这个话题。
问题。

  我们首先来演示一下上文所提到的最简单的版本。在这一场景中,我们希望在应用程序部署中改变的是简单的配置参数,例如链接、密码等。如果您曾经使用Spring
MVC框架开发过Web应用程序,那么您应该知道这里将用到的两个配置文件:

/WEB-INF/applicationContext.xml,它让您可以配置Bean,或者显示应用程序上下文。通过这个文件,您可以定义自己的业务逻辑Bean、资源以及其他可以与Web端口相关联的所有Bean。

/WEB-INF/[servlet-name]-servlet.xml,它被用于配置Web层、视图解析器、控制器、校验器以及其他所有MVC框架中必需的Bean。[servlet-name]指的是在web.xml部署描述符中定义的Spring
dispatcher servlet的名称。

  那么问题在哪儿呢?问题就出在applicationContext.xml中将包括一些特定于主机的Bean。本文将定义。其中,最明显的一个示例就是包含了JDBC连接信息的bean,但是任何一种稍微复杂些的应用程序都有十几个类似的Bean。看一下下面的示例:

<bean

class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property >
<value>org.postgresql.Driver</value>
</property>
<property >
</property>
<property >
<value>postgres</value>
</property>
<property >
<value></value>
</property>

</bean>
  这个解决方案的问题在于对applicationContext.xml文件的维护。对于初学者来说,设想一下,项目放在源代码版本控制系统中,例如CVS。下面,假设您希望在网站中添加新的功能,那么就需要在应用程序上下文定义中添加额外的Bean定义。问题是如何在生产服务器上体现这些改变。

  通常情况下,应用程序的本地实例不会与活动站点使用同样的数据库,因此applicationContext.xml文件将包括让您能够访问本地数据库的设置。当您想提交在源代码版本库中的改变时,就需要注意这些特定于主机属性的同步性。版本库中的文件最终可能使用本地设置中的配置。如果想在生产服务器上更新配置,就必须手动同步这些属性的值。这是非常枯燥的任务,而且还非常容易出错。

  对于应用程序的每个实例来说,这个问题更加重要。假如有三位开发人员正在使用代码段基址,而且他们使用的是本地的数据库。当您提交更改的时候,他们每个人在本地服务器上更新源代码的时候都必须非常谨慎。他们会手动同步这些更改,然后提交他们的工作。这样一来,版本控制系统对于这些配置文件来说已经毫无用处。如果曾经使用过Spring
MVC,那么您应该知道applicationContext.xml是应用程序中的关键组件,因为是它将所有的东西粘合在一起。所以,我们需要一种机制来帮助使应用程序中各项保持有序,这点非常重要。

  正如前面所提到的,这是您可能遇到的较简单的配置问题。更难的问题出现在当需要在不同服务器中进行不同的Bean连接的时候。这类问题常会出现在日常软件开发任务中。例如,假如您的产品有一个客户身份验证模块,可以对来自关系数据库或LDAP服务器中的用户进行身份验证。自然,这一身份验证模块可以使用抽象了特定版本库的Bean进行配置。如果您想改变不同应用程序部署中验证用户的方式,就需要在applicationContext.xml文件中进行不同的Bean连接。这种配置问题常见于在部署中有可配置特性的所有应用程序。

  在下文中,我们将讨论这两种配置问题。首先我们会关注同步的Bean属性问题及其解决方案,接下来,我们会讨论更加复杂的同步Bean连接问题。

解决方案

  同步Bean属性

  这个问题的一种可行的解决方案是将所有特定于主机的参数都放到普通的Java属性文件中,使用Spring的PropertyPlaceHolderConfigurer类,将这些参数写入Bean属性中。

  使用这一解决方案,我们可以生成如下的属性文件(/WEB-INF/jdbc.properties):

jdbc.driver=org.postgresql.Driver

jdbc.url=jdbc:postgresql://localhost/test
jdbc.user=postgres

jdbc.password=

  我们的Bean配置如下:

<bean

class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">

<property >
<value>/WEB-INF/jdbc.properties</value>
</property>
</bean>

<bean

class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property >
<value>$jdbc.driver</value>
</property>
<property >
<value>$jdbc.url</value>
</property>
<property >
<value>$jdbc.user</value>
</property>
<property >
<value>$jdbc.password</value>
</property>

</bean>
  如上所述,我们定义了一个PropertyPlaceholderConfigurer类的实例,并将其位置属性设置为我们的属性文件。该类被实现为Bean工厂的后处理器,并将使用定义在文件中的属性来代替所有的占位符($...value)。

  利用这种技术,我们可以从applicationContext.xml中移除所有特定于主机的配置属性。通过这种方式,我们可以自由地为该文件添加新的Bean,而不必担心特定于主机属性的同步性。这样可以简化生产部署和维护。

Spring MVC学习笔记——JSR303介绍及最佳实践

JSR 303 – Bean Validation 是一个数据验证的规范,2009 年 11 月确定最终方案。2009 年 12 月 Java EE 6 发布,Bean Validation 作为一个重要特性被包含其中。本文将对 Bean Validation 的主要功能进行介绍,并通过一些示例来演示如何在 Java 开发过程正确的使用 Bean Validation。

 

关于 Bean Validation

在任何时候,当你要处理一个应用程序的业务逻辑,数据校验是你必须要考虑和面对的事情。应用程序必须通过某种手段来确保输入进来的数据从语 义上来讲是正确的。在通常的情况下,应用程序是分层的,不同的层由不同的开发人员来完成。很多时候同样的数据验证逻辑会出现在不同的层,这样就会导致代码 冗余和一些管理的问题,比如说语义的一致性等。为了避免这样的情况发生,最好是将验证逻辑与相应的域模型进行绑定。

Bean Validation 为 JavaBean 验证定义了相应的元数据模型和 API。缺省的元数据是 Java Annotations,通过使用 XML 可以对原有的元数据信息进行覆盖和扩展。在应用程序中,通过使用 Bean Validation 或是你自己定义的 constraint,例如 @NotNull, @Max, @ZipCode, 就可以确保数据模型(JavaBean)的正确性。constraint 可以附加到字段,getter 方法,类或者接口上面。对于一些特定的需求,用户可以很容易的开发定制化的 constraint。Bean Validation 是一个运行时的数据验证框架,在验证之后验证的错误信息会被马上返回。

下载 JSR 303 – Bean Validation 规范 http://jcp.org/en/jsr/detail?id=303

Hibernate Validator 是 Bean Validation 的参考实现 . Hibernate Validator 提供了 JSR 303 规范中所有内置 constraint 的实现,除此之外还有一些附加的 constraint。如果想了解更多有关 Hibernate Validator 的信息,请查看 http://www.hibernate.org/subprojects/validator.html

Bean Validation 中的 constraint


表 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) 被注释的元素必须符合指定的正则表达式



表 2. Hibernate Validator 附加的 constraint

Constraint 详细信息
@Email 被注释的元素必须是电子邮箱地址
@Length 被注释的字符串的大小必须在指定的范围内
@NotEmpty 被注释的字符串的必须非空
@Range 被注释的元素必须在合适的范围内

 

一个 constraint 通常由 annotation 和相应的 constraint validator 组成,它们是一对多的关系。也就是说可以有多个 constraint validator 对应一个 annotation。在运行时,Bean Validation 框架本身会根据被注释元素的类型来选择合适的 constraint validator 对数据进行验证。

有些时候,在用户的应用中需要一些更复杂的 constraint。Bean Validation 提供扩展 constraint 的机制。可以通过两种方法去实现,一种是组合现有的 constraint 来生成一个更复杂的 constraint,另外一种是开发一个全新的 constraint。

创建一个包含验证逻辑的简单应用(基于 JSP)

在本文中,通过创建一个虚构的订单管理系统(基于 JSP 的 web 应用)来演示如何在 Java 开发过程中应用 Bean Validation。该简化的系统可以让用户创建和检索订单。

系统设计和运用的技术


图 1. 系统架构
技术分享

图 1 是报表管理系统的结构图,是典型的 MVC(Model-View-Controller)应用。Controller 负责接收和处理请求,Servlet 扮演 Controller 的角色去处理请求、业务逻辑并转向合适的 JSP 页面。在 Servlet 中对数据进行验证。JSP 扮演 View 的角色以图型化界面的方式呈现 Model 中的数据方便用户交互。Model 就是此系统进行操作的数据模型,我们对这部分加以简化不对数据进行持久化。

数据模型


图 2. 数据模型
技术分享

图 2 展示的是订单管理系统的数据模型。

声明了 contraint 的 JavaBean


清单 1. Order.java

				 
 public class Order { 
 // 必须不为 null, 大小是 10 
 @NotNull 
 @Size(min = 10, max = 10) 
 private String orderId; 
 // 必须不为空
 @NotEmpty 
 private String customer; 
 // 必须是一个电子信箱地址
 @Email 
 private String email; 
 // 必须不为空
 @NotEmpty 
 private String address; 
 // 必须不为 null, 必须是下面四个字符串‘created‘, ‘paid‘, ‘shipped‘, ‘closed‘其中之一
 // @Status 是一个定制化的 contraint 
 @NotNull 
 @Status 
 private String status; 
 // 必须不为 null 
 @NotNull 
 private Date createDate; 
 // 嵌套验证
 @Valid 
 private Product product; 

…
 getter 和 setter 
 } 



清单 2. Product.java

				 
 public class Product { 
 // 必须非空
 @NotEmpty 
 private String productName; 
 // 必须在 8000 至 10000 的范围内
 // @Price 是一个定制化的 constraint 
 @Price 
 private float price; 
…
 Getter 和 setter 
 }
			



清单 3. OrderQuery.java

				 
 // ‘to‘所表示的日期必须在‘from‘所表示的日期之后
 // @QueryConstraint 是一个定制化的 constraint 
 @QueryConstraint 
 public class OrderQuery { 
 private Date from; 
 private Date to; 
… omitted …
 Getter and setter 
 } 

 

定制化的 constraint

@Price是一个定制化的 constraint,由两个内置的 constraint 组合而成。


清单 4. @Price 的 annotation 部分

 

				 
 // @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 {}; 
 } 

 

@Status是一个新开发的 constraint.


清单 5. @Status 的 annotation 部分

				 
 @Constraint(validatedBy = {StatusValidator.class}) 
 @Documented 
 @Target( { ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD }) 
 @Retention(RetentionPolicy.RUNTIME) 
 public @interface Status { 
 String message() default "不正确的状态 , 应该是 ‘created‘, ‘paid‘, shipped‘, closed‘其中之一"; 
 Class<?>[] groups() default {}; 
 Class<? extends Payload>[] payload() default {}; 
 } 



清单 6. @Status 的 constraint validator 部分

				 
 public class StatusValidator implements ConstraintValidator<Status, String>{ 
 private final String[] ALL_STATUS = {"created", "paid", "shipped", "closed"}; 
 public void initialize(Status status) { 
 } 
 public boolean isValid(String value, ConstraintValidatorContext context) { 
 if(Arrays.asList(ALL_STATUS).contains(value)) 
 return true; 
 return false; 
 } 
 } 

 

Bean Validation API 使用示例

创建订单

用户在创建一条订单记录时,需要填写以下信息:订单编号,客户,电子信箱,地址,状态,产品名称,产品价格


图 3. 创建订单
技术分享

对这些信息的校验,使用 Bean Validation API


清单 7. 代码片段

 

				 
 protected void doPost(HttpServletRequest req, HttpServletResponse resp) 
 throws ServletException, IOException { 
 HttpSession session = req.getSession(); 
 // 从 request 中获取输入信息
 String orderId = (String) req.getParameter("orderId"); 
 String customer = (String) req.getParameter("customer"); 
 String email = (String) req.getParameter("email"); 
 String address = (String) req.getParameter("address"); 
 String status = (String) req.getParameter("status"); 
 String productName = (String) req.getParameter("productName"); 
 String productPrice = (String) req.getParameter("productPrice"); 
 // 将 Bean 放入 session 中
 Order order = new Order(); 
 order.setOrderId(orderId); 
 order.setCustomer(customer); 
 order.setEmail(email); 
 order.setAddress(address); 
 order.setStatus(status); 
 order.setCreateDate(new Date()); 
 Product product = new Product(); 
 product.setName(productName); 
 if(productPrice != null && productPrice.length() > 0) 
 product.setPrice(Float.valueOf(productPrice)); 
 order.setProduct(product); 
 session.setAttribute("order", order); 
 ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); 
 Validator validator = factory.getValidator(); 
 Set<ConstraintViolation<Order>> violations = validator.validate(order); 
 if(violations.size() == 0) { 
 session.setAttribute("order", null); 
 session.setAttribute("errorMsg", null); 
 resp.sendRedirect("creatSuccessful.jsp"); 
 } else { 
 StringBuffer buf = new StringBuffer(); 
 ResourceBundle bundle = ResourceBundle.getBundle("messages"); 
 for(ConstraintViolation<Order> violation: violations) { 
 buf.append("-" + bundle.getString(violation.getPropertyPath().toString())); 
 buf.append(violation.getMessage() + "<BR>\n"); 
 } 
 session.setAttribute("errorMsg", buf.toString()); 
 resp.sendRedirect("createOrder.jsp"); 
 } 
 } 

 

如果用户不填写任何信息提交订单,相应的错误信息将会显示在页面上


图 4. 验证后返回错误信息
技术分享

其实在整个程序的任何地方都可以调用 JSR 303 API 去对数据进行校验,然后将校验后的结果返回。


清单 8. 调用 JSR 303 API 进行校验

				 
 Order order = new Order(); 
…
 ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); 
 Validator validator = factory.getValidator(); 
 Set<ConstraintViolation<Order>> violations = validator.validate(order); 

 

结束语

JSR 303 的发布使得在数据自动绑定和验证变得简单,使开发人员在定义数据模型时不必考虑实现框架的限制。当然 Bean Validation 还只是提供了一些最基本的 constraint,在实际的开发过程中,用户可以根据自己的需要组合或开发出更加复杂的 constraint

以上是关于spring4 mvc 中使用 JSR-303 Validator ,国际化占位符问题至资源配置问题的主要内容,如果未能解决你的问题,请参考以下文章

Spring MVC学习笔记——JSR303介绍及最佳实践

Spring MVC 和 JSR-303 休眠条件验证

JSR-303 Bean Validation 介绍及 Spring MVC 服务端验证最佳实践

Spring MVC 中的验证

JSR-303 Bean Validation 介绍及 Spring MVC 服务端验证最佳实践

springmvc 进行JSR303校验