【问题标题】:Cannot inject beans in servlet context to Controller无法将 servlet 上下文中的 bean 注入控制器
【发布时间】:2015-01-21 19:47:09
【问题描述】:

我已阅读 Injecting Jaxb2Marshaller in Spring MVC controllerDifference between applicationContext.xml and spring-servlet.xml in Spring Framework 等,但对为什么我的案子不起作用感到困惑。

这是我的应用上下文和 web.xml 中的内容(精简以仅显示相关部分):

web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app .... >

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:/app-context.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    ....
    <servlet>
        <servlet-name>spring-rest</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>spring-rest</servlet-name>
        <url-pattern>/rest/*</url-pattern>
    </servlet-mapping>
    ...
</web-app>

spring-rest-servlet.xml

<beans ....>

    <!-- Only scan for Controllers in Servlet context -->
    <context:component-scan base-package="com.fil.ims" use-default-filters="false">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
        <context:include-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
    </context:component-scan>

    <mvc:annotation-driven />

    <context:annotation-config />

    <bean id="jaxb2Marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
         ....
    </bean>
</beans>

我有一个类似这样的控制器:

@Controller
public class FooControllerImpl implements FooController {
    @Inject
    @Named("jaxb2Marshaller")
    Jaxb2Marshaller jaxb2Marshaller;

    public void setJaxb2Marshaller(Jaxb2Marshaller jaxb2Marshaller) {
        this.jaxb2Marshaller = jaxb2Marshaller;
    }
    //.....
}

我发现我无法在 servlet 上下文中注入 jaxb2Marshaller(抛出NoSuchBeanDefinitionException)。但是,如果我将jaxb2Marshaller 放在主应用程序上下文(app-context.xml)中,则可以正常注入。

我很困惑为什么会这样。据我了解,如果我在web.xml 中有ContextLoaderListener,则提供的上下文配置(app-context.xml)将用于创建顶级应用程序上下文,然后是调度程序servlet 上下文(我的spring-rest-servlet.xml例如)将被创建为顶级应用上下文下的子上下文。

我不明白的是,控制器和要注入的bean(jaxb2Marshaller)都是在调度程序servlet上下文下创建的,为什么我不能将jaxb2Marshaller注入我的FooController? (我知道如果要注入的 bean 位于子应用程序上下文中,它将不起作用,但在这种情况下似乎不是。)

谁能解释一下原因?


更新:

我做了另一个实验(这让我更加困惑),通过在我的控制器中实现ApplicationContextAware,并从控制器中实现applicationContext.getBean("jaxb2Marshaller", Jaxb2Marshaller.class)。返回一个有效的bean(这与我的理解一致)。似乎只有通过注释注入是行不通的。有什么线索吗?

【问题讨论】:

    标签: spring spring-mvc


    【解决方案1】:

    (由于我自己找到了解决方案,该帖子曾经被删除。但是我认为在这里发布我的答案以供参考是很好的,因为似乎我不是唯一一个面临类似问题的人)

    我对app context和bean关系的理解,其实是正确的:

    1. 有一个根上下文
    2. Dispatcher servlet 上下文是根上下文下的子上下文
    3. 控制器和 Jaxb2Marshaller 在 servlet 上下文中创建。

    如果 Controller 和 Jaxb2Marshaller 都在同一个上下文中,为什么注入会失败?

    答案是:在 servlet 上下文中使用 Jaxb2Marshaller 注入控制器不会失败。失败消息来自根上下文中的另一个控制器。

    根本问题是我在根上下文中使用了默认的component-scan,它会扫描带有@Service@Repository 等注释的类,包括 @Controller。这意味着我的控制器也在根上下文中被扫描和创建。一旦我将@Inject/@Autowired 放入我的控制器中以注入一个应该在 servlet 上下文中创建的 bean,它将导致错误,因为根上下文中控制器的“意外副本”无法在子 servlet 上下文中看到 bean .

    解决方案直截了当:排除扫描根上下文中的控制器,仅扫描 servlet 上下文中的控制器:

    根上下文 xml:

    <context:component-scan base-package="com.foo">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
    

    调度程序上下文 xml:

    <context:component-scan base-package="com.foo" use-default-filters="false">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
    

    【讨论】:

      猜你喜欢
      • 2017-07-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-11-09
      • 1970-01-01
      • 2021-08-29
      • 2011-11-21
      • 2019-06-01
      相关资源
      最近更新 更多