精华内容
下载资源
问答
  • spring 类型转换器_Spring中的类型转换
    2020-06-10 20:05:52

    spring 类型转换器

    以下是一些需要类型转换的简单情况:
    情况1。 为了帮助简化bean配置,Spring支持属性值与文本值之间的转换。 每个属性编辑器仅设计用于某些类型的属性。 为了使用它们,我们必须在Spring容器中注册它们。
    案例2。 同样,在使用Spring MVC时,控制器会将表单字段值绑定到对象的属性。 假设对象是由另一个对象组成的,则MVC控制器无法自动将值分配给内部自定义类型对象,因为表单中的所有值都作为文本值输入。 Spring容器将把文本值转换为原始类型,而不转换为自定义类型对象。 为此,我们必须在MVC流中初始化自定义编辑器。

    本文将讨论实现自定义类型对象的转换器的各种方法。 为了详细说明这些,让我们考虑以下用例。 在该示例中,我想模拟游戏场地预订系统。 所以这是我的领域对象:

    public class Reservation {
    
    	public String playGround;
    	public Date dateToReserve;
    	public int hour;
    	public Player captain;
    	public SportType sportType;
    
    	public Reservation(String playGround, Date date, int hour, Player captain, SportType sportType) {
    		this.playGround = playGround;
    		this.dateToReserve = date;
    		this.hour = hour;
    		this.captain = captain;
    		this.sportType = sportType;
    	}
    
    	/**
    	 * Getters and Setters
    	 */
    }
    
    public class Player {
    
    	private String name;
    	private String phone;
    	/**
    	 * Getters and Setters
    	 */
    
    }
    
    public class SportType {
    
    	public static final SportType TENNIS = new SportType(1, "Tennis");
    	public static final SportType SOCCER = new SportType(2, "Soccer");
    
    	private int id;
    	private String name;
    
    	public SportType(int id, String name) {
    		this.id = id;
    		this.name = name;
    	}
    
    	public static Iterable<SportType> list(){
    		return Arrays.asList(TENNIS, SOCCER);
    	}
    
    	public static SportType getSport(int id){
    		for(SportType sportType : list()){
    			if(sportType.getId() == id){
    				return sportType;
    			}
    		}
    		return null;
    	}
    
    	/**
    	 * Getters and Setters
    	 */
    }

    这是案例1的示例:假设我们要定义一个预留bean,这是我们的方法:

    <bean id="dummyReservation" class="com.pramati.model.Reservation">
    	<property name="playGround" value="Soccer Court #1"/>
    	<property name="dateToReserve" value="11-11-2011"/>
    	<property name="hour" value="15"/>
    	<property name="captain">
    		<bean class="com.pramati.model.Player">
    			<property name="name" value="Prasanth"/>
    			<property name="phone" value="92131233124"/>
    		</bean>
    	</property>
    	<property name="sportType">
    		<property name="id" value="1"/>
    		<property name="name" value="TENNIS"/>
    	</property>
    </bean>

    这个bean的定义很冗长。 如果定义看起来像这样,它本来可以表现得更好:

    <bean id="dummyReservation" class="com.pramati.model.Reservation">
    	<property name="playGround" value="Soccer Court #1"/>
    	<property name="dateToReserve" value="11-11-2011"/>
    	<property name="hour" value="15"/>
    	<property name="captain" value="Prasanth,92131233124"/>
    	<property name="sportType" value="1,TENNIS"/>
    </bean>

    为此,我们应该告诉Spring在定义bean的过程中使用自定义转换器。

    这是案例2的示例:假设我的应用程序中有一个JSP,它允许用户在一天的特定时间预订游乐场。

    <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
    <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
    <title>Reservation Form</title>
    <style type="text/css">
    .error {
    	color: #ff0000;
    	font-weight: bold;
    }
    </style>
    </head>
    <body>
    	<form:form method="post" modelAttribute="reservation">
    		<table>
    			<tr>
    				<th>Court Name</th>
    				<td><form:input path="courtName" /></td>
    			</tr>
    			<tr>
    				<th>Reservation Date</th>
    				<td><form:input path="date" /></td>
    			</tr>
    			<tr>
    				<th>Hour</th>
    				<td><form:input path="hour" /></td>
    			</tr>
    			<tr>
    				<th>Player Name</th>
    				<td><form:input path="player.name" /></td>
    			</tr>
    			<tr>
    				<th>Player Contact Number</th>
    				<td><form:input path="player.phone" /></td>
    			</tr>
    			<tr>
    				<th>Sport Type</th>
    				<td><form:select path="sportType" items="${sportTypes}"
    						itemLabel="name" itemValue="id" /></td>
    			</tr>
    			<tr>
    				<td colspan="3"><input type="submit" name="Submit" /></td>
    			</tr>
    		</table>
    	</form:form>
    </body>
    </html>

    这是对应的MVC控制器:

    @Controller
    @RequestMapping
    @SessionAttributes("reservation")
    public class ReservationFormController {
    
    	@Autowired
    	private ReservationService reservationService;
    
    	@ModelAttribute("sportTypes")
    	public Iterable<SportType> getSportTypes(){
    		return SportType.list();
    	}
    
    	@RequestMapping(value="/reservationForm/{captainName}", method=RequestMethod.GET)
    	public String initForm(Model model, @PathVariable String captainName){
    		Reservation reservation = new Reservation();
    		reservation.setPlayer(new Player(captainName, null));
    		reservation.setSportType(SportType.TENNIS);
    		model.addAttribute("reservation", reservation);
    		return "reservationForm";
    	}
    
    	@RequestMapping(value="/reservationForm/{captainName}",method=RequestMethod.POST)
    	public String reserve(@Valid Reservation reservation, BindingResult bindingResult, SessionStatus sessionStatus){
    		validator.validate(reservation, bindingResult);
    		if(bindingResult.hasErrors()){
    			return "/reservationForm";
    		} else{
    			reservationService.make(reservation);
    			sessionStatus.setComplete();
    			return "redirect:../reservationSuccess";
    		}
    	}
    }

    现在您可以看到,在JSP中,我们将表单字段绑定到Reservation对象(modelAttribute =“ reservation”)。 该对象由传递给视图的控制器(在initForm()方法中)保留在模型中。 现在,当我们提交表单时,Spring会引发一条验证消息,指出无法将字段值转换为类型Player和SportType。 为此,我们必须定义自定义转换器并将其注入Spring MVC流中。

    现在的问题是如何定义自定义转换器? Spring提供了两种支持这些自定义转换器的方式:

    • 解决方案1:使用PropertyEditors
    • 解决方案2:使用转换器

    使用PropertyEditor:

    PropertyEditorSupport,实现PropertyEditor接口,是用于帮助构建PropertyEditor的支持类。

    public class SportTypeEditorSupport extends PropertyEditorSupport {
    
    	/**
         * Sets the property value by parsing a given String.  May raise
         * java.lang.IllegalArgumentException if either the String is
         * badly formatted or if this kind of property can't be expressed
         * as text.
         *
         * @param text  The string to be parsed.
         */
    	@Override
    	public void setAsText(String text) throws IllegalArgumentException {
    		try{
    			SportType sportType = SportType.getSport(Integer.parseInt(text));
    			setValue(sportType);// setValue stores the custom type Object into a instance variable in PropertyEditorSupport.
    		}
    		catch(NumberFormatException nfe){
    			throw new RuntimeException(nfe.getMessage());
    		}
    	}
    
    	 /**
         * Gets the property value as a string suitable for presentation
         * to a human to edit.
         *
         * @return The property value as a string suitable for presentation
         *       to a human to edit.
         * <p>   Returns "null" is the value can't be expressed as a string.
         * <p>   If a non-null value is returned, then the PropertyEditor should
         *	     be prepared to parse that string back in setAsText().
         */
    	@Override
    	public String getAsText() {
    		SportType sportType = (SportType)getValue();
    		return Integer.toString(sportType.getId());
    	}
    }

    现在,在PropertyEditorRegistry中注册自定义编辑器。 PropertyEditorRegistrar在PropertyEditorRegistry中注册自定义编辑器。 这是您的操作方式:

    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    import org.springframework.beans.PropertyEditorRegistrar;
    import org.springframework.beans.PropertyEditorRegistry;
    import org.springframework.beans.propertyeditors.CustomDateEditor;
    
    import com.pramati.model.SportType;
    
    public class CustomPropertyEditorRegistrar implements PropertyEditorRegistrar {
    	@Override
    	public void registerCustomEditors(PropertyEditorRegistry registry) {
    		registry.registerCustomEditor(Date.class, new CustomDateEditor(
    				new SimpleDateFormat("dd-MM-yyyy"), true));
    		registry.registerCustomEditor(SportType.class, new SportTypeEditorSupport());
    	}
    }

    CustomEditorConfigurer被实现为Bean工厂后处理器,您可以在实例化任何Bean之前注册自定义属性编辑器。 为此,我们将PropertyEditorRegistry与CustomEditorConfigurer关联。

    <bean id="customPropertyEditorRegistrar" class="com.pramati.spring.mvc.CustomPropertyEditorRegistrar"/>
    
    <bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
    	<property name="propertyEditorRegistrars">
    		<list>
    			<ref bean="customPropertyEditorRegistrar"/>
    		</list>
    	</property>
    </bean>

    现在,当Spring容器看到​​此信息时:

    <property name="captain" value="Prasanth,92131233124"/>

    Spring自动将指定的值转换为Player对象。 但是,此配置对于Spring MVC流还不够。 控制器仍然会抱怨类型不兼容,因为它期望一个Player对象在获取String的地方。 为了将表单字段值解释为自定义类型对象,我们必须进行很少的MVC配置更改。

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.core.convert.ConversionService;
    import org.springframework.validation.Validator;
    import org.springframework.web.bind.WebDataBinder;
    import org.springframework.web.bind.support.WebBindingInitializer;
    import org.springframework.web.context.request.WebRequest;
    
    public class CustomWebBindingInitializer implements WebBindingInitializer {
    	@Autowired
    	private CustomPropertyEditorRegistrar customPropertyEditorRegistrar;
    
    	@Override
    	public void initBinder(WebDataBinder binder, WebRequest request) {
    		customPropertyEditorRegistrar.registerCustomEditors(binder);
    	}
    }

    现在,手动删除并定义必要的bean,因为我们需要将WebBindingInitializer注入RequestMappingHandlerAdapter

    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
    
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
    	<property name="webBindingInitializer">
    		<bean class="com.pramati.spring.mvc.CustomWebBindingInitializer"/>
    	</property>
    </bean>

    现在,控制器会自动将String转换为必要的自定义类型对象。 请注意,我们必须进行单独的配置更改,以简化Spring MVC中的bean配置和表单字段的类型转换。 同样值得指出的是,在扩展PropertyEditorSupport时,我们将自定义类型对象存储在实例变量中,因此使用PropertyEditors并不是线程安全的。 为了克服这些问题,Spring 3.0引入了转换器和格式化程序的概念。

    使用转换器:

    转换器组件用于将一种类型转换为另一种类型,并通过强制将所有此类与转换相​​关的代码放在一个位置来提供更清晰的分隔。 Spring已经支持常用类型的内置转换器,并且该框架可扩展性足以编写自定义转换器。 Spring Formatters进入图片以根据呈现数据的显示来格式化数据。 在考虑编写适合特定业务需求的自定义转换器之前,总是有必要先查看详尽的预制转换器列表。 有关查看预建转换器的列表,请参见org.springframework.core.convert.support软件包。

    回到我们的用例,让我们实现String到SportType转换器:

    import org.springframework.core.convert.ConversionFailedException;
    import org.springframework.core.convert.TypeDescriptor;
    import org.springframework.core.convert.converter.Converter;
    import com.pramati.model.SportType;
    
    public class StringToSportTypeConverter implements Converter<String, SportType> {
    
    	@Override
    	public SportType convert(String sportIdStr) {
    		int sportId = -1;
    		try{
    			sportId = Integer.parseInt(sportIdStr);
    		} catch (NumberFormatException e) {
    			throw new ConversionFailedException(TypeDescriptor.valueOf(String.class), TypeDescriptor.valueOf(SportType.class), sportIdStr, null);
    		}
    
    		SportType sportType = SportType.getSport(sportId);
    		return sportType;
    	}
    
    }

    现在,在ConversionService中注册它,并将其与SpringMVC流链接:

    <mvc:annotation-driven conversion-service="conversionService"/>
    
    <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean" >
    	<property name="converters">
    		<set>
    			<bean class="com.pramati.type.converters.StringToSportTypeConverter"/>
    			<bean class="com.pramati.type.converters.StringToDateConverter"/>
    			<bean class="com.pramati.type.converters.StringToPlayerConverter"/>
    		</set>
    	</property>
    </bean>

    如果您使用的是自定义bean声明而不是‹mvc:annotation-driven /›,则可以使用以下方法:

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.core.convert.ConversionService;
    import org.springframework.validation.Validator;
    import org.springframework.web.bind.WebDataBinder;
    import org.springframework.web.bind.support.WebBindingInitializer;
    import org.springframework.web.context.request.WebRequest;
    
    public class CustomWebBindingInitializer implements WebBindingInitializer {
    
    	@Autowired
    	private ConversionService conversionService;
    
    	@Override
    	public void initBinder(WebDataBinder binder, WebRequest request) {
    		binder.setConversionService(conversionService);
    	}
    
    }

    现在将WebBindingInitializer注入RequestMappingHandlerAdapter。

    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
    	<property name="webBindingInitializer">
    		<bean class="com.pramati.spring.mvc.CustomWebBindingInitializer"/>
    	</property>
    </bean>

    单独注册ConversionService将有助于简化bean配置(案例1)。 为了使案例2正常工作,我们必须在Spring MVC流中注册ConversionService。 请注意,这种类型转换方式也是线程安全的。

    同样,除了使Converters / PropertEditor对应用程序中的所有控制器都可用之外,我们还可以基于每个控制器启用它们。 这是您的操作方式。 删除上面指定的通用配置,并在控制器类中引入@InitBinder,如下所示:

    @Controller
    @RequestMapping
    @SessionAttributes("reservation")
    public class ReservationFormController {
    
    	private ReservationService reservationService;
    	private ReservationValidator validator;
    
    	@Autowired
    	public ReservationFormController(ReservationService reservationService, ReservationValidator validator){
    		this.reservationService = reservationService;
    		this.validator = validator;
    	}
    
    	@Autowired
    	private ConversionService conversionService;
    	@InitBinder
    	protected void initBinder(ServletRequestDataBinder binder) {
    		binder.setConversionService(conversionService);
    	}
    
    	/*@InitBinder
    	protected void initBinder(ServletRequestDataBinder binder) {
    		binder.registerCustomEditor(Date.class, new CustomDateEditor(new SimpleDateFormat("dd-MM-yyyy"), true));
    		binder.registerCustomEditor(SportType.class, new SportTypeEditorSupport(reservationService));
    	}*/
    
    	/*@Autowired
    	private PropertyEditorRegistrar propertyEditorRegistrar;
    	@InitBinder
    	protected void initBinder(ServletRequestDataBinder binder) {
    		propertyEditorRegistrar.registerCustomEditors(binder);
    	}*/
    
    	@ModelAttribute("sportTypes")
    	public Iterable<SportType> getSportTypes(){
    		return SportType.list();
    	}
    
    	@RequestMapping(value="/reservationForm/{userName}", method=RequestMethod.GET)
    	public String initForm(Model model, @PathVariable String userName){
    		Reservation reservation = new Reservation();
    		reservation.setPlayer(new Player(userName, null));
    		reservation.setSportType(SportType.TENNIS);
    		model.addAttribute("reservation", reservation);
    		return "reservationForm";
    	}
    
    	@RequestMapping(value="/reservationForm/{userName}",method=RequestMethod.POST)
    	public String reserve(@Valid Reservation reservation, BindingResult bindingResult, SessionStatus sessionStatus){
    		validator.validate(reservation, bindingResult);
    		if(bindingResult.hasErrors()){
    			return "/reservationForm";
    		} else{
    			reservationService.make(reservation);
    			sessionStatus.setComplete();
    			return "redirect:../reservationSuccess";
    		}
    	}
    
    	@RequestMapping("/reservationSuccess")
    	public void success(){
    
    	}
    }

    因此,如果您看到上面的代码,您会注意到在使用PropertyEditor而不是转换器的地方,注释的代码。 因此,在两种实现方式中都可以使用基于控制器启用类型转换器的功能。

    参考: prasanthnath博客上的JCG合作伙伴 Prasanth Gullapalli 在Spring中进行了类型转换

    翻译自: https://www.javacodegeeks.com/2013/11/type-conversion-in-spring-2.html

    spring 类型转换器

    更多相关内容
  • Spring类型转换

    2020-12-15 22:19:26
    Java是个多类型且强类型语言,类型转换这个概念对它来说并不陌生。比如: 自动类型转换(隐式):小类型 -> 大类型。eg:int a = 10; double b = a; 强制类型转换(显式):大类型 -> 小类型。eg:double a ...

    Java是个多类型且强类型语言,类型转换这个概念对它来说并不陌生。比如:

    • 自动类型转换(隐式):小类型 -> 大类型。eg:int a = 10; double b = a;
    • 强制类型转换(显式):大类型 -> 小类型。eg:double a = 10.123; int b = (int)a;
      • 说明:强转有可能产生精度丢失
    • 调用API类型转换:常见的是字符串和其它类型的互转。eg:parseInt(String); parseBoolean(String); JSON.toJSONString(Obj); LocalDate.parse(String)
      • 说明:API可能来自于JDK提供、一方库、二方库、三方库提供

    在这里插入图片描述

    在企业级开发环境中,会遇到更为复杂的数据转换场景,譬如说:

    1. 输入/传入一个规格字符串(如1,2,3,4),转换为一个数组
    2. 输入/传入一个JSON串(如{"name":"YourBatman","age":18}),转换为一个Person对象
    3. 输入/传入一个URL串(如:C:/myfile.txt、classpath:myfile.txt),转换为一个org.springframework.core.io.Resource对象

    虽说数据输入/传入绝大部分都会是字符串(如Http请求信息、XML配置信息),但结构可以千差万别,那么这就必然会涉及到大量的数据类型、结构转换的逻辑。倘若这都需要程序员自己手动编码做转换处理,那会让人望而生畏甚至怯步。

    还好我们有Spring。从本文起,A哥就帮你解密Spring Framework它是如何帮你接管类型转换,实现“自动化”的。有了此部分知识的储备,后续再讨论自动化数据绑定、自动化数据校验、Spring Boot松散绑定等,一切都变得容易接受得多。

    说明:类型转换其实每个框架都会存在,其中Java领域以Spring的实现最为经典,学会后便可举一反三

    Spring类型转换

    Spring的类型转换也并非一步到位。完全掌握Spring的类型转换并非易事,需要有一定的脉络按步骤进行。本文作为类型转换系列第一篇文章,将绘制目录大纲,将从以下几个方面逐步展开讨论。

    在这里插入图片描述

    早期类型转换之PropertyEditor

    早期的Spirng(3.0之前)类型转换是基于Java Beans接口java.beans.PropertyEditor来实现的(全部继承自PropertyEditorSupport):

    public interface PropertyEditor {
    	...
    	// String -> Object
    	void setAsText(String text) throws java.lang.IllegalArgumentException;
    	// Object -> String
    	String getAsText();
    	...
    }
    

    这类实现举例有:

    • StringArrayPropertyEditor,分隔的字符串和String[]类型互转
    • PropertiesEditor:键值对字符串和Properties类型互转
    • IntegerEditor:字符串和Integer类型互转

    基于PropertyEditor的类型转换作为一种古老的、遗留下来的方式,是具有一些设计缺陷的,如:职责不单一,类型不安全,只能实现String类型的转换等。虽然自Spring 3.0起提供了现代化的类型转换接口,但是此部分机制一直得以保留,保证了向下兼容性。

    说明:Spring 3.0之前在Java领域还未完全站稳脚跟,因此良好的向下兼容显得尤为重要

    这块内容将在本系列后面具体篇章中得到专题详解,敬请关注。

    新一代类型转换接口Converter、GenericConverter

    为了解决PropertyEditor作为类型转换方式的设计缺陷,Spring 3.0版本重新设计了一套类型转换接口,其中主要包括:

    • Converter<S, T>:Source -> Target类型转换接口,适用于1:1转换 
      • StringToPropertiesConverter:将String类型转换为Properties
      • StringToBooleanConverter:将String类型转换为Boolean
      • EnumToIntegerConverter:将Enum类型转换为Integer
    • ConverterFactory<S, R>:Source -> R类型转换接口,适用于1:N转换 
      • StringToEnumConverterFactory:将String类型转任意Enum
      • StringToNumberConverterFactory:将String类型转为任意数字(可以是int、long、double等等)
      • NumberToNumberConverterFactory:数字类型转为数字类型(如int到long,long到double等等)
    • GenericConverter:更为通用的类型转换接口,适用于N:N转换 
      • ObjectToCollectionConverter:任意集合类型转为任意集合类型(如List<String>转为List<Integer> / Set<Integer>都使用此转换器)
      • CollectionToArrayConverter:解释基本同上
      • MapToMapConverter:解释基本同上
    • ConditionalConverter:条件转换接口。可跟上面3个接口组合使用,提供前置条件判断验证

    重新设计的这套接口,解决了PropertyEditor做类型转换存在的所有缺陷,且具有非常高的灵活性和可扩展性。但是,每个接口独立来看均具有一定的局限性,只有使用组合拳方才有最大威力。当然喽,这也造成学习曲线变得陡峭。据我了解,很少有同学搞得清楚新的这套类型转换机制,特别容易混淆。倘若你掌握了是不是自己价值又提升了呢?不信你细品?

    这块内容将在本系列后面具体篇章中得到专题详解,敬请关注。

    新一代转换服务接口:ConversionService

    从上一小节我们知道,新的这套接口中,Converter、ConverterFactory、GenericConverter它们三都着力于完成类型转换。对于使用者而言,如果做个类型转换需要了解到这三套体系无疑成本太高,因此就有了ConversionService用于整合它们三,统一化接口操作。

    此接口也是Spring 3.0新增,用于统一化 底层类型转换实现的差异,对外提供统一服务,所以它也被称作类型转换的门面接口,从接口名称xxxService也能看出来其设计思路。它主要有两大实现:

    1. GenericConversionService:提供模版实现,如转换器的注册、删除、匹配查找等,但并不内置转换器实现
    2. DefaultConversionService:继承自GenericConversionService。在它基础上默认注册了非常多的内建的转换器实现,从而能够实现绝大部分的类型转换需求

    ConversionService转换服务它贯穿于Spring上下文ApplicationContext的多项功能,包括但不限于:BeanWrapper处理Bean属性、DataBinder数据绑定、PropertySource外部化属性处理等等。因此想要进一步深入了解的话,ConversionService是你绕不过去的坎。

    说明:很多小伙伴问WebConversionService是什么场景下使用?我说:它并非Spirng Framework的API,而属于Spring Boot提供的增强,且起始于2.x版本,这点需引起注意

    这块内容将在本系列后面具体篇章中得到专题详解,敬请关注。

    类型转换整合格式化器Formatter

    Spring 3.0还新增了一个Formatter<T>接口,作用为:将Object格式化为类型T。从语义上理解它也具有类型转换(数据转换的作用),相较于Converter<S,T>它强调的是格式化,因此一般用于时间/日期、数字(小数、分数、科学计数法等等)、货币等场景,举例它的实现:

    • DurationFormatter:字符串和Duration类型的互转
    • CurrencyUnitFormatter:字符串和javax.money.CurrencyUnit货币类型互转
    • DateFormatter:字符串和java.util.Date类型互转。这个就使用得太多了,它默认支持什么格式?支持哪些输出方式,这将在后文详细描述

    为了和类型转换服务ConversionService完成整合,对外只提供统一的API。Spring提供了FormattingConversionService专门用于整合Converter和Formatter,从而使得两者具有一致的编程体验,对开发者更加友好。

    这块内容将在本系列后面具体篇章中得到专题详解,敬请关注。

    类型转换底层接口TypeConvert

    定义类型转换方法的接口,它在Spring 2.0就已经存在。在还没有ConversionService之前,它的类型转换动作均委托给已注册的PropertyEditor来完成。但自3.0之后,这个转换动作可能被PropertyEditor来做,也可能交给ConversionService处理。

    它一共提供三个重载方法:

    // @since 2.0
    public interface TypeConverter {
    
     	// value:待转换的source源数据
     	// requiredType:目标类型targetType
     	// methodParam:转换的目标方法参数,主要为了分析泛型类型,可能为null
     	// field:目标的反射字段,为了泛型,可能为null
    	<T> T convertIfNecessary(Object value, Class<T> requiredType) throws TypeMismatchException;
    	<T> T convertIfNecessary(Object value, Class<T> requiredType, MethodParameter methodParam) throws TypeMismatchException;
    	<T> T convertIfNecessary(Object value, Class<T> requiredType, Field field) throws TypeMismatchException;
    
    }
    

    它是Spring内部使用类型转换的入口,最终委托给PropertyEditor或者注册到ConversionService里的转换器去完成。它的主要实现有:

    • TypeConverterSupport:@since 3.2。继承自PropertyEditorRegistrySupport,它主要是为子类BeanWrapperImpl提供功能支撑。作用有如下两方面: 
      1. 提供对默认编辑器(支持JDK内置类型的转换如:Charset、Class、Class[]、Properties、Collection等等)和自定义编辑器的管理(PropertyEditorRegistry#registerCustomEditor)
      2. 提供get/set方法,把ConversionService管理上(可选依赖,可为null)
    • 数据绑定相关:因为数据绑定强依赖于类型转换,因此数据绑定涉及到的属性访问操作将会依赖于此组件,不管是直接访问属性的DirectFieldAccessor还是功能更强大的BeanWrapperImpl均是如此

    总的来说,TypeConverter能把类型的各种实现、API收口于此,Spring把类型转换的能力都转嫁到TypeConverter这个API里面去了。虽然方便了使用,但其内部实现原理稍显复杂,同样的这块内容将在本系列后面具体篇章中得到专题详解,敬请关注。

    Spring Boot使用增强

    在传统Spring Framework场景下,若想使用ConversionService还得手动档去配置,这对于不太了解其运行机制的同学无疑是有使用门槛的。而在Spring Boot场景下这一切都会变得简单许多,可谓使用起来愈发方便了。

    另外,Spring Boot在内建转换器的基础上额外扩展了不少实用转换器,形如:

    • StringToFileConverter:String -> File
    • NumberToDurationConverter
    • DelimitedStringToCollectionConverter

    ✍总结

    基于配置来控制程序运行总比你修改程序代码来得更优雅、更富弹性,但这是需要依赖于数据绑定、数据校验等功能的,而它们又依赖于类型转换。

    虽说几乎所有的框架都会有类型转换的功能模块,但Spring的可能是最为通用、最为经典的存在。因此本系列专题讲解Spring Framework的类型转换,旨在能够帮你你撬开通往跃升的大门,节节攀高。

    展开全文
  • Spring类型转换的实现 基于JavaBeans接口的类型转换实现:基于java.beans.PropertyEditor接口扩展 Spring 3.0+通用类型转换实现 使用场景 基于JavaBeans接口的类型转换 核心职责: 将String类型的内容转换为目标...

    Spring类型转换的实现

    • 基于JavaBeans接口的类型转换实现:基于java.beans.PropertyEditor接口扩展
    • Spring 3.0+通用类型转换实现

    使用场景

    image.png

    基于JavaBeans接口的类型转换

    • 核心职责: 将String类型的内容转换为目标类型的对象
    • 扩展原理

    1.Spring框架将文本内容传递到PropertyEditor实现的setAsText(String)方法。
    2.PropertyEditor#setAsText(String)方法实现将String类型转化为目标类型的对象。
    3.将目标类型的对象传入PropertyEditor#setValue(Object)方法实现需要临时存储传入对象
    4.Spring框架将通过PropertyEditor#getValue()获取类型转换后的对象

    Spring内建PropertyEditor扩展

    image.png

    自定义PropertyEditor扩展

    • 扩展模式

    1.扩展java.beans.PropertyEditorSupport类

    • 实现org.springframework.beans.PropertyEditorRegistrar

    1.实现registerCustomEditors(org.springframework.beans.PropertyEditorRegistry)方法
    2.将PropertyEditorRegistrar实现注册为SpringBean

    • 向org.springframework.beans.PropertyEditorRegistry注册自定义PropertyEditor实现

    1.通用类型实现registerCustomEditor(Class<?>, PropertyEditor)
    2.Java Bean属性类型实现:registerCustomEditor(Class<?>, String, PropertyEditor)

    Spring3通用类型转换接口

    • 类型转换接口
    @FunctionalInterface
    public interface Converter<S, T> {
    	@Nullable
    	T convert(S source);
    }
    
    • 通用类型转换接口
    org.springframework.core.conert.converter.GenericConverter
    核心方法: convert(Object, TypeDescriptor. TypeDescriptor)
    配对类型: org.springframework.core.convert.converter.GenericConverter.ConvertiblePair 
    类型描述: org.springframework.core.convert.TypeDescriptor
    

    GenericConverter接口

    使用场景:用于"复合"类型转换场景,比如Collection, Map数组等
    转换范围: Set getConvertibleTypes()
    配对类型:org.springframework.core.convert.converter.GenericConverter.ConvertiblePair
    转换方法:convert(Object, TypeDescriptor, TypeDescriptor)
    类型描述: org.springframework.core.convert.TypeDescriptor

    优化GenericConverter接口

    增加sourceType和TargetType前置判断,类型条件判断: ConditionalGenericConverter

    public interface ConditionalConverter {
    
    	boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType);
    }
    

    扩展Spring类型转换器

    • 实现转换器接口,任一即可

    1.org.sringframework.core.convert.convert.converter
    2.org.springframework.core.convert.converter.ConverterFactory
    3.org.springframework.core.convert.converter.GenericConerter

    • 注册转换器实现

    1.通过ConversionServiceFactoryBean SpringBean
    2.通过org.springframework.core.convert.ConversionService API

    统一类型转换服务

    image.png

    使用ConversionService作为依赖

    类型转换底层接口-org.springframework.beans.TypeConverter,Spring直接使用此接口实现作为转换实现。

    • 核心方法:convertIfNecessary重载方法
    • 抽象实现:org.springframework.beans.TypeConverterSupport
    • 简单实现: org.springframework.beans.SimpleTypeConverter

    image.png
    image.png

    展开全文
  • 主要介绍了Spring类型转换 ConversionSerivce Convertor的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下
  • 目录 ✍前言 ✍正文 Spring类型转换 早期类型转换之PropertyEditor 新一代类型转换接口Converter、GenericConverter 新一代转换服务接口:ConversionService 类型转换整合格式化器Formatter 类型转换底层接口...

    仰不愧天,俯不愧人,内不愧心。关注公众号【BAT的乌托邦】,有Spring技术栈、MyBatis、JVM、中间件等小而美的原创专栏供以免费学习。分享、成长,拒绝浅尝辄止。本文已被 https://www.yourbatman.cn 收录。

    在这里插入图片描述

    ✍前言

    你好,我是YourBatman。

    Spring Framework是一个现代化的框架,俨然已发展成为Java开发的基石。随着高度封装、高度智能化的Spring Boot的普及,发现团队内越来越少的人知道其深层次机制,哪怕只有一点点。这是让Spirng团队开心,但却是让使用的团队比较担忧的现象。

    在这里插入图片描述
    若运行一个完全黑箱程序无疑像抱着一个定时炸弹,总是如履薄冰、战战兢兢。团队内需要这样的同学来为它保驾护航,惊爆之时方可泰然自诺。所以,你愿意pick吗?

    本系列将讨论Spring Framework里贯穿其上下文,具有举足轻重地位的一个模块:类型转换(也可叫数据转换)。

    ✍正文

    Java是个多类型且强类型语言,类型转换这个概念对它来说并不陌生。比如:

    • 自动类型转换(隐式):小类型 -> 大类型。eg:int a = 10; double b = a;
    • 强制类型转换(显式):大类型 -> 小类型。eg:double a = 10.123; int b = (int)a;
      • 说明:强转有可能产生精度丢失
    • 调用API类型转换:常见的是字符串和其它类型的互转。eg:parseInt(String); parseBoolean(String); JSON.toJSONString(Obj); LocalDate.parse(String)
      • 说明:API可能来自于JDK提供、一方库、二方库、三方库提供

    在这里插入图片描述

    在企业级开发环境中,会遇到更为复杂的数据转换场景,譬如说:

    1. 输入/传入一个规格字符串(如1,2,3,4),转换为一个数组
    2. 输入/传入一个JSON串(如{"name":"YourBatman","age":18}),转换为一个Person对象
    3. 输入/传入一个URL串(如:C:/myfile.txt、classpath:myfile.txt),转换为一个org.springframework.core.io.Resource对象

    虽说数据输入/传入绝大部分都会是字符串(如Http请求信息、XML配置信息),但结构可以千差万别,那么这就必然会涉及到大量的数据类型、结构转换的逻辑。倘若这都需要程序员自己手动编码做转换处理,那会让人望而生畏甚至怯步。

    还好我们有Spring。从本文起,A哥就帮你解密Spring Framework它是如何帮你接管类型转换,实现“自动化”的。有了此部分知识的储备,后续再讨论自动化数据绑定、自动化数据校验、Spring Boot松散绑定等,一切都变得容易接受得多。

    说明:类型转换其实每个框架都会存在,其中Java领域以Spring的实现最为经典,学会后便可举一反三

    Spring类型转换

    Spring的类型转换也并非一步到位。完全掌握Spring的类型转换并非易事,需要有一定的脉络按步骤进行。本文作为类型转换系列第一篇文章,将绘制目录大纲,将从以下几个方面逐步展开讨论。

    在这里插入图片描述

    早期类型转换之PropertyEditor

    早期的Spirng(3.0之前)类型转换是基于Java Beans接口java.beans.PropertyEditor来实现的(全部继承自PropertyEditorSupport):

    public interface PropertyEditor {
    	...
    	// String -> Object
    	void setAsText(String text) throws java.lang.IllegalArgumentException;
    	// Object -> String
    	String getAsText();
    	...
    }
    

    这类实现举例有:

    • StringArrayPropertyEditor,分隔的字符串和String[]类型互转
    • PropertiesEditor:键值对字符串和Properties类型互转
    • IntegerEditor:字符串和Integer类型互转

    基于PropertyEditor的类型转换作为一种古老的、遗留下来的方式,是具有一些设计缺陷的,如:职责不单一,类型不安全,只能实现String类型的转换等。虽然自Spring 3.0起提供了现代化的类型转换接口,但是此部分机制一直得以保留,保证了向下兼容性。

    说明:Spring 3.0之前在Java领域还未完全站稳脚跟,因此良好的向下兼容显得尤为重要

    这块内容将在本系列后面具体篇章中得到专题详解,敬请关注。

    新一代类型转换接口Converter、GenericConverter

    为了解决PropertyEditor作为类型转换方式的设计缺陷,Spring 3.0版本重新设计了一套类型转换接口,其中主要包括:

    • Converter<S, T>:Source -> Target类型转换接口,适用于1:1转换
      • StringToPropertiesConverter:将String类型转换为Properties
      • StringToBooleanConverter:将String类型转换为Boolean
      • EnumToIntegerConverter:将Enum类型转换为Integer
    • ConverterFactory<S, R>:Source -> R类型转换接口,适用于1:N转换
      • StringToEnumConverterFactory:将String类型转任意Enum
      • StringToNumberConverterFactory:将String类型转为任意数字(可以是int、long、double等等)
      • NumberToNumberConverterFactory:数字类型转为数字类型(如int到long,long到double等等)
    • GenericConverter:更为通用的类型转换接口,适用于N:N转换
      • ObjectToCollectionConverter:任意集合类型转为任意集合类型(如List<String>转为List<Integer> / Set<Integer>都使用此转换器)
      • CollectionToArrayConverter:解释基本同上
      • MapToMapConverter:解释基本同上
    • ConditionalConverter:条件转换接口。可跟上面3个接口组合使用,提供前置条件判断验证

    重新设计的这套接口,解决了PropertyEditor做类型转换存在的所有缺陷,且具有非常高的灵活性和可扩展性。但是,每个接口独立来看均具有一定的局限性,只有使用组合拳方才有最大威力。当然喽,这也造成学习曲线变得陡峭。据我了解,很少有同学搞得清楚新的这套类型转换机制,特别容易混淆。倘若你掌握了是不是自己价值又提升了呢?不信你细品?

    这块内容将在本系列后面具体篇章中得到专题详解,敬请关注。

    新一代转换服务接口:ConversionService

    从上一小节我们知道,新的这套接口中,Converter、ConverterFactory、GenericConverter它们三都着力于完成类型转换。对于使用者而言,如果做个类型转换需要了解到这三套体系无疑成本太高,因此就有了ConversionService用于整合它们三,统一化接口操作。

    此接口也是Spring 3.0新增,用于统一化 底层类型转换实现的差异,对外提供统一服务,所以它也被称作类型转换的门面接口,从接口名称xxxService也能看出来其设计思路。它主要有两大实现:

    1. GenericConversionService:提供模版实现,如转换器的注册、删除、匹配查找等,但并不内置转换器实现
    2. DefaultConversionService:继承自GenericConversionService。在它基础上默认注册了非常多的内建的转换器实现,从而能够实现绝大部分的类型转换需求

    ConversionService转换服务它贯穿于Spring上下文ApplicationContext的多项功能,包括但不限于:BeanWrapper处理Bean属性、DataBinder数据绑定、PropertySource外部化属性处理等等。因此想要进一步深入了解的话,ConversionService是你绕不过去的坎。

    说明:很多小伙伴问WebConversionService是什么场景下使用?我说:它并非Spirng Framework的API,而属于Spring Boot提供的增强,且起始于2.x版本,这点需引起注意

    这块内容将在本系列后面具体篇章中得到专题详解,敬请关注。

    类型转换整合格式化器Formatter

    Spring 3.0还新增了一个Formatter<T>接口,作用为:将Object格式化为类型T。从语义上理解它也具有类型转换(数据转换的作用),相较于Converter<S,T>它强调的是格式化,因此一般用于时间/日期、数字(小数、分数、科学计数法等等)、货币等场景,举例它的实现:

    • DurationFormatter:字符串和Duration类型的互转
    • CurrencyUnitFormatter:字符串和javax.money.CurrencyUnit货币类型互转
    • DateFormatter:字符串和java.util.Date类型互转。这个就使用得太多了,它默认支持什么格式?支持哪些输出方式,这将在后文详细描述

    为了和类型转换服务ConversionService完成整合,对外只提供统一的API。Spring提供了FormattingConversionService专门用于整合Converter和Formatter,从而使得两者具有一致的编程体验,对开发者更加友好。

    这块内容将在本系列后面具体篇章中得到专题详解,敬请关注。

    类型转换底层接口TypeConvert

    定义类型转换方法的接口,它在Spring 2.0就已经存在。在还没有ConversionService之前,它的类型转换动作均委托给已注册的PropertyEditor来完成。但自3.0之后,这个转换动作可能被PropertyEditor来做,也可能交给ConversionService处理。

    它一共提供三个重载方法:

    // @since 2.0
    public interface TypeConverter {
    
     	// value:待转换的source源数据
     	// requiredType:目标类型targetType
     	// methodParam:转换的目标方法参数,主要为了分析泛型类型,可能为null
     	// field:目标的反射字段,为了泛型,可能为null
    	<T> T convertIfNecessary(Object value, Class<T> requiredType) throws TypeMismatchException;
    	<T> T convertIfNecessary(Object value, Class<T> requiredType, MethodParameter methodParam) throws TypeMismatchException;
    	<T> T convertIfNecessary(Object value, Class<T> requiredType, Field field) throws TypeMismatchException;
    
    }
    

    它是Spring内部使用类型转换的入口,最终委托给PropertyEditor或者注册到ConversionService里的转换器去完成。它的主要实现有:

    • TypeConverterSupport:@since 3.2。继承自PropertyEditorRegistrySupport,它主要是为子类BeanWrapperImpl提供功能支撑。作用有如下两方面:
      1. 提供对默认编辑器(支持JDK内置类型的转换如:Charset、Class、Class[]、Properties、Collection等等)和自定义编辑器的管理(PropertyEditorRegistry#registerCustomEditor)
      2. 提供get/set方法,把ConversionService管理上(可选依赖,可为null)
    • 数据绑定相关:因为数据绑定强依赖于类型转换,因此数据绑定涉及到的属性访问操作将会依赖于此组件,不管是直接访问属性的DirectFieldAccessor还是功能更强大的BeanWrapperImpl均是如此

    总的来说,TypeConverter能把类型的各种实现、API收口于此,Spring把类型转换的能力都转嫁到TypeConverter这个API里面去了。虽然方便了使用,但其内部实现原理稍显复杂,同样的这块内容将在本系列后面具体篇章中得到专题详解,敬请关注。

    Spring Boot使用增强

    在传统Spring Framework场景下,若想使用ConversionService还得手动档去配置,这对于不太了解其运行机制的同学无疑是有使用门槛的。而在Spring Boot场景下这一切都会变得简单许多,可谓使用起来愈发方便了。

    另外,Spring Boot在内建转换器的基础上额外扩展了不少实用转换器,形如:

    • StringToFileConverter:String -> File
    • NumberToDurationConverter
    • DelimitedStringToCollectionConverter

    ✍总结

    基于配置来控制程序运行总比你修改程序代码来得更优雅、更富弹性,但这是需要依赖于数据绑定、数据校验等功能的,而它们又依赖于类型转换。

    虽说几乎所有的框架都会有类型转换的功能模块,但Spring的可能是最为通用、最为经典的存在。因此本系列专题讲解Spring Framework的类型转换,旨在能够帮你你撬开通往跃升的大门,节节攀高。


    ✔推荐阅读:

    ♥关注A哥♥

    AuthorA哥(YourBatman)
    个人站点www.yourbatman.cn
    E-mailyourbatman@qq.com
    微 信fsx1056342982
    活跃平台
    公众号BAT的乌托邦(ID:BAT-utopia)
    知识星球BAT的乌托邦
    每日文章推荐每日文章推荐

    BAT的乌托邦

    展开全文
  • Spring 类型转换

    2020-09-21 23:11:38
    Spring 类型转换的实现 • 基于 JavaBeans 接口的类型转换实现 基于 java.beans.PropertyEditor 接口扩展 • Spring 3.0+ 通用类型转换实现 使用场景 • 场景分析 场景 基于 JavaBeans 接口的类型转换实现 ...
  • Spring类型转换机制

    2017-09-23 16:56:04
    Spring中的类型转换,主要用在两种场景 Bean的解析,Spring中bean定义的属性都是以文本的形式,构造bean的时候需要转成相应的各种Java类型 Spring Mvc的request请求生命周期中,需要把http的请求中的参数绑定到Java ...
  • 基于最新Spring 5.x,详细介绍了Spring类型转换机制,包括三种最常见的数据类型转换器PropertyEditor、Formatter、Converter、HttpMessageConverter、ConversionService等核心类。
  • 文章目录夯实Spring系列|第二十章:Spring 类型转换(Type Conversion)1.项目环境2.Spring 类型转换的实现3.使用场景4.基于 JavaBeans 接口的类型转换5.Spring 内建 PropertyEditor 扩展6.自定义 PropertyEditor ...
  • 作用:Spring 通过 类型转换器 把 配置文件 中 字符串 类型的数据,转换成了对象中成员变量对应类型的数据,进而完成了注入。 自定义类型转换器 产生原因:当 Spring 内部没有提供特定类型转换器时,而程序员在应用...
  • Spring中的类型转换

    2020-05-14 10:25:39
    以下是一些需要类型转换的简单情况: 情况1。 为了帮助简化bean配置,Spring支持属性值与文本值之间的转换。 每个属性编辑器仅设计用于某些类型的属性。 为了使用它们,我们必须在Spring容器中注册它们。 案例2。 ...
  • TypeConverter暴露转换功能,PropertyEditorRegistry暴露注册转换器服务 //@param value the value to convert //@param requiredType the type we must convert to <T> T convertIfNecessary(@Nullable ...
  • Spring类型转换源码分析

    千次阅读 2018-02-20 17:31:37
    前言 本文基于 spring 4.3.13 版本 在项目中我们经常使用 spring 提供的 IOC 功能,目前主要有两种方式:xml、注解,而这两种方式...在使用 IOC 功能的时候,经常需要利用 spring 提供的类型转换功能,比如 Str...
  • 15-Spring 类型转换

    2021-07-06 22:02:29
    文章目录1.Spring 类型转换的实现2.使用场景3.基于 JavaBeans 接口的类型转换4.Spring 內建 PropertyEditor 扩展5.自定义 PropertyEditor 扩展6.Spring PropertyEditor 的设计缺陷7.Spring 3 通用类型转换接口8....
  • Spring MVC自定义日期类型转换器实例详解  WEB层采用Spring MVC框架,将查询到的数据传递给APP端或客户端,这没啥,但是坑的是实体类中有日期类型的属性,但是你必须提前格式化好之后返回给它们。说真的,以前真没...
  • Spring转换器工作原理

    千次阅读 2018-06-08 00:28:59
    1、在XML文件中配置ConversionServiceFactoryBean,启用...4、DefaultConversionService在构造函数中Add内置转换器 5、Bean的包装器BeanWrapper创建成功后,将调用initBeanWrapper()方法,设置ConversionService
  • 主要介绍了spring boot @ResponseBody转换JSON 时 Date 类型处理方法,主要给大家介绍Jackson和FastJson两种方式,每一种方法给大家介绍的都非常详细,需要的朋友可以参考下
  • spring类型转换

    2020-06-26 21:17:15
    原因:当Spring内部没有提供特定类型转换器时,⽽程序员在应⽤的过程中还需要使⽤,那么就需要程序员⾃⼰定义类型转换器 编写自定义类型转换器 实现Converter接口 applicationContext.xml配置文件 ...
  • 1、创建 StudentConverter 类,并实现 org.springframework.core.convert.converter.Converter 接口,这样它就成为 了一个自定义数据类型转换器,需要指定泛型<String, Student>,表示将 String 类型转为 ...
  • 《孙哥说Spring
  • Sping 类型转换相关
  • 下面小编就为大家带来一篇解决springmvc关于前台日期作为实体类对象参数类型转换错误的问题。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • spring中日期转换方式

    2021-05-07 00:52:39
    -- 此处是为了让前端传来json字符串的date now属性,直接转换成后台直接识别的类型 简单来说就是把前端传来的"now": "2021-05-07 00:29:29" ————>后台2021-05-07 --> <bean id="conversionSer
  • 主要介绍了SpringMVC自定义类型转换器实现解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • Spring MVC请求参数类型转换

    千次阅读 2020-03-26 14:29:12
    对于 Spring MVC 框架而言,它必须将请求参数转换成值对象类中各属性对应的数据类型类型转换是在视图与控制器相互传递数据时发生的。Spring MVC 框架对于基本类型(例如 int、long、float、double、boole...
  • spring-mvc自定义类型转换器(日期格式类型转换器) 提示: @[TOC](文章目录) 前言 . SpringMVC默认已经提供了一些常用的类型转换器,例如客户端提交的字符串转换成int型进行参数设置。·但是不是所有的数据类型...
  • 后续更新
  • 主要给大家介绍了关于Spring MVC数据转换的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起看看吧。

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 183,561
精华内容 73,424
关键字:

spring类型转换

spring 订阅