Spring中的转换服务
Posted
技术标签:
【中文标题】Spring中的转换服务【英文标题】:ConversionService in Spring 【发布时间】:2011-05-19 20:05:56 【问题描述】:我在 Spring 应用程序中遵循此方案。
-
请求被发送到服务器,其中包含对象的 ID 和一些其他要填充到该对象中的参数
从数据库中加载具有此 ID 的对象
在此对象中调用 getter 和 setter 以填充值
然后存储对象
我在this other question 中询问在填充请求的参数之前准备对象的最佳方法是什么。答案是最好的方法是使用conversion service,而不是在@ModelAtribute 注释方法中或在initBinder 中使用编辑器。
所以我尝试使用转换器,但我没有找到类似的例子,我有点卡住了。我编写了如下代码: 在初始化活页夹中,我注册了转换服务。因此,在填充 User 对象上的值之前,调用 convert() 方法以从数据库加载对象。问题是此配置不起作用,因为它将对象用户的 id(用户名字段)转换为对象用户,但随后它尝试使用对象创建一个 setUsername(),所以我得到一个“java.lang .IllegalArgumentException:参数类型不匹配”。
谁能给我一个线索或示例,说明如何使用 ConversionService 来获得所需的行为?
谢谢。
@Autowired
private ConversionService conversionService;
@InitBinder("user")
public void initBinder(@RequestParam("username")String username, WebDataBinder binder)
binder.setConversionService(conversionService);
@RequestMapping(value="/user/save", method=RequestMethod.POST)
public String save(@ModelAttribute("user") User user, Model model)
...
类似:
@Component
public class UserConversionService implements ConversionService
...
@Override
public Object convert(Object name, TypeDescriptor arg1, TypeDescriptor arg2)
return userService.find((String)name);
【问题讨论】:
另见:***.com/a/10240927/59087 【参考方案1】:您正在尝试实现ConversionService
来进行字符串和用户对象之间的转换。但是,这部分是 Converter
实现。你想做的是:
-
编写转换器
使用 ConversionService 注册该转换器
利用 ConversionService。
您的转换器将类似于:
final class UserConverter implements Converter<String, User>
...
public User convert(String username)
return userService.find(username);
然后您需要注册该转换器。您可以编写自己的 ConversionServiceFactoryBean 或覆盖默认值:
<bean id="conversionService"
class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="example.UserConverter"/>
</list>
</property>
</bean>
如果您想明确地使用 ConversionService,就像您一样,只需将其保留为可以自动装配的东西。 Spring 和 factorybean 定义将负责其余的工作。
但是,如果您已经在上下文中使用了<mvc:annotation-driven>
标记,则可以使用其conversion-service
属性来引用您的 ConversionServiceFactoryBean。然后,您根本不需要在您的类中包含 InitBinder 或 ConversionService:只需将 @RequestMapping 的参数设置为您的目标类型 User,即可进行转换,而无需您进行干预。
【讨论】:
我已经成功注册了转换服务,正如你所说的 ,我可以调用 binder.convertIfNecessary(username, User.class) 并调用转换器.但在填充用户之前不会调用它。我没有在映射方法之前调用它。你是什么意思“通过简单地让@RequestMapping 的参数有你的目标类型,用户,转换就会发生”。我试图在方法中添加这样的参数: (@RequestParam("username")String username, @ModelAttribute("userTest")User user) 但它似乎不起作用。 尝试我已经意识到,如果我在我的方法中执行 public String save(@RequestParam("username")User user) 它会调用转换器,但我不想删除 @ModelAttribute 注释,因为我想填充值(并且我不能同时使用 @ModelAttribute 和 @RequestParam 注释来注释相同的参数)。有什么想法吗? @Javi - 嗯,这是很有趣的行为。我本来希望两者都能工作。作为一种临时措施,您可以为您的方法提供一个模型参数,并将转换后的用户显式添加到模型中。它当然不那么整洁,但它肯定会起作用。 @Javai - (非常简短)查看文档,您正在做的事情表明这基本上是一个命名问题。 @ModelAttribute 要么根据类型将带注释的参数放入具有默认名称的模型中,要么指定要使用的名称。我认为如果您对传入参数和模型属性名称使用相同的名称(即在任何地方都将其称为“用户”,而不是提交“用户名”参数的表单),它应该可以工作。还是很惊讶不能使用:“@ModelAttribute("user") @RequestParam("username") User user" 使用转换服务 mvc:annotation-driven 方法有一个 Java 配置替代方案。您将定义一个 AnnotationMethodHandlerAdapter 来启用注释驱动的配置。这个AnnotationMethodHandlerAdapter可以有一个ConfigurableWebBindingInitializer,而ConfigurableWebBindingInitializer有一个setConversionService方法。【参考方案2】:我完全按照 Gary 上面所说的做了,而且效果很好:
我想为解决方案添加更多信息。根据 Spring 文档here,使用 Converter/ConversionService 将 URI 模板变量转换为目标对象。我尝试使用@RequestParam("id") @ModelAttribute("contact") Contact contact
,但我得到了IllegalStateException: Neither BindingResult nor plain target object for bean name 'contact' available as request attribute
,因为我的视图edit.jsp 中没有模型对象contact
。这可以通过声明Model model
和model.addAttribute(contact);
轻松解决。但是,还有更好的方法;使用 URI 模板变量。奇怪的是为什么@RequestParam
不起作用。
没有工作
@RequestMapping("edit") //Passing id as .. edit?id=1
public String editWithConverter(@RequestParam("id") @ModelAttribute("contact") Contact contact)
logger.info("edit with converter");
return "contact/edit";
什么工作
@RequestMapping("edit/contact") //Passing id as .. edit/1
public String editWithConverter(@PathVariable("contact") @ModelAttribute("contact") Contact contact) // STS gave a warning for using contact without @PathVariable
logger.info("edit with converter");
return "contact/edit";
那么这个东西有什么作用.. 像...edit/1
这样的链接隐式调用转换器将字符串'1' 转换为id '1' 的联系人,并将这个联系人对象带到视图中。
不需要@InitBinder
,因为它是在ConversionService
注册的Converter
,所以我可以在我想要的任何地方使用它——隐式或显式。
【讨论】:
以上是关于Spring中的转换服务的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Spring Boot 服务应用程序中的 REST 服务调用之间按原样传递请求参数?
多服务器环境中的用户目标? (Spring WebSocket和RabbitMQ)