Tomcat源码分析 (九)----- HTTP请求处理过程(二)
我們接著上一篇文章的容器處理來講,當(dāng)postParseRequest方法返回true時,則由容器繼續(xù)處理,在service方法中有connector.getService().getContainer().getPipeline().getFirst().invoke(request, response)這一行:
- Connector調(diào)用getService()返回StandardService;
- StandardService調(diào)用getContainer返回StandardEngine;
- StandardEngine調(diào)用getPipeline返回與其關(guān)聯(lián)的StandardPipeline;
Engine處理請求
我們在前面的文章中講過StandardEngine的構(gòu)造函數(shù)為自己的Pipeline添加了基本閥StandardEngineValve,代碼如下:
public StandardEngine() {
super();
pipeline.setBasic(new StandardEngineValve());
try {
setJvmRoute(System.getProperty("jvmRoute"));
} catch(Exception ex) {
log.warn(sm.getString("standardEngine.jvmRouteFail"));
}
}
接下來我們看看StandardEngineValve的invoke()方法。該方法主要是選擇合適的Host,然后調(diào)用Host中pipeline的第一個Valve的invoke()方法。
public final void invoke(Request request, Response response)
throws IOException, ServletException { // Select the Host to be used for this Request
Host host = request.getHost();
if (host == null) {
response.sendError
(HttpServletResponse.SC_BAD_REQUEST,
sm.getString("standardEngine.noHost",
request.getServerName()));
return;
}
if (request.isAsyncSupported()) {
request.setAsyncSupported(host.getPipeline().isAsyncSupported());
} // Ask this Host to process this request
host.getPipeline().getFirst().invoke(request, response);
}
host.getPipeline().getFirst().invoke(request, response),可以看到 Host 容器先獲取自己的管道,再獲取第一個閥門,我們再看看該閥門的 invoke 方法。Host處理請求
分析Host的時候,我們從Host的構(gòu)造函數(shù)入手,該方法主要是設(shè)置基礎(chǔ)閥門。
public StandardHost() {
super();
pipeline.setBasic(new StandardHostValve());
}
StandardPipeline調(diào)用getFirst得到第一個閥去處理請求,由于基本閥是最后一個,所以最后會由基本閥去處理請求。
StandardHost的Pipeline里面一定有 ErrorReportValve 與 StandardHostValve兩個Valve,ErrorReportValve主要是檢測 Http 請求過程中是否出現(xiàn)過什么異常, 有異常的話, 直接拼裝 html 頁面, 輸出到客戶端。
我們看看ErrorReportValve的invoke方法:
public void invoke(Request request, Response response)
throws IOException, ServletException {
// Perform the request
// 1. 先將 請求轉(zhuǎn)發(fā)給下一個 Valve
getNext().invoke(request, response);
// 2. 這里的 isCommitted 表明, 請求是正常處理結(jié)束
if (response.isCommitted()) {
return;
}
// 3. 判斷請求過程中是否有異常發(fā)生
Throwable throwable = (Throwable) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);
if (request.isAsyncStarted() && ((response.getStatus() < 400 &&
throwable == null) || request.isAsyncDispatching())) {
return;
}
if (throwable != null) {
// The response is an error
response.setError();
// Reset the response (if possible)
try {
// 4. 重置 response 里面的數(shù)據(jù)(此時 Response 里面可能有些數(shù)據(jù))
response.reset();
} catch (IllegalStateException e) {
// Ignore
}
// 5. 這就是我們??吹降?500 錯誤碼
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
response.setSuspended(false);
try {
// 6. 這里就是將 異常的堆棧信息組合成 html 頁面, 輸出到前臺
report(request, response, throwable);
} catch (Throwable tt) {
ExceptionUtils.handleThrowable(tt);
}
if (request.isAsyncStarted()) {
// 7. 若是異步請求的話, 設(shè)置對應(yīng)的 complete (對應(yīng)的是 異步 Servlet)
request.getAsyncContext().complete();
}
}
該方法首先執(zhí)行了下個閥門的 invoke 方法。然后根據(jù)返回的Request 屬性設(shè)置一些錯誤信息。那么下個閥門是誰呢?其實就是基礎(chǔ)閥門了:StandardHostValve,該閥門的 invoke 的方法是如何實現(xiàn)的呢?
@Override
public final void invoke(Request request, Response response)
throws IOException, ServletException { // Select the Context to be used for this Request
Context context = request.getContext();
if (context == null) {
response.sendError
(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
sm.getString("standardHost.noContext"));
return;
} // Bind the context CL to the current thread
if( context.getLoader() != null ) {
// Not started - it should check for availability first
// This should eventually move to Engine, it's generic.
if (Globals.IS_SECURITY_ENABLED) {
PrivilegedAction<Void> pa = new PrivilegedSetTccl(
context.getLoader().getClassLoader());
AccessController.doPrivileged(pa);
} else {
Thread.currentThread().setContextClassLoader
(context.getLoader().getClassLoader());
}
}
if (request.isAsyncSupported()) {
request.setAsyncSupported(context.getPipeline().isAsyncSupported());
} // Don't fire listeners during async processing
// If a request init listener throws an exception, the request is
// aborted
boolean asyncAtStart = request.isAsync();
// An async error page may dispatch to another resource. This flag helps
// ensure an infinite error handling loop is not entered
boolean errorAtStart = response.isError();
if (asyncAtStart || context.fireRequestInitEvent(request)) { // Ask this Context to process this request
try {
context.getPipeline().getFirst().invoke(request, response);
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
if (errorAtStart) {
container.getLogger().error("Exception Processing " +
request.getRequestURI(), t);
} else {
request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, t);
throwable(request, response, t);
}
} // If the request was async at the start and an error occurred then
// the async error handling will kick-in and that will fire the
// request destroyed event *after* the error handling has taken
// place
if (!(request.isAsync() || (asyncAtStart &&
request.getAttribute(
RequestDispatcher.ERROR_EXCEPTION) != null))) {
// Protect against NPEs if context was destroyed during a
// long running request.
if (context.getState().isAvailable()) {
if (!errorAtStart) {
// Error page processing
response.setSuspended(false); Throwable t = (Throwable) request.getAttribute(
RequestDispatcher.ERROR_EXCEPTION); if (t != null) {
throwable(request, response, t);
} else {
status(request, response);
}
} context.fireRequestDestroyEvent(request);
}
}
} // Access a session (if present) to update last accessed time, based on a
// strict interpretation of the specification
if (ACCESS_SESSION) {
request.getSession(false);
} // Restore the context classloader
if (Globals.IS_SECURITY_ENABLED) {
PrivilegedAction<Void> pa = new PrivilegedSetTccl(
StandardHostValve.class.getClassLoader());
AccessController.doPrivileged(pa);
} else {
Thread.currentThread().setContextClassLoader
(StandardHostValve.class.getClassLoader());
}
}
Context處理請求
接著Context會去處理請求,同理,StandardContextValve的invoke方法會被調(diào)用:
@Override
public final void invoke(Request request, Response response)
throws IOException, ServletException {
// Disallow any direct access to resources under WEB-INF or META-INF
MessageBytes requestPathMB = request.getRequestPathMB();
if ((requestPathMB.startsWithIgnoreCase("/META-INF/", 0))
|| (requestPathMB.equalsIgnoreCase("/META-INF"))
|| (requestPathMB.startsWithIgnoreCase("/WEB-INF/", 0))
|| (requestPathMB.equalsIgnoreCase("/WEB-INF"))) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
} // Select the Wrapper to be used for this Request
Wrapper wrapper = request.getWrapper();
if (wrapper == null || wrapper.isUnavailable()) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
} // Acknowledge the request
try {
response.sendAcknowledgement();
} catch (IOException ioe) {
container.getLogger().error(sm.getString(
"standardContextValve.acknowledgeException"), ioe);
request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, ioe);
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
return;
} if (request.isAsyncSupported()) {
request.setAsyncSupported(wrapper.getPipeline().isAsyncSupported());
}
wrapper.getPipeline().getFirst().invoke(request, response);
}
Wrapper處理請求
Wrapper是一個Servlet的包裝,我們先來看看構(gòu)造方法。主要作用就是設(shè)置基礎(chǔ)閥門StandardWrapperValve。
public StandardWrapper() {
super();
swValve=new StandardWrapperValve();
pipeline.setBasic(swValve);
broadcaster = new NotificationBroadcasterSupport();
}
接下來我們看看StandardWrapperValve的invoke()方法。
@Override
public final void invoke(Request request, Response response)
throws IOException, ServletException { // Initialize local variables we may need
boolean unavailable = false;
Throwable throwable = null;
// This should be a Request attribute...
long t1=System.currentTimeMillis();
requestCount.incrementAndGet();
StandardWrapper wrapper = (StandardWrapper) getContainer();
Servlet servlet = null;
Context context = (Context) wrapper.getParent(); // Check for the application being marked unavailable
if (!context.getState().isAvailable()) {
response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
sm.getString("standardContext.isUnavailable"));
unavailable = true;
} // Check for the servlet being marked unavailable
if (!unavailable && wrapper.isUnavailable()) {
container.getLogger().info(sm.getString("standardWrapper.isUnavailable",
wrapper.getName()));
long available = wrapper.getAvailable();
if ((available > 0L) && (available < Long.MAX_VALUE)) {
response.setDateHeader("Retry-After", available);
response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
sm.getString("standardWrapper.isUnavailable",
wrapper.getName()));
} else if (available == Long.MAX_VALUE) {
response.sendError(HttpServletResponse.SC_NOT_FOUND,
sm.getString("standardWrapper.notFound",
wrapper.getName()));
}
unavailable = true;
} // Allocate a servlet instance to process this request
try {
// 關(guān)鍵點(diǎn)1:這兒調(diào)用Wrapper的allocate()方法分配一個Servlet實例
if (!unavailable) {
servlet = wrapper.allocate();
}
} catch (UnavailableException e) {
container.getLogger().error(
sm.getString("standardWrapper.allocateException",
wrapper.getName()), e);
long available = wrapper.getAvailable();
if ((available > 0L) && (available < Long.MAX_VALUE)) {
response.setDateHeader("Retry-After", available);
response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
sm.getString("standardWrapper.isUnavailable",
wrapper.getName()));
} else if (available == Long.MAX_VALUE) {
response.sendError(HttpServletResponse.SC_NOT_FOUND,
sm.getString("standardWrapper.notFound",
wrapper.getName()));
}
} catch (ServletException e) {
container.getLogger().error(sm.getString("standardWrapper.allocateException",
wrapper.getName()), StandardWrapper.getRootCause(e));
throwable = e;
exception(request, response, e);
} catch (Throwable e) {
ExceptionUtils.handleThrowable(e);
container.getLogger().error(sm.getString("standardWrapper.allocateException",
wrapper.getName()), e);
throwable = e;
exception(request, response, e);
servlet = null;
} 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
// 關(guān)鍵點(diǎn)2,創(chuàng)建過濾器鏈,類似于Pipeline的功能
ApplicationFilterChain filterChain =
ApplicationFilterFactory.createFilterChain(request, wrapper, servlet); // Call the filter chain for this request
// NOTE: This also calls the servlet's service() method
try {
if ((servlet != null) && (filterChain != null)) {
// Swallow output if needed
if (context.getSwallowOutput()) {
try {
SystemLogHandler.startCapture();
if (request.isAsyncDispatching()) {
request.getAsyncContextInternal().doInternalDispatch();
} else {
// 關(guān)鍵點(diǎn)3,調(diào)用過濾器鏈的doFilter,最終會調(diào)用到Servlet的service方法
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 {
// 關(guān)鍵點(diǎn)3,調(diào)用過濾器鏈的doFilter,最終會調(diào)用到Servlet的service方法
filterChain.doFilter
(request.getRequest(), response.getResponse());
}
} }
} catch (ClientAbortException e) {
throwable = e;
exception(request, response, e);
} catch (IOException e) {
container.getLogger().error(sm.getString(
"standardWrapper.serviceException", wrapper.getName(),
context.getName()), e);
throwable = e;
exception(request, response, e);
} catch (UnavailableException e) {
container.getLogger().error(sm.getString(
"standardWrapper.serviceException", wrapper.getName(),
context.getName()), e);
// throwable = e;
// exception(request, response, e);
wrapper.unavailable(e);
long available = wrapper.getAvailable();
if ((available > 0L) && (available < Long.MAX_VALUE)) {
response.setDateHeader("Retry-After", available);
response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
sm.getString("standardWrapper.isUnavailable",
wrapper.getName()));
} else if (available == Long.MAX_VALUE) {
response.sendError(HttpServletResponse.SC_NOT_FOUND,
sm.getString("standardWrapper.notFound",
wrapper.getName()));
}
// Do not save exception in 'throwable', because we
// do not want to do exception(request, response, e) processing
} catch (ServletException e) {
Throwable rootCause = StandardWrapper.getRootCause(e);
if (!(rootCause instanceof ClientAbortException)) {
container.getLogger().error(sm.getString(
"standardWrapper.serviceExceptionRoot",
wrapper.getName(), context.getName(), e.getMessage()),
rootCause);
}
throwable = e;
exception(request, response, e);
} catch (Throwable e) {
ExceptionUtils.handleThrowable(e);
container.getLogger().error(sm.getString(
"standardWrapper.serviceException", wrapper.getName(),
context.getName()), e);
throwable = e;
exception(request, response, e);
} // Release the filter chain (if any) for this request
// 關(guān)鍵點(diǎn)4,釋放掉過濾器鏈及其相關(guān)資源
if (filterChain != null) {
filterChain.release();
} // 關(guān)鍵點(diǎn)5,釋放掉Servlet及相關(guān)資源
// Deallocate the allocated servlet instance
try {
if (servlet != null) {
wrapper.deallocate(servlet);
}
} catch (Throwable e) {
ExceptionUtils.handleThrowable(e);
container.getLogger().error(sm.getString("standardWrapper.deallocateException",
wrapper.getName()), e);
if (throwable == null) {
throwable = e;
exception(request, response, e);
}
} // If this servlet has been marked permanently unavailable,
// unload it and release this instance
// 關(guān)鍵點(diǎn)6,如果servlet被標(biāo)記為永遠(yuǎn)不可達(dá),則需要卸載掉它,并釋放這個servlet實例
try {
if ((servlet != null) &&
(wrapper.getAvailable() == Long.MAX_VALUE)) {
wrapper.unload();
}
} catch (Throwable e) {
ExceptionUtils.handleThrowable(e);
container.getLogger().error(sm.getString("standardWrapper.unloadException",
wrapper.getName()), e);
if (throwable == null) {
throwable = e;
exception(request, response, e);
}
}
long t2=System.currentTimeMillis(); long time=t2-t1;
processingTime += time;
if( time > maxTime) maxTime=time;
if( time < minTime) minTime=time;
}
通過閱讀源碼,我們發(fā)現(xiàn)了幾個關(guān)鍵點(diǎn)?,F(xiàn)羅列如下,后面我們會逐一分析這些關(guān)鍵點(diǎn)相關(guān)的源碼。
- 關(guān)鍵點(diǎn)1:這兒調(diào)用Wrapper的allocate()方法分配一個Servlet實例
- 關(guān)鍵點(diǎn)2,創(chuàng)建過濾器鏈,類似于Pipeline的功能
- 關(guān)鍵點(diǎn)3,調(diào)用過濾器鏈的doFilter,最終會調(diào)用到Servlet的service方法
- 關(guān)鍵點(diǎn)4,釋放掉過濾器鏈及其相關(guān)資源
- 關(guān)鍵點(diǎn)5,釋放掉Servlet及相關(guān)資源
- 關(guān)鍵點(diǎn)6,如果servlet被標(biāo)記為永遠(yuǎn)不可達(dá),則需要卸載掉它,并釋放這個servlet實例
關(guān)鍵點(diǎn)1 - Wrapper分配Servlet實例
我們來分析一下Wrapper.allocate()方法
@Override
public Servlet allocate() throws ServletException { // If we are currently unloading this servlet, throw an exception
// 卸載過程中,不能分配Servlet
if (unloading) {
throw new ServletException(sm.getString("standardWrapper.unloading", getName()));
} boolean newInstance = false; // If not SingleThreadedModel, return the same instance every time
// 如果Wrapper沒有實現(xiàn)SingleThreadedModel,則每次都會返回同一個Servlet
if (!singleThreadModel) {
// Load and initialize our instance if necessary
// 實例為null或者實例還未初始化,使用synchronized來保證并發(fā)時的原子性
if (instance == null || !instanceInitialized) {
synchronized (this) {
if (instance == null) {
try {
if (log.isDebugEnabled()) {
log.debug("Allocating non-STM instance");
} // Note: We don't know if the Servlet implements
// SingleThreadModel until we have loaded it.
// 加載Servlet
instance = loadServlet();
newInstance = true;
if (!singleThreadModel) {
// For non-STM, increment here to prevent a race
// condition with unload. Bug 43683, test case
// #3
countAllocated.incrementAndGet();
}
} catch (ServletException e) {
throw e;
} catch (Throwable e) {
ExceptionUtils.handleThrowable(e);
throw new ServletException(sm.getString("standardWrapper.allocate"), e);
}
}
// 初始化Servlet
if (!instanceInitialized) {
initServlet(instance);
}
}
} if (singleThreadModel) {
if (newInstance) {
// Have to do this outside of the sync above to prevent a
// possible deadlock
synchronized (instancePool) {
instancePool.push(instance);
nInstances++;
}
}
}
// 非單線程模型,直接返回已經(jīng)創(chuàng)建的Servlet,也就是說,這種情況下只會創(chuàng)建一個Servlet
else {
if (log.isTraceEnabled()) {
log.trace(" Returning non-STM instance");
}
// For new instances, count will have been incremented at the
// time of creation
if (!newInstance) {
countAllocated.incrementAndGet();
}
return instance;
}
} // 如果是單線程模式,則使用servlet對象池技術(shù)來加載多個Servlet
synchronized (instancePool) {
while (countAllocated.get() >= nInstances) {
// Allocate a new instance if possible, or else wait
if (nInstances < maxInstances) {
try {
instancePool.push(loadServlet());
nInstances++;
} catch (ServletException e) {
throw e;
} catch (Throwable e) {
ExceptionUtils.handleThrowable(e);
throw new ServletException(sm.getString("standardWrapper.allocate"), e);
}
} else {
try {
instancePool.wait();
} catch (InterruptedException e) {
// Ignore
}
}
}
if (log.isTraceEnabled()) {
log.trace(" Returning allocated STM instance");
}
countAllocated.incrementAndGet();
return instancePool.pop();
}
}
總結(jié)下來,注意以下幾點(diǎn)即可:
- 卸載過程中,不能分配Servlet
- 如果不是單線程模式,則每次都會返回同一個Servlet(默認(rèn)Servlet實現(xiàn)方式)
Servlet實例為null或者Servlet實例還未初始化,使用synchronized來保證并發(fā)時的原子性- 如果是單線程模式,則使用servlet對象池技術(shù)來加載多個Servlet
接下來我們看看loadServlet()方法
public synchronized Servlet loadServlet() throws ServletException {
// Nothing to do if we already have an instance or an instance pool
if (!singleThreadModel && (instance != null))
return instance;
PrintStream out = System.out;
if (swallowOutput) {
SystemLogHandler.startCapture();
}
Servlet servlet;
try {
long t1=System.currentTimeMillis();
// Complain if no servlet class has been specified
if (servletClass == null) {
unavailable(null);
throw new ServletException
(sm.getString("standardWrapper.notClass", getName()));
}
// 關(guān)鍵的地方,就是通過實例管理器,創(chuàng)建Servlet實例,而實例管理器是通過特殊的類加載器來加載給定的類
InstanceManager instanceManager = ((StandardContext)getParent()).getInstanceManager();
try {
servlet = (Servlet) instanceManager.newInstance(servletClass);
} catch (ClassCastException e) {
unavailable(null);
// Restore the context ClassLoader
throw new ServletException
(sm.getString("standardWrapper.notServlet", servletClass), e);
} catch (Throwable e) {
e = ExceptionUtils.unwrapInvocationTargetException(e);
ExceptionUtils.handleThrowable(e);
unavailable(null);
// Added extra log statement for Bugzilla 36630:
// https://bz.apache.org/bugzilla/show_bug.cgi?id=36630
if(log.isDebugEnabled()) {
log.debug(sm.getString("standardWrapper.instantiate", servletClass), e);
}
// Restore the context ClassLoader
throw new ServletException
(sm.getString("standardWrapper.instantiate", servletClass), e);
}
if (multipartConfigElement == null) {
MultipartConfig annotation =
servlet.getClass().getAnnotation(MultipartConfig.class);
if (annotation != null) {
multipartConfigElement =
new MultipartConfigElement(annotation);
}
}
// Special handling for ContainerServlet instances
// Note: The InstanceManager checks if the application is permitted
// to load ContainerServlets
if (servlet instanceof ContainerServlet) {
((ContainerServlet) servlet).setWrapper(this);
}
classLoadTime=(int) (System.currentTimeMillis() -t1);
if (servlet instanceof SingleThreadModel) {
if (instancePool == null) {
instancePool = new Stack<>();
}
singleThreadModel = true;
}
// 調(diào)用Servlet的init方法
initServlet(servlet);
fireContainerEvent("load", this);
loadTime=System.currentTimeMillis() -t1;
} finally {
if (swallowOutput) {
String log = SystemLogHandler.stopCapture();
if (log != null && log.length() > 0) {
if (getServletContext() != null) {
getServletContext().log(log);
} else {
out.println(log);
}
}
}
}
return servlet;
}
關(guān)鍵的地方有兩個:
- 通過實例管理器,創(chuàng)建Servlet實例,而實例管理器是通過特殊的類加載器來加載給定的類
- 調(diào)用Servlet的init方法
關(guān)鍵點(diǎn)2 - 創(chuàng)建過濾器鏈
創(chuàng)建過濾器鏈?zhǔn)钦{(diào)用的org.apache.catalina.core.ApplicationFilterFactory的createFilterChain()方法。我們來分析一下這個方法。該方法需要注意的地方已經(jīng)在代碼的comments里面說明了。
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
// 1. 如果加密打開了,則可能會多次調(diào)用這個方法
// 2. 為了避免重復(fù)生成filterChain對象,所以會將filterChain對象放在Request里面進(jìn)行緩存
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();
// 從這兒看出過濾器鏈對象里面的元素是根據(jù)Context里面的filterMaps來生成的
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
// 類型和路徑都匹配的情況下,將context.filterConfig放到過濾器鏈里面
for (int i = 0; i < filterMaps.length; i++) {
if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
continue;
}
if (!matchFiltersURL(filterMaps[i], requestPath))
continue;
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
context.findFilterConfig(filterMaps[i].getFilterName());
if (filterConfig == null) {
// FIXME - log configuration problem
continue;
}
filterChain.addFilter(filterConfig);
} // Add filters that match on servlet name second
// 類型和servlet名稱都匹配的情況下,將context.filterConfig放到過濾器鏈里面
for (int i = 0; i < filterMaps.length; i++) {
if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
continue;
}
if (!matchFiltersServlet(filterMaps[i], servletName))
continue;
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
context.findFilterConfig(filterMaps[i].getFilterName());
if (filterConfig == null) {
// FIXME - log configuration problem
continue;
}
filterChain.addFilter(filterConfig);
} // Return the completed filter chain
return filterChain;
}
關(guān)鍵點(diǎn)3 - 調(diào)用過濾器鏈的doFilter
ApplicationFilterChain類的doFilter函數(shù)代碼如下,它會將處理委托給internalDoFilter函數(shù)。
@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);
}
}
ApplicationFilterChain類的internalDoFilter函數(shù)代碼如下:
// 1. `internalDoFilter`方法通過pos和n來調(diào)用過濾器鏈里面的每個過濾器。pos表示當(dāng)前的過濾器下標(biāo),n表示總的過濾器數(shù)量
// 2. `internalDoFilter`方法最終會調(diào)用servlet.service()方法
private void internalDoFilter(ServletRequest request,
ServletResponse response)
throws IOException, ServletException { // Call the next filter if there is one
// 1. 當(dāng)pos小于n時, 則執(zhí)行Filter
if (pos < n) {
// 2. 得到 過濾器 Filter,執(zhí)行一次post++
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 {
// 4. 這里的 filter 的執(zhí)行 有點(diǎn)遞歸的感覺, 通過 pos 來控制從 filterChain 里面拿出那個 filter 來進(jìn)行操作
// 這里把this(filterChain)傳到自定義filter里面,我們自定義的filter,會重寫doFilter,在這里會被調(diào)用,doFilter里面會執(zhí)行業(yè)務(wù)邏輯,如果執(zhí)行業(yè)務(wù)邏輯成功,則會調(diào)用 filterChain.doFilter(servletRequest, servletResponse); ,filterChain就是這里傳過去的this;如果業(yè)務(wù)邏輯執(zhí)行失敗,則return,filterChain終止,后面的servlet.service(request, response)也不會執(zhí)行了
// 所以在 Filter 里面所調(diào)用 return, 則會終止 Filter 的調(diào)用, 而下面的 Servlet.service 更本就沒有調(diào)用到
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 {
//當(dāng)pos等于n時,過濾器都執(zhí)行完畢,終于執(zhí)行了熟悉的servlet.service(request, response)方法。
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);
}
}
}
自定義Filter
@WebFilter(urlPatterns = "/*", filterName = "myfilter")
public class FileterController implements Filter { @Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("Filter初始化中");
} @Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("登錄邏輯");
if("登錄失敗"){
response.getWriter().write("登錄失敗");
//后面的攔截器和servlet都不會執(zhí)行了
return;
}
//登錄成功,執(zhí)行下一個過濾器
filterChain.doFilter(servletRequest, servletResponse);
} @Override
public void destroy() {
System.out.println("Filter銷毀中");
}
}
- pos和n是ApplicationFilterChain的成員變量,分別表示過濾器鏈的當(dāng)前位置和過濾器總數(shù),所以當(dāng)pos小于n時,會不斷執(zhí)行ApplicationFilterChain的doFilter方法;
- 當(dāng)pos等于n時,過濾器都執(zhí)行完畢,終于執(zhí)行了熟悉的servlet.service(request, response)方法。
總結(jié)
以上是生活随笔為你收集整理的Tomcat源码分析 (九)----- HTTP请求处理过程(二)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux服务器I/O性能分析-2
- 下一篇: hdu5698瞬间移动(组合数,逆元)