当给定Inteface作为参数时,依赖注入不起作用

时间:2018-06-26 12:51:41

标签: java spring

这是我的Controller类

@Controller
public class HomeController{

    @RequestMapping("/")
    public String home(MyTest test){
        test.draw();
        return "homePage";
    }
}

在将MyTest(Interface)作为参数传递给home方法时,Spring不会注入其实现类,而是引发异常

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)

但是在直接传递实现类即MyTestImpl时,它可以正常工作。

@RequestMapping("/")
    public String home(MyTestImpl test){
        test.draw();
        return "homePage";
    }

在使用接口的情况下,能否请您在此说明异常的原因。 下面是MyTest实现类

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

4 个答案:

答案 0 :(得分:1)

您的操作方式是完全错误的。这是一个工作代码,

@Controller
public class HomeController{
  private final MyTest test;

   @Autowired
   public HomeController(MyTest test) {
      this.test = test;
   }

   @RequestMapping("/")
   public String home(){
      test.draw();
      return "homePage";
   }
}

@RequestMapping注释的方法的参数应该/将以Pathvariable或RequestParams的形式出现,或者仅仅是HttpServletRequest对象本身。这不是您自动装配实例的方式。

依赖注入在构造函数,字段和接口级别上起作用。不在方法参数级别 。希望它清除。

答案 1 :(得分:1)

首先,我想指出的是,您混用了不同的术语。

如前所述,自动装配是弹簧的核心概念。如前所述,对于非配置bean *,@Autowire应该在设置程序上完成,这是相关的短语:

  

将构造函数,字段,setter方法或config方法标记为由Spring的依赖项注入工具自动装配。

第二,在这里,您尝试将一个bean(@Component)注入到MVC Controller中,更具体地说是将其插入到bean的接口中。

如果您仔细阅读文档(here),您会发现Spring支持各种参数,并将尽最大努力将它们传递给控制器​​方法。

这里的问题是Spring无法知道将什么传递给您的方法。 调用控制器时,spring将看到此参数属于最后一个类别,即Any other argument

如果方法参数与以上任何一个都不匹配,则默认情况下,如果它是由BeanUtils#isSimpleProperty确定的简单类型,则将其解析为@RequestParam,或者将其解析为否则为@ModelAttribute。

因此spring将其视为@ModelAttribute,但由于无法实例化接口而无法知道如何映射它,因此会出错。

现在取决于您的问题,要么:

将您的bean或接口作为@Controller的成员自动装配,例如:

@Controller
public class HomeController{

    //might need @Qualifier if more than one implementation
    @Autowire
    private MyTest test; 

    @RequestMapping("/")
    public String home(){
        test.draw();
        return "homePage";
    }
}

或将您的 实现 传递给您的方法控制器。

  

*尽管我没有在spring文档中找到任何明确说明配置方法的东西,但我很确定这不适用于@Controller方法。

答案 2 :(得分:0)

您的组件是否可以覆盖接口所在的软件包。使用@Autowired批注进行注入。

答案 3 :(得分:0)

您所做的是正确的,除了需要告诉Spring如何反序列化您的接口(即期望使用哪种具体的类实现。如果在MyTest实现中使用JSON序列化(通常是Spring的默认设置),请添加以下注释

    @JsonDeserialize(as=MyTestImpl.class)
    public interface MyTest  {
    ...

还假设您使用的是Post方法,请将Controller更改为:

@PostMapping(value = "/",
            consumes = MediaType.APPLICATION_JSON_UTF8_VALUE,
            produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public String home(MyTest test){
        test.draw();
        return "homePage";
    }

(“产生”参数当然可以是text / html或您返回的任何内容)