Spring 作为成熟的开源框架,已被开发人员广泛使用于日常开发中。Spring 自带的参考文档给开发人员提供了详细的使用介绍。而作为开源框架的 Spring 源码,更为开发人员提供了许多优雅的设计思想和具体实现参考。
文章开始前,我们定义一个名词:Bean Definition:即 Bean 定义,对应于 Spring 框架对一个 Bean 的定义,包括各种不同的属性参数,每个 Bean 都有一个或多个相关的 Bean Definition。 为了文章的可读性,在此我使用斜体表示,不将其翻译。
与 Java 程序中一个对象的执行大概经历定义、实例化和使用等阶段相似。Spring 容器中对象更加明确的经历了定义、实例化和使用等阶段:
对象定义: Spring 容器启动时,会根据开发人员对 Bean(对象)的定义,统一解析这些 Beans 到 Bean Definition容器 (ConcurrentHashMap<String, BeanDefinition>beanDefinitionMap) 中。同时 Spring 也会解析一些容器启动所必须的和一些默认行为的 Beans 到 Bean Definition容器中。 对象实例化: Spring 容器根据一定的规则,将 beanDefinitionMap 中的每个 Bean Definition实例化为具体的 Java 对象,同时会将 Singleton 类型的对象缓存到 bean 容器 (ConcurrentHashMap<String, Object> singletonObjects) 中。 对象使用: 前两步骤为 Spring 容器启动时执行,该步骤为应用程序在执行相关业务逻辑时,会到 Spring 容器中找出(Single 类型)或者实例化(如 Prototype 类型)相关对象,供业务逻辑使用。
本文主要分析 Spring IoC 容器解析基于 XML 配置方式的 Bean Definition的源码实现,给开发人员提供一个知其然,并知其所以然的途径。
Spring 简介及 Bean Definition 配置方式
Spring 是 2003 年兴起的一个轻量级 Java 开发框架,经过十多年的不断积累和演进,如今已成为功能相同或相似解决方案最为流行的开源框架。Spring IoC 改变了传统的对象定义和使用方式,Spring AOP 为开发人员提供了一种简单但功能强大的面向切面的编程方式,Spring MVC 简化了 WEB 开发的流程,使得开发人员更能专注于业务逻辑代码的开发。 Spring 可以用在各种类型的应用开发上。对于 Web Project, 开发人员只要在 web.xml 加入下列代码,并将相关 Spring 依赖文件引入就可以在开发中使用 Spring。
清单 1. Web.xml 引入 Spring
<listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
这是一个典型的 ServletContextListener,Servlet 容器(如 Tomcat 等)在启动时会找到 ContextLoaderListener 并执行其 contextInitialized(ServletContextEvent event) 方法。从这里开始,Spring 将会进行 Bean Definition的解析、Bean Processors 设置和处理、Beans 实例化等工作。从而将程序会用到的 Java 对象定义并根据该定义创建好,提供给开发人员去使用。 这里 Spring 首先需要处理的就是 Bean 的定义。经过不断的发展和演化,Bean 的定义方式有:
基于 XML 文件的配置方式。 基于 Annotation 的配置方式。 基于 Java Code 的配置方式。 用户自定义的配置方式。
基于 XML 配置 Bean Definition 的源码解读
XML 配置 root WebApplicationContext
前文介绍,Servlet 容器启动时如果 web.xml 配置了 ContextLoaderListener,则会调用该对象的初始化方法。根据 Java 语法规定,ContextLoaderListener 的父类 ContextLoader 有一段 static 的代码会更早被执行,这段代码配置了 XML 默认使用的 Context 为 org.springframework.web.context.WebApplicationContext = org.springframework.web.context.support.XmlWebApplicationContext 根据该定义,如果开发人员没有从 web.xml 指定 contextClass 参数,则默认使用 XmlWebApplicationContext 作为 root WebApplicationContext 工具类。
XML 命名空间及 XML 配置文件解析
Spring 采用 XML 命名空间的方式,为框架的扩展提供了极大的方便。
清单 2. XML 配置文件的命名空间定义
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd>
清单 2 展示在 XML 配置文件中引入命名空间的配置范例。
图 1. XML 命名空间及其 handler 定义(查看大图)
展开 Spring 的依赖 jar 文件,可以看到 Spring 各个模块对应的 jar 包都包含一个 META-INF 目录,如果使用了命名空间,则该目录包含了 spring.schemas 和 spring.handlers 两个文件,如图 1 所示。然后 Spring 代码通过 Properties 工具类扫描 project 的 classpath 以及其使用的所有依赖包中 META-INF 目录来得到对应参数值供后续使用。
PropertiesLoaderUtils.loadAllProperties(“META-INF/spring.schemas”, this.classLoader); PropertiesLoaderUtils.loadAllProperties(“META-INF/spring.handlers”, this.classLoader);