filter中使用Spring容器注入bean

[1] XML配置形式

Tomcat容器初始化顺序:监听器–>过滤器–>servlet,因此springMVCservlet初始化之前,过滤器就已经初始化过了,如果在过滤器中需要注入spring容器管理的bean是注入不进去的,因此需要在spring监听器中初始化需要注入的bean,才能在过滤器中注入,而且过滤器的定义需要使用spring定义的DelegatingFilterProxy来实现,web.xml部分代码如下:

<filter>
<filter-name>myFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>myFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

Spring 配置文件中配置该bean:

<bean id="myFilter" class="com.filter.myFilter"></bean>

DelegatingFilterProxy类遵循filter-name-bean的原则,会根据web.xml中filter-name的值查找到spring配置文件中id与filter-name相同的值,然后把接受到的处理信息传递给相对应的类处理。

如果要保留Filter原有的init,destroy方法的调用,还需要配置初始化参数targetFilterLifecycle为true,该参数默认为false,代码例子如下:

<filter>
<filter-name>myFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>myFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

[2] SpringBoot2.0中使用代码注册Filter

Config配置文件如下:

package com.bin.ssodemo;

@Configuration
public class UuapConfig {

    @Bean
    @ConfigurationProperties(prefix = "uuap")
    public UuapAuthenticationFilter uuapAuthenticationFilter() {
        return new UuapAuthenticationFilter();
    }

    @Bean
    public FilterRegistrationBean cas20ValidateRegistration(
            UuapAuthenticationFilter uuapAuthenticationFilter) {
        FilterRegistrationBean cas20 = new FilterRegistrationBean();
        cas20.setFilter(uuapAuthenticationFilter);
        cas20.addUrlPatterns("/*");
        return cas20;
    }

}

附:DelegatingFilterProxy 完整源码

/*
 * Copyright 2002-2017 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.web.filter;

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.util.Assert;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

/**
 * Proxy for a standard Servlet Filter, delegating to a Spring-managed bean that
 * implements the Filter interface. Supports a "targetBeanName" filter init-param
 * in {@code web.xml}, specifying the name of the target bean in the Spring
 * application context.
 *
 * <p>{@code web.xml} will usually contain a {@code DelegatingFilterProxy} definition,
 * with the specified {@code filter-name} corresponding to a bean name in
 * Spring's root application context. All calls to the filter proxy will then
 * be delegated to that bean in the Spring context, which is required to implement
 * the standard Servlet Filter interface.
 *
 * <p>This approach is particularly useful for Filter implementation with complex
 * setup needs, allowing to apply the full Spring bean definition machinery to
 * Filter instances. Alternatively, consider standard Filter setup in combination
 * with looking up service beans from the Spring root application context.
 *
 * <p><b>NOTE:</b> The lifecycle methods defined by the Servlet Filter interface
 * will by default <i>not</i> be delegated to the target bean, relying on the
 * Spring application context to manage the lifecycle of that bean. Specifying
 * the "targetFilterLifecycle" filter init-param as "true" will enforce invocation
 * of the {@code Filter.init} and {@code Filter.destroy} lifecycle methods
 * on the target bean, letting the servlet container manage the filter lifecycle.
 *
 * <p>As of Spring 3.1, {@code DelegatingFilterProxy} has been updated to optionally accept
 * constructor parameters when using Servlet 3.0's instance-based filter registration
 * methods, usually in conjunction with Spring 3.1's
 * {@link org.springframework.web.WebApplicationInitializer} SPI. These constructors allow
 * for providing the delegate Filter bean directly, or providing the application context
 * and bean name to fetch, avoiding the need to look up the application context from the
 * ServletContext.
 *
 * <p>This class was originally inspired by Spring Security's {@code FilterToBeanProxy}
 * class, written by Ben Alex.
 *
 * @author Juergen Hoeller
 * @author Sam Brannen
 * @author Chris Beams
 * @since 1.2
 * @see #setTargetBeanName
 * @see #setTargetFilterLifecycle
 * @see javax.servlet.Filter#doFilter
 * @see javax.servlet.Filter#init
 * @see javax.servlet.Filter#destroy
 * @see #DelegatingFilterProxy(Filter)
 * @see #DelegatingFilterProxy(String)
 * @see #DelegatingFilterProxy(String, WebApplicationContext)
 * @see javax.servlet.ServletContext#addFilter(String, Filter)
 * @see org.springframework.web.WebApplicationInitializer
 */
public class DelegatingFilterProxy extends GenericFilterBean {

    private String contextAttribute;

    private WebApplicationContext webApplicationContext;

    private String targetBeanName;

    private boolean targetFilterLifecycle = false;

    private volatile Filter delegate;

    private final Object delegateMonitor = new Object();


    /**
     * Create a new {@code DelegatingFilterProxy}. For traditional (pre-Servlet 3.0) use
     * in {@code web.xml}.
     * @see #setTargetBeanName(String)
     */
    public DelegatingFilterProxy() {
    }

    /**
     * Create a new {@code DelegatingFilterProxy} with the given {@link Filter} delegate.
     * Bypasses entirely the need for interacting with a Spring application context,
     * specifying the {@linkplain #setTargetBeanName target bean name}, etc.
     * <p>For use in Servlet 3.0+ environments where instance-based registration of
     * filters is supported.
     * @param delegate the {@code Filter} instance that this proxy will delegate to and
     * manage the lifecycle for (must not be {@code null}).
     * @see #doFilter(ServletRequest, ServletResponse, FilterChain)
     * @see #invokeDelegate(Filter, ServletRequest, ServletResponse, FilterChain)
     * @see #destroy()
     * @see #setEnvironment(org.springframework.core.env.Environment)
     */
    public DelegatingFilterProxy(Filter delegate) {
        Assert.notNull(delegate, "Delegate Filter must not be null");
        this.delegate = delegate;
    }

    /**
     * Create a new {@code DelegatingFilterProxy} that will retrieve the named target
     * bean from the Spring {@code WebApplicationContext} found in the {@code ServletContext}
     * (either the 'root' application context or the context named by
     * {@link #setContextAttribute}).
     * <p>For use in Servlet 3.0+ environments where instance-based registration of
     * filters is supported.
     * <p>The target bean must implement the standard Servlet Filter.
     * @param targetBeanName name of the target filter bean to look up in the Spring
     * application context (must not be {@code null}).
     * @see #findWebApplicationContext()
     * @see #setEnvironment(org.springframework.core.env.Environment)
     */
    public DelegatingFilterProxy(String targetBeanName) {
        this(targetBeanName, null);
    }

    /**
     * Create a new {@code DelegatingFilterProxy} that will retrieve the named target
     * bean from the given Spring {@code WebApplicationContext}.
     * <p>For use in Servlet 3.0+ environments where instance-based registration of
     * filters is supported.
     * <p>The target bean must implement the standard Servlet Filter interface.
     * <p>The given {@code WebApplicationContext} may or may not be refreshed when passed
     * in. If it has not, and if the context implements {@link ConfigurableApplicationContext},
     * a {@link ConfigurableApplicationContext#refresh() refresh()} will be attempted before
     * retrieving the named target bean.
     * <p>This proxy's {@code Environment} will be inherited from the given
     * {@code WebApplicationContext}.
     * @param targetBeanName name of the target filter bean in the Spring application
     * context (must not be {@code null}).
     * @param wac the application context from which the target filter will be retrieved;
     * if {@code null}, an application context will be looked up from {@code ServletContext}
     * as a fallback.
     * @see #findWebApplicationContext()
     * @see #setEnvironment(org.springframework.core.env.Environment)
     */
    public DelegatingFilterProxy(String targetBeanName, WebApplicationContext wac) {
        Assert.hasText(targetBeanName, "Target Filter bean name must not be null or empty");
        this.setTargetBeanName(targetBeanName);
        this.webApplicationContext = wac;
        if (wac != null) {
            this.setEnvironment(wac.getEnvironment());
        }
    }

    /**
     * Set the name of the ServletContext attribute which should be used to retrieve the
     * {@link WebApplicationContext} from which to load the delegate {@link Filter} bean.
     */
    public void setContextAttribute(String contextAttribute) {
        this.contextAttribute = contextAttribute;
    }

    /**
     * Return the name of the ServletContext attribute which should be used to retrieve the
     * {@link WebApplicationContext} from which to load the delegate {@link Filter} bean.
     */
    public String getContextAttribute() {
        return this.contextAttribute;
    }

    /**
     * Set the name of the target bean in the Spring application context.
     * The target bean must implement the standard Servlet Filter interface.
     * <p>By default, the {@code filter-name} as specified for the
     * DelegatingFilterProxy in {@code web.xml} will be used.
     */
    public void setTargetBeanName(String targetBeanName) {
        this.targetBeanName = targetBeanName;
    }

    /**
     * Return the name of the target bean in the Spring application context.
     */
    protected String getTargetBeanName() {
        return this.targetBeanName;
    }

    /**
     * Set whether to invoke the {@code Filter.init} and
     * {@code Filter.destroy} lifecycle methods on the target bean.
     * <p>Default is "false"; target beans usually rely on the Spring application
     * context for managing their lifecycle. Setting this flag to "true" means
     * that the servlet container will control the lifecycle of the target
     * Filter, with this proxy delegating the corresponding calls.
     */
    public void setTargetFilterLifecycle(boolean targetFilterLifecycle) {
        this.targetFilterLifecycle = targetFilterLifecycle;
    }

    /**
     * Return whether to invoke the {@code Filter.init} and
     * {@code Filter.destroy} lifecycle methods on the target bean.
     */
    protected boolean isTargetFilterLifecycle() {
        return this.targetFilterLifecycle;
    }


    @Override
    protected void initFilterBean() throws ServletException {
        synchronized (this.delegateMonitor) {
            if (this.delegate == null) {
                // If no target bean name specified, use filter name.
                if (this.targetBeanName == null) {
                    this.targetBeanName = getFilterName();
                }
                // Fetch Spring root application context and initialize the delegate early,
                // if possible. If the root application context will be started after this
                // filter proxy, we'll have to resort to lazy initialization.
                WebApplicationContext wac = findWebApplicationContext();
                if (wac != null) {
                    this.delegate = initDelegate(wac);
                }
            }
        }
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {

        // Lazily initialize the delegate if necessary.
        Filter delegateToUse = this.delegate;
        if (delegateToUse == null) {
            synchronized (this.delegateMonitor) {
                delegateToUse = this.delegate;
                if (delegateToUse == null) {
                    WebApplicationContext wac = findWebApplicationContext();
                    if (wac == null) {
                        throw new IllegalStateException("No WebApplicationContext found: " +
                                "no ContextLoaderListener or DispatcherServlet registered?");
                    }
                    delegateToUse = initDelegate(wac);
                }
                this.delegate = delegateToUse;
            }
        }

        // Let the delegate perform the actual doFilter operation.
        invokeDelegate(delegateToUse, request, response, filterChain);
    }

    @Override
    public void destroy() {
        Filter delegateToUse = this.delegate;
        if (delegateToUse != null) {
            destroyDelegate(delegateToUse);
        }
    }


    /**
     * Return the {@code WebApplicationContext} passed in at construction time, if available.
     * Otherwise, attempt to retrieve a {@code WebApplicationContext} from the
     * {@code ServletContext} attribute with the {@linkplain #setContextAttribute
     * configured name} if set. Otherwise look up a {@code WebApplicationContext} under
     * the well-known "root" application context attribute. The
     * {@code WebApplicationContext} must have already been loaded and stored in the
     * {@code ServletContext} before this filter gets initialized (or invoked).
     * <p>Subclasses may override this method to provide a different
     * {@code WebApplicationContext} retrieval strategy.
     * @return the {@code WebApplicationContext} for this proxy, or {@code null} if not found
     * @see #DelegatingFilterProxy(String, WebApplicationContext)
     * @see #getContextAttribute()
     * @see WebApplicationContextUtils#getWebApplicationContext(javax.servlet.ServletContext)
     * @see WebApplicationContext#ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE
     */
    protected WebApplicationContext findWebApplicationContext() {
        if (this.webApplicationContext != null) {
            // The user has injected a context at construction time -> use it...
            if (this.webApplicationContext instanceof ConfigurableApplicationContext) {
                ConfigurableApplicationContext cac = (ConfigurableApplicationContext) this.webApplicationContext;
                if (!cac.isActive()) {
                    // The context has not yet been refreshed -> do so before returning it...
                    cac.refresh();
                }
            }
            return this.webApplicationContext;
        }
        String attrName = getContextAttribute();
        if (attrName != null) {
            return WebApplicationContextUtils.getWebApplicationContext(getServletContext(), attrName);
        }
        else {
            return WebApplicationContextUtils.findWebApplicationContext(getServletContext());
        }
    }

    /**
     * Initialize the Filter delegate, defined as bean the given Spring
     * application context.
     * <p>The default implementation fetches the bean from the application context
     * and calls the standard {@code Filter.init} method on it, passing
     * in the FilterConfig of this Filter proxy.
     * @param wac the root application context
     * @return the initialized delegate Filter
     * @throws ServletException if thrown by the Filter
     * @see #getTargetBeanName()
     * @see #isTargetFilterLifecycle()
     * @see #getFilterConfig()
     * @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
     */
    protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
        Filter delegate = wac.getBean(getTargetBeanName(), Filter.class);
        if (isTargetFilterLifecycle()) {
            delegate.init(getFilterConfig());
        }
        return delegate;
    }

    /**
     * Actually invoke the delegate Filter with the given request and response.
     * @param delegate the delegate Filter
     * @param request the current HTTP request
     * @param response the current HTTP response
     * @param filterChain the current FilterChain
     * @throws ServletException if thrown by the Filter
     * @throws IOException if thrown by the Filter
     */
    protected void invokeDelegate(
            Filter delegate, ServletRequest request, ServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {

        delegate.doFilter(request, response, filterChain);
    }

    /**
     * Destroy the Filter delegate.
     * Default implementation simply calls {@code Filter.destroy} on it.
     * @param delegate the Filter delegate (never {@code null})
     * @see #isTargetFilterLifecycle()
     * @see javax.servlet.Filter#destroy()
     */
    protected void destroyDelegate(Filter delegate) {
        if (isTargetFilterLifecycle()) {
            delegate.destroy();
        }
    }

}

猜你喜欢

转载自blog.csdn.net/voilet_bin/article/details/83502115