Spring MVC 源码-初始化阶段
我们首先找到DispatcherServlet 这个类,必然是寻找init()方法。然后,我们发现其init方法其实在父类HttpServletBean 中,其源码如下:
@Override
public final void init() throws ServletException {if (logger.isDebugEnabled()) {logger.debug("Initializing servlet '" + getServletName() + "'");}// Set bean properties from init parameters.PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);if (!pvs.isEmpty()) {try {//定位资源BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);//加载配置信息ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));initBeanWrapper(bw);bw.setPropertyValues(pvs, true);}catch (BeansException ex) {if (logger.isErrorEnabled()) {logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);}throw ex;}}// Let subclasses do whatever initialization they like.initServletBean();if (logger.isDebugEnabled()) {logger.debug("Servlet '" + getServletName() + "' configured successfully");}
}
我们看到在这段代码中, 又调用了一个重要的initServletBean() 方法。进入initServletBean()方法看到以下源码:
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 {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");}
}
这段代码中最主要的逻辑就是初始化IOC 容器,最终会调用refresh()方法,前面的章节中对IOC 容器的初始化细节我们已经详细掌握,在此我们不再赘述。我们看到上面的代码中,IOC 容器初始化之后,最后有调用了onRefresh()方法。这个方法最终是在DisptcherServlet 中实现,来看源码:
@Override
protected void onRefresh(ApplicationContext context) {initStrategies(context);
}
/*** Initialize the strategy objects that this servlet uses.* <p>May be overridden in subclasses in order to initialize further strategy objects.*/
//初始化策略
protected void initStrategies(ApplicationContext context) {//多文件上传的组件initMultipartResolver(context);//初始化本地语言环境initLocaleResolver(context);//初始化模板处理器initThemeResolver(context);//handlerMappinginitHandlerMappings(context);//初始化参数适配器initHandlerAdapters(context);//初始化异常拦截器initHandlerExceptionResolvers(context);//初始化视图预处理器initRequestToViewNameTranslator(context);//初始化视图转换器initViewResolvers(context);//initFlashMapManager(context);
}
到这一步就完成了Spring MVC 的九大组件的初始化。接下来,我们来看url 和Controller的关系是如何建立的呢? HandlerMapping 的子类AbstractDetectingUrlHandlerMapping 实现了initApplicationContext()方法,所以我们直接看子类中的初始化容器方法。
@Override
public void initApplicationContext() throws ApplicationContextException {super.initApplicationContext();detectHandlers();
}
/*** 建立当前ApplicationContext中的所有controller和url的对应关系*/
protected void detectHandlers() throws BeansException {ApplicationContext applicationContext = obtainApplicationContext();if (logger.isDebugEnabled()) {logger.debug("Looking for URL mappings in application context: " + applicationContext);}// 获取ApplicationContext容器中所有bean的NameString[] beanNames = (this.detectHandlersInAncestorContexts ?BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Object.class) :applicationContext.getBeanNamesForType(Object.class));// Take any bean name that we can determine URLs for.// 遍历beanNames,并找到这些bean对应的urlfor (String beanName : beanNames) {// 找bean上的所有url(controller上的url+方法上的url),该方法由对应的子类实现String[] urls = determineUrlsForHandler(beanName);if (!ObjectUtils.isEmpty(urls)) {// URL paths found: Let's consider it a handler.// 保存urls和beanName的对应关系,put it to Map<urls,beanName>,该方法在父类AbstractUrlHandlerMapping中实现registerHandler(urls, beanName);}else {if (logger.isDebugEnabled()) {logger.debug("Rejected bean name '" + beanName + "': no URL paths identified");}}}
}
determineUrlsForHandler(String beanName)方法的作用是获取每个Controller 中的url,不同的子类有不同的实现,这是一个典型的模板设计模式。因为开发中我们用的最多的就是用注解来配置Controller 中的url , BeanNameUrlHandlerMapping 是
AbstractDetectingUrlHandlerMapping 的子类,处理注解形式的url 映射.所以我们这里以BeanNameUrlHandlerMapping 来进行分析。我们看BeanNameUrlHandlerMapping 是如何查beanName 上所有映射的url。
@Override
protected String[] determineUrlsForHandler(String beanName) {List<String> urls = new ArrayList<>();if (beanName.startsWith("/")) {urls.add(beanName);}String[] aliases = obtainApplicationContext().getAliases(beanName);for (String alias : aliases) {if (alias.startsWith("/")) {urls.add(alias);}}return StringUtils.toStringArray(urls);
}
到这里HandlerMapping 组件就已经建立所有url 和Controller 的对应关系。
Spring MVC 源码-初始化阶段相关推荐
- Spring MVC源码——Servlet WebApplicationContext
上一篇笔记(Spring MVC源码--Root WebApplicationContext)中记录了下 Root WebApplicationContext 的初始化代码.这一篇来看 Servlet ...
- Spring MVC源码——Root WebApplicationContext
Spring MVC源码--Root WebApplicationContext 打算开始读一些框架的源码,先拿 Spring MVC 练练手,欢迎点击这里访问我的源码注释, SpringMVC官方文 ...
- Spring MVC源码解析——HandlerMapping(处理器映射器)
Sping MVC 源码解析--HandlerMapping处理器映射器 1. 什么是HandlerMapping 2. HandlerMapping 2.1 HandlerMapping初始化 2. ...
- Spring MVC 源码-运行调用阶段
这一步步是由请求触发的,所以入口为DispatcherServlet 的核心方法为doService(),doService()中的核心逻辑由doDispatch()实现,源代码如下: /** 中央控 ...
- 精尽Spring MVC源码分析 - 一个请求的旅行过程
我们先来了解一个请求是如何被 Spring MVC 处理的,由于整个流程涉及到的代码非常多,所以本文的重点在于解析整体的流程,主要讲解 DispatcherServlet 这个核心类,弄懂了这个流程后 ...
- Spring MVC源码解析
Spring Mvc结构解析 上图是Dispatcher Servlet的结构图,从图中可以清楚的看到Dispatcher Servlet的继承链,下面我们将基于Spring4.1.6揭开Spring ...
- Spring MVC源码分析(一) 说明
为什么会有这一个系列的文章 现在正值大学的第一个暑假,这个暑假我准备开始进入框架的学习,首先我选择的是Spring MVC框架,这是自己学的第一个框架,我在学习的过程中不断告诉自己,这一次不是单纯的学 ...
- Spring MVC 源码分析
根据上面分析的Spring MVC 工作机制,从三个部分来分析Spring MVC 的源代码. 其一,ApplicationContext 初始化时用Map 保存所有url 和Controller 类 ...
- Spring MVC源码 ----- @RequestBody和@ResponseBody原理解析
来源:https://www.cnblogs.com/java-chen-hao/p/11187914.html 1. 概述 在SpringMVC的使用时,往往会用到@RequestBody和@Res ...
最新文章
- 在Ubuntu8.10下为PHP安装coreseek全文检索引擎支持的详细步骤
- 到底能不能做一辈子的程序员——大龄程序员将何去何从
- 罗永浩开了一家直播界的新东方
- 由通项为In(1+1\n)的级数引申...
- Java学习:多线程
- 蓝桥杯真题-单词分析
- 计算机win764位相机驱动,Win7万能驱动64位
- Flash鼠绘马蹄莲教程
- 甲方乙方 (1997)
- 【20210825】【数据分析】两个向量做相关性分析时,需不需要统一量纲?
- 小米平板4能装Linux系统吗,终于迎来它!小米平板4进行重大更新
- Android菜鸡的提升之路---自闭症儿童游戏的实现
- 零信任|IAM是基于动态身份的安全新边界
- 基于backtrader的唐奇安结合ADX策略实现(自动多参数调优和回测)
- Windows显示进程路径
- IIS使用的十大原则
- 使用JDBC后千万记得关闭并释放数据库连接资源
- 准备入手iPhone13,纠结选择买13 Pro Max还是13 Pro?
- 《深度学习进阶:自然语言处理》读书笔记:第8章 Attention
- 天梯赛 L1-049 天梯赛座位分配 (20 分) 循环技巧
热门文章
- AceTeamwork 软件成就继续蝉联国内最优秀的项目工时(timesheet)及费用报销(expense)软件...
- 对Spring事务一些问题的讨论
- [Mysql]备份同库中一张表的历史记录 insert into ..select
- 【其他】U盘安装Ubuntu12.04成功后系统无法启动的问题
- 搜集到的数学分析例题(不断更新)
- 对jquery的conflict方法的解读
- 封装getByClass(JS获取class的方法封装为一个函数)
- nginx中文件路径表示方法
- Oracle收购云安全创企Palerra,以加强安全堆栈
- linux下nginx安装与设置开机启动