`
wsjjasper
  • 浏览: 13543 次
  • 性别: 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-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. 方便的变量 ...

    [Pro.Spring.3(2012)].Clarence.Ho.文字版

    该章节介绍了Spring框架如何支持数据类型转换、格式化和校验,帮助开发者在数据输入和输出过程中进行有效的数据处理。读者将学会如何利用Spring的类型转换器、格式化器和校验器来提高数据的准确性和用户体验。 ### ...

    spring 常用的26个包

    3. `org.springframework.core`:这个基础包提供了Spring框架的基本工具类,包括泛型操作、反射、类型转换和资源访问等。 4. `org.springframework.aop`:AOP(面向切面编程)包,提供了面向切面的编程实现,可以...

    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 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 mvc校验

    在Spring MVC框架中,数据校验是一个至关重要的环节,它确保了输入的数据符合业务规则,降低了因错误数据引发的问题。这篇博文"spring mvc校验"可能深入探讨了如何在Spring MVC项目中实现有效的数据验证。 首先,...

    21. Spring Boot过滤器、监听器【从零开始学Spring Boot】

    在Spring Boot框架中,过滤器(Filter)和监听器(Listener)是两个非常重要的概念,它们可以帮助我们实现一些自定义的处理逻辑,如数据校验、日志记录、请求拦截等。下面将详细讲解这两个概念及其在实际开发中的...

    SpringMVC-8 数据类型转换、数据格式化与数据校验

    在Spring MVC框架中,数据类型转换、数据格式化和数据校验是开发Web应用程序时不可或缺的部分。这些功能有助于确保从客户端接收到的数据准确无误,同时提供了一种优雅的方式来处理和展示这些数据。本篇文章将深入...

    springmvc01.zip_DEMO_JSON校验_spring mvc

    - 实践JSON数据的交互,包括校验和返回JSON响应。 - 学习文件上传和下载的实现。 - 探索Spring MVC的高级特性,如拦截器、AOP、异常处理等。 通过这个DEMO,你可以亲手实践上述知识点,加深对Spring MVC的理解,并...

    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数据校验.docx

    4. Controller处理完成后,返回ModelAndView对象,其中包含处理结果(Model)和视图逻辑名称。 5. ViewResolver解析ModelAndView中的逻辑视图名,将其转换为实际的View对象,并从Model中取出数据。 在Spring MVC中...

    springmvc注解式控制器的数据验证、类型转换及格式化 SpringMVC数据验证

    总结来说,Spring MVC的注解式控制器提供了强大的数据验证、类型转换和格式化功能,简化了Web开发过程,提升了应用的安全性和用户体验。通过合理利用这些特性,开发者可以构建更加健壮、易于维护的Web应用。

    spring mvc4 详细教程 PDF

    10. 数据转换和数据格式化及数据校验,可以通过Spring MVC的注解来实现数据的转换、格式化和校验。 11. 处理JSON,Spring MVC提供了HttpMessageConverter来处理JSON数据。 12. 国际化支持,允许开发者构建支持多...

    专题资料(2021-2022年)SpringMVC数据类型转换要点.doc

    在Spring 3之前,数据转换、验证和格式化主要依赖于`PropertyEditor`。这个机制的工作流程如下: 1. 类型转换:使用`PropertyEditor`的`setAsText(String)`方法将字符串转换为对象,然后通过`setValue(Object)`设置...

    spring3.0.jar

    6. **泛型集合支持**:Spring 3.0开始全面支持Java泛型,这使得容器可以更好地理解并管理集合类型,避免了类型转换的麻烦和潜在错误。 7. **JDBC抽象层增强**:Spring 3.0改进了JdbcTemplate和SimpleJdbcTemplate,...

    springbind.zip

    7. **转换和格式化**:Spring MVC允许自定义类型转换器和格式化器,以便在绑定请求参数时处理特定的数据类型。 8. **处理异常**:如果绑定过程出错,Spring会自动抛出异常,并可以配置相应的异常处理器来生成合适的...

Global site tag (gtag.js) - Google Analytics