springmvc是一个基于servlet容器的轻量灵活的mvc框架,在它整个请求过程中,为了能够灵活定制各种需求,所以提供了一系列的组件完成整个请求的映射,响应等等处理。这里我们来分析下springMVC的源码。

首先,spring提供了一个处理所有请求的servlet,这个servlet实现了servlet的接口,就是DispatcherServlet。把它配置在web.xml中,并且处理我们在整个mvc中需要处理的请求,一般如下配置:
<servlet>
<servlet-name>spring-servlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring-servlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
DispatcherServlet也继承了FrameworkServlet抽象类,这个抽象类为整个servlet的处理提供了spring容器的支持,让原本servlet容器的DispatcherServlet拥有能够利用spring容器组件的能力。上面servlet的初始化参数contextConfigLocation就是DispatcherServlet获取spring容器的配置环境。FrameworkServlet继承自org.springframework.web.servlet.HttpServletBean。HttpServletBean实现了servlet容器初始化会调用的init函数。这个init函数会调用FrameworkServlet的初始化加载spring容器方法。方法源码:
@Override
protected final void initServletBean() throws ServletException {
getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
if (this.logger.isInfoEnabled()) {
this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
}
long startTime = System.currentTimeMillis();
try {
// 初始化spring-servlet容器
this.webApplicationContext = initWebApplicationContext();
initFrameworkServlet();
}
catch (ServletException ex) {
this.logger.error("Context initialization failed", ex);
throw ex;
}
catch (RuntimeException ex) {
this.logger.error("Context initialization failed", ex);
throw ex;
}
if (this.logger.isInfoEnabled()) {
long elapsedTime = System.currentTimeMillis() - startTime;
this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
elapsedTime + " ms");
}
}
可以看到这里就触发了spring的对web支持的容器初始化,这里使用的容器为WebApplicationContext.接下来我们就来分析一下整个容器的初始化过程:
protected WebApplicationContext initWebApplicationContext() {
// 查看是否在servlet上下文有所有容器需要继承的根Web容器
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
// 如果容器已经加载
if (this.webApplicationContext != null) {
// A context instance was injected at construction time -> use it
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
if (!cwac.isActive()) {
// The context has not yet been refreshed -> provide services such as
// setting the parent context, setting the application context id, etc
if (cwac.getParent() == null) {
// The context instance was injected without an explicit parent -> set
// the root application context (if any; may be null) as the parent
cwac.setParent(rootContext);
}
configureAndRefreshWebApplicationContext(cwac);
}
}
}
if (wac == null) {
// No context instance was injected at construction time -> see if one
// has been registered in the servlet context. If one exists, it is assumed
// that the parent context (if any) has already been set and that the
// user has performed any initialization such as setting the context id
wac = findWebApplicationContext();
}
if (wac == null) {
// 创建容器
wac = createWebApplicationContext(rootContext);
}
if (!this.refreshEventReceived) {
// Either the context is not a ConfigurableApplicationContext with refresh
// support or the context injected at construction time had already been
// refreshed -> trigger initial onRefresh manually here.
onRefresh(wac);
}
if (this.publishContext) {
// Publish the context as a servlet context attribute.
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
"' as ServletContext attribute with name [" + attrName + "]");
}
}
return wac;
}
如果已经有容器被创建那就初始化。否则创建容器,创建逻辑:
protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
Class<?> contextClass = getContextClass();
if (this.logger.isDebugEnabled()) {
this.logger.debug("Servlet with name '" + getServletName() +
"' will try to create custom WebApplicationContext context of class '" +
contextClass.getName() + "'" + ", using parent context [" + parent + "]");
}
// 判断是否是可配置的容器
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException(
"Fatal initialization error in servlet with name '" + getServletName() +
"': custom WebApplicationContext class [" + contextClass.getName() +
"] is not of type ConfigurableWebApplicationContext");
}
// 如果找到目标容器,并且可配置,然后就开始获取配置文件并开始初始化
ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
wac.setEnvironment(getEnvironment());
wac.setParent(parent);
wac.setConfigLocation(getContextConfigLocation());
// 这里是获取配置文件和完成初始化web容器的入口
configureAndRefreshWebApplicationContext(wac);
return wac;
}
这里完成了 web容器的相关准备工作,开始正式读取配置文件加载和初始化容器。
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
// 给容器设置一个id
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
// The application context id is still set to its original default value
// -> assign a more useful id based on available information
if (this.contextId != null) {
wac.setId(this.contextId);
}
else {
// Generate default id...
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
}
}
wac.setServletContext(getServletContext());
wac.setServletConfig(getServletConfig());
wac.setNamespace(getNamespace());
wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
// The wac environment's #initPropertySources will be called in any case when the context
// is refreshed; do it eagerly here to ensure servlet property sources are in place for
// use in any post-processing or initialization that occurs below prior to #refresh
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
}
/* 这里在容器被激活后,
并且容器还没完成初始化之前可以对容器的相关配置做一些修改,
默认给了空实现,
这里子类可以去重写,能够获得在容器初始化之前做 一些处理*/
postProcessWebApplicationContext(wac);
// 这里讲容器的初始化信息放到一个列表
applyInitializers(wac);
// 这里就开始web容器的初始化
wac.refresh();
}
容器的初始化在AbstractApplicationContext,无论是其他的容器,最终都会调用到refresh()函数,这个函数基本定义了整个容器初始化的整个脉络,这里不展开讲,本博客之后应该会详细分析这块的逻辑,这里大概的注释一下每一个函数完成的操作:
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 这里主要加载了容器当中一些从其他配置文件读取的变量
prepareRefresh();
// 获取容器本身
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 这里完成一些基础组件的依赖
prepareBeanFactory(beanFactory);
try {
// 添加 容器初始化之前的前置处理
postProcessBeanFactory(beanFactory);
// 调用 前置处理器,其中包含invokeBeanDefinitionRegistryPostProcessors与invokeBeanFactoryPostProcessors两类前置处理器的调用
invokeBeanFactoryPostProcessors(beanFactory);
// 注册bean被创建之前的前置处理器
registerBeanPostProcessors(beanFactory);
// 初始化容器的编码源
initMessageSource();
// 初始化一些事件监听器
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// 注册容器监听器
registerListeners();
// 初始化所有非懒加载的beans
finishBeanFactoryInitialization(beanFactory);
// Last step: 事件通知关心容器加载的相关组件
finishRefresh();
}
// 部分代码省略
}
}
}
自此加载完毕核心容器,然后回到FramewordServlet的initWebApplicationContext函数,在调用createWebApplicationContext完成一系列上面的操作之后,需要mvc servlet组件,入口就是onRefresh(ApplocationContext context)方法。它会调用另一个方法initStrategies(ApplicationContext context)。该方法如下:
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
// 获取所有的RequestMappings
initHandlerMappings(context);
// 不同handler的适配器
initHandlerAdapters(context);
// 异常解析器
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
这里我们重点讲解initHandlerMappings与initHandlerAdapters函数,因为这两个是处理servlet请求的入口。
在spring mvc中任何类都可以处理request请求,因为DispacherServlet也是实现了HttpServlet的接口,所以处理请求也是doService里。doService会将请求交给doDispatcher函数处理。然后doDispacher的源码:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// 这里获取当前请求的处理handler
mappedHandler = getHandler(processedRequest, false);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
/* 因为handler可以是任何类,
但是我们的DispacherServlet需要一个统一的处理接口,这个接口就是HandlerAdapter,
不同的HandlerMapping可以获取到各种各样的Handler,
这个handler最后都必须得到HandlerAdapter的支持才能被DispacherServlet所调用。
扩充一个新的HandlerMapping只需要添加一个新的HandlerAdatper即可,有点抽象工厂模式的味道*/
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(request, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Error err) {
triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
但是我们发现获取到的handler并不是Object而是一个HandlerExecutionChain,这个类可以进去查看发现是一堆拦截器和一个handler,主要就是给每一个请求一个前置处理的机会,这里值得一提的是一般来说拦截器和过滤器的区别就是拦截器可以终止后续执行流程,而过滤器一般不终止。过滤器一般是容器级别的,这个handler前置拦截器可以做到更细级别的控制,例如过滤器只定义了init和doFilter,但是这个handler拦截器定义了preHandle和postHandle还有afterCompletion函数,不难理解分别对应不同请求阶段的拦截粒度,更加灵活。
获取处理handler的getHandler代码:
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// 这里获取多个映射方式,一旦找到合适的处理请求的handler即返回
for (HandlerMapping hm : this.handlerMappings) {
if (logger.isTraceEnabled()) {
logger.trace(
"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
}
// HandlerExecutionChain包含了
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
return null;
}
HandlerAdapter处理后返回统一被封装成ModelAndView对象,这个就是包含试图和数据的对象,在经过适当的处理:
processDispatchResult(HttpServletRequest request, HttpServletResponse response,
HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception)
将页面与返回的数据返回给浏览器完成整个请求过程。以上就是springMVC大概的启动过程和请求的处理过程,之后本博客还会陆续分析核心的spring源码,博主一直认为精读著名框架源码是在短时间提升代码能力的捷径,因为这些轮子包含太多设计思想和细小的代码规范,这些读多了都会潜移默化的形成一种本能的东西。设计模式也不断贯穿其中。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
# springMVC容器加载
# 加载spring容器
# Spring源码解析容器初始化构造方法
# spring boot 加载web容器tomcat流程源码分析
# 基于spring-boot和docker-java实现对docker容器的动态管理和监控功能[附完整
# Spring源码分析容器启动流程
# 加载
# 配置文件
# 拦截器
# 实现了
# 的是
# 是一个
# 就开始
# 是在
# 还没
# 太多
# 抽象类
# 那就
# 多个
# 子类
# 还会
# 其他的
# 把它
# 这两个
# 博客
# 给了
相关文章:
如何在VPS电脑上快速搭建网站?
西安市网站制作公司,哪个相亲网站比较好?西安比较好的相亲网站?
建站之星后台密码遗忘或太弱?如何重置与强化?
香港服务器建站指南:免备案优势与SEO优化技巧全解析
内网网站制作软件,内网的网站如何发布到外网?
建站之星与建站宝盒如何选择最佳方案?
网站制作报价单模板图片,小松挖机官方网站报价?
建站之星免费模板:自助建站系统与智能响应式一键生成
b2c电商网站制作流程,b2c水平综合的电商平台?
如何在服务器上三步完成建站并提升流量?
,怎么用自己头像做动态表情包?
为什么Go需要go mod文件_Go go mod文件作用说明
建站之星收费标准详解:套餐费用及年费价格表一览
教学论文网站制作软件有哪些,写论文用什么软件
?
专业公司网站制作公司,用什么语言做企业网站比较好?
香港服务器网站生成指南:免费资源整合与高速稳定配置方案
建站之星导航如何优化提升用户体验?
胶州企业网站制作公司,青岛石头网络科技有限公司怎么样?
如何在Golang中实现微服务服务拆分_Golang微服务拆分与接口管理方法
h5网站制作工具有哪些,h5页面制作工具有哪些?
如何快速重置建站主机并恢复默认配置?
如何通过VPS搭建网站快速盈利?
建站之星在线版空间:自助建站+智能模板一键生成方案
建站之星备案流程有哪些注意事项?
测试制作网站有哪些,测试性取向的权威测试或者网站?
建站主机如何选?性能与价格怎样平衡?
建站之星云端配置指南:模板选择与SEO优化一键生成
建站之星如何实现PC+手机+微信网站五合一建站?
如何获取上海专业网站定制建站电话?
制作网站的软件免费下载,免费制作app哪个平台好?
实例解析Array和String方法
临沂网站制作企业,临沂第三中学官方网站?
如何制作新型网站程序文件,新型止水鱼鳞网要拆除吗?
西安专业网站制作公司有哪些,陕西省建行官方网站?
建站之星3.0如何解决常见操作问题?
高端智能建站公司优选:品牌定制与SEO优化一站式服务
导航网站建站方案与优化指南:一站式高效搭建技巧解析
MySQL查询结果复制到新表的方法(更新、插入)
如何通过西部建站助手安装IIS服务器?
,石家庄四十八中学官网?
如何用VPS主机快速搭建个人网站?
单页制作网站有哪些,朋友给我发了一个单页网站,我应该怎么修改才能把他变成自己的呢,请求高手指点迷津?
如何使用Golang安装API文档生成工具_快速生成接口文档
ppt在线制作免费网站推荐,有什么下载免费的ppt模板网站?
如何在企业微信快速生成手机电脑官网?
如何快速生成可下载的建站源码工具?
如何实现建站之星域名转发设置?
广州网站制作公司哪家好一点,广州欧莱雅百库网络科技有限公司官网?
高端建站如何打造兼具美学与转化的品牌官网?
香港服务器部署网站为何提示未备案?
*请认真填写需求信息,我们会在24小时内与您取得联系。