Spring MVC【三】数据类型转换

目录

一、属性编辑器:PropertyEditor

二、转换器服务:ConversionService

1.DefaultConversionService(默认转换器服务)

2. DefaultFormattingConversionService(格式化转换器服务)

三、类型转换在容器中的使用

1.Spring核心容器的类型转换

2.SpringMVC容器的类型转换


JDK在界面Bean开发技术中提供了PropertyEditor接口,实现该接口的类可以达成字符串类型和特定类型的转换。Spring核心框架提供了基本类型的属性编辑器实现。另外,Spring还提供了转换器(Converter)和转换服务(ConversionService)用来实现任意对象类型之间的转换。Converter和PropertyEditor的功能相似,ConversionService则是不同类型转换器的容器,包含不同类型的转换器。

一、属性编辑器:PropertyEditor

属性编辑器是JavaBean范畴的概念。之所以称为编辑器,是因为其可以用来进行AWT的Java等图形界面开发,不过因为Java界面开发已经较少使用了,属性编辑器现在主要用于进行字符串类型和对象类型之间的转换。Java在java.beans包中提供了属性编辑器的接口PropertyEditor,如下:

public interface PropertyEditor {
    //设置属性的值,基本类型以包装类传入(自动装箱)
    void setValue(Object value);
    //返回属性的当前值。基本类型被封装成对应的包装类实例
    Object getValue();
    //为属性提供一个表示初始值的字符串,属性编辑器以此值作为属性的默认值
    String getJavaInitializationString();
    //将属性对象以字符串表示然后返回,若返回null,表示该对象不能用字符串表示
    String getAsText();
    //使用字符串准换成需要的属性对象
    void setAsText(String text) throws java.lang.IllegalArgumentException;
    //返回表示有效属性值的字符串数组(如boolean属性对应的有效Tag为true和false),以便属性编辑器能以下拉框的方式显示出来。缺省返回null,表示属性没有匹配的字符值有限集合;
    String[] getTags();
    //……省略一些方法
}

在PropertyEditor接口中,Java提供了实现类PropertyEditorSupport,该类实现了接口的所有方法,也可以扩展自定义属性编辑器。如下实现一个日期类型的属性编辑器MyDatePropertyEditor,用于将yyyy-MM-dd格式的字符串转换成Date类型的日期对象。编辑器的代码如下:

public class MyDatePropertyEditor extends PropertyEditorSupport {

	// 字符串准换成日期对象
	@Override
	public void setAsText(String text) throws IllegalArgumentException {
		SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
		Date date = null;
		try {
			date = format.parse(text);
		} catch (ParseException e) {
			e.printStackTrace();
		}
		setValue(date);
	}

}
//调用此方法转换后,使用该类的getValue方法即可获取Date类型的对象

Spring提供了许多实现PropertyEditorSupport接口的属性编辑器,位于org.springframework.beans.propertyeditors包中,这些编辑器可以实现字符串类型与日期类型、布尔类型和数字类型之间的转换,具体如下:

  • 布尔类型编辑器:CustomBooleanEditor
  • 日期类型编辑器:CustomDateEditor
  • 集合类型编辑器:CustomCollectionEditor
  • 映射类型编辑器:CustomMapEditor
  • 数字类型编辑器:CustomNumberEditor
  • URL地址编辑器:URLEditor

二、转换器服务:ConversionService

Spring3.0之后使用转换器(Converter)替代PropertyEditor的机制。除了接口更干净之外,相比PropertyEditor提供String和Object类型的转换,Convert可以实现任意对象之间的类型转换。转换器服务(ConversionService)是各种类型转换器的容器,Spring核心框架提供了转换器服务接口类ConversionService,针对该接口有以下两种类型的实现:

  • DefaultConversionService:默认转换器服务。维护不同类型的转换器(Converter)进行数据类型的转换。DefaultConversionService间接实现了ConversionService和ConverterRegistry接口;ConversionService的convert()方法用于数据类型的转换;ConverterRegistry中的addConverter()方法用于添加转换器
  • DefaultFormattingConversionService:带格式化支持的转换器服务。在DefaultConversionService基础上进行了功能的延伸,支持国际化(Locale)的格式化和解析。DefaultFormattingConversionService除了会调用DefaultConversionService的addDefaultConverters()方法添加默认的转换器之外,还实现了FormatterRegistry(),可以进行格式化转换器的添加

1.DefaultConversionService(默认转换器服务)

Spring核心框架提供了基本类型之间的转换器类,包括数组、字符、集合、枚举、整型、数字型、对象型和字符串类型之间的转换器。这些转换器类继承自Converter接口。在DefaultConversionService中将这些转换器分为三组进行注册和添加;

  • 标尺转换器组(Scalar):主要是基本类型之间的转换,包括String、Number、Boolean、Enum、Charset、Properties和UUID。
  • 集合转换器组(Collection):主要包括String、Object和Array、Collection、Map之间的转换。
  • 其他:包括Byte与Buffer、String与TimeZone、Object与Object等类型之间的转换。

转换器服务可以脱离Spring容器单独使用,以字符串和日期类型为例,DefaultConversionService默认支持将yyyy/mm/dd格式的字符串转换为Date类型的对象,如下:

public static void main(String[] args) {
    ConversionService conversionService = new DefaultConversionService();
    Date date = conversionService.convert("2021/4/28", Date.class);
}

如果Spring默认提供的转换器不够使用,可以实现Converter接口自定义转换器,并将此转换器添加到DefaultConversionService的对象中。如下为String类型与User类型转换器:

public class User {
    private int id;
    private String name;
    private Date birthday;
    //……省略getter和setter方法
}
public class MyUserConvert implements Converter<String, User> {

	@Override
	public User convert(String source) {
		// 规定字符串表示User对象方式示例:123#郭靖#1341-10-15
		String[] strArr = source.split("#");
		User user = null;
		if (strArr.length != 3) {
			return user;
		}
		try {
			Integer id = Integer.parseInt(strArr[0]);
			String name = strArr[1];
			Date birthday = new SimpleDateFormat("yyyy-mm-dd").parse(strArr[2]);
			user = new User(id, name, birthday);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return user;
	}

}

然后通过转换器服务的addConverter()方法将新定义的转换器类型的实例添加到转换器服务的实例中之后,就可以调用convert()方法进行转换,如下:

public static void main(String[] args) {
    DefaultConversionService conversionService = new DefaultConversionService();
    conversionService.addConverter(new MyUserConvert());
    String userStr = "123#郭靖#1341-10-15";
    User user = conversionService.convert(userStr, User.class);
    System.out.println(user);
}

2. DefaultFormattingConversionService(格式化转换器服务)

格式化转换器服务(DefaultFormattingConversionService)调用了DefaultConversionService的addDefaultConverters()方法默认注册的转换器。此外,其还提供了两个扩展功能:

  • 支持国际化的格式化和解析;
  • 可以使用注解(比如@DateTimeFormat)对Bean的属性进行细粒度的配置。

Spring默认提供日期类型格式转换器(DateFormatter、MonthDayFormatter和YearFormatter)和数字类型的日期转换器(NumberStyleFormatter、CurrencyStyleFormatter和PercentStyleFormatter)。Format提供消息、日期等国际化的支持。

格式化转换器也可以通过实现Formatter<T>接口自定义,实现接口的print()和parse()接口方法。

public class MyDateFormatter implements Formatter<Date> {

	// 日期对象转换为字符串
	@Override
	public String print(Date object, Locale locale) {
		SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-mm-dd");
		return dateFormat.format(object);
	}

	// 字符串转换为日期对象
	@Override
	public Date parse(String text, Locale locale) throws ParseException {
		SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-mm-dd", locale);
		return dateFormat.parse(text);
	}
}
public static void main(String[] args) {
    DefaultFormattingConversionService dfcs = new DefaultFormattingConversionService();
    dfcs.addFormatter(new MyDateFormatter());
    String dateStr = dfcs.convert("2021-04-28", String.class);
}

三、类型转换在容器中的使用

在实际开发中,属性编辑器和转换器服务很少单独使用,而是由容器进行调用进行Bean的组装。在SpringMVC项目中,常见的使用方式是对前端请求参数进行自动类型的转换,组装成后端需要的对象类型。

核心容器默认使用属性编辑器进行类型转换,如果配置了转换器服务的Bean,则会使用转换器服务。SpringMVC开启MVC注解驱动之后,会默认注册一个转换器服务Bean。

1.Spring核心容器的类型转换

以User类型为例,他在XML中配置成Bean,如下:

<bean id="user" class="com.mec.springmvc.model.User">
    <property name="id" value="123"></property>
    <property name="name" value="Zhang San"></property>
    <!-- 以字符串注入日期类型的属性值 -->
    <property name="birthday" value="2021/4/28"></property>
</bean>

上述配置中使用yyyy/MM/dd字符串注入日期类型的属性值(配置成yyyy/MM/dd格式之后不用其他配置,Spring默认可以解析这种格式的字符串为Date对象,但是如yyyy-MM-dd这种格式便解析不了)。容器在初始化Bean的时候会将字符串的值转换成Date日期类型。Spring容器默认使用本身定义的属性编辑器进行转换。(Spring中没有默认注册CustomDateEditor编辑器,通过查看PropertyEditorRegistrySupport类的createDefaultEditors()方法可以看到Spring默认注册的编辑器,其中并没有CustomDateEditor)。Spring提供了如下几个Map存储属性编辑器:

public class PropertyEditorRegistrySupport implements PropertyEditorRegistry {
	@Nullable
	private ConversionService conversionService;
	private boolean defaultEditorsActive = false;
	private boolean configValueEditorsActive = false;
        //默认的属性编辑器,createDefaultEditors()方法中就将默认的属性编辑器们设置在该map中
	@Nullable
	private Map<Class<?>, PropertyEditor> defaultEditors;
        //重写默认的属性编辑器,在getDefaultEditor方法中(获取默认的属性编辑器)首先是从该map中获取,接下来才是从defaultEditors中获取
	@Nullable
	private Map<Class<?>, PropertyEditor> overriddenDefaultEditors;

        //存储自定义属性编辑器的map
	@Nullable
	private Map<Class<?>, PropertyEditor> customEditors;
	@Nullable
	private Map<String, CustomEditorHolder> customEditorsForPath;
	@Nullable
	private Map<Class<?>, PropertyEditor> customEditorCache;
}

除了使用Spring默认提供的属性编辑器之外,也可以向容器中加入自定义的编辑器。配置方式如下(注意下面使用value属性直接指定类的全限定名。不同版本配置方式可能有所不同,此处版本为Spring5):

<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
    <property name="customEditors">
        <map>
    	    <entry key="java.util.Date" value="com.mec.springmvc.model.MyDatePropertyEditor" />
    	</map>
    </property>
</bean>	

上面是对CustomEditorConfigurer这个类的customEditors成员(map)进行注入;该类还有一个成员propertyEditorRegistrars(PropertyEditorRegistrar[]类型),通过注入该成员也可以加入自定义的编辑器,如下:

<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
    <property name="propertyEditorRegistrars">
        <array>
    	    <bean class="com.mec.springmvc.model.MyDatePropertyEditor" />
    	</array>
    </property>
</bean>		

此处需要注意的是MydatePropertyEditor除了继承PropertyEditorSupport之外,还需要实现PropertyEditorRegistrar接口及其registerCustomEditors方法,此处该方法实现如下:

public void registerCustomEditors(PropertyEditorRegistry registry) {
    registry.registerCustomEditor(Date.class, this);
}

如果在容器中定义了id值是conversionService的Bean,则会使用转换服务器进行类型转换,和属性编辑器一样,也可以添加自定义的转换器实现特殊类型的转换。如下通过配置ConversionServiceFactoryBean类型的Bean之后,容器就会使用该Bean进行类型转换,转换器服务默认维护了基本类型的类型转换器,设置converters属性可以增加自定义的转换器。

<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
    <!-- 增加自定义的转换器 -->
    <property name="converters">
        <set>
	    <bean class="com.mec.springmvc.model.MyDateConvert"></bean>
	</set>
    </property>
</bean>

上述配置有如下几个注意点:

  • Bean的ID需要指定,且必须是conversionService。
  • Bean的类使用的是FormattingConversionServiceFactoryBean,也可以使用ConversionServiceFactoryBean,但不能使用FormattingConversionService等,也就是说必须使用转换器服务的工厂Bean类。FormattingConversionServiceFactoryBeanFactoryBean<FormattingConversionService>的实现。FactoryBean(工厂Bean)是Spring提供的普通Bean之外的另一种Bean,该Bean提供了getObject()返回实际的对象,也就是可以返回不同类型的实际对象。此类可以实现单例工厂,而且在AOP开发中很有用。
  • BeanFactory:Bean工厂,通过其可以得到不同类型的Bean,也就是Bean容器;
  • FactoryBean:工厂Bean,是一种类型的Bean,是单一Bean的工厂模式。

2.SpringMVC容器的类型转换

在SpringMVC项目中,如果开启了MVC注解功能,则WebMvcConfigurationSupport会自动注册名字为mvcConversionServiceFormattingConversionService的BeanFormattingConversionServiceFactoryBean类型的Bean。MVC注解可以使用XML和类配置的方式开启。

若采用XML方式(<mvc:annotation-driven>)开启MVC注解功能,默认会装配FormattingConversionServiceFactoryBean,如果要增加自定义的转换器,则需要给该配置标签的conversion-service属性指定一个转换器服务的Bean,如下:

 <mvc:annotation-driven conversion-service="conversionService" />	
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
    <!-- 增加自定义的转换器 -->
    <property name="converters">
        <set>
            <bean class="com.mec.springmvc.model.MyDateConvert"></bean>
        </set>
    </property>
</bean>

采用类配置开启方式时,使用@EnableWebMvc注解:

@Configuration
@ComponentScan(basePackages = { "com.mec.springmvc.controller" })
@Import(value = { MvcConvertConfigure.class })
@EnableWebMvc    //开启spring mvc注解
public class WebAppConfig {
    //……
}

如果要增加转换器,则配置类需要继承WebMvcConfigurationSupport类,使用@PostConstruct在该Bean实例初始化后添加自定义的转换器,如下:

@Configuration
public class MvcConvertConfigure extends WebMvcConfigurationSupport {
	@Autowired
	private FormattingConversionService mvcConversionService;

	@PostConstruct
	public void addCustomConvert() {
		if (mvcConversionService != null) {
			mvcConversionService.addConverter(new MyDateConvert());
		}
	}
}
public class MyWebApplicationInitializer implements WebApplicationInitializer {

	@Override
	public void onStartup(ServletContext servletContext) throws ServletException {
		AnnotationConfigWebApplicationContext annoWebContext = new AnnotationConfigWebApplicationContext();
		annoWebContext.register(WebAppConfig.class);
		annoWebContext.registerShutdownHook();
//		annoWebContext.refresh();    //如果配置了@EnableWebMvc,这里一定要注释起来,否则无法启动tomcat。Spring版本5.2.3
		//省略中央控制器配置……
	}
}

Spring容器对Bean的属性进行转换是通过数据绑定的方式进行的,包括Bean包装器(BeanWrapper)和数据绑定(DataBinder、WebDataBinder)两种方式,他们通过调用容器中管理的转换器服务Bean进行数据的类型转换。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值