`
wsjjasper
  • 浏览: 13254 次
  • 性别: Icon_minigender_1
文章分类
社区版块
存档分类
最新评论

4.Spring类型校验和格式转换

阅读更多

类型校验和格式转换

 

 

 

在企业级应用中,校验是一个至关重要的步骤。校验的目的是用来检验那些需要被使用的数据是否符合业务规范同时保证数据完整和有效。

 

在程序开发中,数据校验通常与转换和格式化同时进行。这样做的原因通常是因为原始数据和应用程序中用的数据有所区别。比如,在web程序中,用户在页面输入一些信息。当用户保存时,这些信息被传送到服务器端(页面校验通过后)。在服务器端,一个数据绑定操作将被执行,它将获得HTTP请求,转换,然后绑定到相应的领域对象上(比如,用户的联系信息将被绑定到Contact 对象上),根据预先定义的数据格式规则(比如日期必须为yyyy-MM-dd)。


当数据绑定完成,领域对象将根据校验规则将检查是否有任何约束被违反。如果一切OK,则数据被持久化,伴随一条成功的响应信息返回给用户,否则,返回校验错误信息。在这章的开始,你会学到spring是如何提供巧妙的类型转换,数据格式化以及数据校验支持。这章还会覆盖如下几点:


-spring3的类型转换系统和数据格式化SPI:我们会向您展示它们是如何替换原来的PropertyEditor支持以及如何在java类型中进行转换。

-Spring的校验支持:首先我们会对spring的校验接口作一个介绍,然后我们会着重讨论JSR-303的Bean校验支持。


首先在STS中创建创建一个简单的例子。在STS中创建一个spring模板工程并且选择Simple Spring Utility Project。除此之外添加其他必要的依赖,如表14-1。确保你的工程用的是spring3.1和JDK1.6。

 

Table 14-1. Maven Dependencies for Validation

 

Group ID Artifact ID Version Description
javax.validation validation-api 1.0.0.GA JSR-303 API library.
org.hibernate hibernate-validator 4.2.0.Final Hibernate Validator library that supports JSR-303 Bean Validation.
joda-time joda-time 2.0 Joda-time (joda-time.sourceforge.net/) is a datetime API that Spring Data JPA uses. In this chapter, we will use it in our domain objects.
org.slf4j slf4j-log4j12 1.6.1 The SLF4J logging library (www.slf4j.org) will be used as the logging library for the samples in this chapter. This library will help chain the LF4J logger to the underlying log4j library for logging purposes.

 


Spring类型装换系统

 

在spring3中,一个新型的类型转换系统诞生了。它提供了一套强有力的java类型的转换。在这部分,我们将讨论这个功能是如何运作的以及如何替换原先的PropertyEditor,同时我们也会演示一些自定义的SPI类型转换。

 

使用PropertyEditors进行Sting类型转换

 

在第五章,我们讨论了spring是如何使用PropertyEditor来处理将String类型转换为POJO的属性的问题。让我再做一次快速回顾,同时比较下SPI的类型转换。假设有个包含一系列属性的Contact类,如14-1

 

 

//Listing 14-1. The Contact Class
package com.apress.prospring3.ch14.domain;

import java.net.URL;
import org.joda.time.DateTime;

public class Contact {
	private String firstName;
	private String lastName;
	private DateTime birthDate;
	private URL personalSite;

	// Getter/setter methods omitted
	public String toString() {
		return "First name: " + getFirstName() + " - Last name: "
				+ getLastName() + " - Birth date: " + getBirthDate()
				+ " - Personal site: " + getPersonalSite();
	}
}
 

 

如14-1所示,对于birth date这个属性,我们使用的事jodaTime包下的DateTime类。除此之外,有一个URL属性来表示Contact的个人网址。现在我们假设这些信息存在spring的配置文件里或者propertie文件里。14-2显示了一个spring配置文件(prop-editor-app-context.xml).

Listing 14-2. Spring Configuration for Property Editor

 

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
	xmlns:p="http://www.springframework.org/schema/p"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd">
	<context:annotation-config />
	<context:property-placeholder location="classpath:application.properties" />
	<bean id="dateTimeEditor" class="com.apress.prospring3.ch14.pe.editor.DateTimeEditor">
		<constructor-arg value="${date.format.pattern}" />
	</bean>
	<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
		<property name="customEditors">
			<map>
				<entry key="org.joda.time.DateTime">
					<ref local="dateTimeEditor" />
				</entry>
			</map>
		</property>
	</bean>
	<bean id="clarence" class="com.apress.prospring3.ch14.domain.Contact"
		p:firstName="Clarence" p:lastName="Ho" p:birthDate="1970-12-31"
		p:personalSite="http://www.clarence.com" />
	<bean id="myContact" class="com.apress.prospring3.ch14.domain.Contact"
		p:firstName="${myContact.firstName}" p:lastName="${myContact.lastName}"
		p:birthDate="${myContact.birthDate}" p:personalSite="${myContact.personalSite}" />
</beans>
 

 

如14-2所示,我们创建了2个不同的Contact Bean。Clarence Bean是直接使用配置文件里的值,而myContact Bean,属性的值是通过读取properties 文件。除此之外,还定义了一个自定义编辑器用来将Sting转换为JodaTime格式的日期类型,而且可以看见转换模板也是定义在properties里的。


 

#Listing 14-3. The application.properties File
date.format.pattern=yyyy-MM-dd
myContact.firstName=Scott
myContact.lastName=Tiger
myContact.birthDate=1984-6-30
myContact.personalSite=http://www.somedomain.com
 

 

14-4展示了自定义转换器是如何将Sting转换为JodaTime日期类型的

 

//Listing 14-4. Custom Editor for JodaTime DateTime
package com.apress.prospring3.ch14.pe.editor;

import java.beans.PropertyEditorSupport;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;

public class DateTimeEditor extends PropertyEditorSupport {
	private DateTimeFormatter dateTimeFormatter;

	public DateTimeEditor(String dateFormatPattern) {
		dateTimeFormatter = DateTimeFormat.forPattern(dateFormatPattern);
	}

	public void setAsText(String text) throws IllegalArgumentException {
		setValue(DateTime.parse(text, dateTimeFormatter));
	}
}
 

 

14-4的代码比较简单。我们测试下看看结果,14-5是测试用类:

 

//Listing 14-5. Testing Property Editor
package com.apress.prospring3.ch14.pe;

import org.springframework.context.support.GenericXmlApplicationContext;
import com.apress.prospring3.ch14.domain.Contact;

public class PropEditorExample {
	public static void main(String[] args) {
		GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();
		ctx.load("classpath:prop-editor-app-context.xml");
		ctx.refresh();
		Contact clarence = ctx.getBean("clarence", Contact.class);
		System.out.println("Clarence info: " + clarence);
		Contact myContact = ctx.getBean("myContact", Contact.class);
		System.out.println("My contact info: " + myContact);
	}
}
 

 

正如14-5所显示的,二个不同的Contact Bean被构成并打印。执行结果如下:

 

 

Clarence info: First name: Clarence - Last name: Ho - Birth date: 1970-12-
31T00:00:00.000+08:00 – Personal site: http://www.clarence.com
My contact info: First name: Scott - Last name: Tiger - Birth date: 1984-06-
30T00:00:00.000+08:00 – Personal site: http://www.somedomain.com
 

 

上所示,属性被转换成功。下图展示了PropertyEditors是如何进行转换的。


Spring 3 类型转换介绍

在spring 3.0中,一个通用的类型转换系统被引入。它属于org.springframework.core.convert包下。除了提供多种PropertyEditor支持,它也能实现其他java类型转换(PropertyEditor只关注string类型的转换)

自定义转换的实现
在真正操作之前,我们还是先再用下之前的例子。假设这次我们希望使用类型转换系统来转换birth date日期,这次不用创建PropertyEditor,而是创建一个org.springframework.core.convert.converter.Converter<S,T>的实现。如14-6所示:

//Listing 14-6. Custom DateTime Converter
package com.apress.prospring3.ch14.converter;

import javax.annotation.PostConstruct;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.convert.converter.Converter;

public class StringToDateTimeConverter implements Converter<String, DateTime> {
	private static final String DEFAULT_DATE_PATTERN = "yyyy-MM-dd";
	private DateTimeFormatter dateFormat;
	private String datePattern = DEFAULT_DATE_PATTERN;

	public String getDatePattern() {
		return datePattern;
	}

	@Autowired(required = false)
	public void setDatePattern(String datePattern) {
		this.datePattern = datePattern;
	}

	@PostConstruct
	public void init() {
		dateFormat = DateTimeFormat.forPattern(datePattern);
	}

	public DateTime convert(String dateString) {
		return dateFormat.parseDateTime(dateString);
	}
}
 
如14-6所示我们实现了Converter<String, DateTime>接口。其中的泛型表示是将Sting抓换为DateTime类型。通过标注@Autowired(required=false)来对于date-time模板的注入是可选的(根据模板来转换)。最后,convert()方法用来存放转换逻辑。

配置ConversionService

14-7 shows the configuration file要使用类型转换系统,我们得先在spring里配置一个org.springframework.core.convert.ConversionService实例,如14-7配置文件(conv-service-app-context.xml):

Listing 14-7. Configuration of ConversionService
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
	xmlns:p="http://www.springframework.org/schema/p"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd">
	<context:annotation-config />
	<bean id="conversionService"
		class="org.springframework.context.support.ConversionServiceFactoryBean">
		<property name="converters">
			<list>
				<bean
					class="com.apress.prospring3.ch14.converter.StringToDateTimeConverter" />
			</list>
		</property>
	</bean>
	<bean id="clarence" class="com.apress.prospring3.ch14.domain.Contact"
		p:firstName="Clarence" p:lastName="Ho" p:birthDate="1978-08-09"
		p:personalSite="http://www.clarence.com" />
</beans>
 
如14-7所示,我们可以看到配置ConversionServiceFactoryBean 来构建conversionService Bean。如果不定义的话spring会使用默认基于PropertyEditor的转换系统。然后一个自定义的转换器被定义,用于conversionService Bean。 14-8,测试程序:

//Listing 14-8. Testing ConversionService
package com.apress.prospring3.ch14.convserv;

import org.springframework.context.support.GenericXmlApplicationContext;
import com.apress.prospring3.ch14.domain.Contact;

public class ConvServExample {
	public static void main(String[] args) {
		GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();
		ctx.load("classpath:conv-service-app-context.xml");
		ctx.refresh();
		Contact clarence = ctx.getBean("clarence", Contact.class);
		System.out.println("Contact info: " + clarence);
	}
}
 
运行后输出结果如下,和我们之前得到的clarence bean是一样的:
Contact info: First name: Clarence - Last name: Ho - Birth date: 1978-08-09T00:00:00.000+08:00
– Personal site: http://www.clarence.com
 
任意类型转换

类型转换系统真正强力的地方在于他可以对任意对象进行类型转换。假设我们有另外一个类,叫做AnotherContact,和Contact类是一样的。如下:

//Listing 14-9. The AnotherContact Class
package com.apress.prospring3.ch14.domain;

import java.net.URL;
import org.joda.time.DateTime;

public class AnotherContact {
	private String firstName;
	private String lastName;
	private DateTime birthDate;
	private URL personalSite;
	// Other code omitted
}
 
我们希望将Contact类转换为AnotherContact,将Contact类的firstName和lastName转换为AnotherContact类的firstName和lastName.分别的,我们来实现另一个自定义转换器。14-10所示:
//Listing 14-10. The AnotherContact Class
package com.apress.prospring3.ch14.converter;

import org.springframework.core.convert.converter.Converter;
import com.apress.prospring3.ch14.domain.AnotherContact;
import com.apress.prospring3.ch14.domain.Contact;

public class ContactToAnotherContactConverter implements
		Converter<Contact, AnotherContact> {
	public AnotherContact convert(Contact contact) {
		AnotherContact anotherContact = new AnotherContact();
		anotherContact.setFirstName(contact.getLastName());
		anotherContact.setLastName(contact.getFirstName());
		anotherContact.setBirthDate(contact.getBirthDate());
		anotherContact.setPersonalSite(contact.getPersonalSite());
		return anotherContact;
	}
}
 
这类灰常简单,只是包裹了firstName和lastName属性。在配置文件里替换掉原来conversionService的转换器(conv-service-app-context.xml) 如下所示:

14-11片段.
Listing 14-11. Adding the Converter to the Conversion Service

<bean id="conversionService"
	class="org.springframework.context.support.ConversionServiceFactoryBean">
	<property name="converters">
		<list>
			<bean
				class="com.apress.prospring3.ch14.converter.StringToDateTimeConverter" />
			<bean
				class="com.apress.prospring3.ch14.converter.ContactToAnotherContactConverter" />
		</list>
	</property>
</bean>
 
转换器bean的顺序并不重要,对程序没有影响。下面是测试代码:
//Listing 14-12. Testing Conversion Service
package com.apress.prospring3.ch14.convserv;

// Import statements omitted
public class ConvServExample {
	public static void main(String[] args) {
		GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();
		ctx.load("classpath:conv-service-app-context.xml");
		ctx.refresh();
		Contact clarence = ctx.getBean("clarence", Contact.class);
		System.out.println("Contact info: " + clarence);
		ConversionService conversionService = ctx
				.getBean(ConversionService.class);
		// Convert from Contact to AnotherContact
		AnotherContact anotherContact = conversionService.convert(clarence,
				AnotherContact.class);
		System.out.println("Another contact info: " + anotherContact);
		// Conversion from String to Array
		String[] stringArray = conversionService.convert("a,b,c",
				String[].class);
		System.out.println("String array: " + stringArray[0] + stringArray[1]
				+ stringArray[2]);
		// Conversion from List to Set
		List<String> listString = new ArrayList<String>();
		listString.add("a");
		listString.add("b");
		listString.add("c");
		Set<String> setString = conversionService.convert(listString,
				HashSet.class);
		for (String string : setString)
			System.out.println("Set: " + string);
	}
}
 
在14-2中,除了Contact到AnotherContact的转换,我们还添加了一些string到list,list到set的代码。旨在阐明任何类型都是可以被转换的。跑完后,输出如下:

Contact info: First name: Clarence - Last name: Ho - Birth date: 1978-08-09T00:00:00.000+08:00
– Personal site: http://www.clarence.com
Another contact info: First name: Ho - Last name: Clarence - Birth date: 1978-08-
09T00:00:00.000+08:00 – Personal site: http://www.clarence.com
String array: abc
Set: b
Set: c
Set: a
 

如结果显示,转换结果是正确的,同时那些string转list,list转set的代码也是。通过spring的类型转换系统,你能方便的在任何层里创建自定义的类型转换器。一个可能的应用场景是当你有2个不同的系统但却有相同的Contact信息需要更新时。然而,数据库结构也是不同的(比如,A系统的last name等于B系统的first name。。。)。这时你就能使用类型转换系统来进行转换,赶在持久化之前。

从spring3.0开始,spring MVC使用了大量的类型转换。在web程序中,application context configuration定义了<mvc:annotation-driven/>可以自动注册所有默认转换器。具体的细节我们将会在后面的spring web程序开发中讲述。

下一节:Spring里的数据校验 http://wsjjasper.iteye.com/blog/15775705

分享到:
评论

相关推荐

    Spring 2.0 开发参考手册

    7.10.4. ThreadLocal目标源 7.11. 定义新的通知类型 7.12. 更多资源 8. 测试 8.1. 简介 8.2. 单元测试 8.3. 集成测试 8.3.1. Context管理和缓存 8.3.2. 测试fixture的依赖注入 8.3.3. 事务管理 8.3.4. ...

    Spring-Reference_zh_CN(Spring中文参考手册)

    7.10.4. ThreadLocal目标源 7.11. 定义新的通知类型 7.12. 更多资源 8. 测试 8.1. 简介 8.2. 单元测试 8.3. 集成测试 8.3.1. Context管理和缓存 8.3.2. 测试fixture的依赖注入 8.3.3. 事务管理 8.3.4. 方便的变量 ...

    spring chm文档

    7.10.4. ThreadLocal目标源 7.11. 定义新的通知类型 7.12. 更多资源 8. 测试 8.1. 简介 8.2. 单元测试 8.3. 集成测试 8.3.1. Context管理和缓存 8.3.2. 测试fixture的依赖注入 8.3.3. 事务管理 8.3.4. ...

    Spring中文帮助文档

    7.10.4. ThreadLocal目标源 7.11. 定义新的Advice类型 7.12. 更多资源 8. 测试 8.1. 简介 8.2. 单元测试 8.2.1. Mock对象 8.2.2. 单元测试支持类 8.3. 集成测试 8.3.1. 概览 8.3.2. 使用哪个支持框架 ...

    Spring API

    7.10.4. ThreadLocal目标源 7.11. 定义新的Advice类型 7.12. 更多资源 8. 测试 8.1. 简介 8.2. 单元测试 8.2.1. Mock对象 8.2.2. 单元测试支持类 8.3. 集成测试 8.3.1. 概览 8.3.2. 使用哪个支持框架 ...

    springboot参考指南

    23.6.4. YAML缺点 vii. 23.7. 类型安全的配置属性 i. 23.7.1. 第三方配置 ii. 23.7.2. 松散的绑定(Relaxed binding) iii. 23.7.3. @ConfigurationProperties校验 iii. 24. Profiles i. 24.1. 添加激活的配置...

    Spring.3.x企业应用开发实战(完整版).part2

    1.5.4 通用类型转换系统和属性格式化系统 1.5.5 数据访问层新增OXM功能 1.5.6 Web层的增强 1.5.7 其他 1.6 Spring对Java版本的要求 1.7 如何获取Spring 1.8 小结 第2章 快速入门 2.1 实例功能概述 2.1.1 比Hello ...

    Spring MVC 3.0实战指南.ppt

    4、数据转换、格式化、校验 5、数据模型控制 6、视图及解析器 7、其它 目录: Spring MVC 3.0新特性 Spring MVC框架结构 Spring MVC框架结构 框架的实现者 目录 HTTP请求映射原理 Spring MVC进行映射的依据 通过URL...

    Spring3.x企业应用开发实战(完整版) part1

    1.5.4 通用类型转换系统和属性格式化系统 1.5.5 数据访问层新增OXM功能 1.5.6 Web层的增强 1.5.7 其他 1.6 Spring对Java版本的要求 1.7 如何获取Spring 1.8 小结 第2章 快速入门 2.1 实例功能概述 2.1.1 比Hello ...

    spring+springmvc+mybatis项目案例实现用户角色权限管理

    使用了FormattingConversionServiceFactoryBean对于传入参数中日期或数字字符串进行数据转换和数据格式化 使用了SpringContextHolder方便在自定义线程中调用spring已经实例的bean,如使用service 使用了urlrewrite ...

    Spring3MVC注解教程.ppt

    4、数据转换、格式化、校验 5、数据模型控制 6、视图及解析器 7、其它 目录: Spring MVC 3.0新特性 Spring MVC框架结构 Spring MVC框架结构 框架的实现者 目录 HTTP请求映射原理 Spring MVC进行映射的...

    Java 面试宝典

    4、在 JAVA 中如何跳出当前的多重嵌套循环? .......................................................... 8 5、switch 语句能否作用在 byte 上,能否作用在 long 上,能否作用在 String 上? .. 9 6、short s1 = ...

    Spring攻略(第二版 中文高清版).part1

    1.2 配置Spring IoC容器中的Bean 4 1.2.1 问题 4 1.2.2 解决方案 4 1.2.3 工作原理 4 1.3 调用构造程序创建Bean 14 1.3.1 问题 14 1.3.2 解决方案 14 1.3.3 工作原理 14 1.4 解决构造程序歧义 17 ...

    Spring攻略(第二版 中文高清版).part2

    1.2 配置Spring IoC容器中的Bean 4 1.2.1 问题 4 1.2.2 解决方案 4 1.2.3 工作原理 4 1.3 调用构造程序创建Bean 14 1.3.1 问题 14 1.3.2 解决方案 14 1.3.3 工作原理 14 1.4 解决构造程序歧义 17 ...

    基于Spring MVC的web框架 1.1.11

    工具类数据校验 jsp自定义标签 Spring自定义注解 默认requestMapping 1.1.2 代码生成器 1.1.3 首页修改 dateformat.js 时间参数转换 SpringMVC配置文件集中 快递参数接口 1.1.4 des加解密字符串和文件 1.1.5 redis...

    struts自我学习过程程序以及说明

    转换器: ConvertHWorld.java LocaleConverter.java xwork-conversion.properties struts.xml ConverHWorld.jsp Product.java ProductConfirm.java AddProducts.jsp ShowProducts.jsp ProductConfirm-conversion....

    SpringMVC——处理器方法参数的处理.docx

    2、DataBinder 首先调用Spring Web环境中的ConversionService组件,进行数据类型转换和格式化等操作,将ServletRequest中的信息填充到形参对象中; 3、DataBinder 然后调用Validator组件对已经绑定了请求消息数据的形参...

Global site tag (gtag.js) - Google Analytics