Reputation: 37
This is my Controller class
@Controller
public class HomeController{
@RequestMapping("/")
public String home(MyTest test){
test.draw();
return "homePage";
}
}
on passing MyTest(Interface) as parameter to home method ,Spring does not inject its implementation class, instead throws an exception
SEVERE: Servlet.service() for servlet [dispatcher] in context with path [/spring-mvc-demo] threw exception [Request processing failed; nested exception is java.lang.IllegalStateException: No primary or default constructor found for interface com.managers.MyTest] with root cause
java.lang.NoSuchMethodException: com.managers.MyTest.<init>()
at java.lang.Class.getConstructor0(Unknown Source)
at java.lang.Class.getDeclaredConstructor(Unknown Source)
at org.springframework.web.method.annotation.ModelAttributeMethodProcessor.createAttribute(ModelAttributeMethodProcessor.java:209)
at org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor.createAttribute(ServletModelAttributeMethodProcessor.java:84)
at org.springframework.web.method.annotation.ModelAttributeMethodProcessor.resolveArgument(ModelAttributeMethodProcessor.java:132)
at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:124)
at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:161)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:131)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:871)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:777)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:991)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:925)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:978)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:870)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:621)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:855)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:936)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1004)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1686)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
But on passing directly the implementation class i.e MyTestImpl it works fine .
@RequestMapping("/")
public String home(MyTestImpl test){
test.draw();
return "homePage";
}
Can you please tell the reason of Exception here in case of Interface. Below is MyTest implementation class
@Component
public class MyTestImpl implements MyTest{
@Override
public void draw() {
System.out.println("inside test");
}
}
Spring.xml
<context:component-scan base-package="com.controllers,com.ManagerImpl" />
<mvc:annotation-driven/>
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/view/" />
<property name="suffix" value=".jsp" />
</bean>
Upvotes: 1
Views: 2204
Reputation: 7808
What you are doing is correct, except you need to tell Spring how to de-serialize your interface (i.e. which concrete class implementation is expected. If you use JSON serialization (usual Spring default) in your MyTest implementation add the following annotation
@JsonDeserialize(as=MyTestImpl.class)
public interface MyTest {
...
Also assuming that you are using Post method change your Controller to:
@PostMapping(value = "/",
consumes = MediaType.APPLICATION_JSON_UTF8_VALUE,
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public String home(MyTest test){
test.draw();
return "homePage";
}
("produces" param may of course be text/html or whatever you return)
Upvotes: 0
Reputation: 2220
First of all, i'd like to point out that you are mixing different terminlogies.
Autowiring is as previously stated is a core concept of spring. As previously stated, @Autowire should be done on setters for non config beans*, here is the relevant phrase :
Marks a constructor, field, setter method or config method as to be autowired by Spring's dependency injection facilities.
Secondly, here you are trying to inject a bean (@Component) into a MVC Controller and more specifically an interface of thus bean.
If you look closely at the documentation (here), you can see that Spring supports various parameters and will try as best as it can to pass them to your controller method.
The problem here is that Spring has no way of knowing what to pass to your method.
When the controller is called, spring will see that this parameter falls into the last category, ie Any other argument
:
If a method argument is not matched to any of the above, by default it is resolved as an @RequestParam if it is a simple type, as determined by BeanUtils#isSimpleProperty, or as an @ModelAttribute otherwise.
Thus spring will consider this as a @ModelAttribute, but it cannot know how to map it as an interface cannot be instantiated, hence your error.
Now depending on your problem, either you :
Autowire your bean or interface as a member of your @Controller, ex :
@Controller
public class HomeController{
//might need @Qualifier if more than one implementation
@Autowire
private MyTest test;
@RequestMapping("/")
public String home(){
test.draw();
return "homePage";
}
}
or pass your implementation to your method controller.
*Although i haven't found anything clear in the spring documentation that states what clearly is a config method, i'm pretty sure that does not apply to @Controller methods.
Upvotes: 1
Reputation: 27078
The way you are doing is completely wrong. Here is a working code,
@Controller
public class HomeController{
private final MyTest test;
@Autowired
public HomeController(MyTest test) {
this.test = test;
}
@RequestMapping("/")
public String home(){
test.draw();
return "homePage";
}
}
Arguments to the methods anntotated with @RequestMapping
should/will come in the form Pathvariable or RequestParams or just HttpServletRequest object itself. That is not how you autowire the instances.
Dependency Injection works on Constructor, Field and Interface level. Not at a method parameter level. Hope its clear.
Upvotes: 1
Reputation: 2551
Is your componentscan value covers the package where interface resides. Use @Autowired annotation for injection.
Upvotes: 0