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 Cloud 负载均衡初体验
目录 服务搭建 1.注册中心--Eureka Server 2.服务提供方--Service Provider 3.服务消费方--Service Consumer 服务消费 Feign 与断路器 Hy ...
- Spring(三) Spring IOC
Spring 核心之 IOC 容器 再谈 IOC 与 DI IOC(Inversion of Control)控制反转:所谓控制反转,就是把原先我们代码里面需要实现的对象创 建.依赖的代码,反转给容器 ...
- 深入Asyncio(三)Asyncio初体验
Asyncio初体验 Asyncio在Python中提供的API很复杂,其旨在替不同群体的人解决不同的问题,也正是由于这个原因,所以很难区分重点. 可以根据asyncio在Python中的特性,将其划 ...
- Spring(三)之Ioc、Bean、Scope讲解
Spring容器是Spring Framework的核心.容器将创建对象,将它们连接在一起,配置它们,并管理从创建到销毁的整个生命周期.Spring容器使用DI来管理组成应用程序的组件.这些对象称为S ...
- Spring Cloud Data Flow初体验,以Local模式运行
1 前言 欢迎访问南瓜慢说 www.pkslow.com获取更多精彩文章! Spring Cloud Data Flow是什么,虽然已经出现一段时间了,但想必很多人不知道,因为在项目中很少有人用.不仅 ...
- Spring MVC + Security 4 初体验(Java配置版)
spring Version = 4.3.6.RELEASE springSecurityVersion = 4.2.1.RELEASE Gradle 3.0 + Eclipse Neno(4.6) ...
- Spring boot集成redis初体验
pom.xml: <?xml version="1.0" encoding="UTF-8"?> <project xmlns="ht ...
- spring(三) spring事务操作
前面一篇博文讲解了什么是AOP.学会了写AOP的实现,但是并没有实际运用起来,这一篇博文就算是对AOP技术应用的进阶把,重点是事务的处理. --wh 一.jdbcTemplate 什么是JdbcTem ...
- Ubuntu下Django初体验(三)——django初体验
Django中的重要概念: 一次web访问的实质: 1. 客户发送http请求到web服务回 2. web服务器返回html页面给客户 Django概述: 1. URL配置 建 ...
随机推荐
- 华为三层交换机限制vlan段的指定端口
屏蔽vlan 120这个段的ip的所有2333端口 [NTT_3L]int Vlanif 120 [NTT_3L-Vlanif120]dis this # interface Vlanif120 ip ...
- 主题模型(Topic)
消息队列 RocketMQ - 打造金融级消息服务 - 阿里云 https://www.aliyun.com/product/rocketmq 主题模型(Topic) 发布/订阅(Pub/Sub) 一 ...
- 静默安装Oracle也没那么恐怖
几种必须静默安装的情况 服务器为了减少资源占用,没安装图形组件 不能进入机房,只能远程SSH 想炫(Z)耀(B),静默安装显得有技术含量 磁盘分区要求 如没有特别要求,装机时可按如下分区比较好管理 / ...
- 寻找最小字符串,IP地址——解题报告
寻找最小字符串 题目 思路 在寻找最小字符串的时候需要用到的函数有strcmp和strcpy,首先先输入输入字符串,先假设第一个字符串为最小的字符串min,再然比较接下来的字符串,当接下来的字符串比m ...
- jQuery——样式与动画
通过jQuery,不仅能够轻松地为页面操作添加简单的视觉效果,甚至能创建更精致的动画. ###修改内联CSS jQuery提供了.css()方法. 这个方法集getter(获取方法)和setter(设 ...
- 【Oracle】SQL/92 执行多个表的连接
内连接 外连接 自连接 交叉连接 1.内连接 表名 INNER JOIN 表名 ON 条件 等价于: FROM 表名, 表名 WHERE 条件 SELECT p.name, pt.name, pt.p ...
- StreamingContext详解,输入DStream和Reveiver详解
StreamingContext详解,输入DStream和Reveiver详解 一.StreamingContext详解 1.1两种创建StreamingContext的方式 1.2SteamingC ...
- login shell 和 non-login shell 的相关问题
问题:通过su命令切换用户并没有进入该用户的shell环境.这是为什么? 要解决这个问题,我们必须清楚用login shell 和non-login shell的区别. login sh ...
- 34.vsftpd服务程序--虚拟用户模式
1.创建用于进行FTP 认证的用户数据库文件,其中奇数行为账户名,偶数行为密码. [root@localhost ~]# cd /etc/vsftpd/ [root@localhost vsftpd] ...
- DEDECMS:修改网站底部出现的POWER BY DEDECMS
在include/dedesql.classs.php文件中找到第588行: $arrs1 = array(0x63,0x66,0x67,0x5f,0x70,0x6f,0x77,0x65,0x72,0 ...