龙空技术网

Spring源码分析——一次HTTP请求在Spring中的处理过程

图灵课堂程序员小陳 265

前言:

而今兄弟们对“前端phpget请求协议源码”大致比较重视,兄弟们都想要了解一些“前端phpget请求协议源码”的相关文章。那么小编在网上汇集了一些有关“前端phpget请求协议源码””的相关文章,希望朋友们能喜欢,兄弟们一起来学习一下吧!

一次HTTP请求,要想访问到Web服务器上,前期要在网络环境中经历复杂流程,本文重点关注的是请求到达Web服务器之后在Spring容器中的处理过程,前面的建立连接、域名解析、负载均衡等内容会稍带着说一下。

网络部分域名解析

在域名解析的过程中会有多级的缓存,浏览器首先看一下自己的缓存里有没有,如果没有就向操作系统的缓存要,还没有就检查本机域名解析文件hosts,访问hosts文件找IP地址就是本机的DNS解析。

如果你要访问的资源是静态资源,比如图片、CSS样式表,DNS解析给出的IP地址就是CDN服务器的地址,你拿到的就会是CDN服务器而不是目标网站的实际地址。因为CDN会缓存网站的大部分资源。

由PHP、Java等后台服务动态生成的页面属于“动态资源”,CDN无法缓存,只能从目标网站获取。此时HTTP请求最终会访问到网站IP。

目标网站的服务器对外表现的是一个IP地址,但为了能够扛住高并发,在内部也是一套复杂的架构。通常在入口是负载均衡设备,例如四层的LVS或者七层的Nginx,在后面是许多的服务器,构成一个更强更稳定的集群。

Nginx反向代理和负载均衡

提到反向代理,就要先说正向代理。正向代理一般叫代理。就是请求发起人找一个代理做一件事情,真正做事情的人只认识代理不认识请求发起人。常用的翻墙,就是HTTP中介的一种代理。所以我们使用翻墙代理服务器连接上了国外的网站,但那个网站并不知道我们在使用。

反向代理是服务提供方出一个代理,请求人只跟代理打交道。比如这里请求只发给了Nginx这个代理,后面的就是Nginx自己进行处理,找到真正的业务处理服务器。如果是静态请求,如CSS啥的,就直接转向静态服务器。如果是动态请求就转向Web应用服务器。然而不管是静态服务器还是Web应用服务器都是有好几台服务器。Nginx按照一定的策略对请求向业务服务器进行分配,让压力不集中在1台服务器,这就是负载均衡了。

Nginx除了配置实际处理业务的服务器,还可以配置一些安全策略,比如如果一个IP在1秒内请求了100次,那么将视为攻击,直接返回错误码,不向后传递请求。还可以配置超时等待时间,多媒体文件的允许大小等。

Web应用服务

请求根据Nginx提供的IP和端口找到服务器上对应的Web前端服务。我们服务器上部署的是Tomcat。我们的Web服务采用的是Spring MVC框架,终于说到正题上了。

建立连接

HTTP协议是运行在TCP/IP 基础上的,依靠TCP/IP协议来实现数据的可靠传输。所以浏览器要用HTTP协议收发数据,首先要做的就是建立TCP连接。

Web服务器的默认端口是80,所以浏览器依照TCP协议的规范,使用“三次握手”建立与Web服务器的连接。有了可靠的TCP连接通道后,浏览器按照HTTP协议规定的格式,通过TCP 发送了一个请求报文。Web服务器收到报文后在内部就要处理这个请求。同样也是依据 HTTP协议的规定,解析报文。

HTTP报文的结构

HTTP协议的请求报文和响应报文的结构基本相同,由三大部分组成:

起始行(start line): 描述请求或响应的基本信息;头部字段集合(header):使用 key-value 形式更详细地说明报文;消息正文(entity):实际传输的数据,它不一定是纯文本,可以是图片、视频等二进制数据。

这其中前两部分起始行和头部字段经常又合称为“请求头”或“响应头”,消息正文又称为“实体”,与header对应,很多时候就直接称为body。

HTTP协议规定报文必须有header,但可以没有body,而且在header之后必须要有一个“空行”,也就是“CRLF”,十六进制的“0D0A”。所以,一个完整的HTTP报文就像是下图的这个样子,注意在header和body之间有一个“空行”。

在这个浏览器发出的请求报文里,第一行“GET / HTTP/1.1”就是请求行,而后面的“Host”、“Connection”等等都属于header,报文的最后是一个空白行结束,没有body。在很多时候,特别是浏览器发送GET请求的时候都是这样,HTTP报文经常是只有header而没body,相当于只发了一个超级“大头”过来,每时每刻网络上都会有数不清的“大头”在跑来跑去。

进入Web服务器后的处理必知背景:Servlet规范

为什么要介绍Servlet呢?原因不难理解,Spring的Web框架——Spring MVC就是基于Servlet规范实现的。所以要分析Spring MVC,首先应追根溯源,弄懂Servlet,因此这里需要讲一下Servlet规范。

Servlet是J2EE标准的一部分,是一种运行在Web服务器端的小型Java程序,更具体地说,Servlet是按照Servlet规范编写的一个Java类,用于交互式地浏览和修改数据,生成动态Web内容。在遵守Servlet规范的前提下,我们可将Web应用部署在Servlet容器下。这样做的好处是什么呢?

我觉得可以使开发者聚焦业务逻辑,而不用去关心HTTP协议方面的事情。试想如果我们为了写一个Web应用,还要去解析HTTP协议相关的内容,那会增加很多工作量。如果我们写的Web应用不大,不夸张地说,项目中对HTTP报文解析提供支持的代码会比业务代码还要多,这岂不是得不偿失。

当然,在现实中,有现成的框架可用,并不需要自己造轮子。如果我们基于Servlet规范实现Web应用的话,HTTP协议的处理过程就不需要我们参与了。这些工作交给Servlet容器(例如Tomcat和Jetty)去做就行了,我们只需要关心业务逻辑怎么实现即可。

在Servlet完成初始化后,针对外部对Servlet的每次请求,Servlet容器就可以使用它处理客户端请求了。客户端请求由ServletRequest类型的请求对象表示。Servlet封装响应并返回给请求的客户端,该响应由ServletResponse类型的响应对象表示,这两个对象是由容器通过参数传递到Servlet接口的service()方法。在HTTP请求的场景下,容器提供的请求和响应对象具体类型分别是HttpServletRequest和HttpServletResponse。

浏览器通过HTTP请求访问Servlet的交互过程如下图所示:

不同的Servlet容器都会实现对Servlet规范的实现,其中标准的Servlet规范的实现在javax.servlet-api这个jar包里,在Tomcat里面能找到,我们就从这个jar包中HttpServlet类开始分析,其service()方法源码如下:

//javax.servlet.http.HttpServlet.java@Overridepublic void service(ServletRequest req, ServletResponse res)    throws ServletException, IOException{    HttpServletRequest  request;    HttpServletResponse response;    //验证请求跟返回信息封装对象的类型是不是servlet规范中定义的类型的    //否则抛异常    if (!(req instanceof HttpServletRequest &&            res instanceof HttpServletResponse)) {        throw new ServletException("non-HTTP request or response");    }    //转为HttpServletRequest跟HttpServletResponse类型    request = (HttpServletRequest) req;    response = (HttpServletResponse) res;    service(request, response);}

可以看到其主要的逻辑就是对请求跟返回对象的验证,确保是按照Servlet规范来的。后面自己定义了一个service()方法来完成后面的请求区分逻辑。

//javax.servlet.http.HttpServlet.javaprotected void service(HttpServletRequest req, HttpServletResponse resp)        throws ServletException, IOException {    //获取请求的方法值    String method = req.getMethod();    //判断是不是get请求    if (method.equals(METHOD_GET)) {        //判断之前这个请求对象的最后修改时间是否修改过        long lastModified = getLastModified(req);        //如果值为-1,表示当前的servlet对象不支持请求头中的if-modified-since,这时候就需要调用后面的代码逻辑,代价高        if (lastModified == -1) {            doGet(req, resp);        } else {            //如果不是-1,表示支持,然后获取请求头中的if-modified-since参数            long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);            //如果请求头中的时间小于上次的请求时间,则表示页面需要进行刷新            if (ifModifiedSince < lastModified) {                //设置返回体中的Last-Modified参数                maybeSetLastModified(resp, lastModified);                //进行后面的逻辑                doGet(req, resp);            } else {                //如果两个时间相等则设置返回体的请求状态为304                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 {        String errMsg = lStrings.getString("http.method_not_implemented");        Object[] errArgs = new Object[1];        errArgs[0] = method;        errMsg = MessageFormat.format(errMsg, errArgs);        resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);    }}

可以看到这里的作用其实就是对请求中的请求类型进行判断,来选择后面的逻辑。其中对get请求的分析后面多了一些处理,这些处理是为了避免服务器对不必要逻辑的处理。

Spring对Servlet规范的实现

有了以上Servlet规范的基础知识,我们来看Spring是如何实现规范的。Spring中对Servlet规范的实现类是FrameworkServlet类。其中FrameworkServlet继承了HttpServletBean,因此这个类对doPost()、doDelete()等方法都有实现,但是实现都是差不多的,这里就分析常用的post和get请求的处理逻辑。

//HttpServletBean继承了HttpServlet类public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {    //省略...    @Override    protected final void doGet(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        //进行请求的处理        processRequest(request, response);    }    @Override    protected final void doPost(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        //进行请求的处理                  processRequest(request, response);    }    //省略...}

实际的处理逻辑都在processRequest()这个方法,它是一个protected方法,源码如下:

//FrameworkServlet.javaprotected final void processRequest(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {    //获取当前系统时间,用来计算这个步骤的处理耗时    long startTime = System.currentTimeMillis();    Throwable failureCause = null;    //获取当前服务运行所在地区,在RequestContextFilter中进行处理设值    LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();    //创建相关地区的对象的LocalContext,    LocaleContext localeContext = buildLocaleContext(request);    //获取请求的属性,请求相关属性会在RequestContextHolder中,它是用ThreadLocal实现的    RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();    //创建requestAttributes,ServletRequestAttributes是RequestAttributes子类    ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);    //从request获取WebAsyncManager,在filter阶段会创建WebAsyncManager,表示是不是异步相应的请求    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);    //将FrameworkServlet的内部类RequestBindingInterceptor设置到asyncManager中,用于在异步中初始化跟重新设置FrameworkServlet    asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());    //将localeContext跟requestAttributes设置到LocaleContextHolder跟RequestContextHolder中    initContextHolders(request, localeContext, requestAttributes);    try {        //进行业务处理        doService(request, response);    }    catch (ServletException | IOException ex) {        failureCause = ex;        throw ex;    }    catch (Throwable ex) {        failureCause = ex;        throw new NestedServletException("Request processing failed", ex);    }    finally {        //移除ThreadLocal中的请求相关信息,attribute跟context信息        resetContextHolders(request, previousLocaleContext, previousAttributes);        if (requestAttributes != null) {            requestAttributes.requestCompleted();        }        //打印结果        logResult(request, response, failureCause, asyncManager);        //发布请求处理完成事件        publishRequestHandledEvent(request, response, startTime, failureCause);    }}

在这个方法中主要的逻辑还是准备处理请求需要的上下文和参数等。其中,最重要的就是负责业务处理的doService()方法,它是一个抽象方法,必须由子类来实现。而FrameworkServlet类只有一个子类,那就是大名鼎鼎的DispatchServlet类,来看DispatchServlet类中对doService()方法的实现。

//DispatchServlet.javaprotected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {        logRequest(request);    //如果是一个include请求,<jsp:incluede page="xxx.jsp"/> 这种    //可能在一个请求中嵌套了另外的一个请求,因此需要备份当前请求    Map<String, Object> attributesSnapshot = null;    //是否是一个include请求,通过request中的javax.servlet.include.request_uri属性判断    //JSP在运行期间是会被编译成相应的Servlet类来运行的,    if (WebUtils.isIncludeRequest(request)) {        attributesSnapshot = new HashMap<>();        Enumeration<?> attrNames = request.getAttributeNames();        while (attrNames.hasMoreElements()) {            String attrName = (String) attrNames.nextElement();            //获取包含的请求中的获取A请求的内部B请求设定的spring的策略            if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {                attributesSnapshot.put(attrName, request.getAttribute(attrName));            }        }    }    //设置web应用上下文到请求中,    request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());    //设置本地解析器    request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);    //设置主题解析器    request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);    //设置主题,如果没有设置则为null,默认的为WebApplicationContext    request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());    //将request跟response保存到一个FlashMap中,FlashMap用来将一个请求跟    //另外一个请求关联起来,通常在redirect的时候有用    if (this.flashMapManager != null) {        //如果当前请求的FlashMap在之前的请求中保存过了,则取出来,并去除对应的缓存        FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);        //保存FlashMap到属性中        if (inputFlashMap != null) {            request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));        }        request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());        request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);    }//DispatchServlet.javaprotected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {    HttpServletRequest processedRequest = request;    HandlerExecutionChain mappedHandler = null;    boolean multipartRequestParsed = false;    //从request中获取WebAsyncManager    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);    try {        ModelAndView mv = null;        Exception dispatchException = null;        try {            //检查是不是multipart请求并将请求转化为MultipartHttpServletRequest类型的            processedRequest = checkMultipart(request);            //如果请求不是原来的request请求,则表示是multipart请求并且解析过的            multipartRequestParsed = (processedRequest != request);            //根据request中的url从hanlderMapping中获取            //封装了HandlerInterceptor的HandlerExecutionChain            mappedHandler = getHandler(processedRequest);            //如果不存在对应的处理链则返回            if (mappedHandler == null) {                noHandlerFound(processedRequest, response);                return;            }            //从HandlerExecutionChain中获取Handler然后寻找合适的HandlerAdapter            HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());            //获取请求方式            String method = request.getMethod();            boolean isGet = "GET".equals(method);            //检查是不是GET请求            if (isGet || "HEAD".equals(method)) {                //检查当前的get类型的请求的最后修改时间是不是存在的                //这个参数用来减少数据传输用                long lastModified = ha.getLastModified(request, mappedHandler.getHandler());                //如果浏览器请求中的最后请求时间跟服务器的最后修改时间一致                //并且是get类型请求则直接返回。                if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {                    return;                }            }            //调用applyPreHandle方法,执行全部的前置拦截器            if (!mappedHandler.applyPreHandle(processedRequest, response)) {                return;            }            //执行对应的HandlerAdapter的Handler方法,拿到对应的视图            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());            //检查当前的请求是否正在异步处理,如果是的则直接放弃并返回            if (asyncManager.isConcurrentHandlingStarted()) {                return;            }            //如果处理的结果返回的视图是空的则使用默认的视图,不为空则用处理的结果            applyDefaultViewName(processedRequest, mv);            //调用applyPostHandle方法执行后置拦截器            mappedHandler.applyPostHandle(processedRequest, response, mv);        }        catch (Exception ex) {            dispatchException = ex;        }        catch (Throwable err) {            //进行错误视图的处理            dispatchException = new NestedServletException("Handler dispatch failed", err);        }        //对正常视图或者错误视图的处理        processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);    }    catch (Exception ex) {        triggerAfterCompletion(processedRequest, response, mappedHandler, ex);    }    catch (Throwable err) {        triggerAfterCompletion(processedRequest, response, mappedHandler,                new NestedServletException("Handler processing failed", err));    }    finally {        //检查当前的请求是否正在异步处理        if (asyncManager.isConcurrentHandlingStarted()) {            //如果mappedHandler不是null,则调用对应的mappedHandler中的AsyncHandlerInterceptor            if (mappedHandler != null) {                mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);            }        }        else {            //对流类型的请求,做后置的处理            if (multipartRequestParsed) {                cleanupMultipart(processedRequest);            }        }    }}    try {        //请求分发处理        doDispatch(request, response);    }    finally {        if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {            // Restore the original attribute snapshot, in case of an include.            if (attributesSnapshot != null) {                restoreAttributesAfterInclude(request, attributesSnapshot);            }        }    }}

以上最重要的就是负责请求分发的doDispatch()方法,看懂doDispatch()方法,就能彻底理解一次HTTP请求进入到Spring之后,要做哪些处理。

//DispatchServlet.javaprotected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {    HttpServletRequest processedRequest = request;    HandlerExecutionChain mappedHandler = null;    boolean multipartRequestParsed = false;    //从request中获取WebAsyncManager    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);    try {        ModelAndView mv = null;        Exception dispatchException = null;        try {            //检查是不是multipart请求并将请求转化为MultipartHttpServletRequest类型的            processedRequest = checkMultipart(request);            //如果请求不是原来的request请求,则表示是multipart请求并且解析过的            multipartRequestParsed = (processedRequest != request);            //根据request中的url从hanlderMapping中获取            //封装了HandlerInterceptor的HandlerExecutionChain            mappedHandler = getHandler(processedRequest);            //如果不存在对应的处理链则返回            if (mappedHandler == null) {                noHandlerFound(processedRequest, response);                return;            }            //从HandlerExecutionChain中获取Handler然后寻找合适的HandlerAdapter            HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());            //获取请求方式            String method = request.getMethod();            boolean isGet = "GET".equals(method);            //检查是不是GET请求            if (isGet || "HEAD".equals(method)) {                //检查当前的get类型的请求的最后修改时间是不是存在的                //这个参数用来减少数据传输用                long lastModified = ha.getLastModified(request, mappedHandler.getHandler());                //如果浏览器请求中的最后请求时间跟服务器的最后修改时间一致                //并且是get类型请求则直接返回。                if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {                    return;                }            }            //调用applyPreHandle方法,执行全部的前置拦截器            if (!mappedHandler.applyPreHandle(processedRequest, response)) {                return;            }            //执行对应的HandlerAdapter的Handler方法,拿到对应的视图            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());            //检查当前的请求是否正在异步处理,如果是的则直接放弃并返回            if (asyncManager.isConcurrentHandlingStarted()) {                return;            }            //如果处理的结果返回的视图是空的则使用默认的视图,不为空则用处理的结果            applyDefaultViewName(processedRequest, mv);            //调用applyPostHandle方法执行后置拦截器            mappedHandler.applyPostHandle(processedRequest, response, mv);        }        catch (Exception ex) {            dispatchException = ex;        }        catch (Throwable err) {            //进行错误视图的处理            dispatchException = new NestedServletException("Handler dispatch failed", err);        }        //对正常视图或者错误视图的处理        processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);    }    catch (Exception ex) {        triggerAfterCompletion(processedRequest, response, mappedHandler, ex);    }    catch (Throwable err) {        triggerAfterCompletion(processedRequest, response, mappedHandler,                new NestedServletException("Handler processing failed", err));    }    finally {        //检查当前的请求是否正在异步处理        if (asyncManager.isConcurrentHandlingStarted()) {            //如果mappedHandler不是null,则调用对应的mappedHandler中的AsyncHandlerInterceptor            if (mappedHandler != null) {                mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);            }        }        else {            //对流类型的请求,做后置的处理            if (multipartRequestParsed) {                cleanupMultipart(processedRequest);            }        }    }}

在网上有很多围绕DispatcherServlet的doDispatch()方法说明Spring如何处理请求的图。借用网上一张图描述了DispatchServlet对请求的处理过程。

整个流程梳理如下:

首先会检查当前的请求是不是multipart/form-data类型的请求,是则将请求转换为MultipartHttpServletRequest类型的request解析出URL从HandlerMapping对象中找到对应的封装了HandlerInterceptor集合的HandlerExecutionChain对象,没有找到就直接返回404错误页面从HandlerExecutionChain中获取Handler然后寻找合适的HandlerAdapter检查是不是get请求,如果是,然后在检查lastModified这个属性选择是直接返回还是进行后续的请求处理。调用前面选择的HandlerAdapter的applyPreHandle方法,取到所有拦截器后,for循环调用每个拦截器HandlerInterceptor的preHandle()方法调用前面选择的HandlerAdapter的handle方法,进行逻辑的处理,返回ModelAndView对象检查当前的请求是否正在异步处理,如果是则直接放弃并返回检查返回的ModelAndView的视图是不是null,是则返回默认的,不是则不处理调用前面选择的HandlerAdapter的applyPostHandle方法,取到所有拦截器后,for循环调用每个拦截器HandlerInterceptor的postHandle()方法对正常返回的或者发生异常时生成的视图进行处理,对异步请求或multipart/form-data类型请求进行后续的处理

虽然整个的request请求的逻辑只有这么多,但是里面的每个步骤都是比较复杂的。

我们在业务代码中经常写的响应前端请求的Controller在哪儿呢,上图中并没有画出。实际上,在应用启动的时候,Spring容器会加载这些Controller类,并且解析出URL对应的处理函数,封装成Handler对象,存储到HandlerMapping对象中。当有请求到来的时候,DispatcherServlet从HanderMapping中,查找请求URL对应的Handler,然后调用执行Handler对应的函数代码,最后将执行结果返回给客户端。

为什么要把Controller封装成Handler对象呢,因为定义Controller的方式非常多,是不统一的。以下就展示了三种方式的Controller定义,当然我们用得最多的还是方式一。

//方式一:通过@Controller、@RequestMapping来定义@Controllerpublic class DemoController {    @RequestMapping("/getUserName")    public ModelAndView getUserName() {        ModelAndView model = new ModelAndView("Greeting");        model.addObject("message", "TOM");        return model;    }}//方式二:实现Controller接口 + xml配置文件:配置DemoController与URL的对应关系public class DemoControllerImpl implements Controller {    @Override    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {        ModelAndView model = new ModelAndView("HelloWorld");        model.addObject("message", "HelloWorld");        return model;    }}//方式三:继承HttpServlet抽象类 + xml配置文件:配置DemoExtendServletController类与URL的对应关系public class DemoExtendServletController extends HttpServlet {    @Override    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {        this.doPost(req, resp);    }    @Override    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {        resp.getWriter().write("Hello World!");    }

方式一中的函数的定义很随意、不固定,方式二中的函数定义是 handleRequest()、方式三中的函数定义是HttpServlet的service()方法。如果不作适配,DispatcherServlet需要根据不同类型的Controller,调用不同的函数,会有很多if-else分支判断,而且,如果要增加一个新的Controller的定义方法,我们就要在DispatcherServlet类代码中,对应地增加一段if逻辑,这显然不符合开闭原则。Spring利用适配器模式对代码进行改造,定义了统一的接口HandlerAdapter,并且对每种Controller定义了对应的适配器类。在DispatcherServlet类中,就不需要区分对待不同的Controller对象了,统一调用HandlerAdapter的handle()函数就可以了。详情可参考博文Spring中用到的几种典型的设计模式的适配器模式部分。

标签: #前端phpget请求协议源码