SpringMVC的组件原理剖析
# 1、前端控制器初始化
前端控制器DispatcherServlet是SpringMVC的入口,也是SpringMVC的大脑,主流程的工作都是在此完成的,梳理一下DispatcherServlet 代码。DispatcherServlet 本质是个Servlet,当配置了 load-on-startup 时,会在服务器启动时就执行创建和执行初始化init方法,每次请求都会执行service方法
DispatcherServlet 的初始化主要做了两件事:
获得了一个 SpringMVC 的 ApplicationContext容器;
注册了 SpringMVC的 九大组件。

SpringMVC 的ApplicationContext容器创建时机,Servlet 规范的 init(ServletConfig config) 方法经过子类重写,最终会调用 FrameworkServlet 抽象类的initWebApplicationContext() 方法,该方法中最终获得 一个根Spring容器(Spring产生的),一个子Spring容器(SpringMVC产生的)
HttpServletBean 的初始化方法
public final void init() throws ServletException {
this.initServletBean();
}
FrameworkServlet的initServletBean方法
protected final void initServletBean() throws ServletException {
this.webApplicationContext = this.initWebApplicationContext();//初始化ApplicationContext
this.initFrameworkServlet();//模板设计模式,供子类覆盖实现,但是子类DispatcherServlet没做使用
}
在initWebApplicationContext方法中体现的父子容器的逻辑关系
// 初始化 WebApplicationContext 是一个非常关键的步骤
protected WebApplicationContext initWebApplicationContext() {
// 获得根容器,实际上就是通过 ContextLoaderListener 创建的 ApplicationContext
// 如果配置了 ContextLoaderListener,则能获得根容器,否则为 null
WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
// 定义 Spring MVC 产生的 ApplicationContext 子容器
WebApplicationContext wac = null;
if (wac == null) {
// 创建 Spring MVC 的子容器,创建时将 rootContext 传递过去,实现父子容器关系
wac = this.createWebApplicationContext(rootContext);
}
// 将 Spring MVC 产生的 ApplicationContext 子容器存储到 ServletContext 域中
// key 名称格式是:org.springframework.web.servlet.FrameworkServlet.CONTEXT.<servletName>
if (this.publishContext) {
String attrName = this.getServletContextAttributeName();
this.getServletContext().setAttribute(attrName, wac);
}
return wac; // 通常方法会返回创建好的子容器
}
跟进创建子容器的源码
protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
// 实例化子容器 ApplicationContext,通常是 AnnotationConfigWebApplicationContext 或 GenericWebApplicationContext
ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
// 设置传递过来的 ContextLoaderListener 创建的 rootContext 作为父容器,实现容器继承
wac.setParent(parent);
// 获取 web.xml 中配置的 Spring MVC 配置文件路径,如 "classpath:spring-mvc.xml"
String configLocation = this.getContextConfigLocation();
if (configLocation != null) {
// 设置子容器的配置路径
wac.setConfigLocation(configLocation);
}
// 初始化子容器,加载配置文件中的 Bean 定义并刷新容器
this.configureAndRefreshWebApplicationContext(wac);
// 返回初始化完成的子容器
return wac;
}
子容器中的parent维护着父容器的引用

父容器和子容器概念和关系:
父容器:Spring 通过ContextLoaderListener为入口产生的applicationContext容器,内部主要维护的是applicationContext.xml(或相应配置类)配置的Bean信息;
子容器:SpringMVC通过DispatcherServlet的init() 方法产生的applicationContext容器,内部主要维护的是spring-mvc.xml(或相应配置类)配置的Bean信息,且内部还通过parent属性维护这父容器的引用。
Bean的检索顺序:根据上面子父容器的概念,可以知道Controller存在与子容器中,而Controller中要注入Service时,会先从子容器本身去匹配,匹配不成功时在去父容器中去匹配,于是最终从父容器中匹配到的UserService,这样子父容器就可以进行联通了。但是父容器只能从自己容器中进行匹配,不能从子容器中进行匹配。
注册 SpringMVC的 九大组件,在初始化容器initWebApplicationContext方法中执行了onRefresh方法,进而执行了初始化策略initStrategies方法,注册了九个解析器组件
// DispatcherServlet 初始化 Spring MVC 的九大核心组件
protected void initStrategies(ApplicationContext context) {
this.initMultipartResolver(context); // 1、初始化文件上传解析器
this.initLocaleResolver(context); // 2、初始化国际化解析器
this.initThemeResolver(context); // 3、初始化主题解析器
this.initHandlerMappings(context); // 4、初始化处理器映射器(HandlerMapping)
this.initHandlerAdapters(context); // 5、初始化处理器适配器(HandlerAdapter)
this.initHandlerExceptionResolvers(context); // 6、初始化异常处理解析器
this.initRequestToViewNameTranslator(context);// 7、初始化请求视图名称转换器
this.initViewResolvers(context); // 8、初始化视图解析器
this.initFlashMapManager(context); // 9、初始化 FlashMap 管理器,处理重定向传参
}
以 this.initHandlerMappings(context) 为例,进一步看一下初始化处理器映射器的细节:
// 定义 List 容器存储 HandlerMapping 实例
private List<HandlerMapping> handlerMappings;
// 初始化 HandlerMapping 的方法
private void initHandlerMappings(ApplicationContext context) {
// 初始化集合为 null
this.handlerMappings = null;
// detectAllHandlerMappings 默认为 true,表示是否从所有容器(包括父子容器)检测 HandlerMapping
if (this.detectAllHandlerMappings) {
// 从 Spring 容器中查找所有 HandlerMapping 类型的 Bean,包括父容器
Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
context, HandlerMapping.class, true, false);
// 如果找到的 HandlerMapping 不为空,则将它们加入 handlerMappings 集合
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<>(matchingBeans.values());
// 按照 @Order 注解或者 Ordered 接口进行排序,保证调用顺序
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
// 如果没有从容器中获取到 HandlerMapping,handlerMappings 依然为 null,则加载默认的 HandlerMapping
if (this.handlerMappings == null) {
// 通过配置文件 DispatcherServlet.properties 加载默认策略
this.handlerMappings = this.getDefaultStrategies(context, HandlerMapping.class);
}
}
}
初始化后,映射信息就已经被封装到HandlerMapping中了,可以在获取matchingBeans处打断点验证

# 2、前端控制器执行主流程
上面讲解了一下,当服务器启动时,DispatcherServlet 会执行初始化操作,接下来,每次访问都会执行service方法,我们先宏观的看一下执行流程,在去研究源码和组件执行细节

FrameworkServlet 复写了service(HttpServletRequest request, HttpServletResponse response) 、doGet(HttpServletRequest request, HttpServletResponse response)、doPost(HttpServletRequestrequest, HttpServletResponse response)等方法,这些方法都会调用processRequest方法
protected final void processRequest(HttpServletRequest request, HttpServletResponse response){
this.doService(request, response);
}
进一步调用了doService方法,该方法内部又调用了doDispatch方法,而SpringMVC 主流程最核心的方法就是 doDispatch 方法
protected void doService(HttpServletRequest request, HttpServletResponse response) {
this.doDispatch(request, response);
}
doDispatch方法源码
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null; // 处理器执行链对象,包含处理器和拦截器列表
ModelAndView mv = null; // 模型视图对象
// 1. 通过 HandlerMapping 匹配请求对应的处理器及拦截器,返回 HandlerExecutionChain
mappedHandler = this.getHandler(processedRequest);
// 2. 根据处理器类型,获取对应的 HandlerAdapter 适配器,用于执行处理器逻辑
HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
// 3. 执行拦截器链中所有拦截器的 preHandle 方法,若返回 false 则中断后续处理
mappedHandler.applyPreHandle(processedRequest, response);
// 4. 使用处理器适配器执行具体的控制器 Handler 方法,得到 ModelAndView 对象
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// 5. 执行拦截器链中所有拦截器的 postHandle 方法,可对模型视图做进一步处理
mappedHandler.applyPostHandle(processedRequest, response, mv);
// 6. 视图渲染及后续处理,将 ModelAndView 渲染成页面返回给客户端
this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception) dispatchException);
}
通过RequestMappingHandlerMapping 和RequestMappingHandlerAdaptor模拟主流程