今天看别人的代码,在web.xml文件部署描述符中配置了一个这样的filter:
<filter> <filter-name>shiroFilter</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>shiroFilter</filter-name> <url-pattern>/xx/*</url-pattern> </filter-mapping>同时在使用springSecurity的时候,也会使用类似的配置:
<filter> <filter-name>springSecurityFilterChain</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>springSecurityFilterChain</filter-name> <url-pattern>/xx/*</url-pattern> </filter-mapping>个人觉得,spring-web 炸包下面的这个代理类 主要作用如下:
使filter可以使用spring中的bean,达到可插拔的效果。比如你可以使用shiro或者springSecurity等安全框架来进行权限管理,这样也算是使安全验证这一块与spring解耦。
具体过程:web.xml中的ContextLoaderListener会将spring及你使用的shiro或者springSecurity的相关配置文件加载,然后初始化声明的bean,这其中就有filter中要用到的bean;这样filter用到这些bean的时候就可以到spring的bean容器中去取了。
DelegatingFilterProxy的继承关系如下:
而GenericFilterBean实现了如下接口:
public abstract class GenericFilterBean implements Filter, BeanNameAware, EnvironmentAware, ServletContextAware, InitializingBean, DisposableBean {...}DelegatingFilterProxy类extends抽象类GenericFilterBean,implements接口Filter,Servlet容器在启动时,首先会调用Filter的init方法,init()方法继承自GenericFilterBean类,主要作用是把Filter的初始化参数set到继承于GenericFilterBean类的Filter中,同时调用了留给subClass实现的initFilterBean()方法来从spring的上下文环境中拿你想适配的bean,这是就是shiro的bean。
@Override public final void init(FilterConfig filterConfig) throws ServletException { Assert.notNull(filterConfig, "FilterConfig must not be null"); if (logger.isDebugEnabled()) { logger.debug("Initializing filter '" + filterConfig.getFilterName() + "'"); } this.filterConfig = filterConfig; // Set bean properties from init parameters. try { PropertyValues pvs = new FilterConfigPropertyValues(filterConfig, this.requiredProperties); BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this); ResourceLoader resourceLoader = new ServletContextResourceLoader(filterConfig.getServletContext()); bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.environment)); initBeanWrapper(bw); bw.setPropertyValues(pvs, true); } catch (BeansException ex) { String msg = "Failed to set bean properties on filter '" + filterConfig.getFilterName() + "': " + ex.getMessage(); logger.error(msg, ex); throw new NestedServletException(msg, ex); } // Let subclasses do whatever initialization they like. initFilterBean(); if (logger.isDebugEnabled()) { logger.debug("Filter '" + filterConfig.getFilterName() + "' configured successfully"); } }
/** * Subclasses may override this to perform custom initialization. * All bean properties of this filter will have been set before this * method is invoked. * <p>Note: This method will be called from standard filter initialization * as well as filter bean initialization in a Spring application context. * Filter name and ServletContext will be available in both cases. * <p>This default implementation is empty. * @throws ServletException if subclass initialization fails * @see #getFilterName() * @see #getServletContext() */ protected void initFilterBean() throws ServletException { }
下面贴一下DelegatingFilterProxy的initFilterBean()实现:
@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); } } } }//DelegatingFilterProxy重写了该方法,实现的作用是根据你配置的filter名字去Spring上下文中捞取你引入的filterBean
,设置到此类的this.delegate属性中,以待之后来了请求委托调用。
我们都知道一个filter的主要方法是doFilter(...),让我们来看 一下DelegatingFilterProxy类的这个方法:
@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) { if (this.delegate == null) { WebApplicationContext wac = findWebApplicationContext();//获取spring上下文 if (wac == null) { throw new IllegalStateException("No WebApplicationContext found: " + "no ContextLoaderListener or DispatcherServlet registered?"); } this.delegate = initDelegate(wac);//初始化delegate,也即最终你引入的安全框架的filterBean } delegateToUse = this.delegate; } } // Let the delegate perform the actual doFilter operation. invokeDelegate(delegateToUse, request, response, filterChain);//最终调用实际你引入的filter的doFilter()方法 }在设置shiroFilter时候有一个属性叫targetFilterLifecycle,默认为false。此属性的作用是:是否启用引入filter的init()和destry()方法。代码如下:
/** * 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()) {//如果为true,则调用引入的filter的初始化方法,即shiroFilter的init()方法 delegate.init(getFilterConfig()); } return delegate; }时间有限,写的有点乱。