Tomcat源码:CoyoteAdapter、Valve#invoke、ApplicationFilterChain

news2025/1/15 6:51:38

  前文:

《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使用连接器与容器两部分来分别处理请求的传递与处理,并使用了一些方法来增加了并发以及吞吐能力。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/998602.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

在32位Windows中,DLL是如何导出的?

32 位 Windows 的设计者不必担心将所有内容压缩到 256KB 的内存中。由于 Win32 中的模块基于需求分页&#xff0c;因此你所要做的就是将整个映像映射到内存中&#xff0c;然后运行访问所需的部分。 DLL中的常驻名(resident name)和非常驻名(non-resident name)之间没有区别&am…

5.4 【MySQL】页目录

记录在页中按照主键值由小到大顺序串联成一个单链表&#xff0c;如果我们想根据主键值查找页中的某条记录&#xff0c;可以这样查询&#xff1a; SELECT * FROM page_demo WHERE c1 3; 我们平常想从一本书中查找某个内容的时候&#xff0c;一般会先看目录&#xff0c;找到需…

Docker配置阿里云镜像加速器

Docker配置阿里云镜像加速器 《Docker安装详细步骤》&#xff1a;Docker安装详细步骤_周十一.的博客-CSDN博客、 在前面博文已经介绍了docker 的安装&#xff0c;因为某些原因&#xff0c;我们下载镜像比较慢&#xff0c;今天给大家介绍一下&#xff0c;如何配置阿里云的镜像加…

解决Ubuntu无法安装pycairo和PyGObject

环境&#xff1a;虚拟机Ubuntu20.04&#xff0c;vscode无法安装pycairo和PyGObject 虚拟机Ubuntu20.04&#xff0c;vscode中运行Anaconda搭建的vens 的Python3.8.10 首先在vscode中点击ctrlshiftp&#xff0c;选择Python3.8.10的环境&#xff0c;自动激活Python 最近在搞无人…

在Linux和Windows上安装seata

1 前言 官网地址&#xff1a;https://seata.io/ 源码地址&#xff1a;https://github.com/seata/seata 官网手册&#xff1a;https://seata.io/zh-cn/docs/ops/deploy-guide-beginner.html Seata&#xff0c;一款开源的分布式事务解决方案&#xff0c;致力于提供高性能和简…

STM32CubeProgrammer 用ST-LINK不能烧录 解决方法

如下图所示&#xff0c;连接ST-LINK后可以认出Serial number&#xff0c;点击Connect后&#xff0c;显示“Error : Data read failed”&#xff0c;点击Download后&#xff0c;显示“Error: failed to download Segment[0]”。 此为正常现象&#xff0c;因为芯片加密&#xff0…

docker 容器编排工具 docker-compose从0到精通

compose简介 Docker-Compose 项目是Docker官方的开源项目&#xff0c;负责实现对Docker容器集群的快速编排。 Docker-Compose 项目由 Python 编写&#xff0c;调用 Docker 服务提供的API来对容器进行管理。因此&#xff0c;只要所操作的平台支持 Docker API&#xff0c;就可以…

redis高可用之主从复制、哨兵模式、集群的概述及部署

目录 redis集群有三种模式 一、主从复制 1、主从复制的概念 2、主从复制的作用 3、主从复制的流程 4、主从复制的缺陷 5、搭建redis主从复制 二、redis 哨兵模式 1、哨兵模式的概念 2、哨兵模式的作用 3、故障转移机制 4、搭建Redis 哨兵模式 三、redis集群 1、r…

iOS开发Swift-12-列表UI,TableViewController,动态响应Button勾选-待办事项App(1)

1.创建新项目 为项目添加图标 2.将Table View Controller添加到界面中 将箭头移动到Table View上来,代表它是首页(根页面).选中ViewController,点击Delete,对它进行删除.将代码ViewController.swift也删除掉. 新建一个Cocoa Touch Class. 将TableViewController的cla…

记LGSVL Map Annotation(1) LGSVL本地编译记录、安装

主要的编译参考来着官方文件 Unity安装 安装unity hub 安装2020.3.3f1在unity hub上 但是我发现没有2020.3.3f1&#xff0c;只有2020.3.3f1c1&#xff0c;其实c1就是中国版&#xff0c;没有什么影响 GIT安装 安装GIT安装Git LFS验证git-lfs(输出Git LFS initialized就&am…

【linux】权限管理 详解(文件/访问者/目录 权限、权限指令、粘滞位... ...)

文章目录 权限的概念linux 权限管理文件访问者的类型&#xff08;用户&#xff09;文件类型和访问权限文件类型访问权限 文件权限值的 表示方法文件权限的 设置方法chmodchownchgrpumaskfilesudo 分配权限 目录的权限粘滞位注意 权限的概念 linux中有两种用户: 超级用户&…

Jeecg-Boot /jeecg-boot/jmreport/qurestSql接口sql注入漏洞复现

一、Jeecg-Boot介绍 JeecgBoot 是一款基于代码生成器的低代码开发平台&#xff01;前后端分离架构 SpringBoot2.x&#xff0c;SpringCloud&#xff0c;Ant Design&Vue&#xff0c;Mybatis-plus&#xff0c;Shiro&#xff0c;JWT&#xff0c;支持微服务。强大的代码生成器让…

淘宝整店商品列表信息API接口介绍

淘宝整店商品列表信息API接口&#xff08;Taobao whole store product list information API interface&#xff09;是在开放性API接口的基础之上&#xff0c;根据淘宝官方平台提供的以互联网为发展渠道的电子商务服务&#xff0c;通过数据整合与共享&#xff0c;抽象开发出来的…

java企业数据管理系统

项目介绍 此项目为企业数据管理系统的后端部分&#xff0c;前端部分请参考vue-admin&#xff0c;项目实现了菜单管理、用户管理、角色管理和权限管理四个基础模块&#xff0c;前端菜单管理结合动态路由可自由添加菜单。结合Shiro权限管理实现了菜单和按钮的权限控制。 ❝ 前端…

第36章 封装驱动API接口实验

相信经过前面两个章节的学习已经能够熟练的使用ioctl函数了&#xff0c;在本章节会进行两个实验&#xff0c;每个实验的要完成的任务如下所示&#xff1a; 实验一&#xff1a;通过ioctl对定时器进行控制&#xff0c;分别实现打开定时器、关闭定时器和设置定时时间的功能。 实…

网络基础入门:数据通信与网络基础

1、什么是通信 通信&#xff0c;是指人与人、人与物、物与物之间通过某种媒介和行为进行的信息传递与交流。 2、什么是网络通信 网络通信&#xff0c;是指终端设备之间通过计算机网络进行的通信。 3、常见的术语 术语 说明 数据载荷 最终想要传递的信息 报文 网络中交…

迅为RK3568运行openkylin麒麟系统

RK3568开发板在发布之初已经开发了稳定又好用的Android11/12、Debian、Yocto、BuildrootQT5.15、Ubuntu18/20/22、OpenHarmony v3.2版本等系统。 经过后续的开发&#xff0c;RK3568现已适配openkylin麒麟系统。 CPU&#xff1a;iTOP-3568开发板采用瑞芯微RK3568处理器&#xf…

欠拟合与过拟合

目录 1、相关概念 学习目标 欠拟合与过拟合 2、原因以及解决办法 欠拟合 过拟合 ⭐正则化类别 Lasso &#x1f53a;Ridge &#x1f341;Lasso和Ridge的区别 3、拓展 极大似然估计 最大后验估计 最小二乘法 &#x1f343;作者介绍&#xff1a;双非本科大三网络工程…

xss-domcobble绕过XSSfilter

目录 DOM破坏的原理 例题 多层标签 HTMLCollection 一些常见的标签的关系 三层标签如何获取 例题 DOM破坏的原理 DOMClobber是一种攻击技术&#xff0c;它利用了DOM&#xff08;文档对象模型&#xff09;的特性来破坏或修改网页的结构和功能。 DOMClobber攻击通常发生…