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 上下文的主要内容,如果未能解决你的问题,请参考以下文章

spring DelegatingFilterProxy 过滤器 的原理及运用

DelegatingFilterProxy类的作用

DelegatingFilterProxy 中的 NoSuchBeanDefinitionException

DelegatingFilterProxy(委派过滤器代理类)使用

ClassNotFoundException DelegatingFilterProxy

DelegatingFilterProxy 在没有 Spring Security 的 Spring MVC 应用程序上调用了两次