首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >tomcat请求处理分析(六)servlet的处理过程

tomcat请求处理分析(六)servlet的处理过程

作者头像
cfs
发布于 2018-03-08 07:36:10
发布于 2018-03-08 07:36:10
2.4K00
代码可运行
举报
文章被收录于专栏:编码小白编码小白
运行总次数:0
代码可运行
1.1.1.1  servlet的解析过程

servlet的解析分为两步实现,第一个是匹配到对应的Wrapper,第二个是加载对应的servlet并进行数据,这些数据是怎么到界面的,response.getWrite()获取对应的流,然后写入这个流中,这个流中就有上文的outputBuffer。

匹配到对应Wrapper

   在上文中我们曾经走到过了doRun方法,现在就直接从这里开始

执行顺序如下:

NioEndpoint(run)==>下步调用doRun

NioEndpoint(doRun)==>下步调用state = handler.process(ka,status);

handler实例对象Http11ConnectionHandler其继承AbstractConnectionHandler

AbstractConnectionHandler(process) ==》下步调用 state = processor.process(wrapper);

processor实例对象Http11NioProcessor 其继承AbstractHttp11Processor

AbstractHttp11Processor(process)  ==》下步调用getAdapter().service(request, response);

CoyoteAdapter.service(request,response)这个方法就已经接近核心处理了,代码如下:

在第一处标红的地方,对请求进行了解析,并且匹配到对应的主机和context和wrapper

在第二处标红的地方是加载servlet并进行调用处理

在第三处标红的地方是刷新流,响应到界面

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@SuppressWarnings("deprecation")
@Override
publicvoid service(org.apache.coyote.Request req,
                    org.apache.coyote.Responseres)
    throws Exception {
    Request request = (Request)req.getNote(ADAPTER_NOTES);
    Response response =(Response) res.getNote(ADAPTER_NOTES);

    if (request == null) {

        //创建一个request对象
        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().setQueryStringEncoding
            (connector.getURIEncoding());

    }

    if (connector.getXpoweredBy()){
        response.addHeader("X-Powered-By",POWERED_BY);
    }

    boolean comet =false;
    boolean async = false;
    boolean postParseSuccess = false;

    try {
        //设置执行线程线程名
        req.getRequestProcessor().setWorkerThreadName(THREAD_NAME.get());
        //对uri进行解码,主要是解析报文,如果不合法返回响应码400
        postParseSuccess = postParseRequest(req,request, res, response);

        if (postParseSuccess) {
            //设置是否支持异步
            request.setAsyncSupported(connector.getService().getContainer().getPipeline().isAsyncSupported());

            //不断调用管道加载对应的servlet进行调用,其中传递了response参数,所以可以放入流数据
            connector.getService().getContainer().getPipeline().getFirst().invoke(request,response);

            if (request.isComet()) {
                if (!response.isClosed()&& !response.isError()) {
                    comet = true;
                    res.action(ActionCode.COMET_BEGIN, null);
                    if (request.getAvailable()|| (request.getContentLength() >0 &&(!request.isParametersParsed()))) {
                       event(req, res,SocketStatus.OPEN_READ);
                    }
                } else {
                          request.setFilterChain(null);
                }
            }
        }
        //如果是异步请求
        if (request.isAsync()){
            async = true;
            ReadListener readListener= req.getReadListener();
            if (readListener != null&&request.isFinished()) {
                ClassLoader oldCL =null;
                try {
                    oldCL =request.getContext().bind(false, null);
                    if (req.sendAllDataReadEvent()){
                       req.getReadListener().onAllDataRead();
                    }
                } finally {
                   request.getContext().unbind(false,oldCL);
                }
            }
            Throwable throwable =
                    (Throwable)request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);
            if (!request.isAsyncCompleting()&& throwable !=null) {
               request.getAsyncContextInternal().setErrorState(throwable, true);
            }
        } else if (!comet) {
            //如果为同步请求,Flush并关闭输入输出流
            request.finishRequest();
            response.finishResponse();
        }
    } catch (IOExceptione) {
        // Ignore
    } finally{
        AtomicBoolean error = new AtomicBoolean(false);
        res.action(ActionCode.IS_ERROR,error);

        if (request.isAsyncCompleting()&& error.get()) {
            res.action(ActionCode.ASYNC_POST_PROCESS,  null);
            async = false;
        }

        if (!async&& !comet) {
            if (postParseSuccess){
                        request.getMappingData().context.logAccess(
                        request, response,
                        System.currentTimeMillis()- req.getStartTime(),
                        false);
            }
        }

       req.getRequestProcessor().setWorkerThreadName(null);

        if (!comet &&!async) {
            request.recycle();
            response.recycle();
        } else{
            request.clearEncoders();
            response.clearEncoders();
        }
    }
}
1.1.1.1.1     构造对应request的MappingData属性
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
postParseRequest:CoyoteAdapter(org.apache.catalina.connector)
connector.getService().getMapper().map(serverName,decodedURI,version, request.getMappingData());
 
下面是request部分构造代码
protected final MappingDatamappingData =new MappingData();
public MappingDatagetMappingData() {
    return mappingData;
}

   根据调用方法,我们可以知道其传入的参数有request实例成员对象mappingData的引用类型,所以下面的匹配的Context以及Wrapper所到的mappingData都是当前request的属性

============================================

map: Mapper (org.apache.catalina.mapper)

internalMap(host.getCharChunk(),uri.getCharChunk(), version, mappingData);

internalMap:758,Mapper (org.apache.catalina.mapper)

在这里面匹配到了对应的虚拟主机,存放到了mappingData中去,以及Context主要采用了二分查找获取游标进行匹配

然后其调用internalMapWrapper(contextVersion,uri, mappingData);匹配到了Wrapper存放到mappingData,其匹配规则如下

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * a: 对全新的路径进行精准匹配
 * b: 对全新的路径进行通配符匹配
 * c: 根据全新的路径,进行查找是否存在相应的文件,如果存在相应的文件,则需要将该文件返回。在回前我们需要进一步确认,这个文件是不是讲文件内容源码返回,还是像jsp文件一样,进行一定的处理然后再返回,所以又要确认下文件的扩展名是怎样的
 *   c1: 尝试寻找能够处理该文件扩展名的servlet,即进行扩展名匹配,如果找到,则使用对应的servlet
 *   c2: 如果没找到,则默认使用defaultWrapper,即DefaultServlet(它只会将文件内容源码返回,不做任何处理)
 * d: 对全新的路径进行扩展名匹配(与c的目的不同,c的主要目的是想返回一个文件的内容,在返回内容前涉及到扩展名匹配,所以4c的前提是存在对应路径的文件)

 * 案例1: a.html,a、b没有匹配到,到c的时候,找到了该文件,然后又尝试扩展名匹配,来决定是走c1还是c2,由于.html还没有对应的servlet来处理,就使用了默认的DefaultServlet
 * 案例2: a.jsp,同上,在走到c的时候,找到了处理.jsp对应的servlet,所以走了c1
 * 案例3: a.action,如果根目录下有a.action文件,则走到c1的时候,进行扩展名匹配,匹配到了SecondServlet,即走了c1,使用SecondServlet来处理请求;如果根目录下没有a.action文件,则走到了d,进行扩展名匹配,同样匹配到了SecondServlet,即走了d,同样使用SecondServlet来处理请求
 * 案例4: first/abc,执行b的时候,就匹配到了FirstServlet,所以使用FirstServlet来处理请求
 * */
private final void internalMapWrapper(ContextVersioncontextVersion,
                                      CharChunkpath,
                                      MappingDatamappingData)throws IOException {

  。。。。。。。
        if (found) {
            mappingData.wrapperPath.setString(wrappers[pos].name);
            if (path.getLength() >length) {
                mappingData.pathInfo.setChars
                    (path.getBuffer(),
                     path.getOffset()+ length,
                     path.getLength()- length);
            }
            mappingData.requestPath.setChars
                (path.getBuffer(), path.getOffset(),path.getLength());
            mappingData.wrapper=wrappers[pos].object;
            mappingData.jspWildCard=wrappers[pos].jspWildCard;
        }
    }
}
1.1.1.1.2     加载servlet并调用

   一起执行顺序来看一下一个servlet如何进行加载

invoke:98,StandardEngineValve (org.apache.catalina.core)

代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * 基于请求的服务名选择合适的虚拟主机进行请求处理
 *
 * 如果不能匹配到对应主机,返回对应的http错误
 *
 * @param request 执行请求
 * @param response Response tobe produced
 *
 */
@Override
publicfinal void invoke(Request request,Response response)
    throws IOException,ServletException{

    //根据请求找到对应的host
    Host host =request.getHost();
    if (host == null) {
        response.sendError
            (HttpServletResponse.SC_BAD_REQUEST,
             sm.getString("standardEngine.noHost",
                          request.getServerName()));
        return;
    }
    //设置当前请求是否支持异步
    if (request.isAsyncSupported()){
       request.setAsyncSupported(host.getPipeline().isAsyncSupported());
    }

    //org.apache.catalina.valves.AccessLogValve[localhost]
   //org.apache.catalina.valves.ErrorReportValve[localhost]
   //org.apache.catalina.core.StandardHostValve[localhost]
    /**
     * 调用host的第一个valve
     *
     * 其执行原理是获取根据管道获取第一个阀门AccessLogValve调用其invoke方法
     *
     * AccessLogValve的invoke第一行调用getNext().invoke()调用了ErrorReportValve
     *
     * 同理调用了StandardHostValve的invoke方法所以实际调用的最先的是invoke方法
     * */
    host.getPipeline().getFirst().invoke(request,response);
}

invoke:143,StandardHostValve (org.apache.catalina.core)

  下步调用context.getPipeline().getFirst().invoke(request,response);

invoke:504,AuthenticatorBase(org.apache.catalina.authenticator)

   下步调用getNext().invoke(request, response);

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 *
 * 基于URI的request获取对应的Wrapper如果没有匹配到返回一个HTTP错误
 *
 * 在这个方法中做的事情主要是获取wrapper然后进行对应管道的阀门进行调用
 * @param request Request tobe processed
 * @param response Response tobe produced
 *
 * @exception IOException if aninput/output error occurred
 * @exception ServletException ifa servlet error occurred
 */
@Override
publicfinal void invoke(Request request,Response response)
    throws IOException,ServletException{

    // Disallow any directaccess to resources under WEB-INF or META-INF
    MessageBytesrequestPathMB = request.getRequestPathMB();
    if ((requestPathMB.startsWithIgnoreCase("/META-INF/",0))
            ||(requestPathMB.equalsIgnoreCase("/META-INF"))
            ||(requestPathMB.startsWithIgnoreCase("/WEB-INF/",0))
            ||(requestPathMB.equalsIgnoreCase("/WEB-INF"))) {
       response.sendError(HttpServletResponse.SC_NOT_FOUND);
        return;
    }

    //获取当前request利用的wrapper
    Wrapper wrapper =request.getWrapper();
    if (wrapper == null||wrapper.isUnavailable()) {
       response.sendError(HttpServletResponse.SC_NOT_FOUND);
        return;
    }

    // Acknowledge the request
    try {
        response.sendAcknowledgement();
    } catch(IOExceptionioe) {
        container.getLogger().error(sm.getString(
                "standardContextValve.acknowledgeException"),ioe);
        request.setAttribute(RequestDispatcher.ERROR_EXCEPTION,ioe);
        response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
        return;
    }

    if (request.isAsyncSupported()){
       request.setAsyncSupported(wrapper.getPipeline().isAsyncSupported());
    }
    
   wrapper.getPipeline().getFirst().invoke(request,response);
}

invoke:98,StandardWrapperValve (org.apache.catalina.core)

其主要操作如下:获取到对应的StandardWrapper,然后分配一个servlet,具体在loadServlet中进行实例话,再分配由于是成员变量,只有第一次调用的时候才会进行分配,之后直接返回第一次的实例化对一下对象,具体看allocate方法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public final void invoke(Request request,Responseresponse)
    throws IOException,ServletException{

 
   //获取到对应的StandardWrapper
    StandardWrapper wrapper= (StandardWrapper) getContainer();
    //每个请求开始servlet都是为空
    Servlet servlet = null;
    Context context =(Context) wrapper.getParent();
    try {
        if (!unavailable){
            servlet = wrapper.allocate();
        }
    }        

    MessageBytes requestPathMB =request.getRequestPathMB();
    DispatcherTypedispatcherType = DispatcherType.REQUEST;
    if (request.getDispatcherType()==DispatcherType.ASYNC)dispatcherType = DispatcherType.ASYNC;
    request.setAttribute(Globals.DISPATCHER_TYPE_ATTR,dispatcherType);
    request.setAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR,
            requestPathMB);
    // 创建过滤器实例
    ApplicationFilterChain filterChain =
            ApplicationFilterFactory.createFilterChain(request,wrapper, servlet);

  
        if ((servlet !=null) &&(filterChain !=null)) {
          if (context.getSwallowOutput()) {
                try {
                    SystemLogHandler.startCapture();
                    if (request.isAsyncDispatching()){
                        request.getAsyncContextInternal().doInternalDispatch();
                    } else if(comet) {
                       filterChain.doFilterEvent(request.getEvent());
                    } 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 if(comet) {
                   filterChain.doFilterEvent(request.getEvent());
                } else{
                    filterChain.doFilter
                       (request.getRequest(), response.getResponse());
                }
            }

        }
    }

    //释放过滤器
    if (filterChain!=null) {
        if (request.isComet()){
            // If this is a Cometrequest, then the same chain will be used for the
            // processing of allsubsequent events.
            filterChain.reuse();
        } else{
            filterChain.release();
        }
    }

    // Deallocate theallocated servlet instance
    try {
        if (servlet !=null) {
            wrapper.deallocate(servlet);
        }
    } catch (Throwablee) {
        ExceptionUtils.handleThrowable(e);
        container.getLogger().error(sm.getString("standardWrapper.deallocateException",
                         wrapper.getName()),e);
        if (throwable == null) {
            throwable = e;
            exception(request,response, e);
        }
    }


    try {
        if ((servlet !=null) &&
            (wrapper.getAvailable() ==Long.MAX_VALUE)) {
            wrapper.unload();
        }
    } catch (Throwablee) {
        ExceptionUtils.handleThrowable(e);
        container.getLogger().error(sm.getString("standardWrapper.unloadException",
                         wrapper.getName()),e);
        if (throwable == null) {
            throwable = e;
            exception(request,response, e);
        }
    }
    long t2=System.currentTimeMillis();

    long time=t2-t1;
    processingTime += time;
    if( time > maxTime)maxTime=time;
    if( time < minTime)minTime=time;
}

   servlet的调用

   按照这个顺序执行完所有过滤器就会执行对应的servlet,这是因为在创建过滤器

ApplicationFilterChain filterChain =             ApplicationFilterFactory.createFilterChain(request,wrapper, servlet);    的时候,将servlet给注入进去了,当过滤器执行完了,会执行调用servlet的service, 由于自己写的servlet是会继承HttpServlet的,所以将调用其service方法

   调用如下:

internalDoFilter:,ApplicationFilterChain

方法如下:下面展示了两个service ,同在HttpServlet只是方法的参数有所不同,加载过程先调用一个,然后第一个再调用第二个,根据请求方法调用自己对应的Servlet中的doGet等一些列方法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
protected void service(HttpServletRequest req,HttpServletResponseresp)
    throws ServletException,IOException{

    //获取对应的方法
    String method = req.getMethod();

    /**
     * 根据请求method调用对应方法
     * GET ==>doGet(req, resp)
     * head ==> doHead(req, resp)
     * post ==>doPost(req,resp)
     *
     * */
    if (method.equals(METHOD_GET)) {
        long lastModified= getLastModified(req);
        if (lastModified == -1) {
            // servlet doesn't supportif-modified-since, no reason
            // to go through furtherexpensive logic
            doGet(req,resp);
        } else{
            long ifModifiedSince;
            try {
                ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
            } catch(IllegalArgumentExceptioniae) {
                // Invalid date header -proceed as if none was set
                ifModifiedSince = -1;
            }
            if (ifModifiedSince< (lastModified /1000 * 1000)) {
                // If the servlet mod timeis later, call doGet()
                // Round down to thenearest second for a proper compare
                // A ifModifiedSince of-1 will always be less
                maybeSetLastModified(resp,lastModified);
                doGet(req,resp);
            } else{
               resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
            }
        }

    } else if (method.equals(METHOD_HEAD)) {
        long lastModified =getLastModified(req);
        maybeSetLastModified(resp,lastModified);
        doHead(req,resp);

    } else if(method.equals(METHOD_POST)) {
        doPost(req, resp);

    } else if(method.equals(METHOD_PUT)) {
        doPut(req, resp);

    } else if(method.equals(METHOD_DELETE)) {
        doDelete(req, resp);

    } else if(method.equals(METHOD_OPTIONS)) {
        doOptions(req,resp);

    } else if(method.equals(METHOD_TRACE)) {
        doTrace(req,resp);

    } else {
        //
        // Note that this means NOservlet supports whatever
        // method was requested, anywhereon this server.
        //

        String errMsg = lStrings.getString("http.method_not_implemented");
        Object[] errArgs = newObject[1];
        errArgs[0] = method;
        errMsg = MessageFormat.format(errMsg,errArgs);

        resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED,errMsg);
    }
}

上面已经讲述了一个servlet调用的过程,他的信息是如何返回掉流中,我们的看一下response,getWrite方法

  可以看出这个流最终将outputBuffer给封装,其write方法

  所以是写到上文封装的流中,最后一并解析到页面,可以参照请求到响应流。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2017年08月19日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Tomcat卷二---请求流程源码分析
设计了这么多层次的容器,Tomcat是怎么确定每一个请求应该由哪个Wrapper容器里的 Servlet来处理的呢?
大忽悠爱学习
2022/05/09
8130
Tomcat卷二---请求流程源码分析
【Tomcat源码分析】从零开始理解 HTTP 请求处理 (第三篇)
承接上文容器处理机制,当 postParseRequest方法返回真值时,容器将继续处理请求。在 service 方法中,通过 connector.getService().getContainer().getPipeline().getFirst().invoke(request, response) 这行代码,容器将请求传递至管道的第一步,开启后续的处理流程。
@派大星
2024/10/08
2090
【Tomcat源码分析】从零开始理解 HTTP 请求处理 (第三篇)
Tomcat源码解析(八):一个请求的执行流程(附Tomcat整体总结)
Tomcat源码解析(四):StandardServer和StandardService
Java微观世界
2025/01/21
3180
Tomcat源码解析(八):一个请求的执行流程(附Tomcat整体总结)
Tomcat笔记:Tomcat的执行流程解析
Bootstrap的main方法先new了一个自己的对象(Bootstrap),然后用该对象主要执行了四个方法:
朝雨忆轻尘
2019/06/19
1K0
Tomcat笔记:Tomcat的执行流程解析
Tomcat中Filter是怎样执行的
Filter是什么?Filter是servlet规范中定义的java web组件, 在所有支持java web的容器中都可以使用 它是位于前端请求到servlet之间的一系列过滤器,也可以称之为中间件,它主要是对请求到达servlet之前做一些额外的动作:
tunsuy
2022/10/27
6710
tomcat源码解读五 Tomcat中Request的生命历程
     Request在tomcat中是一个非常核心的的实例,下面以NIO为例来解读一下在各个时期下的状态(其实在Tomcat的几种模式中到了这里之后的处理都是差不多的) 1.1 创建coyote/Request      这个request并不是我们最终在servlet中使用的Request,它是tomcat内部处理请求的一种有效方法,其创建过程是在接收到客户请求处理套接字构建Processor具体实现类的构造器中构建,以NIO模式为例则是在实例化请求处理类Http11NioProcessor时候构建,
cfs
2018/03/08
2.2K0
tomcat源码解读五  Tomcat中Request的生命历程
谈谈 Tomcat 请求处理流程
来源:github.com/c-rainstorm/blog/blob/master/tomcat/
芋道源码
2020/07/03
1.6K0
谈谈 Tomcat 请求处理流程
【Tomcat】《How Tomcat Works》英文版GPT翻译(第十一章)
You have learned in Chapter 5 that there are four types of containers: engine, host, context, and wrapper. You have also built your own simple contexts and wrappers in previous chapters. A context normally has one or more wrappers, in which each wrapper represents a servlet definition. This chapter will now look at the standard implementation of the Wrapper interface in Catalina. It starts by explaining the sequence of methods that get invoked for each HTTP request and continues with the javax.servlet.SingleThreadModel interface. The chapter concludes by explaining the StandardWrapper and StandardWrapperValve classes. The application that accompanies this chapter uses StandardWrapper instances to represents servlets.
阿东
2024/01/19
2040
【Tomcat】《How Tomcat Works》英文版GPT翻译(第十一章)
深入理解 Servlet
Servlet(Server Applet)是Java Servlet的简称,称为小服务程序或服务连接器,用Java编写的服务器端程序,具有独立于平台和协议的特性,主要功能在于交互式地浏览和生成数据,生成动态Web内容。
周三不加班
2019/06/04
9250
深入理解 Servlet
聊聊springboot session timeout参数设置
本文主要介绍下spring boot中对session timeout参数值的设置过程。
code4it
2018/09/17
12.5K0
tomcat如何关闭response的outputStream
在写文件下载的时候,遇到了一个问题,就是这个ServletOutputStream到底要不要自己flush以及close。这里以tomcat容易为例,解读一下。
code4it
2018/09/17
2.1K0
jetty、servlet以及spring的衔接源码分析
对于一个请求来讲,如果只是需要一个静态页面,可以直接在服务器上根据路径访问得到,但是如果请求的数据是一个动态页面,即只有在运行时从后台数据库获取,再拼装东西返回,然后生成一个对应的html文件。在Java中为了实现这个功能,使用的就是Servlet规范。
爬蜥
2019/05/26
7830
6张图说清楚Tomcat原理及请求流程
很多东西在时序图中体现的已经非常清楚了,没有必要再一步一步的作介绍,本文以图为主,然后对部分内容加以简单解释。
程序员追风
2019/09/10
5930
6张图说清楚Tomcat原理及请求流程
从零开始手写Tomcat的教程11节----StandardWrapper
记录已经创建的存活的servlet对象实例个数,包括STM和non-STM的servlet类实例
大忽悠爱学习
2022/05/10
5330
从零开始手写Tomcat的教程11节----StandardWrapper
Tomcat内存马之Filter内存马剖析
在Tomcat中Filter是一种可用于拦截HTTP请求和响应的组件,Filter可以在请求到达Servlet之前对请求进行预处理,在响应返回给客户端之前对响应进行后处理,从而实现一些共性的处理逻辑,比如:日志记录、权限校验、字符编码转换等
Al1ex
2025/02/08
2600
Tomcat内存马之Filter内存马剖析
走进JavaWeb技术世界8:浅析Tomcat9请求处理流程与启动部署过程
本系列文章将整理到我在GitHub上的《Java面试指南》仓库,更多精彩内容请到我的仓库里查看
Java技术江湖
2019/10/22
3370
面试官:谈谈 Tomcat 请求处理流程,我一脸懵逼。。
很多东西在时序图中体现的已经非常清楚了,没有必要再一步一步的作介绍,所以本文以图为主,然后对部分内容加以简单解释。
良月柒
2021/07/13
4231
面试官:谈谈 Tomcat 请求处理流程,我一脸懵逼。。
Tomcat Filter之动态注入
最近,看到好多不错的关于“无文件Webshell”的文章,对其中利用上下文动态的注入Filter的技术做了一下简单验证,写一下测试总结,不依赖任何框架,仅想学习一下tomcat的filter。
CN_Simo
2020/07/07
1.4K0
走进JavaWeb技术世界8:浅析Tomcat9请求处理流程与启动部署过程
本系列文章将整理到我在GitHub上的《Java面试指南》仓库,更多精彩内容请到我的仓库里查看
Java技术江湖
2019/11/19
3820
走进JavaWeb技术世界8:浅析Tomcat9请求处理流程与启动部署过程
Java-Servlet请求方式doXXX、service 具体分析
说起Servlet的接收处理请求的方式,想必各位都并不陌生,如doGet、doPost、service...
Arebirth
2019/09/24
5570
Java-Servlet请求方式doXXX、service 具体分析
推荐阅读
相关推荐
Tomcat卷二---请求流程源码分析
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档