【问题标题】:Spring does not use annotated constructor (@JsonCreator) for instantiationSpring 不使用带注释的构造函数 (@JsonCreator) 进行实例化
【发布时间】:2014-12-21 22:19:15
【问题描述】:

我正在使用带有 JavaConfig 和 Jackson 2.4.3 的 Spring 4.1.1。

我的控制器如下所示:

@RestController
public interface PatientWebService {

    @RequestMapping(value = "/patients", method = POST)
    PatientResource createPatient(@RequestBody PatientResource resource);
}

我发送的 json 如下所示:

{
    "firstName": "Max",
    "lastName": "Mustermann",
    "birthDate": "1964-04-14",
    "sex": "MAN"
}

我希望它被解析成这个类:

package at.landsteiner.patient.web;

import at.landsteiner.patient.Patient;
import at.landsteiner.patient.Sex;
import at.landsteiner.web.EntityResource;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;

import java.time.LocalDate;

public class PatientResource extends EntityResource {

    private String firstName;
    private String lastName;
    private LocalDate birthDate;
    private Sex sex;

    @JsonCreator
    public PatientResource(@JsonProperty("firstName") String firstName, @JsonProperty("lastName") String lastName, @JsonProperty("birthDate") LocalDate birthDate, @JsonProperty("sex") Sex sex) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.birthDate = birthDate;
        this.sex = sex;
    }

    // getter
}

这是我得到的输出:

19:43:41,842 INFO  [stdout] (default task-1) 19:43:41 DEBUG o.s.w.s.DispatcherServlet.doService - DispatcherServlet with name 'DispatcherServlet' processing POST request for [/questionnaire-api/patients]
19:43:41,846 INFO  [stdout] (default task-1) 19:43:41 DEBUG o.s.w.s.m.m.a.RequestMappingHandlerMapping.getHandlerInternal - Looking up handler method for path /patients
19:43:41,850 INFO  [stdout] (default task-1) 19:43:41 DEBUG o.s.w.s.m.m.a.RequestMappingHandlerMapping.getHandlerInternal - Returning handler method [public at.landsteiner.patient.web.PatientResource at.landsteiner.patient.web.PatientWebServiceImpl.createPatient(at.landsteiner.patient.web.PatientResource)]
19:43:41,850 INFO  [stdout] (default task-1) 19:43:41 DEBUG o.s.b.f.s.DefaultListableBeanFactory.doGetBean - Returning cached instance of singleton bean 'patientWebServiceImpl'
19:43:41,868 INFO  [stdout] (default task-1) 19:43:41 DEBUG o.s.w.s.m.m.a.ExceptionHandlerExceptionResolver.resolveException - Resolving exception from handler [public at.landsteiner.patient.web.PatientResource at.landsteiner.patient.web.PatientWebServiceImpl.createPatient(at.landsteiner.patient.web.PatientResource)]: org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [at.landsteiner.patient.web.PatientResource]: No default constructor found; nested exception is java.lang.NoSuchMethodException: at.landsteiner.patient.web.PatientResource.<init>()
19:43:41,869 INFO  [stdout] (default task-1) 19:43:41 DEBUG o.s.w.s.m.a.ResponseStatusExceptionResolver.resolveException - Resolving exception from handler [public at.landsteiner.patient.web.PatientResource at.landsteiner.patient.web.PatientWebServiceImpl.createPatient(at.landsteiner.patient.web.PatientResource)]: org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [at.landsteiner.patient.web.PatientResource]: No default constructor found; nested exception is java.lang.NoSuchMethodException: at.landsteiner.patient.web.PatientResource.<init>()
19:43:41,870 INFO  [stdout] (default task-1) 19:43:41 DEBUG o.s.w.s.m.s.DefaultHandlerExceptionResolver.resolveException - Resolving exception from handler [public at.landsteiner.patient.web.PatientResource at.landsteiner.patient.web.PatientWebServiceImpl.createPatient(at.landsteiner.patient.web.PatientResource)]: org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [at.landsteiner.patient.web.PatientResource]: No default constructor found; nested exception is java.lang.NoSuchMethodException: at.landsteiner.patient.web.PatientResource.<init>()
19:43:41,878 INFO  [stdout] (default task-1) 19:43:41 DEBUG o.s.w.s.DispatcherServlet.processRequest - Could not complete request
19:43:41,879 INFO  [stdout] (default task-1) org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [at.landsteiner.patient.web.PatientResource]: No default constructor found; nested exception is java.lang.NoSuchMethodException: at.landsteiner.patient.web.PatientResource.<init>()
19:43:41,879 INFO  [stdout] (default task-1)    at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:107) ~[spring-beans-4.1.1.RELEASE.jar:4.1.1.RELEASE]
19:43:41,879 INFO  [stdout] (default task-1)    at org.springframework.web.method.annotation.ModelAttributeMethodProcessor.createAttribute(ModelAttributeMethodProcessor.java:139) ~[spring-web-4.1.1.RELEASE.jar:4.1.1.RELEASE]
19:43:41,879 INFO  [stdout] (default task-1)    at org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor.createAttribute(ServletModelAttributeMethodProcessor.java:79) ~[spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
19:43:41,879 INFO  [stdout] (default task-1)    at org.springframework.web.method.annotation.ModelAttributeMethodProcessor.resolveArgument(ModelAttributeMethodProcessor.java:105) ~[spring-web-4.1.1.RELEASE.jar:4.1.1.RELEASE]
19:43:41,879 INFO  [stdout] (default task-1)    at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:77) ~[spring-web-4.1.1.RELEASE.jar:4.1.1.RELEASE]
19:43:41,879 INFO  [stdout] (default task-1)    at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:157) ~[spring-web-4.1.1.RELEASE.jar:4.1.1.RELEASE]
19:43:41,879 INFO  [stdout] (default task-1)    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:124) ~[spring-web-4.1.1.RELEASE.jar:4.1.1.RELEASE]
19:43:41,879 INFO  [stdout] (default task-1)    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104) ~[spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
19:43:41,879 INFO  [stdout] (default task-1)    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:781) ~[spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
19:43:41,879 INFO  [stdout] (default task-1)    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:721) ~[spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
19:43:41,879 INFO  [stdout] (default task-1)    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:83) ~[spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
19:43:41,879 INFO  [stdout] (default task-1)    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:943) ~[spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
19:43:41,879 INFO  [stdout] (default task-1)    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:877) ~[spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
19:43:41,879 INFO  [stdout] (default task-1)    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:966) [spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
19:43:41,879 INFO  [stdout] (default task-1)    at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:868) [spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
19:43:41,879 INFO  [stdout] (default task-1)    at javax.servlet.http.HttpServlet.service(HttpServlet.java:707) [jboss-servlet-api_3.1_spec-1.0.0.Final.jar!/:1.0.0.Final]
19:43:41,879 INFO  [stdout] (default task-1)    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:842) [spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
19:43:41,879 INFO  [stdout] (default task-1)    at javax.servlet.http.HttpServlet.service(HttpServlet.java:790) [jboss-servlet-api_3.1_spec-1.0.0.Final.jar!/:1.0.0.Final]
19:43:41,882 INFO  [stdout] (default task-1)    at io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:85) [undertow-servlet-1.0.15.Final.jar!/:1.0.15.Final]
19:43:41,882 INFO  [stdout] (default task-1)    at io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:61) [undertow-servlet-1.0.15.Final.jar!/:1.0.15.Final]
19:43:41,882 INFO  [stdout] (default task-1)    at io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36) [undertow-servlet-1.0.15.Final.jar!/:1.0.15.Final]
19:43:41,882 INFO  [stdout] (default task-1)    at org.wildfly.extension.undertow.security.SecurityContextAssociationHandler.handleRequest(SecurityContextAssociationHandler.java:78) [wildfly-undertow-8.1.0.Final.jar!/:8.1.0.Final]
19:43:41,882 INFO  [stdout] (default task-1)    at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:25) [undertow-core-1.0.15.Final.jar!/:1.0.15.Final]
19:43:41,882 INFO  [stdout] (default task-1)    at io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:113) [undertow-servlet-1.0.15.Final.jar!/:1.0.15.Final]
19:43:41,882 INFO  [stdout] (default task-1)    at io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:56) [undertow-servlet-1.0.15.Final.jar!/:1.0.15.Final]
19:43:41,882 INFO  [stdout] (default task-1)    at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:25) [undertow-core-1.0.15.Final.jar!/:1.0.15.Final]
19:43:41,882 INFO  [stdout] (default task-1)    at io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:45) [undertow-core-1.0.15.Final.jar!/:1.0.15.Final]
19:43:41,882 INFO  [stdout] (default task-1)    at io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:61) [undertow-servlet-1.0.15.Final.jar!/:1.0.15.Final]
19:43:41,882 INFO  [stdout] (default task-1)    at io.undertow.security.handlers.AuthenticationMechanismsHandler.handleRequest(AuthenticationMechanismsHandler.java:58) [undertow-core-1.0.15.Final.jar!/:1.0.15.Final]
19:43:41,882 INFO  [stdout] (default task-1)    at io.undertow.servlet.handlers.security.CachedAuthenticatedSessionHandler.handleRequest(CachedAuthenticatedSessionHandler.java:70) [undertow-servlet-1.0.15.Final.jar!/:1.0.15.Final]
19:43:41,882 INFO  [stdout] (default task-1)    at io.undertow.security.handlers.SecurityInitialHandler.handleRequest(SecurityInitialHandler.java:76) [undertow-core-1.0.15.Final.jar!/:1.0.15.Final]
19:43:41,882 INFO  [stdout] (default task-1)    at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:25) [undertow-core-1.0.15.Final.jar!/:1.0.15.Final]
19:43:41,882 INFO  [stdout] (default task-1)    at org.wildfly.extension.undertow.security.jacc.JACCContextIdHandler.handleRequest(JACCContextIdHandler.java:61) [wildfly-undertow-8.1.0.Final.jar!/:8.1.0.Final]
19:43:41,882 INFO  [stdout] (default task-1)    at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:25) [undertow-core-1.0.15.Final.jar!/:1.0.15.Final]
19:43:41,882 INFO  [stdout] (default task-1)    at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:25) [undertow-core-1.0.15.Final.jar!/:1.0.15.Final]
19:43:41,883 INFO  [stdout] (default task-1)    at io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:240) [undertow-servlet-1.0.15.Final.jar!/:1.0.15.Final]
19:43:41,883 INFO  [stdout] (default task-1)    at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:227) [undertow-servlet-1.0.15.Final.jar!/:1.0.15.Final]
19:43:41,883 INFO  [stdout] (default task-1)    at io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:73) [undertow-servlet-1.0.15.Final.jar!/:1.0.15.Final]
19:43:41,883 INFO  [stdout] (default task-1)    at io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:146) [undertow-servlet-1.0.15.Final.jar!/:1.0.15.Final]
19:43:41,883 INFO  [stdout] (default task-1)    at io.undertow.server.Connectors.executeRootHandler(Connectors.java:177) [undertow-core-1.0.15.Final.jar!/:1.0.15.Final]
19:43:41,883 INFO  [stdout] (default task-1)    at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:727) [undertow-core-1.0.15.Final.jar!/:1.0.15.Final]
19:43:41,883 INFO  [stdout] (default task-1)    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0]
19:43:41,883 INFO  [stdout] (default task-1)    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0]
19:43:41,883 INFO  [stdout] (default task-1)    at java.lang.Thread.run(Thread.java:744) [na:1.8.0]
19:43:41,884 INFO  [stdout] (default task-1) Caused by: java.lang.NoSuchMethodException: at.landsteiner.patient.web.PatientResource.<init>()
19:43:41,884 INFO  [stdout] (default task-1)    at java.lang.Class.getConstructor0(Class.java:2971) ~[na:1.8.0]
19:43:41,884 INFO  [stdout] (default task-1)    at java.lang.Class.getDeclaredConstructor(Class.java:2165) ~[na:1.8.0]
19:43:41,884 INFO  [stdout] (default task-1)    at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:104) ~[spring-beans-4.1.1.RELEASE.jar:4.1.1.RELEASE]
19:43:41,884 INFO  [stdout] (default task-1)    ... 43 common frames omitted

如何配置 Spring 以将实例化委托给 Jackson?
甚至可以将@JsonCreator 的使用与@RequestBody 结合使用吗?

我的 JavaConfig 如下所示:

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = { "at.landsteiner" })
public class WebConfig extends WebMvcConfigurerAdapter {

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(converter());
    }

    @Bean
    public MappingJackson2HttpMessageConverter converter() {
        ObjectMapper mapper = new ObjectMapper();

        mapper.registerModule(new JSR310Module());
        mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);

        return new MappingJackson2HttpMessageConverter(mapper);
    }
}

【问题讨论】:

  • 我得到一个完全不同的错误。尝试获取类的无参数构造函数是没有意义的。您确定要向我们展示确切的类和错误吗?
  • 是的,很确定。因为如果我添加一个无参数的构造函数,错误就会消失,但是对象是空的。从堆栈跟踪中我可以看出 spring 尝试实例化 bean 本身,因此不关心 @JsonCreator 注释。
  • 我已经添加了我所说的堆栈跟踪部分,未找到默认构造函数。也许有帮助。
  • 当您将指定的 JSON 发布到控制器时,您可以将日志记录级别调高到 DEBUG 并发布日志的输出吗?
  • 有控制器实现吗?

标签: java spring-mvc jackson


【解决方案1】:

虽然我没有用Spring 4.1.1测试过(但是用Spring 4.0.5做过),但我发现问题是注解@RequestBody只存在于界面中。

Spring 不会从接口中查看方法参数的注解(甚至不确定这是否可能),而只会从具体实现中查看。

由于具体实现上缺少注释,Spring 确实尝试使用 Jackson 来实例化对象,但使用常规 bean 方法。

如果你只是在具体方法的参数中添加注释,一切都会正常工作

【讨论】:

    猜你喜欢
    • 2016-09-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多