在web容器中, 最先加載的是 Listener, 其次是 Filter 最后是 Servlet.
Filter是一個(gè)特殊的 Servlet, 其生命周期分為以下三部分:
- init(FilterConfig filterConfig) // 為Filter初始化 提供支持
- doFilter(ServletRequest request, ServletResponse response, FilterChain chain) //這個(gè)是 Filter的核心方法被攔截請(qǐng)求在這里被處理
- destroy() // Filter 實(shí)例銷毀前的準(zhǔn)備工作 //Called by the web container to indicate to a filter that it is being taken out of service.
通常我們將自定義的Filter配置在web容器的web.xml中, 大致分為兩個(gè)部分來配置這個(gè)Filter.
//聲明filter
<filter>
<filter-name>filter-name</filter-name>
<filter-class>com.xx.FilterClass</filter-class>
//初始化參數(shù)
<init-param>
<param-name>key</param-name>
<param-value>value</param-value>
</init-param>
</filter>
//filter 攔截規(guī)則
<filter-mapping>
<filter-mapping>
<filter-name>filter-name</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
和servlet不同的是, 如果配置多個(gè)filter, 則其聲明的先后順序是區(qū)別的, servlet在匹配到后流程至此終止, 而 filter不一定, 他是可以向下傳遞下去進(jìn)入下一個(gè)filter. 這里能夠使 Filter形成鏈?zhǔn)秸{(diào)用關(guān)鍵點(diǎn)在于 doFilter方法參數(shù)FilterChain {doFilter(ServletRequest request, ServletResponse response, FilterChain chain)}
FilterChain 是一個(gè)接口, 他只有一個(gè)方法 doFilter(ServletRequest var1, ServletResponse var2). 在我們希望將 #request和 #response傳遞給下一個(gè) Filter處理的時(shí)候就會(huì)使用 FilterChain. 如:
filterChain.doFilter(request, response) ;
這樣就會(huì)進(jìn)入下一個(gè)Filter的處理邏輯. 如此形成有名的Filter職責(zé)鏈. 類似鏈表結(jié)構(gòu). 鏈表是持有自己的引用, 但是FilterChain不是, 他有點(diǎn)類似代理和鏈表的結(jié)合體. 經(jīng)過查看 tomcat源碼, 才知道, 這個(gè)職責(zé)鏈?zhǔn)怯蓋eb容器管理的. 不同的容器實(shí)現(xiàn)FilterChain接口方法 #doFilter 就可以了. 例如在tomcat中, 實(shí)現(xiàn)類叫ApplicationFilterChain 這個(gè)類包含我們聲明的全部Filter, 并且有序, 然后記錄當(dāng)前Filter在Filter鏈中的索引值, 獲取該索引值的Filter然后調(diào)用doFilter同時(shí)將自己傳給這個(gè)Filter, 代碼如下:
filter.doFilter(request, response, this);
如果全部Filter執(zhí)行完畢, 則將流程交給對(duì)應(yīng)的servlet實(shí)例執(zhí)行具體的業(yè)務(wù).
servlet.service(request, response);
至此全部流程完畢.
我接觸到的這種職責(zé)鏈模式很多地方有使用, spring#CompositeFilter就是職責(zé)鏈很經(jīng)典的使用而且也重新實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的職責(zé)鏈管理器. 在 spring-web模塊中可以看見:
private static class VirtualFilterChain implements FilterChain {
private final FilterChain originalChain;
private final List<? extends Filter> additionalFilters;
private int currentPosition = 0;
public VirtualFilterChain(FilterChain chain, List<? extends Filter> additionalFilters) {
this.originalChain = chain;
this.additionalFilters = additionalFilters;
}
@Override
public void doFilter(final ServletRequest request, final ServletResponse response)
throws IOException, ServletException {
if (this.currentPosition == this.additionalFilters.size()) {
this.originalChain.doFilter(request, response);
}
else {
this.currentPosition++;
Filter nextFilter = this.additionalFilters.get(this.currentPosition - 1);
nextFilter.doFilter(request, response, this);
}
}
}
根據(jù)描述, CompositeFilter通常需要結(jié)合 FIlter代理類#DelegatingFilterProxy
/**
* A generic composite servlet {@link Filter} that just delegates its behavior
* to a chain (list) of user-supplied filters, achieving the functionality of a
* {@link FilterChain}, but conveniently using only {@link Filter} instances.
*
* <p>This is useful for filters that require dependency injection, and can
* therefore be set up in a Spring application context. Typically, this
* composite would be used in conjunction with {@link DelegatingFilterProxy},
* so that it can be declared in Spring but applied to a servlet context.
*
* @author Dave Syer
* @since 3.1
*/
這個(gè)類我們配置在web.xml文件中, 作為對(duì)外唯一可見的一個(gè)Filter. 如:
<filter>
<filter-name>compositeFilter</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>compositeFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
查看 DelegatingFilterProxy源碼, DelegatingFilterProxy會(huì)在spring容器bean初始化完成之后初始化被代理的Filter.
public class DelegatingFilterProxy extends GenericFilterBean{
@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);
}
}
}
}
}
public abstract class GenericFilterBean implements
Filter, BeanNameAware, EnvironmentAware, ServletContextAware, InitializingBean, DisposableBean {
/**
* Calls the {@code initFilterBean()} method that might
* contain custom initialization of a subclass.
* <p>Only relevant in case of initialization as bean, where the
* standard {@code init(FilterConfig)} method won't be called.
* @see #initFilterBean()
* @see #init(javax.servlet.FilterConfig)
*/
@Override
public void afterPropertiesSet() throws ServletException {
initFilterBean(); //這是一個(gè)空方法, 需要子類實(shí)現(xiàn)
}
}
這樣就形成了Filter代理 和Filter職責(zé)鏈.
使用DelegatingFilterProxy的目的多用于我們?cè)谧远x業(yè)務(wù)FIlter的時(shí)候常常需要依賴spring管理的bean. 但是因?yàn)樵趙eb容器中, Filter首先被加載, 這之后spring容器才會(huì)被加載(這里還沒有代碼驗(yàn)證過),所以如果在Filter中依賴spring管理bean則會(huì)出現(xiàn)空指針引用, 無法使用. 當(dāng)使用DelegatingFilterProxy后, 這種情況就改變了. 根據(jù)這個(gè)類的實(shí)現(xiàn). 容器加載DelegatingFilterProxy這個(gè)Filter后, 會(huì)等待spring容器初始化全部bean完成后#afterPropertiesSet方法被調(diào)用的時(shí)候才會(huì)初始化DelegatingFilterProxy這個(gè)Filter代理的全部Filter, 這時(shí)候被代理Filter就能夠拿到spring容器里的全部bean.
上面的說法, 我覺得不對(duì), DelegatingFilterProxy 首先可以將多個(gè)Filter串聯(lián)在一起, 多個(gè)Filter可以以Spring Bean的形式的定義在spring配置文件中, 而不用全部聲明到web.xml文件中. 然后這些Filter初始化都委托給DelegatingFilterProxy. 我覺得這是這個(gè)類本來的職責(zé).
按照web容器規(guī)范, Listener/FIlter/Servlet他們?cè)趙eb容器中的的初始化順序是 Listener -> FIlter -> Servlet ; 如果引入spring框架, 一般, 我們都是使用 Spring#ContextLoaderListener來加載spring以及初始化Spring IOC容器, 所以 這個(gè)先后順序沒有問題, 只是Filter中如果依賴了spring bean拿不到bean. 無法注入, 因?yàn)閣eb.xml中無法注入spring bean, Filter也拿不到ApplicationContext, 所以沒有辦法拿到spring#bean, 只有把 Filter也納入spring管理這樣才可以拿到spring bean.
注意: 以下配置節(jié)點(diǎn)targetFilterLifecycle
<filter>
<filter-name>compositeFilter</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>
targetFilterLifecycle為 true代表spring管理filter的初始化, 即 init和 destroy方法在適當(dāng)?shù)臅r(shí)候會(huì)被spring調(diào)用. 如果為false則這兩個(gè)方法無效.具體代碼如下:
/**
* 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;
}
/**
* 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();
}
}