Servlet過濾器原始碼分析
1.概述
Servlet過濾器是服務端和客戶端請求與響應的中間層元件,可以進行過濾請求,處理完畢後再轉給下一個資源。
在實際場景中,可能需要多個過濾器。即採用了過濾器鏈的方式來組織,如圖所示。
2. 原始碼相關
先看幾個主要的原始碼類
Filter
public interface Filter {
public void init(FilterConfig filterConfig) throws ServletException;
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException;
public void destroy();
}
FilterChain(攔截器鏈)
public interface FilterChain {
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException;
}
ApplicationFilterConfig
public final class ApplicationFilterConfig implements FilterConfig, Serializable {
}
本篇主要回答幾個問題:
1)過濾器鏈元素如何新增?
在ApplicationFilterChain類(FilterChain的實現類)中的addFilter()中,如下。
ApplicationFilterChain
/**
* Filters.
*/
private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0];
void addFilter(ApplicationFilterConfig filterConfig) {
// Prevent the same filter being added multiple times
for(ApplicationFilterConfig filter:filters)
if(filter==filterConfig)
return;
if (n == filters.length) {
ApplicationFilterConfig[] newFilters =
new ApplicationFilterConfig[n + INCREMENT];
System.arraycopy(filters, 0, newFilters, 0, n);
filters = newFilters;
}
filters[n++] = filterConfig;
}
2)過濾器鏈如何依次執行?
基本邏輯:
當過濾器沒有執行完時,依次執行過濾器。(pos<n)
若執行完過濾器內容,則呼叫servlet.service(),放行。
final class ApplicationFilterChain implements FilterChain {
@Override
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
if( Globals.IS_SECURITY_ENABLED ) {
final ServletRequest req = request;
final ServletResponse res = response;
try {
java.security.AccessController.doPrivileged(
new java.security.PrivilegedExceptionAction<Void>() {
@Override
public Void run()
throws ServletException, IOException {
internalDoFilter(req,res);
return null;
}
}
);
} catch( PrivilegedActionException pe) {
Exception e = pe.getException();
if (e instanceof ServletException)
throw (ServletException) e;
else if (e instanceof IOException)
throw (IOException) e;
else if (e instanceof RuntimeException)
throw (RuntimeException) e;
else
throw new ServletException(e.getMessage(), e);
}
} else {
internalDoFilter(request,response);
}
}
private void internalDoFilter(ServletRequest request,
ServletResponse response)
throws IOException, ServletException {
// Call the next filter if there is one
if (pos < n) {
ApplicationFilterConfig filterConfig = filters[pos++];
try {
Filter filter = filterConfig.getFilter();
if (request.isAsyncSupported() && "false".equalsIgnoreCase(
filterConfig.getFilterDef().getAsyncSupported())) {
request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE);
}
if( Globals.IS_SECURITY_ENABLED ) {
final ServletRequest req = request;
final ServletResponse res = response;
Principal principal =
((HttpServletRequest) req).getUserPrincipal();
Object[] args = new Object[]{req, res, this};
SecurityUtil.doAsPrivilege ("doFilter", filter, classType, args, principal);
} else {
filter.doFilter(request, response, this);
}
} catch (IOException | ServletException | RuntimeException e) {
throw e;
} catch (Throwable e) {
e = ExceptionUtils.unwrapInvocationTargetException(e);
ExceptionUtils.handleThrowable(e);
throw new ServletException(sm.getString("filterChain.filter"), e);
}
return;
}
// We fell off the end of the chain -- call the servlet instance
try {
if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
lastServicedRequest.set(request);
lastServicedResponse.set(response);
}
if (request.isAsyncSupported() && !servletSupportsAsync) {
request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR,
Boolean.FALSE);
}
// Use potentially wrapped request from this point
if ((request instanceof HttpServletRequest) &&
(response instanceof HttpServletResponse) &&
Globals.IS_SECURITY_ENABLED ) {
final ServletRequest req = request;
final ServletResponse res = response;
Principal principal =
((HttpServletRequest) req).getUserPrincipal();
Object[] args = new Object[]{req, res};
SecurityUtil.doAsPrivilege("service",
servlet,
classTypeUsedInService,
args,
principal);
} else {
servlet.service(request, response);
}
} catch (IOException | ServletException | RuntimeException e) {
throw e;
} catch (Throwable e) {
e = ExceptionUtils.unwrapInvocationTargetException(e);
ExceptionUtils.handleThrowable(e);
throw new ServletException(sm.getString("filterChain.servlet"), e);
} finally {
if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
lastServicedRequest.set(null);
lastServicedResponse.set(null);
}
}
}
}
示例:
兩個過濾器FilterA、FilterB。
a.兩個過濾器都實現了Filter介面,通過addFilter()新增到攔截器鏈中。
b.依次執行過濾器的doFilter()
從過濾器鏈中獲取當前的過濾器
ApplicationFilterConfig filterConfig = filters[pos++];
’
執行過濾器的doFilter() ,並將攔截器鏈作為引數傳入,即此處的this。這裡以FilterA為例。
filter.doFilter(request, response, this);
FilterA方法執行完畢後,呼叫chain的doFilter。【函式回撥】
chain.doFilter(request,response);
再次回到攔截器鏈中,獲取當前的過濾器。
ApplicationFilterConfig filterConfig = filters[pos++];
拿到FilterB,迴圈以上步驟。
3) Tomcat與過濾器之間的呼叫?
在第2點中說到了,FilterChain的doFilter(),那他是被誰呼叫的呢?這要說到過濾器是Servlet的,即Servlet容器。這裡以Tomcat為例。
StandardWrapperValue
final class StandardWrapperValve extends ValveBase {
public final void invoke(Request request, Response response)
throws IOException, ServletException {
......
// Create the filter chain for this request
ApplicationFilterChain filterChain =
ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
// Call the filter chain for this request
// NOTE: This also calls the servlet's service() method
if ((servlet != null) && (filterChain != null)) {
// Swallow output if needed
if (context.getSwallowOutput()) {
try {
SystemLogHandler.startCapture();
if (request.isAsyncDispatching()) {
request.getAsyncContextInternal().doInternalDispatch();
} else {
filterChain.doFilter(request.getRequest(),
response.getResponse());
}
} finally {
String log = SystemLogHandler.stopCapture();
if (log != null && log.length() > 0) {
context.getLogger().info(log);
}
}
} else {
if (request.isAsyncDispatching()) {
request.getAsyncContextInternal().doInternalDispatch();
} else {
filterChain.doFilter
(request.getRequest(), response.getResponse());
}
}
}
......
}
}
tomcat的Container是如何處理請求的?
通過Pipeline-Value管道。
依然是使用責任鏈模式,但是Pipeline-Valve使用的責任鏈模式和普通的責任鏈模式有些不同!區別主要有以下兩點:
(1)每個Pipeline都有特定的Valve,而且是在管道的最後一個執行,這個Valve叫做BaseValve,BaseValve是不可刪除的;
(2)在上層容器的管道的BaseValve中會呼叫下層容器的管道。
Container包含四個子容器,而這四個子容器對應的BaseValve分別在:StandardEngineValve、StandardHostValve、StandardContextValve、StandardWrapperValve。
當執行到StandardWrapperValve的時候,會在StandardWrapperValve中建立FilterChain,並呼叫其doFilter方法來處理請求,這個FilterChain包含著我們配置的與請求相匹配的Filter和Servlet,其doFilter方法會依次呼叫所有的Filter的doFilter方法和Servlet的service方法,這樣請求就得到了處理!
3.小結
在專案中,應用過濾器的地方會比較少,更多的是用攔截器。過濾器是Servlet容器中的,還未到HTTPServletRequest中。因此無法獲取到請求的細節引數,相對攔截器來說,靈活性差一些。當然,這也是他的設計初衷決定的。在簡單處理請求響應時候,過濾器完全可以勝任。
發現問題,質疑,找到答案。然後繼續發現問題,解決問題。這是個很有意思的過程。
相關文章
- 過濾Servlet--過濾器Servlet過濾器
- Servlet過濾器Servlet過濾器
- Servlet過濾器介紹之原理分析薦Servlet過濾器
- servlet的過濾器filter類Servlet過濾器Filter
- DRF之過濾類原始碼分析原始碼
- Vue原始碼閱讀--過濾器Vue原始碼過濾器
- Spring Security系列之核心過濾器原始碼分析(四)Spring過濾器原始碼
- Servlet 2.3過濾器程式設計 (轉)Servlet過濾器程式設計
- Servlet 2.3過濾器程式設計(二) (轉)Servlet過濾器程式設計
- Vue initAssetRegisters()建立元件、指令、過濾器原始碼Vue元件過濾器原始碼
- Fabric 1.0原始碼分析(11)consenter(共識外掛) #filter(過濾器)原始碼Filter過濾器
- 原始碼分析SpringCloud Gateway如何載入斷言(predicates)與過濾器(filters)原始碼SpringGCCloudGateway過濾器Filter
- tomcat原始碼 -- servlet呼叫過程Tomcat原始碼Servlet
- 過濾器應用【編碼、敏感詞、壓縮、轉義過濾器】過濾器
- Spring Security 核心過濾器鏈分析Spring過濾器
- 【過濾器】web中過濾器的使用與亂碼問題解決過濾器Web
- 過濾器過濾器
- jetty、servlet以及spring的銜接原始碼分析JettyServletSpring原始碼
- mvc原始碼解讀(11)-mvc四大過濾器之AuthorizationFilterMVC原始碼過濾器Filter
- mvc原始碼解讀(12)-mvc四大過濾器之ActionFilterMVC原始碼過濾器Filter
- mvc原始碼解讀(13)-MVC四大過濾器之ResultFilterMVC原始碼過濾器Filter
- mvc原始碼解讀(14)-mvc四大過濾器之ExceptionFilterMVC原始碼過濾器ExceptionFilter
- Redis布隆過濾器分析與總結Redis過濾器
- 4、過濾器的使用及自定義過濾器過濾器
- 點雲濾波器與過濾器過濾器
- Java Servlet (1) —— Filter過濾請求與響應JavaServletFilter
- 今日完善了字元編碼過濾器。字元過濾器
- 代理過濾器過濾器
- vue 過濾器Vue過濾器
- Filter過濾器Filter過濾器
- hbase過濾器過濾器
- CAN過濾器過濾器
- 26、過濾器過濾器
- jms過濾器過濾器
- DataV過濾器過濾器
- Vue過濾器Vue過濾器
- Xor過濾器:比布隆Bloom過濾器更快,更小過濾器OOM
- springBoot 過濾器去除請求引數前後空格(附原始碼)Spring Boot過濾器原始碼