注:分析的版本是SpringFramework-4.3.x,源码可自行到Github上下载
先上一张图
图1 DelegatingFilterProxy的类继承图
DelegatingFilterProxy只是直接继承了GenericFilterBean,如下
List-1
public class DelegatingFilterProxy extends GenericFilterBean {
DelegatingFilterProxy覆写了父类中的如下几个方法
List-2
@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);
}
}
使用Template pattern模式,DelegatingFilterProxy中的属性targetBeanName,它是个bean的名称,且这个bean要实现Filter。Spring会自动从web application中取出targetBeanName对应的bean,之后使用这个bean.
由于DelegatingFilterProxy间接实现了Filter,所以要实现doFilter方法,如下:
List-3
@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);
}
/**
* 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);
}
在doFilter中调用了invokeDelegate方法,在invokeDelegate中,又调用了delegate的doFilter方法。
调用的时序图如下:
图2 DelegatingFilterProxy的doFilter调用时序图
1. DelegatingFilterProxy中有个属性,如下List-4所示。为什么即使没有显示的指定webApplicationContext的值,DelegatingFilterProxy依然可以从web container中得到targetBeanName对应的bean,why? 这是因为DelegatingFilterProxy实现了Filter,而Filter接口中有个方法init,如下List-5所示,init方法的参数FilterConfig中有个方法getServletContext,getServletContext方法返回一个ServletContext,ServletContext有getAttribute方法,这个方法可以拿到绑定到ServletContext的值。如果了解过ContextLoadListener和DispatcherServlet实现的,那么应该明白了。
List-4
private WebApplicationContext webApplicationContext;
List-5
public void init(FilterConfig filterConfig) throws ServletException;
List-6
public ServletContext getServletContext();
2. 如下List-7所示,在web.xml中配置DelegatingFilterProxy时,我们并没有指定DelegatingFilterProxy的targetBeanName,那么web container在处理DelegatingFilterProxy时,这个targetBeanName是什么呢,它的值是不能为null的。答案:在上面List-2中,方法initFilterBean()里面,有this.targetBeanName = getFilterName(),那么我们来看看getFilterName()是如何实现的,如下List-8所示,由于DelegatingFilterProxy间接实现了Filter,所以它可以拿到FilterConfig,从FilterCofig中可以拿到这个Filter的filterName,从List-8中看出,当targetBeanName为null时,会用filterName进行赋值,按List-7中的配置的话,这个targetBeanName的值就是springSecurityFilterChain。注:问题2,我也是看了这篇博客后,才明白的。
List-7
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
List-8
protected final String getFilterName() {
return (this.filterConfig != null ? this.filterConfig.getFilterName() : this.beanName);
}
(adsbygoogle = window.adsbygoogle || []).push({});