Tomcat源码:CoyoteAdapter、Valve#invoke、ApplicationFilterChain

2023-11-10

  前文:

《Tomcat源码:启动类Bootstrap与Catalina的加载》

《Tomcat源码:容器的生命周期管理与事件监听》

《Tomcat源码:StandardServer与StandardService》

《Tomcat源码:Container接口》

《Tomcat源码:StandardEngine、StandardHost、StandardContext、StandardWrapper》

 《Tomcat源码:Pipeline与Valve》

《Tomcat源码:连接器与Executor、Connector》

《Tomcat源码:ProtocolHandler与Endpoint》

《Tomcat源码:Acceptor与Poller、PollerEvent》

《Tomcat源码:SocketProcessor、ConnectionHandler与Http11Processor》

前言

        在前文中,我们介绍了Processor如何接收来自EndPoint的Socket,读取字节流解析成 Tomcat Request 和 Response 对象,最后将请求传递给了Adapter做进一步的处理。本文我们就来介绍下一个请求是如何从连接器被转发到容器中并由相应的servlet处理的。

目录

前言

一、CoyoteAdapter

        1、CoyoteAdapter

       2、service

        3、invoke

二、Valve#invoke

        1、invoke

        2、wrapper.allocate

        3、loadServlet  

        4、initServlet

三、ApplicationFilterChain

        1、createFilterChain

        findFilterMaps

         2、doFilter

         internalDoFilter


一、CoyoteAdapter

        1、CoyoteAdapter

        CoyoteAdapter的创建是在Connector#initInternal中完成的,这里会new一个CoyoteAdapter对象并将当前的Connector 对象传入其中。

public class Connector extends LifecycleMBeanBase {

    /**
     * Coyote adapter.
     */
    protected Adapter adapter = null;

    protected void initInternal() throws LifecycleException {
        super.initInternal();
        // Initialize adapter
        adapter = new CoyoteAdapter(this);
        protocolHandler.setAdapter(adapter);
        // ...
}

        CoyoteAdapter类继承自 Adapter 接口,该接口定义了适配器的基本功能。

public class CoyoteAdapter implements Adapter {
    /**
     * Construct a new CoyoteProcessor associated with the specified connector.
     *
     * @param connector CoyoteConnector that owns this processor
     */
    public CoyoteAdapter(Connector connector) {
        super();
        this.connector = connector;
    }
}

       2、service

        然后我们来看service方法。

    public void service(org.apache.coyote.Request req, org.apache.coyote.Response res) throws Exception {

        Request request = (Request) req.getNote(ADAPTER_NOTES);
        Response response = (Response) res.getNote(ADAPTER_NOTES);

        if (request == null) {
            // Create objects
            request = connector.createRequest();
            request.setCoyoteRequest(req);
            response = connector.createResponse();
            response.setCoyoteResponse(res);

            // Link objects
            request.setResponse(response);
            response.setRequest(request);

            // Set as notes
            req.setNote(ADAPTER_NOTES, request);
            res.setNote(ADAPTER_NOTES, response);

            // Set query string encoding
            req.getParameters().setQueryStringCharset(connector.getURICharset());
        }
        // ...
        try {
            postParseSuccess = postParseRequest(req, request, res, response);
            if (postParseSuccess) {
                request.setAsyncSupported(connector.getService().getContainer().getPipeline().isAsyncSupported());
                connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
            }
            request.finishRequest();
            response.finishResponse();
        } catch (IOException e) {
        } finally {
            // ...
        }
    }

        首先根据 org.apache.coyote.Request#getNote(ADAPTER_NOTES) 和 org.apache.coyote.Response#getNote(ADAPTER_NOTES) 来获取 org.apache.catalina.connector.Request 和 org.apache.catalina.connector.Response 对象,
如果获取不到 org.apache.catalina.connector.Request 对象,就创建 org.apache.catalina.connector.Request 和 org.apache.catalina.connector.Response 对象,并分别设置到 notes 数组的 notes[1] 元素里。

Request request = (Request) req.getNote(ADAPTER_NOTES);
Response response = (Response) res.getNote(ADAPTER_NOTES);

public final class Request {
    /**
     * Notes.
     */
    private final Object notes[] = new Object[Constants.MAX_NOTES];

    public Object getNote(int pos) {
        return notes[pos];
    }
}

public final class Response {
    /**
     * Notes.
     */
    final Object notes[] = new Object[Constants.MAX_NOTES];

    public Object getNote(int pos) {
        return notes[pos];
    }
}

        Connector 的 createRequest 和 createResponse 也很简单。

    /**
     * Create (or allocate) and return a Request object suitable for specifying the contents of a Request to the
     * responsible Container.
     *
     * @return a new Servlet request object
     */
    public Request createRequest() {
        Request request = new Request();
        request.setConnector(this);
        return (request);
    }


    /**
     * Create (or allocate) and return a Response object suitable for receiving the contents of a Response from the
     * responsible Container.
     *
     * @return a new Servlet response object
     */
    public Response createResponse() {
        Response response = new Response();
        response.setConnector(this);
        return (response);
    }

        然后就进入 try-catch 语句块。首先调用 postParseRequest 方法,这个方法是在 http 的 header 解析完之后,对 Request 和 Response 做一些设置的工作,里面包扩了uri参数解析,Host映射等重要步骤。

        3、invoke

        调用完 postParseRequest 后就进入关键代码 if (postParseRequest) 里,在这个 if 里,先调用一下 request.setAsyncSupported,然后调用        

connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);

         这里的connector 是在 CoyoteAdapter 初始化时传入的,根据我们前文中对容器组件结构的介绍,这里connector.getService() 返回的是 Connector 关联的 Service 属性,而后续的getContainer() 返回的是 Service 里的容器 Engine 属性,至于getPipeline().getFirst()则是获取Engine容器的Valve。(这块内容我们在前文《Tomcat源码:Pipeline与Valve》已做过详细介绍,这里就不赘述了)

public CoyoteAdapter(Connector connector) {
    super();
    this.connector = connector;
}

        由于各层容器见的关系如下图所示,因此最后会调用StandardWrapperValve类的invoke方法。

    // StandardEngineValve.java
    public void invoke(Request request, Response response) throws IOException, ServletException {
        // 获取一个 Host 对象,获取不到就直接返回
        Host host = request.getHost();
        if (host == null) {
            if (!response.isError()) {
                response.sendError(404);
            }
            return;
        }
        if (request.isAsyncSupported()) {
            request.setAsyncSupported(host.getPipeline().isAsyncSupported());
        }
        host.getPipeline().getFirst().invoke(request, response);
    }    

    // StandardHostValve.java
    public void invoke(Request request, Response response) throws IOException, ServletException {
        Context context = request.getContext();
        if (context == null) {
            if (!response.isError()) {
                response.sendError(404);
            }
            return;
        }
        // 其余代码
        context.getPipeline().getFirst().invoke(request, response);
    }  

    // StandardContextValve.java
    public void invoke(Request request, Response response) throws IOException, ServletException {
        Wrapper wrapper = request.getWrapper();
        if (wrapper == null || wrapper.isUnavailable()) {
            response.sendError(HttpServletResponse.SC_NOT_FOUND);
            return;
        }
        // Acknowledge the request
        try {
            response.sendAcknowledgement(ContinueResponseTiming.IMMEDIATELY);
        } 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;
        }
        wrapper.getPipeline().getFirst().invoke(request, response);
    }

         整个流程如下图所示,StandardEngineValve、StandardHostValve、StandardContextValve这三个 Valve 的 invoke 方法的核心逻辑就是调用子容器的 Pipeline 的 Valve 的invoke 方法,也就是 StandardEngineValve#invoke -> StandardHostValve#invoke -> StandardContextValve#invoke -> StandardWrapper#invoke 方法。 

                

        最后response.finishResponse()负责将数据返回给客户端。

    request.finishRequest();
    response.finishResponse();

二、Valve#invoke

        1、invoke

         紧接上文,invoke(request, response)最后会调用StandardWrapperValve类的invoke方法。这段比较长,下面我们来分别讲解下

final class StandardWrapperValve extends ValveBase {
    public void invoke(Request request, Response response) throws IOException, ServletException {

        boolean unavailable = false;
        StandardWrapper wrapper = (StandardWrapper) getContainer();
        Servlet servlet = null;
        Context context = (Context) wrapper.getParent();
        // Allocate a servlet instance to process this request
        try {
            if (!unavailable) {
                servlet = wrapper.allocate();
            }
        } catch (Throwable e) {
            // ...
        }

        // Create the filter chain for this request
        ApplicationFilterChain filterChain = ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);

        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());
                    }
                }

            }
        } finally {
				// ...
        }
    }
}

        2、wrapper.allocate

         首先是wrapper.allocate(),其实现类在StandardWrapper中,从注释中可以看出,这里会先根据singleThreadModel 属性判断是否为单线程模型,然后根据不同的情况选择调用loadServlet、initServlet来加载或是初始化servlet对象。

public class StandardWrapper extends ContainerBase implements ServletConfig, Wrapper, NotificationEmitter {

    /**
     * Does this servlet implement the SingleThreadModel interface?
     *
     * @deprecated This will be removed in Tomcat 10.1 onwards.
     */
    @Deprecated
    protected volatile boolean singleThreadModel = false;

    /**
     * The (single) possibly uninitialized instance of this servlet.
     */
    protected volatile Servlet instance = null;

    public Servlet allocate() throws ServletException {
        boolean newInstance = false;
        // 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 {
                            instance = loadServlet();
                            newInstance = true;
                            if (!singleThreadModel) {
                                countAllocated.incrementAndGet();
                            }
                        } catch (Throwable e) {
                            // ...
                        }
                    }
                    if (!instanceInitialized) {
                        initServlet(instance);
                    }
                }
            }
            if (singleThreadModel) {
                if (newInstance) {
                    synchronized (instancePool) {
                        instancePool.push(instance);
                        nInstances++;
                    }
                }
            } else {
                if (!newInstance) {
                    countAllocated.incrementAndGet();
                }
                return instance;
            }
        }
        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
                    }
                }
            }
            countAllocated.incrementAndGet();
            return instancePool.pop();
        }
    }
}

        3、loadServlet  

       loadServlet这里的逻辑很简单,就是通过instanceManager.newInstance(servletClass)来创建

    /**
     * The fully qualified servlet class name for this servlet.
     */
    protected String servletClass = null;

    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;
        }
        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()));
            }
            InstanceManager instanceManager = ((StandardContext) getParent()).getInstanceManager();
            try {
                servlet = (Servlet) instanceManager.newInstance(servletClass);
            } catch (Throwable e) {
               // ...
            }
            initServlet(servlet);
        } finally {
           // ...
        }
        return servlet;

    }

        getParent()).getInstanceManager()回到StandardContext容器中获取 instanceManager对象,这个对象是在StandardContext的startInternal方法中初始化的,默认的实现类为DefaultInstanceManager ,从其具体实现可以看到这里newInstance实际上就是调用servlet的构造方法创建了实例(clazz.getConstructor().newInstance())。

public class DefaultInstanceManager implements InstanceManager {

    @Override
    public Object newInstance(String className)
            throws IllegalAccessException, InvocationTargetException, NamingException, InstantiationException,
            ClassNotFoundException, IllegalArgumentException, NoSuchMethodException, SecurityException {
        Class<?> clazz = loadClassMaybePrivileged(className, classLoader);
        return newInstance(clazz.getConstructor().newInstance(), clazz);
    }
}

        4、initServlet

         servlet实例创建好了之后就是调用initServlet来初始化了,内容也很简单,就是调用了servlet的初始化方法init(),我们之前有专门介绍过servlet,有兴趣的可以看下这篇文章《Tomcat:servlet与servlet容器》

    private synchronized void initServlet(Servlet servlet) throws ServletException {
        if (instanceInitialized && !singleThreadModel) {
            return;
        }
        // Call the initialization method of this servlet
        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) {
                        SecurityUtil.remove(servlet);
                    }
                }
            } else {
                servlet.init(facade);
            }
            instanceInitialized = true;
        }catch (Throwable f) {
            // ...
        }
    }

        在获取到了servlet对象后,下一步会获取过滤器调用链,并使用doFilter方法来进行过滤。

三、ApplicationFilterChain

        1、createFilterChain

        createFilterChain从名字可以看出来是创建过滤链。首先创建ApplicationFilterChain对象,然后调用context.findFilterMaps()获取所有的过滤器,最后通过映射路径和servlet名来匹配能作用当前servlet上的过滤器,最后调用context.findFilterConfig来获取一个 ApplicationFilterConfig 对象,并调用filterChain.addFilter(filterConfig) 把这个对象加入到 filterChain 里。

    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;
    }

        findFilterMaps

        StandardContext中定义了filterMaps用来保存应用程序里的 web.xml 文件里配置的 Filter 的相关信息的,比如名字、作用路径等。因此拿到 FilterMap[] 数组后,会分别调用matchDispatcher、matchFiltersURL这两个 for 循环。

    /**
     * @return the set of filter mappings for this Context.
     */
    @Override
    public FilterMap[] findFilterMaps() {
        return filterMaps.asArray();
    }

        在前文介绍StandardContext时我们提到过在web.xml被解析时会给context添加了一个监听器ContextConfig,这给监听器内部会调用方法为context容器查找出web.xml中所有的过滤器,并调用addFilterDef、addFilterMap将过滤器以不同的形式和内容注入到context中去。

        StandardContext#startInternal中会调用filterStart方法,该方法内会将过滤器封装到filterConfigs中,供上文中的context.findFilterConfig方法调用。

public class ContextConfig implements LifecycleListener {
    private void configureContext(WebXml webxml) {
        for (FilterDef filter : webxml.getFilters().values()) {
            if (filter.getAsyncSupported() == null) {
                filter.setAsyncSupported("false");
            }
            context.addFilterDef(filter);
        }
        for (FilterMap filterMap : webxml.getFilterMappings()) {
            context.addFilterMap(filterMap);
        }
	}
}

public class StandardContext extends ContainerBase implements Context, NotificationEmitter {
     /**
     * The set of filter definitions for this application, keyed by filter name.
     */
    private HashMap<String, FilterDef> filterDefs = new HashMap<>();

    /**
     * The set of filter mappings for this application, in the order they were defined in the deployment descriptor with
     * additional mappings added via the {@link ServletContext} possibly both before and after those defined in the
     * deployment descriptor.
     */
    private final ContextFilterMaps filterMaps = new ContextFilterMaps();

    private HashMap<String, ApplicationFilterConfig> filterConfigs = new HashMap<>();

    public void addFilterMap(FilterMap filterMap) {
        validateFilterMap(filterMap);
        // Add this filter mapping to our registered set
        filterMaps.add(filterMap);
        fireContainerEvent("addFilterMap", filterMap);
    }

    public FilterMap[] findFilterMaps() {
        return filterMaps.asArray();
    }

    public boolean filterStart() {
        // Instantiate and record a FilterConfig for each defined filter
        boolean ok = true;
        synchronized (filterConfigs) {
            filterConfigs.clear();
            for (Entry<String, FilterDef> entry : filterDefs.entrySet()) {
                String name = entry.getKey();
                if (getLogger().isDebugEnabled()) {
                    getLogger().debug(" Starting filter '" + name + "'");
                }
                try {
                    ApplicationFilterConfig filterConfig = new ApplicationFilterConfig(this, entry.getValue());
                    filterConfigs.put(name, filterConfig);
                } catch (Throwable t) {
                    t = ExceptionUtils.unwrapInvocationTargetException(t);
                    ExceptionUtils.handleThrowable(t);
                    getLogger().error(sm.getString("standardContext.filterStart", name), t);
                    ok = false;
                }
            }
        }
        return ok;
    }

    protected synchronized void startInternal() throws LifecycleException {
        // ...
        if (ok) {
            if (!filterStart()) {
                log.error(sm.getString("standardContext.filterFail"));
                ok = false;
             }
         }
    }

    public FilterConfig findFilterConfig(String name) {
        return filterConfigs.get(name);
    }
}



         2、doFilter

        doFilter会将请求一层层的经过上文中匹配到的过滤器链中的各个组件处理,可以看到这里其实是调用了internalDoFilter方法

    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);
        }
    }

         internalDoFilter

        internalDoFilter 方法里分了两部分,if (pos < n) 和 if 后面的 try-catch 语句。

        if (pos < n)先从 filter 是数组里获取下一个 ApplicationFilterConfig 对象。

ApplicationFilterConfig filterConfig = filters[pos++];

         然后通过 filterConfig.getFilter() 获取一个 Filter 对象,并调用这个 Filter 的 doFilter 方法,并把 ApplicationFilterChain 对象传入 Filter#doFilter。

        在 if (pos < n) 里的 n 是这个 ApplicationFilterChain 里包含的 Filter 的总数。每次调用 internalDoFilter 方法,pos 就加1,也就是获取下一个 ApplicationFilterConfig,直到 pos 等于 n 的时候才不执行 if 语句,为了达到这个效果,应用程序必须在自定义的在 Filter 里执行 FilterChain#doFilter 方法,才能再次进入 ApplicationFilterChain#internalDoFilter 方法,这样就完成了调用 ApplicationFilterChain 里所有的 Filter 的 doFilter 方法。
这种方式是责任链模式的一种体现。

        internalDoFilter里的 try-catch 是在执行完所有的 Filter#doFilter 方法之后执行的。
try 语句块里先设置一下属性,然后执行

servlet.service(request, response);

        通过调用 Servlet 对象的 service 方法来处理请求。到这里,tomcat 终于把请求交给了应用程序了,tomcat 整个接受请求、转发处理请求的流程就差不多完结了。

    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);
            }
        }
    }

        本文中流程处理过程总结如下图,到这里我们整个tomcat请求传递与映射的逻辑就整理完了,总体来看整个tomcat使用连接器与容器两部分来分别处理请求的传递与处理,并使用了一些方法来增加了并发以及吞吐能力。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Tomcat源码:CoyoteAdapter、Valve#invoke、ApplicationFilterChain 的相关文章

  • JavaMail Gmail 问题。 “准备启动 TLS”然后失败

    mailServerProperties System getProperties mailServerProperties put mail smtp port 587 mailServerProperties put mail smtp
  • 我需要在 Spring 中检查每个控制器中的有效会话吗? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 假设在 Spring Mvc 的 Web 应用程序中 我们是否需要检查每个控制器或 jsps 中的有效会话 我该如何解决 MVC 中的
  • 在Windows上安装Java 11 OpenJDK(系统路径问题)

    Java 11 最近发布了 众所周知 这个版本没有安装文件 当然 要在没有安装程序的情况下安装 Java 我将系统设置 PATH 和 JAVA HOME 设置为解压缩 Java 11 的文件夹的地址 根据对类似问题的已接受回复建议 唯一的事
  • java.io.IOException: %1 不是有效的 Win32 应用程序

    我正在尝试对 XML 文档进行数字签名 为此我有两个选择 有一个由爱沙尼亚认证中心为程序员创建的库 还有一个由银行制作的运行 Java 代码的脚本 如果使用官方 认证中心 库 那么一切都会像魅力一样进行一些调整 但是当涉及到银行脚本时 它会
  • 使用 ANTLR 为 java 源代码生成抽象语法树

    如何使用 ANTLR 从 java src 代码生成 AST 有什么帮助吗 好的 步骤如下 前往ANTLR站点 http www antlr org 并下载最新版本 下载Java g和JavaTreeParser g文件来自here htt
  • Convert.FromBase64String 方法的 Java 等效项

    Java 中是否有相当于Convert FromBase64String http msdn microsoft com en us library system convert frombase64string aspx which 将指
  • 如何在 Java 中禁用 System.out 以提高速度

    我正在用 Java 编写一个模拟重力的程序 其中有一堆日志语句 到 System out 我的程序运行速度非常慢 我认为日志记录可能是部分原因 有什么方法可以禁用 System out 以便我的程序在打印时不会变慢 或者我是否必须手动检查并
  • HDFS:使用 Java / Scala API 移动多个文件

    我需要使用 Java Scala 程序移动 HDFS 中对应于给定正则表达式的多个文件 例如 我必须移动所有名称为 xml从文件夹a到文件夹b 使用 shell 命令我可以使用以下命令 bin hdfs dfs mv a xml b 我可以
  • hibernate总是自己删除表中的所有数据

    您好 我正在开发一个 spring mvc 应用程序 它使用 hibernate 连接到存储文件的 mysql 数据库 我有两个方法 一个方法添加我选择的特定文件路径中的所有文件 另一种方法调用查询以返回从 mysql 存储的文件列表 问题
  • OnClick 事件中的 finish() 如何工作?

    我有一个Activity一键退出Activity 通过layout xml我必须设置OnClick事件至cmd exit调用 this finish 效果很好 public void cmd exit View editLayout thi
  • 在具有相同属性名称的不同数据类型上使用 ModelMapper

    我有两节课说Animal AnimalDto我想用ModelMapper将 Entity 转换为 DTO 反之亦然 但是对于具有相似名称的一些属性 这些类应该具有不同的数据类型 我该如何实现这一目标 动物 java public class
  • 使用 SAX 进行 XML 解析 |如何处理特殊字符?

    我们有一个 JAVA 应用程序 可以从 SAP 系统中提取数据 解析数据并呈现给用户 使用 SAP JCo 连接器提取数据 最近我们抛出了一个异常 org xml sax SAXParseException 字符引用 是无效的 XML 字符
  • 当单元格内的 JComboBox 中有 ItemEvent 时,如何获取 CellRow

    我有一个 JTable 其中有一列包含 JComboBox 我有一个附加到 JComboBox 的 ItemListener 它会根据任何更改进行操作 但是 ItemListener 没有获取更改的 ComboBox 所在行的方法 当组合框
  • Keycloak - 自定义 SPI 未出现在列表中

    我为我的 keycloak 服务器制作了一个自定义 SPI 现在我必须在管理控制台上配置它 我将 SPI 添加为模块 并手动安装 因此我将其放在 module package name main 中 并包含 module xml 我还将其放
  • Java - 不要用 bufferedwriter 覆盖

    我有一个程序可以将人员添加到数组列表中 我想做的是将这些人也添加到文本文件中 但程序会覆盖第一行 因此这些人会被删除 如何告诉编译器在下一个空闲行写入 import java io import java util import javax
  • 将2-3-4树转换为红黑树

    我正在尝试将 2 3 4 树转换为 java 中的红黑树 但我无法弄清楚它 我将这两个基本类编写如下 以使问题简单明了 但不知道从这里到哪里去 public class TwoThreeFour
  • 休眠以持久保存日期

    有没有办法告诉 Hibernate java util Date 应该持久保存 我需要这个来解决 MySQL 中缺少的毫秒分辨率问题 您能想到这种方法有什么缺点吗 您可以自己创建字段long 或者使用自定义的UserType 实施后User
  • com.jcraft.jsch.JSchException:身份验证失败

    当我从本地磁盘上传文件到远程服务器时 出现这样的异常 com jcraft jsch JSchException Auth fail at org apache tools ant taskdefs optional ssh Scp exe
  • KeyPressed 和 KeyTyped 混淆[重复]

    这个问题在这里已经有答案了 我搜索过之间的区别KeyPressedand KeyTyped事件 但我仍然不清楚 我发现的一件事是 Keypressed 比 KeyTyped 首先被触发 请澄清一下这些事件何时被准确触发 哪个适合用于哪个目的
  • JAVA - 如何从扫描仪读取文件中检测到“\n”字符

    第一次海报 我在读取文本文件的扫描仪中读取返回字符时遇到问题 正在读取的文本文件如下所示 test txt start 2 0 30 30 1 1 90 30 0 test txt end 第一行 2 表示两个点 第二行 位置索引 0 xp

随机推荐

  • jeecg boot MybatisInterceptor 的使用笔记

    目前使用的旧版本是2 0 2 更新了新版本2 2 1 sql UPDATE tablename set update time updateTimeNew where ID id and update time updateTime 使用中
  • 【CSS】CSS餐厅小游戏练习1~32关的参考答案

    链接 CSS3餐厅练习 玩法 利用各种选择器选中抖动的物体即可通关 1 Type Selector 元素选择器 元素选择器 作用 根据标签名来选中指定的元素 语法 标签名 例子 p h1 div plate 2 Type Selector
  • 07 - 日志服务器的搭建与攻击

    目录 1 项目环境 2 拓扑图 3 思路 4 配置GW CentOS 7 4 1 配置ens33 4 2 配置ens32 4 3 配置ens34 5 配置Server CentOS 7 1804 7 配置Syslog CentOS 7 18
  • webpack4入门笔记——打包模式选择

    什么是mode webpack4增加了mode配置项 这样会做一些默认的工作 一定程度上解决了webpacke配置复杂的弊端 语法 mode development production 复制代码 不配置默认是production 这是2种
  • Python中最小堆结构的heapq模块

    堆是非线性的树形的数据结构 完全二叉树 有两种堆 最大堆与最小堆 heapq库中的堆默认是最小堆 最大堆 树中各个父节点的值总是大于或等于任何一个子节点的值 最小堆 树中各个父节点的值总是小于或等于任何一个子节点的值 我们一般使用二叉堆来实
  • 用python送父亲节祝福,父亲节:程序员硬核示爱,“爸”气告白!

    转载来自51cto https blog 51cto com 15127557 2665130 转眼 已至6月第三周了 2020年即将过去一半 2020年06月21日 星期日 庚子年 鼠年 五月初一 也就是明天 这一天也是父亲节 父亲节 F
  • 【168】Java调用EXE并利用多线程接收EXE的输出流

    代码一共分两个类 分别是 CmdInputStreamRunnable 和 CmdUtils CmdInputStreamRunnable java import java io author 张超 操作系统执行命令时会有输出 这些输出会被
  • c语言中的顺序点

    http bbs csdn net topics 370153775 经常可以在一些讨论组里看到下面的提问 谁知道下面C语句给n赋什么值 m 1 n m m 最近有位不相识的朋友发email给我 问为什么在某个C 系统里 下面表达式打印出两
  • solidworks 之迈迪齿轮设计方法 粗浅解释

    之前对同步带比较了解 从来未涉及到齿轮的设计 对迈迪的工具不是了解 之前从来无法争取生成过齿轮 特此标记方法 必须要有这个标记 根据经验系数 其他的也没有尝试 之前默认选项是用户自定义 之后就可以选择模数 然后齿数 然后到 齿数决定了齿轮的
  • CTF 加解密合集

    0x00 前言 本篇的目的是对工具的收集 以及对一些题目的整理 持续更新 CTF 加解密合集 CTF Web合集 网络安全知识库 文中工具皆可关注 皓月当空w 公众号 发送关键字 工具 获取 0x01 古典加密 1 栅栏加密 CTF加密解密
  • Mac电脑——MySQL数据库root密码忘记了,不要紧,看我操作

    1 先把MySQL服务器停止运行 2 打开MySQL的文件 并修改my cnf文件 mac电脑是my cnf windows电脑是my ini 3 my cnf文件无法打开 我们可以将该文件的后缀名my cnf修改为my txt 修改完后在
  • 归零码和不归零码、单极性码和双极性码

    关于基带数字信号表示 下面有一些常见的细节 对于传输数字信号来说 最常用的方法是用不同的电压电平来表示两个二进制数字 即数字信号由矩形脉冲组成 a 单极性不归零码 无电压表示 0 恒定正电压表示 1 每个码元时间的中间点是采样时间 判决门限
  • python 获取时间间隔,Python:从时间间隔到值的映射

    I m refactoring a function that given a series of endpoints that implicitly define intervals checks if a number is inclu
  • JPA多条件查询之AND和OR混合查询

    JPA多条件查询这种业务场景是很常见的 比如说这种 像这种同一个查询条件可以多选的用OR语句来查询 比如 材质 之间选了 PU 和 橡胶 就用OR 不同查询条件之间则用AND语句查询 比如 品牌 和 材质 之间就用AND拼接 我现在要根据不
  • 相对比较全的webpack5配置

    const path require path const MiniCssExtractPlugin require mini css extract plugin const HtmlWebpackPlugin require html
  • 【论文精读】CVPR2021 - ReDet:一种用于航空目标检测的旋转等变检测器

    论文精读 CVPR2021 ReDet 一种用于航空目标检测的旋转等变检测器 论文原文 R3Det Refined Single Stage Detector with Feature Refinement for Rotating Obj
  • 成为技术传播者(一):写在前面

    这几年一直是在传说中的 高科技行业 里混迹 于是也有幸体验了很多来自高科技的悖论 譬如说 专门开发OA的一家软件公司 自己没有一套像样的OA系统 所有的文档都靠MSN传来传去 或者放在机器上开共享访问 再譬如说 给别人做敏捷方法培训的一个家
  • 使用Python 和matlab 进行AR模型的仿真(自回归模型)

    对钙信号的动力学进行建模 AR模型 import matplotlib pyplot as plt import numpy as np if name main length 500 time range length gamma 0 9
  • 用Linux搭建chrony服务器同步时间

    准备工作 在准备之前需要准备两台虚拟机 一台当做服务器使用 另一台当作虚拟机客户端使用 具体方法 鼠标右键某一个虚拟机 gt 点击管理 gt 点击克隆 提示 克隆好虚拟机后 记得修改主机名和IP地址 不然就和第一台冲突了 修改IP地址 使用
  • Tomcat源码:CoyoteAdapter、Valve#invoke、ApplicationFilterChain

    前文 Tomcat源码 启动类Bootstrap与Catalina的加载 Tomcat源码 容器的生命周期管理与事件监听 Tomcat源码 StandardServer与StandardService Tomcat源码 Container接