Spring-IOC容器实现分析

c#小王子 c#小王子 2022-03-31 343 Java

准备工作


我们都知道,IOC容器和AOP是Spring框架的核心,"To the developer, for the developer and bythe developer" - 简化JAVA企业应用的的开发是Spring框架的目标,为更好的使用IOC容器,我们结合Spring IOC的源代码对它的实现作一个分析。在了解IOC容器实现的基础上,Spring的使用者可以跟好的使用IOC容器和Spring框架,同时如果需要对Spring框架作自己的扩展,这些方面的了解也是很有必要的。我们在这里假设读者已经具备对Spring IOC容器使用的基本知识 - 关于对Spring IOC容器的使用,可以参考以下的参考资料,这里就不对一些使用和配置的问题多做讲解了。


• Spring Framework Reference Guide

• Spring In Action

• Expert One-on-one J2EE Development without EJB

• Professional Java Development with the Spring Framework


还需要准备好Spring的源代码,我们这里用的代码是Spring2.0,当然了一个可以查看源代码的编辑器也是需要的,这里使用的是Eclipse3.2 - 很多说明性的图例都是直接从屏幕拷贝下来的。下面是一些文章中用到的专有词汇:

上下文:ApplicationContext

Bean定义信息:BeanDefinition

Bean工厂:BeanFactory

工厂Bean:FactoryBean

单件:Singleton

概述:基本IOC容器和上下文


因为IOC容器为应用开发者管理对象之间的依赖关系提供了很多便利和基础服务,所以业界有许多IOC容器供开发者选择,Spring Framework就是其中的一个。对Spring IOC容器的使用来说,我们常常接触到的Bean工厂和上下文就是IOC容器的表现形式,在这些Spring提供的基本IOC容器的接口定义和实现的基础上,我们通过定义Bean定义信息来管理应用中的对象依赖关系。在使用Spring IOC容器的时候,了解Bean工厂和上下文之间的区别对我们了解Spring IOC容器是比较重要的。从实现上来看,IOC容器定义的基本接口是在Bean工厂定义的,也就是说Bean工厂是Spring IOC容器的最基本的形式,很显然,BeanFactory只是一个接口类,没有给出IOC容器的实现,只是对IOC容器需要提供的最基本的服务做了定义,象我们下面看到的DefaultListableBeanFactory,XmlBeanFactory, ApplicationContext这些都可以看成是IOC容器的某种具体实现。看看Bean工厂是怎样定义IOC容器的基本服务的:


public interface BeanFactory {
//这里是对工厂Bean的转义定义,因为如果使用bean的名字检索IOC容器得到的对象是工厂Bean生成的对象,
//如果需要得到工厂Bean本身,需要使用转义的名字来向IOC容器检索
String FACTORY_BEAN_PREFIX = "&";
//这里根据bean的_____名字,在IOC容器中得到bean实例,这个IOC容器就象一个大的抽象工厂,用户可以根据名字得到需要的bean
//在Spring中,Bean和普通的JAVA对象不同在于:
//Bean已经包含了我们在Bean定义信息中的依赖关系的处理,同时Bean是已经被放到IOC容器中进行管理了,有它自己的生命周期
Object getBean(String name) throws BeansException;
//这里根据bean的名字和Class类型来得到bean实例,和上面的方法不同在于它会抛出异常:如果根据名字取得的bean实例的Class类型和需要的不同的话。
Object getBean(String name, Class requiredType) throws BeansException;
//这里提供对bean的检索,看看是否在IOC容器有这个名字的bean
boolean containsBean(String name);
//这里根据bean名字得到bean实例,并同时判断这个bean是不是单件,在配置的时候,默认的
Bean被配置成单件形式,如果不需要单件形式,需要用户在Bean定义信息中标注出来,这样IOC容器在每次接受到用户的getBean要求的时候,会生成一个新的Bean返回给客户使用 - 这就是Prototype形式
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
//这里对得到bean实例的Class类型
Class getType(String name) throws NoSuchBeanDefinitionException;
//这里得到bean的别名,如果根据别名检索,那么其原名也会被检索出来
String[] getAliases(String name);
}

这个BeanFactory接口为IOC容器的使用提供了使用规范,在这个基础上,Spring还提供了它符合这个IOC容器接口的实现供开发人员使用,比如XmlBeanFactory和各种常见的上下文,我们先看一下XmlBeanFactory这个IOC容器的实现,和那些上下文相比,它提供了基本的IOC容器的基本功能;我们可以认为直接的BeanFactory的实现是IOC容器的基本形式,而各种上下文的实现是IOC容器的高级表现形式。XmlBeanFactory的实现是这样的:

public class XmlBeanFactory extends DefaultListableBeanFactory {
//这里为容器定义了一个默认使用的bean定义读取器,在Spring的使用中,Bean定义信息的读取是容器初始化的一部分,但是在实现上是和容器的注册以及依赖的注入是分开的,这样可以使用灵活的
bean定义读取机制。
private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
//这里需要一个Resource类型的Bean定义信息,实际上的定位过程是由Resource的构建过程来完成的。
public XmlBeanFactory(Resource resource) throws BeansException {
this(resource, null);
}
//在初始化函数中使用读取器来对资源进行读取,得到bean定义信息。这里完成整个IOC容器对
Bean定义信息的载入和注册过程
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws
BeansException {
super(parentBeanFactory);
this.reader.loadBeanDefinitions(resource);
}


我们看到XmlBeanFactory使用了DefaultListableBeanFactory作为它持有的IOC容器实现,在这个基础上,添加了XML形式的Bean定义信息的读取功能。从这个角度看,这个DefaultListableBeanFactory是很重要的一个Spring IOC实现。下面我们可以看到上下文也和XmlBeanFactory一样,通过持有这个DefaultListableBeanFactory来获得基本的IOC容器的功能。通过编程式的使用DefaultListableBeanFactory我们可以看到IOC容器使用的一些基本过程:


ClassPathResource res = new ClassPathResource("beans.xml");

DefaultListableBeanFactory factory = new DefaultListableBeanFactory();

XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);

reader.loadBeanDefinitions(res);


这些代码演示了以下几个步骤:


1. 创建IOC配置文件的抽象资源

2. 创建一个BeanFactory,这里我们使用DefaultListableBeanFactory

3. 创建一个载入bean定义信息的读取器,这里使用XmlBeanDefinitionReader来载入XML形式的bean定义信息,配置给BeanFactory

4. 从定义好的资源位置读入配置信息,具体的解析过程由XmlBeanDefinitionReader来完成,这样完成整个载入和注册bean定义的过程。我们的IoC容器就建立起来这个基本过程我们可以看到,IOC容器建立的基本步骤,这些我们可以编程式的完成这些配置,但在Spring中,它提供的上下文已经为我们作了这些事情,所以从这个角度说上下文是一个高级形态上的IOC容器。更方便了用户的使用,相比于那些基本的IOC容器的BeanFactory实现,上下文除了提供基本的上面看到的容器的基本功能外,还为用户提供了以下的附加服务更方便的让客户使用容器:

• 可以支持不同的信息源,我们看到ApplicationContext扩展了MessageSource

• 访问资源, 体现在对ResourceLoader和Resource的支持上面,这样我们可以从不同地方得到

bean定义资源,这样用户程序可以灵活的定义Bean定义信息

• 支持应用事件,继承了接口ApplicationEventPublisher,这样在上下文中引入了事件机制而BeanFactory没有在上下文环境中,这些上下文提供的基础服务更丰富了基本IOC容器的功能。所以一般我们建议客户使用上下文作为IOC容器来使用 - 和XmlBeanFactory一样,上下文是通过持有

DefaultListableBeanFactory这个基本的IOC容器实现来提供IOC容器的基本功能的,这一点可以在下面我们分析IOC容器的初始化过程中看得很清楚。


IOC容器和上下文的初始化

简单来说,IOC容器和上下文的初始化包括Bean定义信息的资源定位,载入和注册过程。在上面编程式的使用DefaultListableBeanFactory中我们可以大致的看到上述过程的实现。值得注意的是,Spring把这三个过程的完成分开并让不同的模块来完成,这样可以让用户更加灵活的对这三个过程来进行剪裁,定义出自己最合适的IOC容器的初始化。比如Bean定义信息的资源定位由ResourceLoader通过统一的Resource接口来完成,这个Resource接口对各种形式的资源信息的使用提供了统一的接口,比如在文件系统中的Bean定义信息可以使用FileSystemResource,在类路径中可以使用上面看到的ClassPathResource等等。第二个关键的部分是Bean定义信息的载入,这个载入过程就是把用户定义好的Bean表示成IOC容器内部的数据结构的过程,在下面我们可以看到这个数据结构就是BeanDefintion,下面我们会对这个载入的过程做一个详细的分析;第三个过程是向IOC容器注册这些Bean定义信息的过程,这个过程是通过调用BeanDefinitionRegistry接口的实现来完成的,这个注册过程把载入过程中解析得到的Bean定义信息向IOC容器中进行注册 - 在IOC容器内部往往使用一个象HashMap这样的容器来持有这些Bean定义。值得注意的是,IOC容器和上下文的初始化一般不包含Bean的依赖注入的实现,关于依赖注入实现的过程在下面也会进行详细的分析。好了,下面我们详细的看一看IOC容器和上下文的Bean定义信息的资源定位,载入和注册过程是怎么实现的。


Bean定义信息的资源定位在上面编程式使用DefaultListableBeanFactory的时候,我们可以看到首先定义一个Resource来定位

容器使用的Bean定信息:

ClassPathResource res = new ClassPathResource("beans.xml");这个定义的Resource并不是让DefaultListableBeanFactory直接使用,而是让BeanDefinitionReader来使用,这里我们也可以看到使用上下文对于直接使用DefaultListableBeanFactory的好处,因为在上下文中的使用中,Spring已经为我们提供了一系列具备Resource功能的实现,比如我们常看到的FileSystemXmlApplicationContext,ClassPathXmlApplicationContext,XmlWebApplicationContext,我们下面就看看FileSystemXmlApplicationContext是怎样完成这个资源定位过程的,先看看这些类的继承体系:

可以看到,这个FileSystemXmlApplicationContext已经通过继承具备了ResourceLoader:


public class FileSystemXmlApplicationContext extends AbstractXmlApplicationContext {
//通过这个字符串数组可以持有多个资源位置
private String[] configLocations;
public FileSystemXmlApplicationContext(String configLocation) throws BeansException
{
this(new String[] {configLocation});
}
//这里是一系列初始化函数,得到Resource在文件系统中的位置,并通过refresh来初始化整个IOC容器
//这个refresh调用时容器的初始化调用入口
public FileSystemXmlApplicationContext(String[] configLocations) throws
BeansException {
this(configLocations, null);
}
public FileSystemXmlApplicationContext(String[] configLocations, ApplicationContext
parent)
throws BeansException {
super(parent);
this.configLocations = configLocations;
refresh();
}
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh)
throws BeansException {
this(configLocations, refresh, null);
}
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh,
ApplicationContext parent)
throws BeansException {
super(parent);
this.configLocations = configLocations;
if (refresh) {
refresh();
}
}
protected String[] getConfigLocations() {
return this.configLocations;
}
//这里是具体的关于在文件系统中定义Bean定义信息的实现
//通过构造一个FileSystemResource来得到一个在文件系统中定义的Bean定义信息
//这个getResourceByPath是在BeanDefinitionReader的loadBeanDefintion中被调用的。
protected Resource getResourceByPath(String path) {
if (path != null && path.startsWith("/")) {
path = path.substring(1);
}
return new FileSystemResource(path);
}
}

从下面的调用关系就可以很清楚的看到在初始化调用的refresh中怎样会触发实际的对资源位置的定位过程:


大家会比较奇怪,这个FileSystemXmlApplicationContext在什么地方定义了需要的


BeanDefinitionReader呢?我们看看它的基类AbstractRefreshableApplicationContext:
public abstract class AbstractRefreshableApplicationContext extends
AbstractApplicationContext {
/** 这里定义的beanFactory就是ApplicationContext使用的Bean工厂
private DefaultListableBeanFactory beanFactory;
.....
//这个refreshBeanFactory是refresh的一个过程,主要是完成对上下文中IOC容器的初始化
protected final void refreshBeanFactory() throws BeansException {
// Shut down previous bean factory, if any.
synchronized (this.beanFactoryMonitor) {
if (this.beanFactory != null) {
this.beanFactory.destroySingletons();
this.beanFactory = null;
}
}
// 这里初始化IOC容器
try {
//这里创建一个DefaultListableBeanFactory作为上下文使用哪个的IOC容器
DefaultListableBeanFactory beanFactory = createBeanFactory();
//这里调用BeanDefinitionReader来载入Bean定义信息
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
if (logger.isInfoEnabled()) {
logger.info("Bean factory for application context [" + getDisplayName() + "]:
" + beanFactory);
}
}
catch (IOException ex) {
throw new ApplicationContextException(
"I/O error parsing XML document for application context [" +
getDisplayName() + "]", ex);
}
}
//这就是在上下文中创建DefaultListableBeanFactory的地方
protected DefaultListableBeanFactory createBeanFactory() {
return new DefaultListableBeanFactory(getInternalParentBeanFactory());
}
//这里是使用BeanDefinitionReader载入Bean定义的地方,因为允许有多种载入的方式,虽然用得
最多的是XML定义的形式,这里委托给子类完成
protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)
throws IOException, BeansException;
}
这个loadBeanDefinitios在子类AbstractXmlApplicationContext中的实现:
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws
IOException {
// 这里创建XmlBeanDefinitionReader作为读入器
XmlBeanDefinitionReader beanDefinitionReader = new
XmlBeanDefinitionReader(beanFactory);
// 这里配置ResourceLoader,因为DefaultResourceLoader是父类,所以this可以直接被使用
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// 这是启动Bean定义信息载入的过程
initBeanDefinitionReader(beanDefinitionReader);
loadBeanDefinitions(beanDefinitionReader);
}
从上面的代码可以看到,在初始化FileSystmXmlApplicationContext的过程中,启动了IOC容器的初
始化 - refresh , 这个初始化时通过定义的XmlBeanDefinitionReader来完成的,使用的IOC容器是
DefualtListableBeanFactory,具体的资源载入在XmlBeanDefinitionReader读入Bean定义的时候
实现- 在AbstractBeanDefinitionReader中:
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
//这里取得置入的ResourceLoader,使用的是DefaultResourceLoader
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader == null) {
throw new BeanDefinitionStoreException(
"Cannot import bean definitions from location [" + location + "]: no
ResourceLoader available");
}
if (resourceLoader instanceof ResourcePatternResolver) {
// 这里对Resource进行解析
try {
Resource[] resources = ((ResourcePatternResolver)
resourceLoader).getResources(location);
int loadCount = loadBeanDefinitions(resources);
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + loadCount + " bean definitions from location
pattern [" + location + "]");
}
return loadCount;
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"Could not resolve bean definition resource pattern [" + location + "]",
ex);
}
}
else {
// 这里调用DefaultResourceLoader去取得Resource
Resource resource = resourceLoader.getResource(location);
int loadCount = loadBeanDefinitions(resource);
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + loadCount + " bean definitions from location [" +
location + "]");
}
return loadCount;
}
}
具体的取得Resource实现我们可以看看DefaultResourceLoader是怎样完成的:
public Resource getResource(String location) {
Assert.notNull(location, "Location must not be null");
//这里处理带classpath: 前缀的资源定义,直接返回一个ClassPathResource
if (location.startsWith(CLASSPATH_URL_PREFIX)) {
return new
ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()),
getClassLoader());
}
else {
try {
// 然后使用URLResource
URL url = new URL(location);
return new UrlResource(url);
}
catch (MalformedURLException ex) {
// 如果都不能处理交给子类的getResourceByPath,比如我们前面看到的
FileSystemXmlApplicationContext的实现
return getResourceByPath(location);
}
}
}

以上的代码对定位资源的过程做了一个基本的描述,其中涉及到的基本的类有DefaultResourceLoader这是定位Resource的基本类,由于这个定位过程是在IOC容器的初始化中完成的,所以我们可以看到一个上下文初始化的基本过程和定义IOC容器,Bean定义读入器的大概过程。


【下载地址】

百度网盘链接:https://pan.baidu.com/s/1u107HaNklyeTjZe1J83GKg

提取码:u8eq


相关文章


使用-JFreeChart来创建基于web的图表

使用-JFreeChart来创建基于web的图表

XStream使用文档

XStream使用文档

WebService发布过程及常见问题

WebService发布过程及常见问题

webpack实战入门进阶调优分享

webpack实战入门进阶调优分享

weblogic调优参数及监控指标

weblogic调优参数及监控指标

weblogic节点管理

weblogic节点管理

weblogic管理控制台概述

weblogic管理控制台概述

weblogic-部署和启动

weblogic-部署和启动

WebLogic-Server-性能及调优-调优-Java-虚拟机

Java 虚拟机(Java virtual machine,简称 JVM)是一种虚拟“执行引擎”实例,可在微处理器上执行 Java 类文件中的字节码。调整 JVM 的方式会影响 Weblogic Server 和应用程序的性能。

Velocity用户教程

Velocity是一个基于java的模板引擎(template engine)。它允许任何人仅仅简单的使用模板语言(template language)来引用由java代码定义的对象。

Velocity用户手册

Velocity 用户手册是帮助页面设计者和内容提供者认识 Velocity 和其简单而功能强大的脚本语言――Velocity 模板语言(VTL)。在手册上的许多例子,都是用 Velocity 插入动态的内容到网页上,但是所有的 VLT 例子都能应用到其他的页面和模板中。


文章热度: 166291
文章数量: 333
推荐阅读

FlashFXP绿色版网盘下载,附激活教程 780

FlashFxp百度网盘下载链接:https://pan.baidu.com/s/1MBQ5gkZY1TCFY8A7fnZCfQ。FlashFxp是功能强大的FTP工具

Adobe Fireworks CS6 Ansifa绿色精简版网盘下载 505

firework可以制作精美或是可以闪瞎眼的gif,这在广告领域是需要常用的,还有firework制作下logo,一些原创的图片还是很便捷的,而且fireworks用法简单,配合dw在做网站这一块往往会发挥出很强大的效果。百度网盘下载链接:https://pan.baidu.com/s/1fzIZszfy8VX6VzQBM_bdZQ

navicat for mysql中文绿色版网盘下载 477

Navicat for Mysql是用于Mysql数据库管理的一款图形化管理软件,非常的便捷和好用,可以方便的增删改查数据库、数据表、字段、支持mysql命令,视图等等。百度网盘下载链接:https://pan.baidu.com/s/1T_tlgxzdQLtDr9TzptoWQw 提取码:y2yq

火车头采集器(旗舰版)绿色版网盘下载 786

火车头采集器是站长常用的工具,相比于八爪鱼,简洁好用,易于配置。火车头能够轻松的抓取网页内容,并通过自带的工具对内容进行处理。站长圈想要做网站,火车头采集器是必不可少的。百度网盘链接:https://pan.baidu.com/s/1u8wUqS901HgOmucMBBOvEA

Photoshop(CS-2015-2023)绿色中文版软件下载 1155

安装文件清单(共46G)包含Window和Mac OS各个版本的安装包,从cs到cc,从绿色版到破解版,从安装文件激活工具,应有尽有,一次性打包。 Photoshop CC绿色精简版 Photoshop CS6 Mac版 Photoshop CC 2015 32位 Photoshop CC 2015 64位 Photoshop CC 2015 MAC版 Photoshop CC 2017 64位 Adobe Photoshop CC 2018 Adobe_Photoshop_CC_2018 Photoshop CC 2018 Win32 Photoshop CC 2018 Win64

知之

知之平台是全球领先的知识付费平台。提供各个领域的项目实战经验分享,提供优质的行业解决方案信息,来帮助您的工作和学习

使用指南 建议意见 用户协议 友情链接 隐私政策 Powered by NOOU ©2020 知之