Java Spring 中的转换服务

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4347284/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 16:18:19  来源:igfitidea点击:

ConversionService in Spring

javaspringdata-bindingspring-mvcconverter

提问by Javi

I'm following this scheme in a Spring application.

我在 Spring 应用程序中遵循这个方案。

  1. Request is sent to the server with the id of the object and some other params to be populated in this object
  2. The object with this id is loaded from the database
  3. getters and setters are invoked in this object to populate the values
  4. the object is then stored
  1. 请求将使用对象的 id 和要填充到此对象中的其他一些参数发送到服务器
  2. 从数据库加载具有此 id 的对象
  3. 在此对象中调用 getter 和 setter 以填充值
  4. 然后存储对象

I asked in this other questionwhat was the best way to prepare the object before populate the params of the request. The answer was that the best way was to use a conversion serviceinstead of doing it in a @ModelAtribute annotated method or with an editor in the initBinder.

我在另一个问题中问了在填充请求的参数之前准备对象的最佳方法是什么。答案是最好的方法是使用转换服务,而不是在 @ModelAtribute 注释方法或 initBinder 中的编辑器中进行。

So I have tried to use a converter, but I haven't found a similar example and I'm a little stuck. I have written a code like the one below: In the init binder I register the conversion service. So before populating the values on the User object convert() method is invoked to load the object from the database. The problem is that this configuration doen't work because it is converting the id (username field) of the Object User into an Object user, but then it tries to make a setUsername() with the object so I get a "java.lang.IllegalArgumentException: argument type mismatch".

所以我尝试使用转换器,但我还没有找到类似的例子,我有点卡住了。我编写了如下代码:在 init binder 中,我注册了转换服务。因此,在填充用户对象上的值之前,调用 convert() 方法以从数据库加载对象。问题是此配置不起作用,因为它将对象用户的 id(用户名字段)转换为对象用户,但随后它尝试使用该对象创建一个 setUsername(),因此我得到一个“java.lang .IllegalArgumentException: 参数类型不匹配”。

Can anyone give me a clue or an example of the way of using the ConversionService to get the desired behaviour?

任何人都可以给我一个使用 ConversionService 来获得所需行为的线索或示例吗?

Thanks.

谢谢。

@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) {        
    ...
}

with something like:

像这样:

@Component
public class UserConversionService implements ConversionService{
    ...        
    @Override
    public Object convert(Object name, TypeDescriptor arg1, TypeDescriptor arg2) {
        return userService.find((String)name); 
    }
}

采纳答案by GaryF

You're trying to implement a ConversionServiceto do the conversion between Strings and User objects. However, it's Converterimplementations that do this part. What you want to do is:

您正在尝试实现 aConversionService以在 Strings 和 User 对象之间进行转换。但是,它是Converter执行此部分的实现。你想要做的是:

  1. Write a Converter
  2. Register that converter with a ConversionService
  3. Make use of the ConversionService.
  1. 编写转换器
  2. 使用 ConversionService 注册该转换器
  3. 使用 ConversionService。

Your converter would be something like:

您的转换器将类似于:

final class UserConverter implements Converter<String, User> {
    ...
    public User convert(String username) {
        return userService.find(username);
    }

}

You then need to register that converter. You can either write your own ConversionServiceFactoryBean or override the default:

然后您需要注册该转换器。您可以编写自己的 ConversionServiceFactoryBean 或覆盖默认值:

<bean id="conversionService"
      class="org.springframework.context.support.ConversionServiceFactoryBean">
    <property name="converters">
        <list>
            <bean class="example.UserConverter"/>
        </list>
    </property>
</bean>

If you want to use the ConversionService explicitly, as you have, just leave it as something that can be autowired. Spring and that factorybean definition will take care of the rest.

如果你想显式地使用 ConversionService,就像你所拥有的那样,只需将它保留为可以自动装配的东西。Spring 和那个 factorybean 定义将负责其余的工作。

If, however, you're already using the <mvc:annotation-driven>tag in your context, you can use its conversion-serviceattribute to reference your ConversionServiceFactoryBean. You then don't need to have InitBinder or ConversionService in your class at all: by simply having a parameter of a @RequestMapping have your target type, User, the conversion will take place without you having to intervene.

但是,如果您已经<mvc:annotation-driven>在上下文中使用了该标记,则可以使用其conversion-service属性来引用您的 ConversionServiceFactoryBean。然后,您根本不需要在您的类中使用 InitBinder 或 ConversionService:只需让 @RequestMapping 的参数具有您的目标类型 User,转换将在您无需干预的情况下进行。

回答by Ajay

I did exactly what Gary is saying above and it worked:

我完全按照加里上面所说的做了,它奏效了:

I want to add some more information to the solution. As per the Spring documentation herea URI template variable gets translated to the target object using the Converter/ConversionService. I tried to use a @RequestParam("id") @ModelAttribute("contact") Contact contact, but I was getting an IllegalStateException: Neither BindingResult nor plain target object for bean name 'contact' available as request attributefor not having the model object contactin my view edit.jsp. This can be easily resolved by declaring a Model modeland model.addAttribute(contact);. However, there is an even better way; using the URI template variable. It's strange why @RequestParamdid not work.

我想在解决方案中添加更多信息。根据此处的 Spring 文档使用 Converter/ConversionService 将 URI 模板变量转换为目标对象。我尝试使用@RequestParam("id") @ModelAttribute("contact") Contact contact,但IllegalStateException: Neither BindingResult nor plain target object for bean name 'contact' available as request attribute由于contact在我的视图 edit.jsp 中没有模型对象,我得到了。这可以通过声明 aModel model和轻松解决model.addAttribute(contact);。然而,还有更好的方法;使用 URI 模板变量。很奇怪为什么@RequestParam不起作用。

DID NOT WORK

不工作

@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";
}

WHAT WORKED

什么有效

@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";
}

So what does this thing do .. a link like ...edit/1implicitly invokes the converter for String '1' to Contact of id '1' conversion, and brings this contact object to the view. No need for @InitBinderand since its a Converterregistered with the ConversionServiceI can use this anywhere I want - implicitly or explicitly.

那么这个东西有什么作用..一个链接像...edit/1隐式调用字符串'1'到id'1'转换的联系人的转换器,并将这个联系人对象带到视图中。不需要@InitBinder并且因为它已Converter注册ConversionService我可以在任何我想要的地方使用它- 隐式或显式