DelegatingFilterProxy 的独立 Spring 上下文

Posted

技术标签:

【中文标题】DelegatingFilterProxy 的独立 Spring 上下文【英文标题】:Separate Spring context for DelegatingFilterProxy 【发布时间】:2014-12-23 22:39:12 【问题描述】:

我正在尝试使用 DelegatingFilterProxy 将过滤器安装到 Spring Web 应用程序中。该应用程序对我来说是一个黑匣子。我无法控制它,但我知道它使用 Spring。我只控制我的过滤器。

应用程序本身以通常的方式在 web.xml (tomcat 7) 中配置,<listener>...ContextLoaderListener...</listener> 通过 <context-param> 指定的 Spring 配置。

我的第一次尝试是分享应用的上下文。我将自己的 spring 配置 XML 添加到 context-param。我的过滤器加载得很好,但我破坏了应用程序。我不确定它到底是怎么坏的,但看起来它不能再建立数据库连接了。我检查了明显的东西。没有 bean 名称冲突或属性名称冲突。

我真正想做的是拥有 2 个完全独立的上下文,这样我的过滤器可能就无法影响黑盒应用程序。有没有办法可以配置 web.xml 以使 Spring 为我的过滤器创建一个新的上下文?

***上有几个类似的问题,但细节差别很大。

我正在按要求发布我的 web.xml,但请记住,这不是我想要做的。你在这里看到的是我的过滤器和共享相同上下文的网络应用程序,这不是我想要的。我在问是否有可能拥有两个完全独立的上下文,以便过滤器和 Web 应用程序彼此完全隔离(至少在 Spring 级别)。

我下面的部分是过滤器声明和过滤器映射以及spring xml上下文配置的第二行(类路径:...)

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>

    <display-name>xxx</display-name>
    <description>xxx</description>

    <filter>
        <filter-name>myAccessFilter</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>myAccessFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <context-param>
        <param-name>log4jConfigLocation</param-name>
        <param-value>/WEB-INF/log4j.properties</param-value>
    </context-param>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            /WEB-INF/spring/applicationContext.xml
            classpath:my-access-spring.xml
        </param-value>
    </context-param>

    <listener>
        <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
    </listener>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <listener>
        <listener-class>flex.messaging.HttpFlexSession</listener-class>
    </listener>

    <servlet>
        <servlet-name>spring-flex</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value></param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet>
        <servlet-name>xxx-rest</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <!-- Map all /messagbroker requests to the DispatcherServlet for handling -->
    <servlet-mapping>
        <servlet-name>spring-flex</servlet-name>
        <url-pattern>/messagebroker/*</url-pattern>
    </servlet-mapping>

    <servlet-mapping>
        <servlet-name>xxx-rest</servlet-name>
        <url-pattern>/rest/*</url-pattern>
    </servlet-mapping>

    <error-page>
        <error-code>401</error-code>
        <location>/error-401.html</location>
    </error-page>

    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
        <welcome-file>index.htm</welcome-file>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>

</web-app>

时间过去了:关于为什么在这种情况下共享上下文是一个坏主意,我有更多信息。事实证明,我确实有 bean 冲突。特别是,我的过滤器和底层应用程序都使用了一个名为“dataSource”的 bean(当然)。

一旦我重命名了我的 bean,我从 Spring 中得到了一个非常明确的信息:

没有定义 [javax.sql.DataSource] 类型的限定 bean:预期 单个匹配 bean 但找到 2: dataSource,deoDataSource

大概应用程序使用的是按类型而不是按名称连接,所以我有一个明显的冲突。

所以!最初的问题仍然存在:是否可以配置 Spring 为我的过滤器创建单独的上下文?

谢谢, 弗雷德

【问题讨论】:

请发布您的 web.xml。 在数据源 bean 上尝试 autowire-candidate="false"。或者,您可以通过不同的ContextLoaderListener 加载的setContextAttribute 使用不同的上下文。 @Pavel,感谢您的两个建议。您能否详细说明第二个并发布示例?可以在 web.xml 中定义单独的上下文吗? 可以有任意多的上下文。每个根上下文都存储在特定的ServletContext 属性下。所有相关组件(DispatcherServletDelegatingFilterProxyContextLoaderListener)都将属性名称作为参数。下班后我会尽量写一个正确的答案:)。 @Pavel,指的是 Spring 3 还是 Spring 4?我正在使用 3.2,并且我正在对文档进行干燥。你在谈论contextId吗?不过我还没看完。我只是想确定版本。 【参考方案1】:

有两种方法可以解决您的问题:

让 Spring 不使用你的 bean 进行自动装配 为过滤器代理指定不同的 spring 上下文

禁用自动装配

可以通过 XML 配置将特定 bean 作为自动装配候选者排除:

<bean class="foo.bar.Baz" autowire-candidate="false" />

定义第二个应用上下文

我的 cmets 对您的问题不正确 - 您不能让 ContextLoaderListener 加载两个单独的上下文。但是您可以通过使用DispatcherServlet 为您加载第二个上下文来进行破解:

<!-- Use this just to load second application context -->
<servlet>
    <servlet-name>filterContextLoader</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:filter-context.xml</param-value>
    </init-param>
    <init-param>
        <param-name>contextAttribute</param-name>
        <param-value>filterContext</param-value>
    </init-param>
</servlet>

<filter>
    <filter-name>customFilter</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    <init-param>
        <param-name>contextAttribute</param-name>
        <param-value>filterContext</param-value>
    </init-param>
</filter>

当然,DispatcherServlet 会使用所谓的 默认策略 并自动注册各种不必要的 bean(默认处理程序映射、处理程序适配器等),这有一个缺点。但这应该是无害的。如果你想注册干净的上下文而不需要不必要的 bean,你需要实现自己的 ServletContextListener 来做到这一点。

【讨论】:

谢谢,帮了大忙! 这种方法怎么样:我可以在我的过滤器的 init() 方法中创建自己的上下文。我的过滤器将不再由 DelegatingFilterProxy 加载,而是由标准方法加载。然后我的 init 方法可以隐藏上下文,根据需要调用 getBean() 等等。想法? 是的,那也可以...只要确保properly destroy上下文when the time comes。

以上是关于DelegatingFilterProxy 的独立 Spring 上下文的主要内容,如果未能解决你的问题,请参考以下文章