• 【Tomcat8源码学习之十三】Tomcat请求处理过程之容器处理


    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 &quot;%r&quot; %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
  • 相关阅读:
    JDK源码之LinkedList
    MySQL常规知识点总结
    根据不同分辨率载入相应CSS样式表
    关于1.0.0版Backbone.js调用validate
    弹性盒模型计算
    使用CocoaPods
    Objective-C中的Block[转]
    将UTF8编码转化为中文 - NSString方法
    添加第三方类库造成的Undefined symbols for architecture i386:编译错误
    App适配iPhone 6/ Plus和iOS 8:10条小秘诀
  • 原文地址:https://www.cnblogs.com/cac2020/p/12797097.html
Copyright © 2020-2023  润新知