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 定义将负责其余的工作。

但是,如果您已经在上下文中使用了&lt;mvc:annotation-driven&gt; 标记,则可以使用其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 modelmodel.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框架中的DAO和Service层是什么?

如何在 Spring Boot 服务应用程序中的 REST 服务调用之间按原样传递请求参数?

多服务器环境中的用户目标? (Spring WebSocket和RabbitMQ)

Spring Boot 中的 DTO 转换器模式

事务中的 Spring Boot LazyInitializationException

Spring MVC 测试,MockMVC:方便地将对象与 JSON 进行转换