接前一篇 Spring Framework 官方文档学习(四)之Validation、Data Binding、Type Conversion(一)

本篇主要内容:Spring Type Conversion(ConversionService)、Spring Field Formatting、globle date & time format、Spring Validation。

本篇上承自前一篇,建议先看前一篇。

4、Spring Type Conversion (Spring类型转换)

上一篇有提到,Spring早期使用PropertyEditor进行Object与String的转换。

Spring 3 引入了core.convert 包,提供了通用的类型转换系统。该系统定义了一个SPI -- 实现了类型转换逻辑,还定义了一个API -- 用于在runtime执行类型转换。在Spring容器内,该系统可以用作PropertyEditors 的替代品来转换外部bean property value strings 到需要的property 类型。

插一句,SPI 是 Service Provider Interface,与API的区别见 difference-between-spi-and-api 。

4.1 Converter SPI

接口很简单:

1 package org.springframework.core.convert.converter;
2
3 public interface Converter<S, T> {
4
5     T convert(S source);
6
7 }

View Code

想要创建自己的converter,实现这个接口即可。

需要注意集合/数组的转换需要额外的支持。一般情况下不必考虑这个,了解即可。

Spring在core.convert.support 包中以提供了几个converter实现,包括从String到Number以及其他常见类型的转换。

来看一下 StringToInteger:

1 package org.springframework.core.convert.support;
2
3 final class StringToInteger implements Converter<String, Integer> {
4
5     public Integer convert(String source) {
6         return Integer.valueOf(source);
7     }
8
9 }

View Code

4.2 ConverterFactory

当你需要从类层次上的转换逻辑时,例如,从String转成java.lang.Enum对象时,实现ConverterFactory即可:

1 package org.springframework.core.convert.converter;
2
3 public interface ConverterFactory<S, R> {
4
5     <T extends R> Converter<S, T> getConverter(Class<T> targetType);
6
7 }

View Code

例子:

 1 package org.springframework.core.convert.support;
 2
 3 final class StringToEnumConverterFactory implements ConverterFactory<String, Enum> {
 4
 5     public <T extends Enum> Converter<String, T> getConverter(Class<T> targetType) {
 6         return new StringToEnumConverter(targetType);
 7     }
 8
 9     private final class StringToEnumConverter<T extends Enum> implements Converter<String, T> {
10
11         private Class<T> enumType;
12
13         public StringToEnumConverter(Class<T> enumType) {
14             this.enumType = enumType;
15         }
16
17         public T convert(String source) {
18             return (T) Enum.valueOf(this.enumType, source.trim());
19         }
20     }
21 }

View Code

4.3、GenericConverter(略)

4.4、ConditionalGenericConverter(略)

4.5、ConversionService API 转换服务API

ConversionService定义了一个统一的API以执行类型转换逻辑,是一个facade(门面)接口。

 1 package org.springframework.core.convert;
 2
 3 public interface ConversionService {
 4     // 判断,能否将源类型转成目标类型
 5     boolean canConvert(Class<?> sourceType, Class<?> targetType);
 6     // 转换
 7     <T> T convert(Object source, Class<T> targetType);
 8     // 判断
 9     boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType);
10     // 转换
11     Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
12
13 }

View Code

从上面的代码可以推理,该接口的实现必定是先判断能否转换,如能转换再调用相应的converter进行转换。--问题,从哪里调用converter?ConverterRegistry!

所以多数 ConversionService的实现类 也实现了 ConverterRegistry,因为它提供了一个 SPI 以注册 converters 。在内部,一个ConversionService的实现委托注册的converters来执行类型转换逻辑。

重点:Spring在core.convert.support 包中提供了一个健全的实现。GenericConversionService 是一个通用的实现,适用于多数环境。ConversionServiceFactory 提供了一个便捷的工厂以创建常见的ConversionService配置。

4.6、Configuring a ConversionService (配置)

一个ConversionService是一个 stateless 的对象,被设计成在应用启动时被实例化,然后在多个线程之间共享。(PropertyEditor则是线程不安全的)

在一个Spring应用中,可以给每个容器配置一个ConversionService。Spring会加载并调用它。你也可以注入需要的地方,直接调用它。

注意如果没有ConversionService被注册,会使用原始的基于PropertyEditor的系统

注册一个默认的ConversionService(id不可修改):

<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean"/>

注意,ConversionServiceFactoryBean的Javadoc说 返回的conversionService是DefaultConversionService 实例。

DefaultConversionService 可以在strings、numbers、enums、collections、maps以及其他常用类型之间进行转换。

要补充或者覆盖默认的转换器,需要设置converters 属性。其值可以是Converter、ConverterFactory或者GenericConverter的实现。

1 <bean id="conversionService"
2         class="org.springframework.context.support.ConversionServiceFactoryBean">
3     <property name="converters">
4         <set>
5             <bean class="example.MyCustomConverter"/>
6         </set>
7     </property>
8 </bean>

View Code

个人经验:

1、非web项目的Spring应用,不会自动注册ConversionService bean,就是说默认基于PropertyEditor。(web项目暂时没测)

2、@Configuration class 中进行ConversionService bean定义时,有几个选择:使用其实现类,如GenericConversionService、DefaultConversionService 或者其他;也可以,使用ConversionServiceFactoryBean。区别在于,GenericConversionService 默认没有注册converters,DefaultConversionService 注册了很多converters,ConversionServiceFactoryBean 则提供了DefaultConversionService。

-- 所以,一般情况下,直接使用ConversionServiceFactoryBean 即可。

针对第二点,代码如下:

package cn.larry.config;import javax.annotation.PostConstruct;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.context.support.ConversionServiceFactoryBean;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.validation.DataBinder;/**
* @author LarryZeal
*
*/
//@Profile("conversionService")
@Configuration
public class ConversionServiceConfig {/** @Autowired* public ConversionService conversionService; // spring boot 默认木有这个,所以无法注入啊。* * @PostConstruct* public void run(){* Integer convert = conversionService.convert("123", int.class);* System.out.println(convert);* }*/// GenericConversionService 没有注册converters,需要手动注册。
/*    @Beanpublic ConversionService conversionService() {GenericConversionService conversionService = new GenericConversionService();boolean canConvert = conversionService.canConvert(String.class, int.class);System.out.println("------------"+canConvert+"----------"); //GenericConversionService默认没有convertInteger convert = conversionService.convert("123", int.class);System.out.println(convert);return conversionService;}*/// DefaultConversionService 才默认注册了converters。
/*    @Beanpublic ConversionService conversionService1() {DefaultConversionService conversionService = new DefaultConversionService();boolean canConvert = conversionService.canConvert(String.class, int.class);System.out.println("------------"+canConvert+"----------");Integer convert = conversionService.convert("123", int.class);System.out.println(convert);return conversionService;}*//*** This implementation creates a DefaultConversionService. * Subclasses may override createConversionService() in order to return a GenericConversionService instance of their choosing. * * 这个可以取代上面的DefaultConversionService,基本一致。* * @return*/@Beanpublic ConversionServiceFactoryBean conversionService(){return new ConversionServiceFactoryBean();}private DataBinder dataBinder;}

View Code

另,

1,通常也可以在Spring MVC应用中使用ConversionService。待后续。See Section 22.16.3, “Conversion and Formatting” in the Spring MVC chapter.

2,在特定环境下,可能希望在转换时应用格式。See Section 9.6.3, “FormatterRegistry SPI” for details on using FormattingConversionServiceFactoryBean.

4.7、编码使用ConversionService

bean定义后注入即可使用,上面代码已展示了如何定义bean。

 1 @Service
 2 public class MyService {
 3
 4     @Autowired
 5     public MyService(ConversionService conversionService) {
 6         this.conversionService = conversionService;
 7     }
 8
 9     public void doIt() {
10         this.conversionService.convert(...)
11     }
12 }

View Code

多数情况下不能用于复合类型,如集合。例如,如果需要编码转换List<Integer>到List<String>,需要提供源类型和目标类型的正式定义。

幸运的是,TypeDescriptor 提供了多种选项,使其变得简单直接:

1 DefaultConversionService cs = new DefaultConversionService();
2
3 List<Integer> input = ....
4 cs.convert(input,
5     TypeDescriptor.forObject(input), // List<Integer> type descriptor
6     TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(String.class)));

View Code

再次提醒,DefaultConversionService 自动注册的converters,可用于大多数环境。其中包含了collection converters、scalar converters,以及基本的Object到String的converter。

另,同样的converters也可以使用DefaultConversionService 的static addDefaultConverters 注册到任意ConverterRegistry 中。

值类型的转换器,会被自动复用到值类型的array和collection中,不必再去创建。 -- 值类型这个概念,应该就是基本类型吧。

5、Spring Field Formatting (Spring字段格式化)

上面有提到, core.convert 是一个通用目的的类型转换系统。提供了统一的ConversionService API和强类型的Converter SPI,以实现转换逻辑。Spring容器使用该系统来绑定bean property values。

此外,SpEL 和 DataBinder 使用该系统绑定 field values。例如,当SpEL需要强制 a Short to a Long来完成expression.setValue(Object bean, Object value)尝试时,core.convert系统负责执行该强制。

---------------------------------上面是回顾,现在开始新内容--------------------------------

但是,除了格式转换,你还经常需要本地化String values -- 就是以当地格式展示,如货币、日期等。通用的core.convert Converter SPI不能直接完成格式化需求。

基于此,Spring 3 引入了 Formatter SPI,相比PropertyEditors简单直接。

ConversionService 为Converter SPI和Formatter SPI提供了统一的 API。

个人体会:一个是粗粒度的,一个是细粒度的。

5.1 Formatter SPI

package org.springframework.format;
public interface Formatter<T> extends Printer<T>, Parser<T> {}

public interface Printer<T> {String print(T fieldValue, Locale locale);
}

import java.text.ParseException;
public interface Parser<T> {T parse(String clientValue, Locale locale) throws ParseException;
}

要创建自己的Formatter,实现Formatter接口即可。注意异常和线程安全。

format 包的子包中提供了几个Formatter的实现类。number,datetime,datetime.joda。参考DateFormatter :

 1 package org.springframework.format.datetime;
 2
 3 public final class DateFormatter implements Formatter<Date> {
 4
 5     private String pattern;
 6
 7     public DateFormatter(String pattern) {
 8         this.pattern = pattern;
 9     }
10
11     public String print(Date date, Locale locale) {
12         if (date == null) {
13             return "";
14         }
15         return getDateFormat(locale).format(date);
16     }
17
18     public Date parse(String formatted, Locale locale) throws ParseException {
19         if (formatted.length() == 0) {
20             return null;
21         }
22         return getDateFormat(locale).parse(formatted);
23     }
24
25     protected DateFormat getDateFormat(Locale locale) {
26         DateFormat dateFormat = new SimpleDateFormat(this.pattern, locale);
27         dateFormat.setLenient(false);
28         return dateFormat;
29     }
30
31 }

View Code

5.2、注解驱动的Formatting

绑定一个注解到一个formatter,实现 AnnotationFormatterFactory 即可。详见这里。略。

5.2.1、Format Annotation API 

便携的格式化注解API存在于org.springframework.format.annotation包中。

Use @NumberFormat to format java.lang.Number fields. Use @DateTimeFormat to format java.util.Date, java.util.Calendar, java.util.Long, or Joda Time fields.

例子:

public class MyModel {@DateTimeFormat(iso=ISO.DATE)private Date date;}

5.3、FormatterRegistry SPI

这是一个SPI,用于注册formatters和converters。其实现FormattingConversionService 适用于多数环境。

该实现可以编码式配置或者声明式配置,通过FormattingConversionServiceFactoryBean。

另外,其还实现了ConversionService,所以可以配置和DataBinder、SpEL一起使用。

http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#format-FormatterRegistry-SPI

5.4、FormatterRegistrar SPI

通过FormatterRegistry注册formatters和converters。 -- 蛋疼,两个词分不清。稍后研究下。

http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#format-FormatterRegistrar-SPI

6、配置全局 date & time 格式

默认,如果没有使用注解@DateTimeFormat,会使用 DateFormat.SHORT 风格。当然,你可以定义自己的全局格式。

需要确保Spring没有注册默认的formatters,然后手动注册所有的formatters。使用 DateFormat.SHORT 或 DateFormatterRegistrar  -- 这取决于你是否使用Joda Time 库。

例如,

 1 @Configuration
 2 public class AppConfig {
 3
 4     @Bean
 5     public FormattingConversionService conversionService() {
 6
 7         // Use the DefaultFormattingConversionService but do not register defaults
 8         DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService(false);
 9
10         // Ensure @NumberFormat is still supported
11         conversionService.addFormatterForFieldAnnotation(new NumberFormatAnnotationFormatterFactory());
12
13         // Register date conversion with a specific global format
14         DateFormatterRegistrar registrar = new DateFormatterRegistrar();
15         registrar.setFormatter(new DateFormatter("yyyyMMdd"));
16         registrar.registerFormatters(conversionService);
17
18         return conversionService;
19     }
20 }

View Code

注意,如果使用Spring MVC,记住,需要显式的配置使用conversion service。对于基于Java的@Configuration来说,这意味着继承WebMvcConfigurationSupport 并重写mvcConversionService()。对于XML来说,应该使用<mvc:annotation-driven>元素的'conversion-service'属性。See Section 22.16.3, “Conversion and Formatting” for details.

http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#format-configuring-formatting-globaldatetimeformat

7、Spring Validation

Spring 3 引入了validation support的几个增强。

第一,完整的支持JSR-303 Bean Validation API。

第二,编码式使用时,Spring的DataBinder现在可以validate objects,也可以bind to them。

第三,Spring MVC现在支持声明式校验@Controller的输入。

7.1、JSR-303 Bean Validation API概览

允许定义声明式校验限制。如:

public class PersonForm {@NotNull@Size(max=64)private String name;@Min(0)private int age;}

For general information on JSR-303/JSR-349, see the Bean Validation website. For information on the specific capabilities of the default reference implementation, see the Hibernate Validator documentation.

7.2、配置一个Bean Validation Provider

Spring提供了对Bean Validation API的完整的支持。包括启动其实现(provider),并将其作为Spring的bean。

所以,需要validation时,可以注入 javax.validation.ValidatorFactory 或者 javax.validation.Validator。

使用LocalValidatorFactoryBean 来配置默认的Validator:

<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>

上面,会引动Bean Validation使用其自身的启动机制来初始化。如果在classpath中有JSR-303/JSR-349 provider,如Hibernate Validator,会被自动探测到。

7.2.1、注入一个Validator

LocalValidatorFactoryBean implements both javax.validation.ValidatorFactory and javax.validation.Validator, as well as Spring’s org.springframework.validation.Validator.

可以注入这几个接口的任意一个引用来使用。

①如果倾向于使用Bean Validation API,直接注入javax.validation.Validator

1 import javax.validation.Validator;
2
3 @Service
4 public class MyService {
5
6     @Autowired
7     private Validator validator;
8
9 }

View Code

②如果倾向于使用Spring Validation API,可以注入org.springframework.validation.Validator:

1 import org.springframework.validation.Validator;
2
3 @Service
4 public class MyService {
5
6     @Autowired
7     private Validator validator;
8
9 }

View Code

7.2.2、配置自定义限制(Constraints)

每个Bean Validation constraint都由两部分组成。首先,一个@Constraint注解,声明了the constraint和其可配置的properties。其次,javax.validation.ConstraintValidator接口的实现,实现了the constraint's behavior。为了将声明与实现关联起来,每个@Constraint注解引用了一个相关的ValidationConstraint实现类。在运行时,当一个ConstraintValidatorFactory在你的domain model中遇到constraint 注解时会实例化被引用的实现。

默认的,LocalValidatorFactoryBean 配置了一个SpringConstraintValidatorFactory ,其使用Spring来创建ConstraintValidator实例。

下面是一个例子,一个定制的 @Constraint 声明,紧接着相关联的ConstraintValidator 实现--使用Spring来依赖注入:

1 @Target({ElementType.METHOD, ElementType.FIELD})
2 @Retention(RetentionPolicy.RUNTIME)
3 @Constraint(validatedBy=MyConstraintValidator.class)
4 public @interface MyConstraint {
5 }

View Code

1 import javax.validation.ConstraintValidator;
2
3 public class MyConstraintValidator implements ConstraintValidator {
4
5     @Autowired;
6     private Foo aDependency;
7
8     ...
9 }

View Code

As you can see, a ConstraintValidator implementation may have its dependencies @Autowired like any other Spring bean.

7.2.3、Spring-driven Method Validation

方法验证,由Bean Validation 1.1 支持,作为一个自定义的扩展同样也被Hibernate Validator 4.3支持,通过一个 MethodValidationPostProcessor  bean定义,它可以被集成到Spring context中。

<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor"/>

为了支持Spring-driven method validation,所有的目标类都需要使用Spring的@Validated注解,可以选择声明校验组。详见MethodValidationPostProcessor的javadoc。

7.2.4、更多配置选项(略)

7.3、配置一个DataBinder

从Spring 3开始,可以通过一个Validator来配置一个DataBinder实例。一旦配置了,就可以通过调用binder.validate()来调用Validator。所有校验Errors会自动添加到binder的BindingResult。

当编码式使用DataBinder时,这样可以用于在绑定后调用校验逻辑:

 1 Foo target = new Foo();
 2 DataBinder binder = new DataBinder(target);
 3 binder.setValidator(new FooValidator());
 4
 5 // bind to the target object
 6 binder.bind(propertyValues);
 7
 8 // validate the target object
 9 binder.validate();
10
11 // get BindingResult that includes any validation errors
12 BindingResult results = binder.getBindingResult();

View Code

一个DataBinder也可以配置多个Validator实例 -- 通过dataBinder.addValidators和dataBinder.replaceValidators。

Spring Framework 官方文档学习(四)之Validation、Data Binding、Type Conversion(二)相关推荐

  1. Spring Framework 官方文档学习(四)之Validation、Data Binding、Type Conversion

    本篇太乱,请移步: Spring Framework 官方文档学习(四)之Validation.Data Binding.Type Conversion(一) 写了删删了写,反复几次,对自己的描述很不 ...

  2. Spring Framework 官方文档学习(三)之Resource

    起因 标准JDK中使用 java.net.URL 来处理资源,但有很多不足,例如不能限定classpath,不能限定 ServletContext 路径. 所以,Spring提供了 Resource ...

  3. Spring Boot 官方文档学习(一)入门及使用

    Spring Boot 官方文档学习(一)入门及使用 个人说明:本文内容都是从为知笔记上复制过来的,样式难免走样,以后再修改吧.另外,本文可以看作官方文档的选择性的翻译(大部分),以及个人使用经验及问 ...

  4. Spring 4 官方文档学习 Spring与Java EE技术的集成

    本部分覆盖了以下内容: Chapter 28, Remoting and web services using Spring -- 使用Spring进行远程和web服务 Chapter 29, Ent ...

  5. Spring指南之使用Spring缓存数据(Spring Framework官方文档之缓存抽象详解)

    1.请参见官方文档Spring指南之使用 Spring 缓存数据 2.请参见Spring官方文档之缓存抽象 3.参见github代码 文章目录 一.简介 二.你将创造什么(What You Will ...

  6. Spring 4 官方文档学习(十一)Web MVC 框架之异常处理

    1.HandlerExceptionResolver Spring HandlerExceptionResolver的实现们会处理controller执行过程中发送的unexpected except ...

  7. Spring 4 官方文档学习(十)数据访问之JDBC

    说明:未修订版,阅读起来极度困难 1.Spring框架JDBC的介绍 Spring JDBC - who does what? 动作 Spring 你 定义连接参数   是 打开连接 是   指定SQ ...

  8. 《Spring 5 官方文档》5. 验证、数据绑定和类型转换(二)

    5.5 Spring类型转换 Spring 3引入了core.convert包来提供一个一般类型的转换系统.这个系统定义了实现类型转换逻辑的服务提供接口(SPI)以及在运行时执行类型转换的API.在S ...

  9. Spring Data Commons 官方文档学习

    Spring Data Commons 官方文档学习   -by LarryZeal Version 1.12.6.Release, 2017-07-27 为知笔记版本在这里,带格式. Table o ...

最新文章

  1. TCP/IP详解学习笔记(9)-TCP协议概述
  2. 计算机类会议论文2021截稿,科学网—人工智能 | 国际会议截稿信息3条 - 李昕的博文...
  3. ArrayList与数组
  4. sklearn自学指南(part50)--独立成分分析(ICA)
  5. USACO Training Section 1.2 [USACO1.2]方块转换 Transformations
  6. python string length_如何使用python获取字符串长度?哪些方法?
  7. vue传值到后端_Vue.js快速入门就从这儿开始特别是后端程序员
  8. 排序算法(二)--堆排序(JAVA)
  9. HDU2084 数塔【DP】
  10. 台大李宏毅Machine Learning 2017Fall学习笔记 (16)Unsupervised Learning:Neighbor Embedding
  11. 大牛精心挑选的25个Visual Basic学习资料汇总
  12. 单元测试 测试用例 用例测试文件golang的单元测试
  13. matlab编制刚度矩阵,平面3节点三角形单元刚度矩阵matlab程序
  14. 计算机在高分子材料中的应用软件,计算机技术在高分子材料工程中的应用(10页)-原创力文档...
  15. Python制作绘图板,基础功能实现
  16. python基础教程四级查数据_四六级成绩还可以这样查?Python助你装B一步到位!!!...
  17. PHP 屏幕亮度,window_Win8系统 调节电脑屏幕亮度的四种方法,第一页:Win8系统下调节电脑屏 - phpStudy...
  18. 设计师都在用的素材网站,真的纯免费,还能商用
  19. 小白深度学习入坑指南
  20. H3C AC:访客认证配置

热门文章

  1. solaris perl print不输出_IC设计中的perl编程
  2. mysql 上级组织参数值_MYSQL组织结构设计构思(快速查上级和下级)
  3. 2021年qs世界大学计算机科学排名,2015年QS世界大学计算机专业排名
  4. flutter release 版本 调试_腾讯课堂Flutter工程实践系列——接入篇
  5. ES2021 更新的内容!
  6. weblogic点击服务器没有信息,无法安装Weblogic服务器12C,jar文件甚至没有打开
  7. 超级警探大战悍匪2java_JavaWeb之会话技术
  8. java在初始化过程_Java初始化和实例化顺序
  9. python质量转换程序,Python库的文件转换成MP3和设置它们的质量
  10. Vue-cli 4.x 中的全局样式配置