詳解Tomcat中Filter的執(zhí)行流程
前言
Filter是什么?Filter是servlet規(guī)范中定義的java web組件, 在所有支持java web的容器中都可以使用 它是位于前端請求到servlet之間的一系列過濾器,也可以稱之為中間件,它主要是對請求到達(dá)servlet之前做一些額外的動作:
- 1、權(quán)限控制
- 2、監(jiān)控
- 3、日志管理
- 4、等等
這里涉及到兩個接口:Filter和FilterChain
Filter和FilterChain密不可分, Filter可以實現(xiàn)依次調(diào)用正是因為有了FilterChain。
1、Filter接口
public interface Filter {
// 容器創(chuàng)建的時候調(diào)用, 即啟動tomcat的時候調(diào)用
public void init(FilterConfig filterConfig) throws ServletException;
// 由FilterChain調(diào)用, 并且傳入FilterChain本身, 最后回調(diào)FilterChain的doFilter()方法
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException;
// 容器銷毀的時候調(diào)用, 即關(guān)閉tomcat的時候調(diào)用
public void destroy();
}2、FilterChain接口
public interface FilterChain {
// 由Filter.doFilter()中的chain.doFilter調(diào)用
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException;
}執(zhí)行流程
在前面的文章中,我們知道,tomcat啟動會執(zhí)行StandardWrapperValve.java類的invoke方法:
public final void invoke(Request request, Response response){
......
MessageBytes requestPathMB = request.getRequestPathMB();
DispatcherType dispatcherType = DispatcherType.REQUEST;
if (request.getDispatcherType()==DispatcherType.ASYNC) dispatcherType = DispatcherType.ASYNC;
request.setAttribute(Globals.DISPATCHER_TYPE_ATTR,dispatcherType);
request.setAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR,
requestPathMB);
// 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
Container container = this.container;
try {
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());
}
}
}
} catch (ClientAbortException | CloseNowException e) {
}
......
}上面的代碼做了如下一些動作:
- 1、每次請求過來都會創(chuàng)建一個過濾器鏈(filterChain),并把待執(zhí)行的servlet對象存放到過濾器鏈中。對于每個url,對應(yīng)的filter個數(shù)都是不固定的,filterchain需要保存每個請求所對應(yīng)的一個filter數(shù)組,以及調(diào)用到的filter的position,以便繼續(xù)向下調(diào)用filter。
- 2、創(chuàng)建了filterChain之后,就開始執(zhí)行doFilter進(jìn)行請求的鏈?zhǔn)教幚怼?/li>
1、創(chuàng)建filterChain
下面我們具體來看看filterChain是怎么創(chuàng)建的
public static ApplicationFilterChain createFilterChain(ServletRequest request,
Wrapper wrapper, Servlet servlet) {
// If there is no servlet to execute, return null
if (servlet == null)
return null;
// Create and initialize a filter chain object
ApplicationFilterChain filterChain = null;
if (request instanceof Request) {
Request req = (Request) request;
if (Globals.IS_SECURITY_ENABLED) {
// Security: Do not recycle
filterChain = new ApplicationFilterChain();
} else {
filterChain = (ApplicationFilterChain) req.getFilterChain();
if (filterChain == null) {
filterChain = new ApplicationFilterChain();
req.setFilterChain(filterChain);
}
}
} else {
// Request dispatcher in use
filterChain = new ApplicationFilterChain();
}
filterChain.setServlet(servlet);
filterChain.setServletSupportsAsync(wrapper.isAsyncSupported());
// Acquire the filter mappings for this Context
StandardContext context = (StandardContext) wrapper.getParent();
FilterMap filterMaps[] = context.findFilterMaps();
// If there are no filter mappings, we are done
if ((filterMaps == null) || (filterMaps.length == 0))
return filterChain;
// Acquire the information we will need to match filter mappings
DispatcherType dispatcher =
(DispatcherType) request.getAttribute(Globals.DISPATCHER_TYPE_ATTR);
String requestPath = null;
Object attribute = request.getAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR);
if (attribute != null){
requestPath = attribute.toString();
}
String servletName = wrapper.getName();
// Add the relevant path-mapped filters to this filter chain
for (FilterMap filterMap : filterMaps) {
if (!matchDispatcher(filterMap, dispatcher)) {
continue;
}
if (!matchFiltersURL(filterMap, requestPath))
continue;
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
context.findFilterConfig(filterMap.getFilterName());
if (filterConfig == null) {
// FIXME - log configuration problem
continue;
}
filterChain.addFilter(filterConfig);
}
// Add filters that match on servlet name second
for (FilterMap filterMap : filterMaps) {
if (!matchDispatcher(filterMap, dispatcher)) {
continue;
}
if (!matchFiltersServlet(filterMap, servletName))
continue;
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
context.findFilterConfig(filterMap.getFilterName());
if (filterConfig == null) {
// FIXME - log configuration problem
continue;
}
filterChain.addFilter(filterConfig);
}
// Return the completed filter chain
return filterChain;
}上面的代碼做了一下幾件事:
- 1、把要執(zhí)行的servlet存放到過濾器鏈中。
- 2、如果沒有配置過濾器則return一個空的過濾器鏈(只包含上面設(shè)置的servlet)。
- 3、如果配置url-pattern過濾器,則把匹配的過濾器加入到過濾器鏈中
- 4、如果配置servlet-name過濾器,則把匹配的過濾器加入到過濾器鏈中
注意: filterChain.addFilter()順序與web.xml中定義的Filter順序一致,所以過濾器的執(zhí)行順序是按定義的上下順序決定的。
2、執(zhí)行dofilter
創(chuàng)建了chain之后,就開始執(zhí)行鏈?zhǔn)秸埱罅?,具體的邏輯如下:
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);
}
}
}上面的代碼邏輯如下:
- 1、通過position索引判斷是否執(zhí)行完了所有的filter
- 2、如果沒有,取出當(dāng)前待執(zhí)行的索引filter,調(diào)用其doFilter方法,在上面的接口說明中,我們看到,所有的filter類都繼承了filter接口,都實現(xiàn)了dofilter方法;我們也注意到,該方法接收一個filterChain對象。在這段代碼中,
filter.doFilter(request, response, this);可以看到,將自身引用傳遞進(jìn)去了,那么各個filter在dofilter的方法中,可以根據(jù)自身業(yè)務(wù)需要,來判斷是否需要繼續(xù)進(jìn)行下面的filter鏈?zhǔn)綀?zhí)行,如果需要,就執(zhí)行filterChain.doFilter方法,此時就又回到了此代碼中。如果反復(fù) - 3、如果執(zhí)行完了所有的filter,則開始執(zhí)行servlet業(yè)務(wù)模塊
servlet.service(request, response);
以上就是詳解Tomcat中Filter是怎樣執(zhí)行的的詳細(xì)內(nèi)容,更多關(guān)于Tomcat Filter執(zhí)行的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
tomcat正常啟動但網(wǎng)頁卻無法訪問的幾種解決方法
本文主要介紹了tomcat正常啟動但網(wǎng)頁卻無法訪問的幾種解決方法,根據(jù)自身使用情況和一些網(wǎng)上搜索到的結(jié)果,匯總整理一下,具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-05-05
如何在IntelliJ IDEA 2018上配置Tomcat并運行第一個JavaWeb項目
這篇文章主要介紹了在IntelliJ IDEA 2018上配置Tomcat并運行第一個JavaWeb項目,需要的朋友可以參考下2020-09-09

