《看透SpringMVC源码》笔记之总结
来源:互联网 发布:卡是3g的手机是2g网络 编辑:程序博客网 时间:2024/05/29 08:52
原理总结
结构
Tomcat可以分为两大部分:连接器和容器,连接器专门用于处理网络连接相关的事情,如Socket连接、request封装、连接线程池维护等工作,容器用来存放我们编写的网站程序,Tomcat中一共有4层容器:Engine、Host、Context和Wrapper。一个Wrapper对应一个Servlet,一个Context对应一个应用,一个Host对应一个站点,Engine是引擎,一个容器只有一个。Context和Host的区别是Host代表站点,如不同的域名,而Context表示一个应用,比如,默认情况下webapps/ROOT中存放的为主应用,对应一个站点的跟路径,如www.excelib.com。webapps下别的目录则存放别的子应用,对应站点的子路径,如webapps/test目录存放着www.excelib.com/test应用,而所有webapps下的应用都属于同一个站点,它们每一个都对应一个Context,如果想要添加一个新的站点,如blog.excelib.com,则需要使用Host。一套容器和多个连接器组成一个Service,一个Tomcat中可以有多个Service。
Servlet接口一共定义了5个方法,其中init方法和destroy用于初始化和销毁Servlet,整个生命周期中只会被调用一次;service方法实际处理请求;getServletConfig方法返回的ServletConfig,可以获取到配置Servlet时使用init-param配置的参数,还可以获取ServletContext;getServletInfo方法可以获取到一些Servlet相关的信息,如作者、版权等,这个方法需要自己实现,默认返回空字符串。
Java提供了两个Servlet的实现类:GenericServlet和HttpServlet。GenericServlet主要做了三件事情:
1. 实现了ServletConfig接口,让我们可以直接调用ServletConfig中的方法;
2. 提供了无参的init方法;
3. 提供了log方法。
HttpServlet主要做了两件事:
1. 将ServletRequest和ServletResponse转换为了HttpServletRequest和HttpServletResponse;
2. 根据Http请求类型(如Get、Post等)将请求路由到了7个不同的处理方法,这样在编写代码时只需要将不同类型的处理代码编写到不同的方法中就可以了,如常见的doGet、doPost方法就是在这里定义的。
SpringMVC本质是个Servlet,这个Servlet继承自HttpServlet。SpringMVC中提供了三个层次的Servlet:HttpServetBean、FrameworkServlet和DispatcherServlet,它们相互继承,HttpServletBean直接继承自Java的HttpServlet。HttpServlet用于将Servlet中配置的参数设置到相应的属性中,FrameworkServlet初始化了SpringMVC中所使用的WebApplicationContext,具体处理请求的9大组件是在DispatcherServlet中初始化的。整个Servlet继承结构如图:
请求过程
SpringMVC中请求的处理主要在DispatcherServlet中,不过它上一层的FrameworkServlet也做了一些工作,首先它将所有的请求都转发到processRequest方法,然后在processRequest方法中做了三件事:
1. 调用了doService模板方法具体处理请求,doService方法在DispatcherServlet中实现;
2. 将当前请求的LocaleContext和ServletRequestAttribute在处理请求前设置到了LocaleContextHolder和RequestHolder,并在处理完成后恢复;
3. 请求处理完成后发布一个ServletRequestHandledEvent类型的消息。
DispatcherServlet在doService方法中将webApplicationContext、localeResolver、themeResolver、themeSource、FlashMap和FlashMapManager设置到request的属性中以方便使用,然后将请求交给doDispatch方法进行具体处理。
DispatcherServer的doDispatch方法按执行过程大致可以分为4步:
1. 根据request找到Handler;
2. 根据找到的Handler找到对应的HandlerAdapter;
3. 用HnadlerAdapter调用Handler处理请求;
4. 调用processDispatchResult方法处理Handler处理之后的结果(主要处理异常和找到View并渲染输出给用户)。
实际跟踪一个请求
http://localhost:8080/articles/67/comment?comment=好s2赞klkl
- 请求发送到服务器后,服务器程序就会分配一个Socket线程来跟它连接,接着创建出request和response,然后交给对应的Servlet处理,这样请求就从Servlet容器传递到了Servlet(Servlet容器)。
- 在Servlet中请求首先被HttpServlet处理,在HttpServlet的service方法中将ServletRequest和ServletResponse转换为HttpServletRequest和HttpServletResponse,并调用转换后的service方法(Java的HttpServlet)。
- 接下来请求就到了SpringMVC,在SrpingMVC中首先由FrameworkServlet的service方法进行处理,这里service方法又会将请求交给HttpServlet的service方法处理(FrameworkServlet)。
- 在HttpServlet的service方法中会根据请求类型将请求传递到doGet方法(Java的HttpServlet)。
- doGet方法在SpringMVC的FrameServlet中,它有将传递到了processRequest方法,然后在processRequest方法中将当前请求的LocaleContext和RequestAttributes设置到LocaleContextHolder和RequestContextHolder后将请求传递到了doService方法,都Service方法在DispatcherServlet里实现(FrameworkServlet)。
- DispatcherServlet的doService方法将webApplicationContext、localeResolver、themeResolver、themeSource、outputFlashMap和flashnMapManager设置到了request的属性中,然后将请求传递到了doDispatch方法中(DispatcherServlet)。
- DispatcherServlet的doDispatch中首先调用checkMultipart方法检查是不是上传请求,然后调用getHandler方法获取到Handler(DispatcherServlet)。
- getHandler方法获取Handler的过程会遍历容器中所有的HandlerMapping,< mvc: annotation-driven>标签配置的HnadlerMapping是RequestMappingHandlerMapping和BeanNameUrlHandlerMapping,在用RequestMappingHandlerMapping匹配时我们的请求会和其初始化时读取到定义的@RequestMapping(value={“/articles/{articleId}/comment”})所注释的内容相匹配,然后根据这个条件找到定义的处理方法doComment(RequestMappingHandlerMapping)。
- 找到处理器后调用RequestMappingInfoHandlerMapping里的handleMatch方法会将匹配的Pattern(/articles/{articleId}/comment)和articleId这个PathVariable设置到了request的属性中(RequestMappingInfoHandlerMapping)。
- 找到Handler后返回DispatcherServlet的doDispatch方法中,然后调用getHandlerAdapter方法根据Handler查找HandlerAdapter,也就是根据工具找使用工具的人。查找的方式也是遍历配置的所有HandlerAdapter,然后分别调用它们的supports方法进行检查,检查的方法通常是看Handler的类型是否支持,< mvc:annotation-driven/> 标签配置的HandlerAdapter是RequestMappingHandlerAdapter、HttpRequestHandlerAdapter和SimpleControllerHandlerAdapter,最后找到RequestMappingHandlerAdapter(DispatcherServlet)。
- DispatcherServlet的doDispatch方法中检查到是Get请求,然后检查是否可以使用缓存,因为RequestMappingHandlerAdapter的getLastModified方法直接返回-1,所以不会使用缓存,接着调用了HandlerInterceptor的preHandler方法,这里没有配置HandlerInterceptor,这一步就不管了,接下来用RequestMappingHandlerAdapter使用Handler处理请求(DispatcherServlet)。
- RequestMappingHandlerAdapter首先在其父类的handle方法中直接将请求传递到了它的handleInternal方法,handleInternal方法首先调用getSessionAttributeHandler初始化了本处理器对应的SessionAttributeHandler,并判断出注释有@SessionArributes,进而调用checkAndPrepare方法禁止了Reponse的缓存,然后将请求传递到了invokeHandleMethod方法(RequestMappingHandlerAdapter)。
- RequestMappingHandlerAdapter的invokeHandleMethod方法中首先创建了WebDataBinderFactory、ModelFactory和ServletInvocableHandlerMethod,ModelFactory创建过程中会找到定义的注释了@ModelAttribute的repalceSensitiveWords方法(RequestMappingHandlerAdapter)。
- 接着创建ModelAndViewContainer,并调用ModelFactory的initMothod方法给Model设置参数,这里会调用了定义的replaceSensitionWords方法,调用前会使用RequestParamMethodArgumentResolver解析出“comment”参数的值(“好s2赞klkl”)并设置给replaceSensitiveWords方法,方法处理完(去除敏感词)后,ModelFactory将其使用@ModelAttribute注释中的“comment”作为name,取出敏感词后的评论内容作为value设置到Model中(ModelFactory)。
- 接下来调用ServletInvocableHandlerMethod的invokeAndHandle方法实际执行处理。首先在父类InvocableHandlerMethod的invokeForRequest方法中调用了getMethodArgumentValues方法来解析参数,@PathVariable、RedirectAttributes、Model三个参数分别使用PathVariableMethodArgumentResolver、RedirectAttributesMethodArgumentResolver和ModelMethodProcessor三个参数解析器来解析,第一个返回在HandlerMapping中(第9步)设置到request属性中的值(67),第二个会新建一个RedirectAttributeModelMap然后设置到mavContainer中并返回,第三个直接返回mavContainer中的Model,这时的Model中已经保存了前面去敏感词后的comment参数(InvocableHandlerMethod)。
- 接下来调用invocableHandleMethod的doInvoke方法处理请求,这里实际调用了我们编写的doComment方法,其中将“comment”设置到RedirectAttribute中,通过FlashMap传递,将“articleId”设置到Model中,因为它在@SessionAttributes中进行了设置,所以会保存到SessionAttributes中,处理完成后返回“redirect:/showArticle”,将请求返回到ServletInvocableHandlerMethod(定义的FollowMeController)。
- ServletInvocableHandlerMethod使用HandlerMethodReturnValueHandler处理返回值,因为返回的是String,所以使用的是ViewNameMethodReturnValueHandler,它首先将返回值“redirect:/showArticle”设置到mavContainer的view里,然后将mavContainer中的redirect标志redirectModelScenario设置为了true,这样ServletInvocableHandlerMethod就处理完了,接着将请求返回RequestMappingHandlerAdapter(ServletInvocableHanlerMethod)。
- RequestMappinHandlerAdapter调用getMethodAndView方法对返回值进一步处理,首先使用ModelFatory的updateModel方法处理@SessionAttributes注释,将其中的“articleId”参数从Model中取出并使用sessionAttributeHandler保存;然后使用mavContainer中的Model和View创建ModelAndView;最后检查到Model是RedirectAttributes类型(因为我们返回的是redirect视图,而且设置了RedirectAttribute属性,所以mavContainer中getModel会返回RedirectAttributes类型的Model),这时会将之前保存到RedirectAttributes中的“comment”参数设置到outputFlashMap,这样RequestMappingHandlerAdapter的处理就完成了,请求返回DispatcherServlet中(RequestMappingHandlerAdapter)。
- DispatcherServlet在doDispatch方法中首先检查返回的View是否为空,如果为空使用RequestToViewNameTranslator查找默认View,然后执行HandlerInterceptor的applyPostHandle方法,这里这两项都不需要处理。接下来将请求传递到processDispatchResult方法(DispatcherServlet)。
- DispatcherServlet的processDispatchResult方法中首先判断是否有异常,这里没有则不需要处理,然后调用render方法进行页面的渲染。render方法中首先使用localeResolver解析出Locale;然后调用resolveViewName方法解析出View,解析过程使用到了ViewResolver,这里使用的是我们配置的InternalResourceViewResolver,具体处理方法在UrlBasedViewResolver的createView方法中,它检查到是redirect的返回值,所以创建了RedirectView类型的View;然后调用View的render方法渲染出(DispatcherServlet)。
- RedirectView的render方法在父类AbstractView中定义,其中调用了RedirectView的renderMergedOutputModel方法,renderMegedOutputModel方法中将request属性中保存的outputFlashMap和FlashMapManager取出,使用FlashMapManager将outputFlashMap保存到了Session中,然后调用sendRedirect方法使用response的sendRedirect方法将请求发出,然后请求返回DispatcherServlet的processDispatchResult方法(RedirectView)。
- DispatcherServlet的processDispatchResult方法接着调用Handler的triggerAfterCompletion方法,进而调用拦截器的afterCompletion方法,然后将请求返回到Framework-Servlet(DispatcherServlet)。
- 在FrameworkServlet的processRequest方法中将原来的LocaleContext和RequestAttributes恢复到LocaleContextHolder和RequestContextHolder中,并发出ServletRequestHandledEvent消息,最后将请求返回给Servlet容器(FrameworkServlet)。
这样一个完整的请求就处理完了。
- 《看透SpringMVC源码》笔记之总结
- 《看透springMVC源码》笔记之Servlet
- 《看透springMVC源码》笔记之核心Servlet
- 《看透springMVC源码》笔记之HandlerMapping
- 《看透SpringMVC源码》笔记之HandlerAdapter
- 《看透springMVC源码》之Tomcat的生命周期
- 《看透springMVC源码》之Container分析
- 《看透springMVC源码》之Connector分析
- 《看透springMVC源码》之Tomcat的顶级结构及启动过程
- 2016书单总结--看透SpringMvc源代码分析与实践-概述
- springMVC学习笔记之源码分析
- springmvc源码笔记
- 《看透springMvc源代码分析与实践》笔记1网站架构演变
- 《看透springMvc源代码分析与实践》笔记2常见协议和标准
- springMVC笔记总结
- SpringMVC学习笔记总结
- 看透springMvc源代码分析与实现这本书中第八章实例总结(controller和View)
- springmvc源码阅读笔记 --HandlerMapping
- JAVA中StringBuffer的各种方法详解
- linux下的网络编程(转载文章链接)
- java源码学习(一)
- sqlmap数据转中文字符串
- PRML系列:1.5 Decision Theory
- 《看透SpringMVC源码》笔记之总结
- 【NOI2017模拟6.2】字符串
- 用IOT的思维来管理我们的查看我们重要业务的服务器健康状态-实现IOT设备远端控制!
- Docker+Jenkins_自动化持续集成
- 不了解Java虚拟机的小伙伴看这一本书就够了!
- tinyxml2的使用
- CentOS7上部署搭建Kafka集群
- redis的两种持久化
- 以LeNet为例分析CNN中的参数量