Web IOC 容器初体验

我们还是从大家最熟悉的 DispatcherServlet 开始,我们最先想到的还是 DispatcherServlet 的 init()
方法。我们发现在 DispatherServlet 中并没有找到 init()方法。但是经过探索,往上追索在其父类
HttpServletBean 中找到了我们想要的 init()方法,如下:
 
/**
* Map config parameters onto bean properties of this servlet, and
* invoke subclass initialization.
* @throws ServletException if bean properties are invalid (or required
* properties are missing), or if subclass initialization fails.
*/
@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");
  }
}
在 init()方法中,真正完成初始化容器动作的逻辑其实在 initServletBean()方法中,我们继续跟进
initServletBean()中的代码在 FrameworkServlet 类中:
@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 {
    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");
  }
}
在上面的代码中终于看到了我们似曾相识的代码 initWebAppplicationContext(),继续跟进:
 
从上面的代码中可以看出,,在 configAndRefreshWebApplicationContext()方法中,调用 refresh()方
法,这个是真正启动 IOC 容器的入口,后面会详细介绍。IOC 容器初始化以后,最后调用了
DispatcherServlet 的 onRefresh()方法,在 onRefresh()方法中又是直接调用 initStrategies()方法初始
化 SpringMVC 的九大组件: 
@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);
//handlerMapping
initHandlerMappings(context);
//初始化参数适配器
initHandlerAdapters(context);
//初始化异常拦截器
initHandlerExceptionResolvers(context);
//初始化视图预处理器
initRequestToViewNameTranslator(context);
//初始化视图转换器
initViewResolvers(context);
//
initFlashMapManager(context);
}
 
/**
* Map config parameters onto bean properties of this servlet, and
* invoke subclass initialization.
* @throws ServletException if bean properties are invalid (or required
* properties are missing), or if subclass initialization fails.
*/
@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.
 
 
 

IOC 容器初始化小结

现在通过上面的代码,总结一下 IOC 容器初始化的基本步骤:
1、初始化的入口在容器实现中的 refresh()调用来完成。
2、对 Bean 定义载入 IOC 容器使用的方法是 loadBeanDefinition(),
其中的大致过程如下:通过 ResourceLoader 来完成资源文件位置的定位,DefaultResourceLoader
是默认的实现,同时上下文本身就给出了 ResourceLoader 的实现,可以从类路径,文件系统,URL 等
方式来定为资源位置。如果是 XmlBeanFactory 作为 IOC 容器,那么需要为它指定 Bean 定义的资源,
也 就 是 说 Bean 定 义 文 件 时 通 过 抽 象 成 Resource 来 被 IOC 容 器 处 理 的 , 容 器 通 过
BeanDefinitionReader 来 完 成 定 义 信 息 的 解 析 和 Bean 信 息 的 注 册 , 往 往 使 用 的 是
XmlBeanDefinitionReader 来 解 析 Bean 的 XML 定 义 文 件 - 实 际 的 处 理 过 程 是 委 托 给
BeanDefinitionParserDelegate 来完成的,从而得到 bean 的定义信息,这些信息在 Spring 中使用
BeanDefinition对象来表示-这个名字可以让我们想到loadBeanDefinition(),registerBeanDefinition()
这些相关方法。它们都是为处理 BeanDefinitin 服务的,容器解析得到 BeanDefinition 以后,需要把
它在 IOC 容器中注册,这由 IOC 实现 BeanDefinitionRegistry 接口来实现。注册过程就是在 IOC 容器
内部维护的一个 HashMap 来保存得到的 BeanDefinition 的过程。这个 HashMap 是 IOC 容器持有
Bean 信息的场所,以后对 Bean 的操作都是围绕这个 HashMap 来实现的。
然后我们就可以通过 BeanFactory 和 ApplicationContext 来享受到 Spring IOC 的服务了,在使用 IOC
容器的时候,我们注意到除了少量粘合代码,绝大多数以正确 IOC 风格编写的应用程序代码完全不用关
心如何到达工厂,因为容器将把这些对象与容器管理的其他对象钩在一起。基本的策略是把工厂放到已
知的地方,最好是放在对预期使用的上下文有意义的地方,以及代码将实际需要访问工厂的地方。Spring
本身提供了对声明式载入web应用程序用法的应用程序上下文,并将其存储在ServletContext中的框架
实现。
以下是容器初始化全过程的时序图: 

Spring(三) Spring IOC 初体验的更多相关文章

  1. Spring Cloud 负载均衡初体验

    目录 服务搭建 1.注册中心--Eureka Server 2.服务提供方--Service Provider 3.服务消费方--Service Consumer 服务消费 Feign 与断路器 Hy ...

  2. Spring(三) Spring IOC

    Spring 核心之 IOC 容器 再谈 IOC 与 DI IOC(Inversion of Control)控制反转:所谓控制反转,就是把原先我们代码里面需要实现的对象创 建.依赖的代码,反转给容器 ...

  3. 深入Asyncio(三)Asyncio初体验

    Asyncio初体验 Asyncio在Python中提供的API很复杂,其旨在替不同群体的人解决不同的问题,也正是由于这个原因,所以很难区分重点. 可以根据asyncio在Python中的特性,将其划 ...

  4. Spring(三)之Ioc、Bean、Scope讲解

    Spring容器是Spring Framework的核心.容器将创建对象,将它们连接在一起,配置它们,并管理从创建到销毁的整个生命周期.Spring容器使用DI来管理组成应用程序的组件.这些对象称为S ...

  5. Spring Cloud Data Flow初体验,以Local模式运行

    1 前言 欢迎访问南瓜慢说 www.pkslow.com获取更多精彩文章! Spring Cloud Data Flow是什么,虽然已经出现一段时间了,但想必很多人不知道,因为在项目中很少有人用.不仅 ...

  6. Spring MVC + Security 4 初体验(Java配置版)

    spring Version = 4.3.6.RELEASE springSecurityVersion = 4.2.1.RELEASE Gradle 3.0 + Eclipse Neno(4.6) ...

  7. Spring boot集成redis初体验

    pom.xml: <?xml version="1.0" encoding="UTF-8"?> <project xmlns="ht ...

  8. spring(三) spring事务操作

    前面一篇博文讲解了什么是AOP.学会了写AOP的实现,但是并没有实际运用起来,这一篇博文就算是对AOP技术应用的进阶把,重点是事务的处理. --wh 一.jdbcTemplate 什么是JdbcTem ...

  9. Ubuntu下Django初体验(三)——django初体验

    Django中的重要概念: 一次web访问的实质: 1. 客户发送http请求到web服务回 2. web服务器返回html页面给客户 Django概述: 1. URL配置             建 ...

随机推荐

  1. ClickHouse入门:表引擎-HDFS

    前言插件及服务器版本服务器:ubuntu 16.04Hadoop:2.6ClickHouse:20.9.3.45 文章目录 简介 引擎配置 HDFS表引擎的两种使用形式 引用 简介 ClickHous ...

  2. IP2188中文资料书

    IP2188 是一款集成 12 种.用于 USB 输出端口的快充协议 IC,支持 USB 端口充电协议.支持 11种快充协议,包括 USB TypeC PD2.0/PD3.0/PPS DFP,HVDC ...

  3. Redis-第五章节-8种数据类型

    目录 一.Redis对key的操作 二.五种数据类型 String类型 List(集合) Set(集合) Hash(哈希) Zset(有序集合) 三.三种特殊数据类型 geospatial(地理位置) ...

  4. 使用Azure Runbook 发送消息到Azure Storage Queue

    客户需要定时发送信息到Azure Storage Queue,所以尝试使用Azure Runbook实现这个需求. 首先新增一个Azure Automation Account的资源. 因为要使用Az ...

  5. 【pytest】(十二)参数化测试用例中的setup和teardown要怎么写?

    还是一篇关于pytest的fixture在实际使用场景的分享. fixture我用来最多的就是写setup跟teardown了,那么现在有一个用例是测试一个列表接口,参数化了不同的状态值传参,来进行测 ...

  6. XV6学习(1) Lab util

    正在学习MIT的6.S081,把做的实验写一写吧. 实验的代码放在了Github上. 第一个实验是Lab util,算是一个热身的实验,没有涉及到系统的底层,就是使用系统调用来完成几个用户模式的小程序 ...

  7. selenium浏览器弹出框alert 操作

    1.简介 在WebDriver中要处理JS生成的alert.confirm以及prompt,需要 switch_to.alert() 来选取(定位)警告弹窗,在对弹窗进行关闭.输入等信息操作. 2.操 ...

  8. jQuery 实现复制功能

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  9. Go RPC 框架 KiteX 性能优化实践 原创 基础架构团队 字节跳动技术团队 2021-01-18

    Go RPC 框架 KiteX 性能优化实践 原创 基础架构团队 字节跳动技术团队 2021-01-18

  10. 一站式入口服务|爱奇艺微服务平台 API 网关实战 原创 弹性计算团队 爱奇艺技术产品团队

    一站式入口服务|爱奇艺微服务平台 API 网关实战 原创 弹性计算团队 爱奇艺技术产品团队