博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
spring mvc之请求过程源码分析
阅读量:6835 次
发布时间:2019-06-26

本文共 10117 字,大约阅读时间需要 33 分钟。

简介

上一篇,我们分析了spring mvc启动过程的源码,这一节,来一起分析下在用户请求controller的过程中,spring mvc做了什么事?

一、准备

我写这么一个controller

package com.jacky.controller;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import org.springframework.web.bind.annotation.ResponseBody;import org.springframework.web.method.HandlerMethod;@Controller    @RequestMapping("/bbb")    public class IndexController {        @RequestMapping(value = "/test.do", method = RequestMethod.GET)        public String index() {            return "index";        }        @RequestMapping(value = "/aaa.do", method = RequestMethod.GET)        @ResponseBody        public String aaa() {            return "aaa";        }    }

 

二、用户请求controller的过程(http://localhost:8080/spring-mvc-demo/bbb/aaa.do)

上一篇,我们知道了,spring mcv基于servlet的,核心类是DispatcherServlet,那根据Servlet的知识,请求首先service()方法,但是在DispatherServlet中并没有找到service方法,在其父类FrameworkServlet中找到service方法,那我们就从这里看起。

protected void service(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());        if (HttpMethod.PATCH == httpMethod || httpMethod == null) {            processRequest(request, response);        }        else {            super.service(request, response);        }    }

2.1、接下来我们看处理请求方法processRequest(request, response)

protected final void processRequest(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {            doService(request, response);        }

2.2、接下来我们看看doService(request,response)方法

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {        doDispatch(request, response);    }

2.3、接下来我们看看DispatcherServlet的doDispatch(request,reponse)方法

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {        HttpServletRequest processedRequest = request;        HandlerExecutionChain mappedHandler = null;        ModelAndView mv = null;        Exception dispatchException = null;        processedRequest = checkMultipart(request);        multipartRequestParsed = (processedRequest != request);        //根据请求获得请求执行链        mappedHandler = getHandler(processedRequest);        //根据处理器获得对应的适配器        HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());        //调用controller中的method的方法之前先调用拦截器的preHandle方法        if (!mappedHandler.applyPreHandle(processedRequest, response)) {            return;        }        //根据请求调用controller中的method,然后返回ModelAndView        mv = ha.handle(processedRequest, response, mappedHandler.getHandler());        //调用spring mvc拦截器的postHandle方法        mappedHandler.applyPostHandle(processedRequest, response, mv);    }

这个方法里面有很多很关键的方法,没事我们一个个看里面的实现细节

2.4、首先我们看看获得请求执行链的方法getHandler(processedRequest)

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {        //遍历spring mvc启动时初始化好的handlerMappings(类型为List
) for (HandlerMapping hm : this.handlerMappings) { //利用handlerMaping取得处理器执行链 HandlerExecutionChain handler = hm.getHandler(request); if (handler != null) { return handler; } } return null; }

2.5、接下来我们看看handlerMaping是怎么取得处理器执行链的?

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {        //获得HandlerMethod对象        Object handler = getHandlerInternal(request);        //获得处理器执行链对象        HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);        return executionChain;    }

2.6、首先我来看看怎么获得HandleMethod对象的

protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {        //获得请求路径,这里是lookupPath="/bbb/aaa.do"        String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);        //开启读锁        this.mappingRegistry.acquireReadLock();        try {            //根据url获得HandleMethod            HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);            return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);        }        finally {            //释放读锁            this.mappingRegistry.releaseReadLock();        }    }

2.7、接下来我们看看getHandlerExecutionChain是怎么获得处理器执行链的

protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {        HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?                (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));        String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);        for (HandlerInterceptor interceptor : this.adaptedInterceptors) {            if (interceptor instanceof MappedInterceptor) {                MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;                if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {                    //往处理器执行链的List
类型的interceptorList集合中,存放拦截器 chain.addInterceptor(mappedInterceptor.getInterceptor()); } } else { chain.addInterceptor(interceptor); } } return chain; }

从这里我们可以看到处理器其实封装了拦截器集合和一个handleMethod对象(封装了controller Class对象,浏览器请求的Controler中的method方法对象,参数)

2.8、上面,我们知道了,springMVC是怎么通过handleMaping获得handle(HandleMethod)对象,以及怎么添加拦截器,组装成处理器执行链,接下来我们继续看看springMVC是怎么获得处理器对应的

适配器的。

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {        //遍历spring mvc初始化时,设置的DispatcherServlet的成员变量List
handlerAdapters for (HandlerAdapter ha : this.handlerAdapters) {
//判断适配器支不支持handler if (ha.supports(handler)) { return ha; } } throw new ServletException("No adapter for handler [" + handler + "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler"); }

因为有会遍历很多种适配器,从上面我们知道,handlerd的类型是HandleMethod,所以的我们看的是RequestMappingHandlerAdapter适配器,根据RequestMappingHandlerAdapter的继承关系

可以知道,RequestMappingHandlerAdapter继承了AbstractHandlerMethodAdapter,在ha.supports(handler)方法中,刚好在AbstractHandlerMethodAdapter类中,我们来看看

public final boolean supports(Object handler) {        return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));    }

 就是判断传入的handler是否是属于HandlerMethod类型的,如果是,就返回true,然后RequestMappingHandlerAdapter对象

2.9、springMVC怎么获得处理请求的适配器我们看完了,接下来,我们看看,获得适配器后,是怎么调用到controller中的对应的method的?

//根据请求调用controller中的method,然后返回ModelAndView mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); 首先调用的是AbstractHandlerMethodAdapter类的handle方法
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)            throws Exception {        return handleInternal(request, response, (HandlerMethod) handler);    }

可以看到这个方法没干什么,只是调用了handleInternal方法,那我们就看看这个方法

protected ModelAndView handleInternal(HttpServletRequest request,                                          HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {        ModelAndView mav;        mav = invokeHandlerMethod(request, response, handlerMethod);        return mav;    }

这个方法主要是调了invokeHandlerMethod方法,那我们继续来看卡这个方法干了什么?

protected ModelAndView invokeHandlerMethod(HttpServletRequest request,                                               HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {        ServletWebRequest webRequest = new ServletWebRequest(request, response);        try {            WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);            ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);            //把handlerMethod封装成ServletInvocableHandlerMethod            ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);            //设置参数解析器            invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);            //设置spring mvc请求controller的method返回值处理器            invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);            invocableMethod.setDataBinderFactory(binderFactory);            //设置参数名称发现器            invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);            ModelAndViewContainer mavContainer = new ModelAndViewContainer();            mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));            modelFactory.initModel(webRequest, mavContainer, invocableMethod);            mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);            invocableMethod.invokeAndHandle(webRequest, mavContainer);            return getModelAndView(mavContainer, modelFactory, webRequest);        }        finally {            webRequest.requestCompleted();        }    }

3.0、接下来我们看看真正的调用和处理方法invocableMethod.invokeAndHandle(webRequest, mavContainer);

public void invokeAndHandle(ServletWebRequest webRequest,                                ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {        //在这里通过反射调用controller中的method方法        Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);        setResponseStatus(webRequest);        if (returnValue == null) {            if (isRequestNotModified(webRequest) || hasResponseStatus() || mavContainer.isRequestHandled()) {                mavContainer.setRequestHandled(true);                return;            }        } else if (StringUtils.hasText(this.responseReason)) {            mavContainer.setRequestHandled(true);            return;        }        mavContainer.setRequestHandled(false);        //在这里通过返回值处理器处理器进行二次处理,例如:如果加了方法加了reponseBody注解,就把结果序列化json字符串再返回        this.returnValueHandlers.handleReturnValue(                returnValue, getReturnValueType(returnValue), mavContainer, webRequest);            }

 

 

转载地址:http://gnqkl.baihongyu.com/

你可能感兴趣的文章
TP 框架没有考虑完善的功能点:1、表达式查询不支持INSTR形式的查询
查看>>
你不可不知的家庭装修禁忌
查看>>
关于i++和++i
查看>>
如何处理win10系统内置Linux系统闪退问题
查看>>
在Ubuntu上通过命令行安装Elisa KDE音乐播放器
查看>>
CentOS下命令行和桌面模式的切换方法
查看>>
linux下socket编程
查看>>
android中解压文件
查看>>
如何进行大数据分析及处理?
查看>>
runtime运行时编程一些相关知识
查看>>
转基因和基因突变
查看>>
git 使用经验
查看>>
shell脚本参数"$10"问题
查看>>
GSM协议编号及其内容
查看>>
mac下的抓包工具Charles
查看>>
iOS 四种保存数据的方式!
查看>>
innodb和myisam
查看>>
内存数据库服务运营之路
查看>>
UIBubbleTableView
查看>>
UIMenuController的使用,对UILabel拷贝以及定制菜单
查看>>