【问题标题】:Issues using Spring's DomainClassConverter in Spring MVC在 Spring MVC 中使用 Spring 的 DomainClassConverter 的问题
【发布时间】:2014-01-08 19:37:06
【问题描述】:

我正在尝试在我的 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)

我从上面了解到的是,我不必编写查找器方法,并且 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 文档在我的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 方式(至少尝试了此处和 Internet 上其他地方建议的许多组合)。仅供其他可能感兴趣并获得一些有用信息的人参考这里使用的代码。

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 配置文件。 (可能需要清理)

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"/>

【问题讨论】:

  • applicationcontext.xml 是什么,它是您的调度程序 servlet 上下文吗? stackoverflow.com/questions/3652090/… MVC 标记和转换器可能在调度程序 servlet 使用的上下文中声明 - 而不是根应用程序 Web 应用程序上下文。
  • 不要定义自己的转换服务(或自定义 UserConverter)。删除你的 conversionService bean 定义!
  • @Pavel 你能详细说明一下吗?在这种情况下,我认为转换服务处于错误的上下文中,因此它被调度程序 servlet 中的那个所掩盖,但通常自定义 bean 的显式声明没有任何问题。
  • @BorisTreukhov 声明自定义 bean 没有错。然而,自定义 UserConverter 正在复制 DomainClassConverter 应该做的事情,它的存在使问题变得更加复杂。根据conversionService,这就是我有点过于鲁莽的地方......肯定需要一个。
  • 由于某种原因,XML 存在问题。我对 Java Config 进行了同样的尝试,并且 DomainClassConverter 工作得非常好。

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


【解决方案1】:

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

【讨论】:

  • 该评论是针对 UserConverter 类的吗?即使这样,错误也是一样的: public NewUser convert(String username) { return newUserRepository.findOne(Integer.parseInt(username)); } }
  • @oneworld 不确定是否有必要,但我使用的是FormattingConversionServiceFactoryBean,而不是ConversionServiceFactoryBean
【解决方案2】:

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

【讨论】:

【解决方案3】:

ref

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

不应该是这个原因吗?

【讨论】:

  • JpaRepository 扩展了 PagingAndSortingRepository,后者又扩展了 CrudRepository,所以这应该不是问题
【解决方案4】:

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

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

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

这是自定义控制器的代码,客户是像用户这样的域类:

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>

【讨论】:

猜你喜欢
  • 2020-10-31
  • 2018-02-04
  • 1970-01-01
  • 2012-09-08
  • 2014-05-05
  • 2013-08-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多