我们首先找到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 源码-初始化阶段相关推荐

  1. Spring MVC源码——Servlet WebApplicationContext

    上一篇笔记(Spring MVC源码--Root WebApplicationContext)中记录了下 Root WebApplicationContext 的初始化代码.这一篇来看 Servlet ...

  2. Spring MVC源码——Root WebApplicationContext

    Spring MVC源码--Root WebApplicationContext 打算开始读一些框架的源码,先拿 Spring MVC 练练手,欢迎点击这里访问我的源码注释, SpringMVC官方文 ...

  3. Spring MVC源码解析——HandlerMapping(处理器映射器)

    Sping MVC 源码解析--HandlerMapping处理器映射器 1. 什么是HandlerMapping 2. HandlerMapping 2.1 HandlerMapping初始化 2. ...

  4. Spring MVC 源码-运行调用阶段

    这一步步是由请求触发的,所以入口为DispatcherServlet 的核心方法为doService(),doService()中的核心逻辑由doDispatch()实现,源代码如下: /** 中央控 ...

  5. 精尽Spring MVC源码分析 - 一个请求的旅行过程

    我们先来了解一个请求是如何被 Spring MVC 处理的,由于整个流程涉及到的代码非常多,所以本文的重点在于解析整体的流程,主要讲解 DispatcherServlet 这个核心类,弄懂了这个流程后 ...

  6. Spring MVC源码解析

    Spring Mvc结构解析 上图是Dispatcher Servlet的结构图,从图中可以清楚的看到Dispatcher Servlet的继承链,下面我们将基于Spring4.1.6揭开Spring ...

  7. Spring MVC源码分析(一) 说明

    为什么会有这一个系列的文章 现在正值大学的第一个暑假,这个暑假我准备开始进入框架的学习,首先我选择的是Spring MVC框架,这是自己学的第一个框架,我在学习的过程中不断告诉自己,这一次不是单纯的学 ...

  8. Spring MVC 源码分析

    根据上面分析的Spring MVC 工作机制,从三个部分来分析Spring MVC 的源代码. 其一,ApplicationContext 初始化时用Map 保存所有url 和Controller 类 ...

  9. Spring MVC源码 ----- @RequestBody和@ResponseBody原理解析

    来源:https://www.cnblogs.com/java-chen-hao/p/11187914.html 1. 概述 在SpringMVC的使用时,往往会用到@RequestBody和@Res ...

最新文章

  1. 在Ubuntu8.10下为PHP安装coreseek全文检索引擎支持的详细步骤
  2. 到底能不能做一辈子的程序员——大龄程序员将何去何从
  3. 罗永浩开了一家直播界的新东方
  4. 由通项为In(1+1\n)的级数引申...
  5. Java学习:多线程
  6. 蓝桥杯真题-单词分析
  7. 计算机win764位相机驱动,Win7万能驱动64位
  8. Flash鼠绘马蹄莲教程
  9. 甲方乙方 (1997)
  10. 【20210825】【数据分析】两个向量做相关性分析时,需不需要统一量纲?
  11. 小米平板4能装Linux系统吗,终于迎来它!小米平板4进行重大更新
  12. Android菜鸡的提升之路---自闭症儿童游戏的实现
  13. 零信任|IAM是基于动态身份的安全新边界
  14. 基于backtrader的唐奇安结合ADX策略实现(自动多参数调优和回测)
  15. Windows显示进程路径
  16. IIS使用的十大原则
  17. 使用JDBC后千万记得关闭并释放数据库连接资源
  18. 准备入手iPhone13,纠结选择买13 Pro Max还是13 Pro?
  19. 《深度学习进阶:自然语言处理》读书笔记:第8章 Attention
  20. 天梯赛 L1-049 天梯赛座位分配 (20 分) 循环技巧

热门文章

  1. AceTeamwork 软件成就继续蝉联国内最优秀的项目工时(timesheet)及费用报销(expense)软件...
  2. 对Spring事务一些问题的讨论
  3. [Mysql]备份同库中一张表的历史记录 insert into ..select
  4. 【其他】U盘安装Ubuntu12.04成功后系统无法启动的问题
  5. 搜集到的数学分析例题(不断更新)
  6. 对jquery的conflict方法的解读
  7. 封装getByClass(JS获取class的方法封装为一个函数)
  8. nginx中文件路径表示方法
  9. Oracle收购云安全创企Palerra,以加强安全堆栈
  10. linux下nginx安装与设置开机启动