Tomcat源码版本:apache-tomcat-8.5.54-src
JDK源码版本:jdk1.8.0_171
一、请求流程关键代码
public class CoyoteAdapter implements Adapter { ... @Override public void service(org.apache.coyote.Request req, org.apache.coyote.Response res) throws Exception { ... connector.getService() //StandardService .getContainer() //StandardEngine .getPipeline() //StandardPipeline .getFirst() //StandardEngineValve .invoke(request, response); ... } } final class StandardEngineValve extends ValveBase { ... @Override public final void invoke(Request request, Response response) throws IOException, ServletException { Host host = request.getHost(); .... host //StandardHost .getPipeline() //StandardPipeline .getFirst() //AccessLogValve、ErrorReportValve、StandardHostValve .invoke(request, response); } ... } final class StandardHostValve extends ValveBase { public final void invoke(Request request, Response response) throws IOException, ServletException { Context context = request.getContext(); .... try { context.bind(Globals.IS_SECURITY_ENABLED, MY_CLASSLOADER); if (!asyncAtStart && !context.fireRequestInitEvent(request.getRequest())) { return; } try { if (!response.isErrorReportRequired()) { context //StandardContext .getPipeline() //StandardPipeline .getFirst() //AuthenticatorBase、StandardContextValve .invoke(request, response); } } catch (Throwable t) { ... } ... if (!request.isAsync() && !asyncAtStart) { context.fireRequestDestroyEvent(request.getRequest()); } } finally { .... context.unbind(Globals.IS_SECURITY_ENABLED, MY_CLASSLOADER); } } ... } final class StandardContextValve extends ValveBase { ... @Override public final void invoke(Request request, Response response) throws IOException, ServletException { ... Wrapper wrapper = request.getWrapper(); ... wrapper //StandardWrapper .getPipeline() //StandardPipeline .getFirst() //StandardWrapperValve .invoke(request, response); } } final class StandardWrapperValve extends ValveBase { ... @Override public final void invoke(Request request, Response response) throws IOException, ServletException { ... StandardWrapper wrapper = (StandardWrapper) getContainer(); Servlet servlet = null; Context context = (Context) wrapper.getParent(); ... // Allocate a servlet instance to process this request try { if (!unavailable) { //调用StandardWrapper的allocate的方法来获得一个servlet实例 servlet = wrapper.allocate(); } } catch (UnavailableException e) { ... } catch (ServletException e) { ... } catch (Throwable e) { ... } ... //调用它的 private createFilterChain 方法获得过滤链 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 { //调用过滤器链的 doFilter 方法。 包括调用 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 { //最终会调用Servlet的service方法 filterChain.doFilter(request.getRequest(), response.getResponse()); } } } } catch (ClientAbortException | CloseNowException e) { ... } catch (IOException e) { ... } catch (UnavailableException e) { ... } catch (ServletException e) { ... } catch (Throwable e) { ... } finally { // Release the filter chain (if any) for this request if (filterChain != null) { filterChain.release(); } // Deallocate the allocated servlet instance try { if (servlet != null) { wrapper.deallocate(servlet); } } catch (Throwable e) { ... } // If this servlet has been marked permanently unavailable, // unload it and release this instance try { if ((servlet != null) && (wrapper.getAvailable() == Long.MAX_VALUE)) { wrapper.unload(); } } catch (Throwable e) { ... } ... } } } //加载Servlet public class StandardWrapper extends ContainerBase implements ServletConfig, Wrapper, NotificationEmitter { @Override public Servlet allocate() throws ServletException { ... // If not SingleThreadedModel, return the same instance every time if (!singleThreadModel) { // Load and initialize our instance if necessary if (instance == null || !instanceInitialized) { synchronized (this) { if (instance == null) { try { if (log.isDebugEnabled()) { log.debug("Allocating non-STM instance"); } instance = loadServlet();//加载Servelt newInstance = true; if (!singleThreadModel) { countAllocated.incrementAndGet(); } } catch (ServletException e) { ... } catch (Throwable e) { ... } } if (!instanceInitialized) { initServlet(instance);//调用Servlet初始化方法 } } } ... } synchronized (instancePool) { while (countAllocated.get() >= nInstances) { 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) { } } } if (log.isTraceEnabled()) { log.trace(" Returning allocated STM instance"); } countAllocated.incrementAndGet(); return instancePool.pop(); } } public synchronized Servlet loadServlet() throws ServletException { ... Servlet servlet; try { .... InstanceManager instanceManager = ((StandardContext)getParent()).getInstanceManager(); try { servlet = (Servlet) instanceManager.newInstance(servletClass);//根据全限定名将Servlet实例化 } catch (ClassCastException e) { ... } catch (Throwable e) { ... } ... initServlet(servlet);//调用Servlet初始化方法 fireContainerEvent("load", this); loadTime=System.currentTimeMillis() -t1; } finally { ... } return servlet; } private synchronized void initServlet(Servlet servlet) throws ServletException { ... try { if( Globals.IS_SECURITY_ENABLED) { boolean success = false; try { Object[] args = new Object[] { facade }; SecurityUtil.doAsPrivilege("init", servlet, classType, args); success = true; } finally { if (!success) { // destroy() will not be called, thus clear the reference now SecurityUtil.remove(servlet); } } } else { servlet.init(facade);//调用servlet init方法 传递封装的请求对象 } instanceInitialized = true; } catch (UnavailableException f) { ... } catch (ServletException f) { ... } catch (Throwable f) { ... } } } //调用链 最终执行Servlet的Service方法 public 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) { ... } } else { internalDoFilter(request,response); } } private void internalDoFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { 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);//执行Servlet的Service方法 } } catch (IOException | ServletException | RuntimeException e) { ... } catch (Throwable e) { ... } finally { ... } } }
Servlet执行结束之后的逻辑:
public class CoyoteAdapter implements Adapter { @Override public void service(org.apache.coyote.Request req, org.apache.coyote.Response res) throws Exception { .... try { postParseSuccess = postParseRequest(req, request, res, response); if (postParseSuccess) { //check valves if we support async request.setAsyncSupported( connector.getService().getContainer().getPipeline().isAsyncSupported()); // Calling the container connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);//Servlet调用链 } if (request.isAsync()) { ... } else {
//执行以下两个方法 就会将输出信息刷到浏览器进行渲染 request.finishRequest(); response.finishResponse(); } } catch (IOException e) { } finally { .... // 记录访问日志 if (!async && postParseSuccess) { Context context = request.getContext(); Host host = request.getHost(); long time = System.currentTimeMillis() - req.getStartTime(); if (context != null) { context.logAccess(request, response, time, false); } else if (response.isError()) { if (host != null) { host.logAccess(request, response, time, false); } else { connector.getService().getContainer().logAccess( request, response, time, false); } } } req.getRequestProcessor().setWorkerThreadName(null); ... } } ... }
二、测试Demo
1、Demo目录
2、关键代码
servlet:
package com.test; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class HelloWorld extends HttpServlet { private static final long serialVersionUID = 1L; public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.err.println("HelloWorld doGet"); doPost(request, response); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.err.println("HelloWorld doPost"); String name = request.getParameter("name"); //获取jsp页面传过来的参数 String pwd = request.getParameter("pwd"); String sex = request.getParameter("sex"); String home = request.getParameter("home"); String info = request.getParameter("info"); //request.setAttribute("username", name); //向request域中放置参数 //request.getRequestDispatcher("/denglu.jsp").forward(request, response); //转发到登录页面 response.sendRedirect("hello.jsp");//重定向到首页 } }
package com.test; import java.io.IOException; import javax.servlet.Servlet; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletResponse; public class Index implements Servlet{ @Override public void init(ServletConfig paramServletConfig) throws ServletException { System.out.println("Index Servlet init"); } @Override public void service(ServletRequest paramServletRequest, ServletResponse paramServletResponse) throws ServletException, IOException { System.out.println("Index Servlet service"); HttpServletResponse res = (HttpServletResponse)paramServletResponse; res.sendRedirect("index.jsp");//重定向到首页 } @Override public ServletConfig getServletConfig() { System.out.println("Index Servlet getServletConfig"); return null; } @Override public String getServletInfo() { System.out.println("Index Servlet getServletInfo"); return null; } @Override public void destroy() { System.out.println("Index Servlet destroy"); } }
web.xml:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1"> <display-name>WebDemo</display-name> <servlet> <servlet-name>index</servlet-name> <servlet-class>com.test.Index</servlet-class> </servlet> <servlet> <servlet-name>hello</servlet-name> <servlet-class>com.test.HelloWorld</servlet-class> </servlet> <servlet-mapping> <servlet-name>index</servlet-name> <url-pattern>/index</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping> <welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> <welcome-file>index.jsp</welcome-file> <welcome-file>default.html</welcome-file> <welcome-file>default.htm</welcome-file> <welcome-file>default.jsp</welcome-file> </welcome-file-list> </web-app>
jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> </head> <body>首页 </body> </html>
3、部署路径
<?xml version="1.0" encoding="UTF-8"?> <Server port="8005" shutdown="SHUTDOWN"> <Listener className="org.apache.catalina.startup.VersionLoggerListener" /> <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" /> <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" /> <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" /> <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" /> <GlobalNamingResources> <Resource name="UserDatabase" auth="Container" type="org.apache.catalina.UserDatabase" description="User database that can be updated and saved" factory="org.apache.catalina.users.MemoryUserDatabaseFactory" pathname="conf/tomcat-users.xml" /> </GlobalNamingResources> <Service name="Catalina"> <Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" /> <Engine name="Catalina" defaultHost="localhost"> <Realm className="org.apache.catalina.realm.LockOutRealm"> <Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/> </Realm> <Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true"> <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="localhost_access_log" suffix=".txt" pattern="%h %l %u %t "%r" %s %b" /> <Context docBase="E:workspacemotWebDemoweb" path="/WebDemo" reloadable="true" /> </Host> </Engine> </Service> </Server>
4、调试打印
请求:http://localhost:8080/WebDemo/index
控制台输出:
Index Servlet init
Index Servlet service