Spring MVC中使用Spring的DomainClassConverter的问题。

时间:2022-09-11 16:27:37

I am trying to use Spring's DomainClassConverter feature with my Spring MVC project. (I have only very basic knowledge of Spring MVC and Spring, apologies in advance for any naive question here).

我正在尝试在我的Spring MVC项目中使用Spring的DomainClassConverter特性。(我对Spring MVC和Spring只有非常基本的了解,如果有任何幼稚的问题,请事先道歉)。

From the API docs:

从API文档:

The DomainClassConverter allows you to use domain types in your Spring MVC controller 
method signatures directly, so that you don't have to manually lookup the instances via 
the repository: (PS: Example 1.20)

What I understood from the above is that I don't have to write a finder method and the Spring supplies the User object. So these are the steps I did:

我从上面理解到,我不必编写查找器方法,而Spring提供用户对象。这就是我所做的步骤:

Included the below line of XML in applicationcontext.xml.

在applicationcontext.xml中包含下面一行XML。

<bean class="org.springframework.data.web.config.SpringDataWebConfiguration" />
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
    <list>
        <bean class="com.rl.userservice.controller.UserConverter"/>
    </list>
</property>

Included this dependency in my pom.xml per the Spring Data REST doc:

在pom中包含这个依赖项。xml / Spring数据REST doc:

<dependencies>
    <dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>spring-data-rest-webmvc</artifactId>
        <version>2.0.0.BUILD-SNAPSHOT</version>
    </dependency>
</dependencies>

My controller looks like the below:

我的控制器如下图所示:

@Controller
@RequestMapping(value = "/api/newuser")
public class NewUserServiceController {
      @Autowired
  NewUserRepository newUserRepository;

  @RequestMapping("/{id}")
    public String showUserForm(@PathVariable("id") NewUser newUser, Model model) {

      model.addAttribute("newUser", newUser);
      return "userForm";
    } 
}

Repository is like this:

存储库是这样的:

@Repository
public interface NewUserRepository extends JpaRepository<NewUser, Integer> {
}

This is my converter service:

这是我的转换器服务:

final class UserConverter implements Converter<Integer, NewUser> {
    NewUserRepository newUserRepository;

    public NewUser convert(Integer username) {
        return newUserRepository.findOne(username);
    } 
}

When I run the program tomcat starts successful, but when accessing the URL localhost:8080/userservice/api/newuser/1 I get the below exception:

当我运行程序tomcat启动成功时,但是当访问URL localhost:8080/userservice/api/newuser/1时,我得到以下异常:

type Exception report
    message
    description The server encountered an internal error () that prevented it from fulfilling this request.
    exception
    org.springframework.beans.ConversionNotSupportedException: Failed to convert value of type 'java.lang.String' to required type
'com.mpp.userservice.domain.NewUser'; nested exception is
java.lang.IllegalStateException: Cannot convert value of type  
[java.lang.String] to required type
[com.mpp.userservice.domain.NewUser]: no matching editors or
conversion strategy found
      org.springframework.beans.TypeConverterSupport.doConvert(TypeConverterSupport.java:71)
      org.springframework.beans.TypeConverterSupport.convertIfNecessary(TypeConverterSupport.java:45)
      org.springframework.validation.DataBinder.convertIfNecessary(DataBinder.java:595)
      org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver.resolveArgument(AbstractNamedValueMethodArgumentResolver.java:101)
      org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:77)
      org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:162)
      org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:123)
      org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104)
      org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:745)
      org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:686)
      org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
      org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:925)
      org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:856)
      org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:936)
      org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:827)
      javax.servlet.http.HttpServlet.service(HttpServlet.java:617)
      org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:812)
      javax.servlet.http.HttpServlet.service(HttpServlet.java:717)

    root cause
    java.lang.IllegalStateException: Cannot convert value of type [java.lang.String] to required type
[com.mpp.userservice.domain.NewUser]: no matching editors or
conversion strategy found
      org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:264)
      org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:93)
      org.springframework.beans.TypeConverterSupport.doConvert(TypeConverterSupport.java:61)
      org.springframework.beans.TypeConverterSupport.convertIfNecessary(TypeConverterSupport.java:45)
      org.springframework.validation.DataBinder.convertIfNecessary(DataBinder.java:595)
      org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver.resolveArgument(AbstractNamedValueMethodArgumentResolver.java:101)
      org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:77)
      org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:162)
      org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:123)
      org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104)
      org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:745)
      org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:686)
      org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
      org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:925)
      org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:856)
      org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:936)
      org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:827)
      javax.servlet.http.HttpServlet.service(HttpServlet.java:617)
      org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:812)
      javax.servlet.http.HttpServlet.service(HttpServlet.java:717)

    note The full stack trace of the root cause is available in the Apache Tomcat/6.0.29 logs.
    mpp.

Though not the best code, here's my controller:

虽然不是最好的代码,以下是我的控制器:

public @ResponseBody ResponseEntity<ModelMap> getUserTypeJSON(@PathVariable("userID" String userID, HttpServletResponse response)  { 
    UserType UserType = UserTypeRepository.findOne(id);
    model.addAttribute("Name",UserType.getName());  
    ...
} 

There is an example here that I referenced, but this is using custom converter but does not seem to be using the domain converter service. Please advise. Is this the way to go if I want to reduce boilerplate code of writing CRUD operations? What is the real benefit of this DomainClassConverter when I can get the data in in the other way?

我在这里引用了一个示例,但它使用的是自定义转换器,但似乎不使用域转换器服务。请建议。如果我想要减少编写CRUD操作的样板代码,这条路可以走吗?当我用另一种方式获取数据时,这个DomainClassConverter的真正好处是什么呢?

Updated per Oliver Gierke suggestion - still does not work, same error

更新根据奥利弗·吉尔克的建议-仍然不能工作,同样的错误

The document describes:

文件描述:

<mvc:annotation-driven conversion-service="conversionService" />
<bean class="org.springframework.data.repository.support.DomainClassConverter">
  <constructor-arg ref="conversionService" />
</bean>

So I updated my applicationcontext.xml as below, but the same issue:

所以我更新了我的applicationcontext。xml如下所示,但同样的问题:

    <mvc:annotation-driven conversion-service="conversionService"/>

  <bean class="org.springframework.data.repository.support.DomainClassConverter">
    <constructor-arg ref="conversionService" />
  </bean>

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

Still the same issue.

还是同样的问题。

Update: DomainClassConverter works with Java Config, but not the XML way (at least experimented with a lot of combinations suggested here and else where on the internet). Just for the others who might be interested and get some useful info here's the code used.

更新:DomainClassConverter使用的是Java Config,而不是XML方式(至少在这里和internet上的其他地方尝试了很多组合)。对于其他可能感兴趣并得到一些有用信息的人,这里是所使用的代码。

pom.xml (Might require clean-up)

砰的一声。xml(可能需要清理)

<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-rest-webmvc</artifactId>
    <version>2.0.0.M1</version>
</dependency>

<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-jpa</artifactId>
    <version>1.4.3.RELEASE</version>
</dependency>

The controller file (Might require clean-up)

控制器文件(可能需要清理)

@RequestMapping("/domain/{id}")
  public @ResponseBody ResponseEntity<ModelMap> showDomainUserForm(@PathVariable("id") User userMatch, HttpServletResponse response) {

  // some code omitted…  

    ModelMap model = new ModelMap();        
    model.addAttribute("DOMAIN-MAP","Domain Controller Service");
    model.addAttribute("Name",userMatch.getName());
    model.addAttribute("Phone",userMatch.getPhone());                                           

    // some code omitted…


  } 

The Java Config file assembled using examples from resource1 and resource2. (Might require clean-up)

使用resource1和resource2中的示例组装的Java配置文件。(可能需要清理)

package com.rl.userservice.controller;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.repository.support.DomainClassConverter;
import org.springframework.format.support.FormattingConversionService;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

@Configuration
@ComponentScan
@EnableWebMvc
public class WebConfig extends WebMvcConfigurationSupport{
     @Bean
     public RequestMappingHandlerMapping requestMappingHandlerMapping() {
      RequestMappingHandlerMapping handlerMapping = super.requestMappingHandlerMapping();
      handlerMapping.setUseSuffixPatternMatch(false);
      handlerMapping.setUseTrailingSlashMatch(false);
      return handlerMapping;
     } 


     @Bean
     public DomainClassConverter<?> domainClassConverter() {
         return new DomainClassConverter<FormattingConversionService>(mvcConversionService());
     }

     @Override
     public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
         configurer.enable();
     }

}

Add the below bean definition in the applicationContext.xml

在applicationContext.xml中添加下面的bean定义

<bean class="com.rl.userservice.controller.WebConfig"/>

4 个解决方案

#1


1  

A URL is a String, so {id} is a String too. Therefore your service must be able to convert a String to NewUser, not an Integer as yours does.

URL是一个字符串,所以{id}也是一个字符串。因此,您的服务必须能够将字符串转换为NewUser,而不是像您的服务那样转换为整数。

#2


0  

Please have a look at the relevant section of the reference documentation to find out about the correct way to configure the DomainClassConverter.

请参阅参考文档的相关部分,了解配置DomainClassConverter的正确方法。

#3


0  

The ref says

裁判说

Currently the repository has to implement CrudRepository to be eligible to be discovered for conversion.

目前,存储库必须实现CrudRepository才能被发现以进行转换。

Shouldn't that be the reason?

难道这不是原因吗?

#4


0  

This configuration sets up a custom conversion service and passes it to annotation scanning mechanism that detects and sets up the controllers:

此配置设置自定义转换服务,并将其传递给检测和设置控制器的注释扫描机制:

<bean name="conversionService" class="rest.gateway.services.MyConversionService"/>

<mvc:annotation-driven conversion-service="conversionService" />

And this is the code for the custom controller, customer being a domain class like User:

这是自定义控制器的代码,客户是一个域类,比如用户:

public class MyConversionService extends DefaultConversionService {

    public MyConversionService() {
        super();
        addConverter(String.class, Customer.class, new Converter<String, Customer>() {
            @Override
            public Customer convert(String source) {
                return new Customer("123456","Doe","John");
            }
        });
    }
}

Have a try with this because this is working for version 2.0.0.M1:

尝试一下这个,因为它适用于2.0.0.0 . m1版本:

    <dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>spring-data-rest-webmvc</artifactId>
        <version>2.0.0.M1</version>
    </dependency>

<repositories>
    <repository>
        <id>spring-milestones</id>
        <name>Spring Milestones</name>
        <url>http://repo.spring.io/milestone</url>
        <snapshots>
            <enabled>false</enabled>
        </snapshots>
    </repository>
</repositories>

#1


1  

A URL is a String, so {id} is a String too. Therefore your service must be able to convert a String to NewUser, not an Integer as yours does.

URL是一个字符串,所以{id}也是一个字符串。因此,您的服务必须能够将字符串转换为NewUser,而不是像您的服务那样转换为整数。

#2


0  

Please have a look at the relevant section of the reference documentation to find out about the correct way to configure the DomainClassConverter.

请参阅参考文档的相关部分,了解配置DomainClassConverter的正确方法。

#3


0  

The ref says

裁判说

Currently the repository has to implement CrudRepository to be eligible to be discovered for conversion.

目前,存储库必须实现CrudRepository才能被发现以进行转换。

Shouldn't that be the reason?

难道这不是原因吗?

#4


0  

This configuration sets up a custom conversion service and passes it to annotation scanning mechanism that detects and sets up the controllers:

此配置设置自定义转换服务,并将其传递给检测和设置控制器的注释扫描机制:

<bean name="conversionService" class="rest.gateway.services.MyConversionService"/>

<mvc:annotation-driven conversion-service="conversionService" />

And this is the code for the custom controller, customer being a domain class like User:

这是自定义控制器的代码,客户是一个域类,比如用户:

public class MyConversionService extends DefaultConversionService {

    public MyConversionService() {
        super();
        addConverter(String.class, Customer.class, new Converter<String, Customer>() {
            @Override
            public Customer convert(String source) {
                return new Customer("123456","Doe","John");
            }
        });
    }
}

Have a try with this because this is working for version 2.0.0.M1:

尝试一下这个,因为它适用于2.0.0.0 . m1版本:

    <dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>spring-data-rest-webmvc</artifactId>
        <version>2.0.0.M1</version>
    </dependency>

<repositories>
    <repository>
        <id>spring-milestones</id>
        <name>Spring Milestones</name>
        <url>http://repo.spring.io/milestone</url>
        <snapshots>
            <enabled>false</enabled>
        </snapshots>
    </repository>
</repositories>