Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring does not use annotated constructor (@JsonCreator) for instantiation

I am using Spring 4.1.1 with JavaConfig and Jackson 2.4.3.

My controller looks like this:

@RestController
public interface PatientWebService {

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

The json I am sending looks like this:

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

and I want it to be parsed into this class:

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
}

This is the output I am getting:

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

How do I configure Spring to delegate the instantiation to Jackson?
Is it even possible to combine the use of @JsonCreator with @RequestBody?

My JavaConfig looks like this:

@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);
    }
}
like image 310
Thomas Eizinger Avatar asked Oct 26 '14 17:10

Thomas Eizinger


1 Answers

Although I did not test with Spring 4.1.1 (but did with Spring 4.0.5), I found that the problem is that the annotation @RequestBody is only present in the interface.

Spring will not look at the annotation of the method argument from the interface (not even sure if that's possible), but only from the concrete implementation.

Because of the missing annotation on the concrete implementation, Spring does attempt to use Jackson at all for instantiating the object, but uses the regular bean method.

If you simply add the annotation the argument of the concrete method as well, everything will work just fine

like image 140
geoand Avatar answered Nov 19 '22 01:11

geoand