spring类型转换器_Spring中的类型转换

news/2024/7/10 1:09:37 标签: spring, java, python, vue, spring boot
<a class=spring类型转换器" width="403px" height="256px" style="outline: none;" />

spring类型转换器

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

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

java">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控制器:

java">@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的支持类。

java">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中注册自定义编辑器。 这是您的操作方式:

java">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配置更改。

java">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转换器:

java">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 /›,则可以使用以下方法:

java">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,如下所示:

java">@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类型转换器


http://www.niftyadmin.cn/n/1707983.html

相关文章

嵌入Maven

这是一个非常罕见的用例&#xff0c;但有时您需要它。 如何将Maven嵌入到您的应用程序中&#xff0c;以便您可以以编程方式运行目标&#xff1f; 简短的答案是&#xff1a;这很棘手。 我涉足了我的java webapp自动同步项目的问题&#xff0c;有时我决定不嵌入它。 最终&#xf…

linux捕获shell命令,shell脚本进阶之信号的捕捉trap

shell脚本之信号的捕捉​trap&#xff0c;翻译过来就是陷阱的意思&#xff0c;shell脚本中的陷阱是专门用来捕捉信号的。啥信号呢&#xff1f;比如经常使用的kill -9&#xff0c;kill -15&#xff0c;CTRLC等都属于信号1、查看所有可用的信号trap -l或kill -l即可[rootlinux1 ~…

testng接口自动化测试_TestNG:在一个测试类中使用@DataProvider依次运行测试

testng接口自动化测试许多Java开发人员和自动化测试工程师在他们的工作中都使用TestNG作为测试框架。 我也不例外。 这是一个显而易见的选择&#xff0c;因为TestNG提供了非常强大的工具集&#xff0c;这使处理各种测试变得更加容易。 为了证明这一点&#xff0c;我将在本文中向…

linux中调节init进程的优先级,Init进程设置SELinux的Policy

Init进程的main()函数有一段代码用来初始化SELinux&#xff0c;如下所示&#xff1a;int main(......) {......//设置SELinux的回调函数union selinux_callback cb;cb.func_log klog_write;selinux_set_callback(SELINUX_CB_LOG,cb);cb.func_audit audit_callback;selinux_se…

linux脚本取当前日期格式,PowerShell中使用Get-Date获取日期时间并格式化输出的例子...

在PowerShell中有一个Get-Date的cmdlet&#xff0c;使用它可以直接返回当前的日期和时间。使用-Format参数可以返回当前的年、月、日、时、分、秒等。Get-Date的直接使用在PowerShell中直接调用Get-Date&#xff0c;可以返回当前的日期和时间&#xff0c;包括年、月、日、时、分…

linux网卡绑定拓扑,Linux双网卡绑定实现故障转移实践

由于业务需求&#xff0c;我们的业务已经将所有主机做成集群模式现在需要将主机设备做成高可用状态也就是避免网络设备的单点导致业务的中断。目前网络拓扑如下所示&#xff1a;以上拓扑中在核心交换上做的路由&#xff0c;在接入交换上做的链路聚合。所以在此我们需要将服务器…

Python基础复习笔记01(内置的数据类型的性能)

文章目录1浅谈列表 和 字典2分析其数据类型的性能2.1列表操作的时间复杂度2.2字典操作的时间复杂度&#xff1a;投资的回报通常遵循80/20原则&#xff1a;所得利益的80% 源自最简单系统的20%。大多数软件用80%的时间仅仅完成20%的有效指令。 1浅谈列表 和 字典 列表和字典是Py…

MATLAB科学计算03(微积分与泰勒傅里叶级数)

文章目录1 微积分解析解1.1 单变量函数的极限1.2 多变量函数的极限2 级数2.1 Taylor泰勒 幂级数展开2.2 Fourier傅里叶级数展开2.3 级数求和3 数值微分4 数值积分4.1 梯形法&#xff1a;4.2 单变量数值积分4.3 integral 数值积分4.4 quadgk()函数&#xff08;自适应高斯-勒让德…