SpringMVC源码分析_1 SpringMVC容器启动和加载原理
SpringMVC源码分析_1 SpringMVC启动和加载原理
1 SpringMVC容器启动和加载原理
作者:田超凡
版权所有,严禁复制转载
1 SpringMVC和Servlet的关系
Servlet是Sun公司提供的一门用于开发动态web资源的技术。
Sun公司在其API中提供了一个servlet接口(javax.servlet.Servlet),用户若想用发一个动态web资源(即开发一个Java程序向浏览器输出数据),需要完成以下2个步骤:
1、编写一个Java类,实现servlet接口(继承javax.servlet.HttpServlet,重写doGet()/doPost()方法处理GET、POST请求)
2、在web.xml中配置<servlet>和<servletMapping>,指定不同Servlet拦截不同格式的请求并处理,在SpringBoot中,如果需要集成servlet,可以不用在web.xml配置<servlet>,直接使用@WebServlet注解标注自定义Servlet即可,常用的servlet映射相关配置在这个注解里面已经帮我们封装好了,在启动类标注@ServletComponentScan即可扫描并注册自定义的servlet到SpringBoot容器中
3 Servlet是线程不安全的单例模式实现,每个Servlet都有自己的生命周期,总的来说,Servlet声明周期主要包括四个阶段:加载和实例化、初始化(init)、服务(service)、销毁(destroy),每个Servlet都只会被初始化一次,每次请求都是交给service方法执行
4 常见Servlet继承关系:自定义Servlet -> HttpServlet -> GenericServlet -> Servlet
按照一种约定俗成的称呼习惯,通常我们也把实现了servlet接口的java程序,称之为Servlet
SpringMVC是Spring家族中的基于MVC Model II模式实现的一大视图层框架,SpringMVC底层核心控制器DispatcherServlet本质就是基于Servlet重新封装的,所以说,SpringMVC可以理解为是Spring对Servlet重新封装的一套功能更加齐全、使用方式更加灵活、和Spring完美契合的Web框架。
SpringMVC是依赖于Servlet容器和生命周期管理的。
2 Servlet核心初始化器ServletContainerInitializer
主要作用:监听Web容器启动,注册第三方组件
SpringMVC中的作用:
当Web容器启动时,注册SpringMVC核心控制器DispatcherServlet
在web容器启动时为提供给第三方组件机会做一些初始化的工作,例如注册servlet或者filtes等,servlet规范中通过ServletContainerInitializer实现此功能。
每个框架要使用ServletContainerInitializer就必须在对应的jar包的META-INF/services 目录创建一个名为javax.servlet.ServletContainerInitializer的文件,文件内容指定具体的ServletContainerInitializer实现类。
- Servlet容器启动时会扫描当前应用里面每一个jar包META-INF/services/javax.servlet.ServletContainerInitializer文件中定义的ServletContainerInitializer的实现
- 自定义ServletContainerInitializer的实现类,使用javax.servlet.annotation.@HandlesTypes声明
必须绑定在META-INF/services/javax.servlet.ServletContainerInitializer中
该文件的内容就是自定义ServletContainerInitializer实现类的全类名;
相关代码
@HandlesTypes(value = MyHandlesType.class) /** |
3 基于注解方式无xml启动SpringMVC
核心思想:
- 监听 Web容器启动,实现WebApplicationContext接口,重写onStartup()
- 在监听到Web容器启动时,先初始化SpringMVC注解驱动上下文AnnotationConfigWebApplicationContext,注册SpringMVC核心配置类。
- 创建SpringMVC核心控制器DispatcherServlet并注册到ServletContext容器上下文中,作用域是整个SpringMVC容器全局共享
定义SpringMVC核心配置类,作用等效于SpringMVC核心配置文件springmvc.xml
开启SpringMVC使配置生效的方式有两种,只能任选其一,不能同时配置:
- 核心配置类使用@EnableWebMvc标注启用SpringMVC
- 核心配置类继承WebMvcConfigurationSupport
原理:
@EnableWebMvc注解中通过ImportSelector引入了DelegatingWebMvcConfiguration核心配置类,它也继承了WebMvcConfigurationSupport
所以如果核心配置类继承了WebMvcConfigurationSupport又同时标注@EnableWebMvc的话,自定义的SpringMVC配置可能会被覆盖掉,因为SpringMVC容器在加载配置类的时候,会优先加载@EnableWebMvc注解中引入的配置类中的配置项(DelegatingWebMvcConfiguration)作为SpringMVC生效的配置项
@Configuration |
监听Web容器启动,定义DispatcherServlet初始化器,在容器启动时创建DispatcherServlet并注册到ServletContext上下文中
SpringMVC注解驱动上下文AnnotationConfigWebApplicationContext和SpringIOC上下文ApplicationContext的关系:
AnnotationConfigWebApplicationContext
- AbstractRefreshableWebApplicationContext
- ConfigurableWebApplicationContext
- WebApplicationContext
- ApplicationContext
因此总的来说,SpringMVC容器可以看做是SpringIOC容器的子容器
自定义SpringMVC初始化器,初始化SpringMVC上下文,创建核心控制器DispatcherServlet
public class WebInitializer implements WebApplicationInitializer { } |
4 SpringMVC拦截器使用
拦截器(Interceptor)与过滤器(Filter)区别
拦截器和过滤器都是基于SpringAOP实现,能够对请求执行之前和之后实现拦截。
过滤器是基于Servlet容器实现,对Web请求之前和之后实现拦截
拦截器不需要依赖于Servlet、不仅可以实现Web请求还有其他方法拦截等。
过滤器和拦截器的区别:
1 过滤器是Tomcat自带的,拦截器是SpringMVC自带的
2 过滤器只能拦截Web请求,过滤器不仅能够拦截Web请求,还可以拦截方法执行
3 过滤器和拦截器同时生效的情况下,过滤器会优先执行
- 自定义拦截器需要实现HandlerInterceptor拦截器接口,DispatcherServlet在拦截到请求之后会基于作用链调用HandlerInterceptor所有子拦截器的拦截方法,统一和HandlerMethod一起封装到了HandlerExecutionChain作用链中
HandlerInterceptor预定义了三种类型的拦截方法:
- preHandle在业务处理器处理请求之前被调用;
- postHandle在业务处理器处理请求执行完成后,生成视图之前执行;
- afterCompletion在DispatcherServlet完全处理完请求后被调用(生成视图之后执行),可用于清理资源等。
注意:afterCompletion除了作为最终拦截在请求方法执行完毕且视图渲染完毕之后执行,在前置拦截不通过、请求方法执行过程中出现异常,都会在返回响应前调用afterCompletion完成对当前请求的最终拦截处理
自定义Token验证拦截器UserTokenInterceptor
public class TokenInterceptor implements HandlerInterceptor { public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { |
在SpringMVC核心配置类中注入自定义拦截器,继承WebMvcConfigurationSupport并重写addInterceptors方法把自定义的拦截器加入到SpringMVC容器中
注意:
如果需要自定义SpringMVC配置,最好直接继承WebMvcConfigurationSupport,不要使用@EnableWebMvc注解(该注解表示使用默认的SpringMVC配置DelegatingWebMvcConfiguration来初始化SpringMVC容器),否则会覆盖自定义的配置项导致无法生效,包括拦截器配置。
@Configuration @Bean /** public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) { } |
注意:使用拦截器一定要关闭@EnableWebMvc 否则拦截器不会生效。
原理:
1 @EnableWebMvc表示采用默认的SpringMVC初始化配置(DelegatingWebMvcConfiguration)来启动SpringMVC容器
2 @EnableWebMvc底层会通过@Import(DelegatingWebMvcConfiguration)加载DelegatingWebMvcConfiguration这个委托配置类
此处DelegatingWebMvcConfguration也继承了WebMvcConfigurationSupport,会覆盖掉我们自定义的SpringMVC配置,这是因为SpringIOC容器在加载配置类的时候会优先加载注解定义的基于ImportSelector引入的类
5 SpringMVC多线程异步处理
- 使用异步注解@EnableAsync
- Servlet上下文开启异步支持dynamic.setAsyncSupported(true);
- 基于Callable创建线程实现异步操作
@RequestMapping("/pay") |
使用异步Callable 带返回结果
@RequestMapping(value = "/asyncPay") |
dynamic.setAsyncSupported(true); 开启异步处理请求
6 SpringMVC容器启动和加载原理图
7 SpringMVC容器启动和加载源码分析
- 自定义SpringMVC容器初始化器,实现WebApplicationInitializer,
WebApplicationInitializer是Web容器初始化的监听器,作用等效于ServletContainerInitializer
在监听方法onStartup中初始化SpringMVC注解驱动上下文AnnotationConfigWebApplicationContext
根据OOP继承原则,多级继承关系下构造函数的执行顺序是先执行父类构造函数,再执行子类构造函数。
此处SpringMVC注解驱动上下文AnnotationConfigWebApplicationContext继承链和构造函数执行顺序(自底向上)如下:
AnnotationConfigWebApplicationContext继承关系
- AbstractRefreshableWebApplicationContext –2 setDisplayName()
- AbstractRefreshableConfigApplicationContext
- AbstractRefreshableApplicationContext
- AbstractApplicationContext –1 PathMatchingResourcePatternResolver
- ConfigurableApplicationContext
- ApplicationContext
AbstractRefreshableWebAppliationContext实现的接口
- ConfigurableWebApplicationContext
- WebApplicationContext
- ApplicationContext
- AnnotationConfigWebApplicationContext创建完成后,调用register()注册SpringMVC核心配置类,使SpringMVC核心配置生效
AnnotationConfigWebApplicationContext中的全局变量componentClasses存放的是需要在Web容器初始化时加载到SpringIOC容器中的SpringMVC核心配置类
- 创建SpringMVC核心控制器DispatcherServlet,将创建好的SpringMVC注解驱动上下文AnnotationConfigWebApplicationContext传入给FrameworkServlet,注入到WebApplicationContext中
标记当前DispatcherServlet作为统一拦截HTTP请求并调用doService()处理请求的Servlet
- 将创建好的DispatcherServlet注册到ServletContext上下文中
调用addMapping绑定拦截URL规则是默认拦截所有请求
调用setLoadOnStartup(1)标识DispatcherServlet作为优先级最高的Servlet,永远最先执行拦截。
setAsyncSupported(true)表示启用Servlet对异步处理的支持,等效于@EnableAsync
- 开始执行DispatcherServlet的初始化操作,DispatcherServlet本质就是一个Servlet,依据Servlet生命周期的定义,加载和实例化都已经执行完毕,接下来开始初始化DispatcherServlet,调用init()实现初始化
DispatcherServlet在Servlet中的继承关系如下:
DispatcherServlet
- FrameworkServlet
- HttpServlet
- GenericServlet
- Servlet
8 DispatcherServlet初始化流程源码分析
GenericServlet init()
-> HttpServletBean init()
-> FrameworkServlet initServletBean()
-> initWebApplicationContext()
(1) configureAndRefreshWebApplicationContext() 加载SpringMVC核心配置类到SpringIOC容器中
加载SpringMVC核心配置类到SpringIOC容器的实现步骤:
FrameworkServlet initWebApplicationContext()
-> configureAndRefreshWebApplicationContext()
-> AbstractApplicationContext refresh()
-> obtainFreshBeanFactory()
-> AbstractRefreshableApplicationContext refreshBeanFactory()
-> AnnotationConfigWebApplicationContext loadBeanDefinitions()
-> 将注册到AnnotationConfigWebApplicationContext中的SpringMVC核心配置类componentClasses批量加载到SpringIOC容器中
AnnotatedBeanDefinitionReader regist() / ClassPathBeanDefinitionScanner scan()
(2) onRefresh() -> initStrategies()初始化DispatcherServlet
-> DispatcherServlet onRefresh()
-> initStrategies()
-> HandlerMapping、HandlerAdapter、HandlerExceptionResolver、ViewResolver等DispatcherServlet处理请求流程中的核心接口和类进行初始化
注意:在初始化这些DispatcherServlet处理请求过程中的核心类和接口时,会默认先从spring-webmvc包中的DispatcherServlet.properties加载对应类型指定的所有需要被加载的类,把加载后的类存放到对应集合中,如handlerMappings、handlerAdapters、viewResolvers、handlerExceptionResolvers等
SpringMVC源码分析_1 SpringMVC容器启动和加载原理相关推荐
- 简单直接让你也读懂springmvc源码分析(3.1)-- HandlerMethodReturnValueHandler
该源码分析系列文章分如下章节: springmvc源码分析(1)-- DispatcherServlet springmvc源码分析(2)-- HandlerMapping springmvc源码分析 ...
- SpringMVC源码分析(4)剖析DispatcherServlet重要组件
简单介绍了一个请求的处理过程, 简略描述了调用过程,并没有涉及过多细节,如url匹配,报文解析转换等. <SpringMVC源码分析(2)DispatcherServlet的初始化>:介绍 ...
- springMVC源码分析--访问请求执行ServletInvocableHandlerMethod和InvocableHandlerMethod
在之前一篇博客中 springMVC源码分析--RequestMappingHandlerAdapter(五)我们已经简单的介绍到具体请求访问的执行某个Controller中的方法是在RequestM ...
- SpringMVC源码分析_框架原理图
SpringMVC源码分析_框架原理图 ...
- 【spring源码分析】IOC容器初始化(二)
前言:在[spring源码分析]IOC容器初始化(一)文末中已经提出loadBeanDefinitions(DefaultListableBeanFactory)的重要性,本文将以此为切入点继续分析. ...
- Mybatis 源码分析(一)配置文件加载流程
Mybatis 源码分析(一)配置文件加载流程 1.项目构建 引入依赖 <dependency><groupId>org.mybatis</groupId>< ...
- 【SA8295P 源码分析】08 - XBL Loader 加载 SMSS、XBL Config、SHRM、CDT 、DDR、APDP、RamDump、OEM_MISC、AOP、QSEE过程分析
[SA8295P 源码分析]08 - XBL Loader 加载 SMSS.XBL Config.SHRM.CDT .DDR.APDP.RamDump.OEM_MISC.AOP.QSEE Dev Co ...
- SpringMVC源码分析(二)
1.DispatcherServlet源码分析 1.@InitBinder(续) 1.DataBinder概述 package org.springframework.validation; 此类所在 ...
- SpringMVC源码分析系列[转]
说到java的mvc框架,struts2和springmvc想必大家都知道,struts2的设计基本上完全脱离了Servlet容器,而springmvc是依托着Servlet容器元素来设计的,同时sp ...
最新文章
- 在leangoo里怎么复制列表,删除列表,插入列表?
- 汇编 cmp_ARM汇编语言入门(二)
- ROS学习之URDF
- 喜欢linux的朋友加QQ群了170838394
- CodeForces 1514A Perfectly Imperfect Array
- 吴恩达深度学习 —— 2.14 向量化逻辑回归的梯度输出
- XP电脑开机就检查硬盘
- 能源DEA-动态SBM模型
- 思科ASA防火墙部署和基本配置
- Fragment运行时错误
- 【机器学习】网络表征学习、网络嵌入必读论文
- js使用在指定数据前面或后面插入数据,对List数据排序
- 【Linux】一万七千字详解 —— 基本指令(二)
- Phoenix FD Maya 软件插件
- System.out. 输出到指定文件中
- 薄膜检测有哪些工艺流程,快来做功课
- windows多线程(八) 信号量Semaphore实例
- 基于titanic数据集介绍数据分析处理流程
- CNN经典网络模型(四):GoogLeNet简介及代码实现(PyTorch超详细注释版)
- 深信服上网行为管理(AC)部署三两事
热门文章
- flask mysql orm_Flask笔记:数据库ORM操作MySQL+pymysql/mysql-python+SQLAlchemy/Flask-SQLAlchemy...
- 我是女孩子,我适合学前端开发吗?
- datetime.datetime类介绍
- Mob研究院|长租公寓洞察:蛋壳破了,自如安否?
- python是一个免费开源的软件吗_Python是开源的吗?使用Python的软件开发领域
- 【Typora主题设置】Typora如果修改代码块样式
- 算法之动态规划算法简介
- 【190319】VC++ MP3播放器_MP3解码源代码
- 网络安全测试跟常规软件测试有什么不同?
- JXL导出excel