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

时间:2013-12-20 02:11:48

标签: spring spring-mvc data-binding spring-data spring-data-jpa

我试图在我的Spring MVC项目中使用Spring的DomainClassConverter功能。 (我对Spring MVC和Spring只有非常基本的知识,请提前为任何天真的问题道歉。)

来自API docs

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)

我从上面的理解是,我不必编写finder方法,Spring提供User对象。所以这些是我做的步骤:

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>

根据Spring Data REST doc:

在我的pom.xml中包含此依赖项
<dependencies>
    <dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>spring-data-rest-webmvc</artifactId>
        <version>2.0.0.BUILD-SNAPSHOT</version>
    </dependency>
</dependencies>

我的控制器如下所示:

@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
public interface NewUserRepository extends JpaRepository<NewUser, Integer> {
}

这是我的转换服务:

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

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

当我运行程序时,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.

虽然不是最好的代码,但这是我的控制器:

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

我引用了一个示例here,但这是使用自定义转换器,但似乎没有使用域转换器服务。请指教。如果我想减少编写CRUD操作的样板代码,这是可行的吗?当我以其他方式获取数据时,DomainClassConverter的真正好处是什么?

根据Oliver Gierke建议更新 - 仍然无效,同样的错误

该文件描述:

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

所以我更新了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>

仍然是同一个问题。

更新:DomainClassConverter适用于Java Config,但不适用于XML方式(至少在这里建议了很多组合以及互联网上的其他地方)。仅针对可能感兴趣的其他人,并在此处获取一些有用的信息。

pom.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>

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

@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…


  } 

使用resource1resource2中的示例汇总Java Config文件。 (可能需要清理)

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();
     }

}

在applicationContext.xml

中添加以下bean定义
<bean class="com.rl.userservice.controller.WebConfig"/>

4 个答案:

答案 0 :(得分:1)

网址为String,因此{id}也是String。因此,您的服务必须能够将String转换为NewUser,而不能转换为Integer

答案 1 :(得分:0)

请查看relevant section of the reference documentation,了解配置DomainClassConverter的正确方法。

答案 2 :(得分:0)

ref

  

目前,存储库必须实现CrudRepository   有资格被发现进行转换。

不应该是这个原因吗?

答案 3 :(得分:0)

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

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

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

这是自定义控制器的代码,客户是像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");
            }
        });
    }
}

尝试使用此功能,因为这适用于版本2.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>