Spring源码学习【一】初识IOC容器

目录

一、IOC和DI

二、Spring的IOC容器设计

(一)BeanFactory系列容器接口

(二)ApplicationContext系列容器接口


一、IOC和DI

IOC(Inversion Of Control):控制反转,是Spring的核心。所谓控制反转,就是将对象的创建和管理交由Spring容器控制,这是一种重要的面向对象设计思想,能够帮助我们设计出低耦合的程序。

DI(Dependency Injection):依赖注入。在运行期间,动态地为对象注入其所依赖的对象,提高组件的复用,提高程序的灵活性和可扩展性。

控制反转和依赖注入是对同一概念的不同描述,控制反转以容器的角度描述,而依赖注入则是以应用程序的角度描述,这两种说法殊途同归,分离了对象和其所依赖的资源,使得程序更加灵活,为我们提供了重要的面向对象设计思想。

二、Spring的IOC容器设计

Spring中有两个系列的容器:

1. 实现BeanFactory接口的简单IOC容器,这些容器提供了IOC容器的基本功能。

2. 实现ApplicationContext接口的应用上下文,这些应用上下文不仅提供了IOC容器的基本功能,而且提供了一些高级功能,是IOC容器的高级形式。

IOC容器接口设计图如图1所示,Spring设计了如此之多的IOC容器接口,一方面可以通过接口的叠加来扩展IOC容器的功能,另一方面可以方便特定容器的定制实现。

图1 IOC容器接口设计图

(一)BeanFactory系列容器接口

从图1可以看到,BeanFactory是IOC容器接口中最顶层的接口,定义了IOC容器的基本规范。BeanFactory共有三个直接的子类:ListableBeanFactory、HierarchicalBeanFactory 和 AutowireCapableBeanFactory。

ListableBeanFactory接口表示Bean是可列表化的。

HierarchicalBeanFactory接口表示Bean是有继承关系的,可以管理父IOC容器。

AutowireCapableBeanFactory接口表示Bean是自动装配的。

从图1中可以看到,BeanFactory系列接口中一个主要的继承路径就是从BeanFactory到ConfigurableListableBeanFactory。在HierarchicalBeanFactory接口的基础上,扩展了对BeanFactory的配置功能,如设置父IOC容器、配置Bean后置处理器等。

BeanFactory接口定义了IOC容器最基本的形式,这些功能的定义可以在BeanFactory接口中看到,通过这一系列的接口方法,可以实现不同的IOC容器,接口代码如下所示:

public interface BeanFactory { 
	/**
	 * 转义符号,用于获取FactoryBean本身(即用于生产和管理Bean的Bean)
	 * 在Spring中,所有的Bean都是有BeanFactory进行管理的,而BeanFactory是一个特殊的FactoryBean
	 * 通过getBean(&factoryBeanName)会返回名为factoryBeanName的FactoryBean
	 */
	String FACTORY_BEAN_PREFIX = "&"; 
	/**
	 * 根据名字获取指定的Bean实例,此方法使Spring BeanFactory可以当做单例或原型模式来使用
	 * Bean的别名会被翻译为规范的Bean名称,如果在工厂实例中找不到对应的Bean,则会询问父工厂
	 */
	Object getBean(String name) throws BeansException; 
	/**
	 * 根据指定的名字获取Bean实例,并根据Class类型对Bean进行类型检查
	 * 当Bean的类型与requiredType不一致时会抛出BeanNotOfRequiredTypeException
	 */
	<T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException;
	/**
	 * 根据指定的名字获取Bean实例,可以指定创建Bean时需要的构造参数,且会覆盖BeanDefinition
	 * 中的默认参数,参数仅用于创建新实例
	 * 当给定了构造参数但Bean不是原型作用域时,会抛出BeanDefinitionStoreException
	 * 原因是Spring中Bean默认的作用域是容器内的单例,要想在同一个容器中使用不同的Bean实例,则需要为
	 * Bean指定原型作用域(scope="prototype")
	 */
	Object getBean(String name, Object... args) throws BeansException;
	/**
	 * 根据指定的名字获取Bean实例,可以指定创建Bean时需要的构造参数,且会覆盖BeanDefinition
	 * 中的默认参数,参数仅用于创建新实例
	 */
	<T> T getBean(Class<T> requiredType) throws BeansException;
	/**
	 * 返回与给定对象类型唯一匹配的bean实例,可指定Bean的构造参数
	 * 当匹配到多个Bean时会抛出NoUniqueBeanDefinitionException
	 * 当给定了构造参数但Bean不是原型作用域时,会抛出BeanDefinitionStoreException
	 */
	<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
	/**
	 * 是否存在给定名称的Bean
	 * 注意:由于BeanDefinition的具体或抽象、懒加载或饿加载、作用域的不同,该方法返回true不代表
	 * getBean(String name)会获得一个Bean实例
	 */
	boolean containsBean(String name);
	/**
	 * 这个Bean是否对应于一个单例
	 * 注意:由于Bean的作用域有单例(singleton)、原型(prototype)、会话(session)、请求(request)
	 * 等,该方法返回false只代表Bean是非单例的
	 */
	boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
	/**
	 * 这个bean是否总是提供独立的实例
	 * 注意:由于Bean的作用域有单例(singleton)、原型(prototype)、会话(session)、请求(request)
	 * 等,该方法返回false只代表Bean是非原型的
	 */
	boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
	/**
	 * 检查给定名称的Bean是否与指定类型匹配
	 */
	boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionEx-ception;
	boolean isTypeMatch(String name, @Nullable Class<?> typeToMatch) throws NoSuchBeanDefinition-Exception;
	/**
	 * 获得给定名称的Bean的Class类型
	 */
	Class<?> getType(String name) throws NoSuchBeanDefinitionException;
	/**
	 * 获得给定名称的Bean的别名
	 */
	String[] getAliases(String name);
}

BeanFactory接口只定义了IOC容器的基本行为,忽略了IOC容器的具体实现,要知道工厂是如何生产和管理对象的,就需要深入到具体IOC容器的实现,DefaultListableBeanFactory、XmlBeanFactory等就是基本IOC容器的具体实现,下面以XmlBeanFactory为例看看IOC容器的简单实现。

XmlBeanFactory是一个简单的IOC容器的实现,它继承自DefaultListableBeanFactory。Spring中将DefaultListableBeanFactory作为一个默认的功能完整的IOC容器使用,XmlBeanFactory在DefaultListableBeanFactory的基础上扩展了读取以XML格式定义的BeanDefinition(Spring中描述Bean的类)的功能,XmlBeanFactory源码如下所示:

public class XmlBeanFactory extends DefaultListableBeanFactory {
	/**
	 * XmlBeanDefinitionReader用于处理以XML文件格式定义的BeanDefinition
	 */
	private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
	/**
	 * 根据给定的Resource创建一个XmlBeanFactory
	 * Resource是Spring用来封装I/O操作的类
	 */
	public XmlBeanFactory(Resource resource) throws BeansException {
		this(resource, null);
	}
	/**
	 * 根据给定的Resource和父工厂创建一个XmlBeanFactory,并使用XmlBeanDefinitionReader对象
	 * 加载BeanDefinitions
	 */
	public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
		super(parentBeanFactory);
		this.reader.loadBeanDefinitions(resource);
	}
}

下面尝试使用XmlBeanFactory,代码如下:

ClassPathResource resource = new ClassPathResource("beans.xml");
XmlBeanFactory factory = new XmlBeanFactory(resource);

可见XmlBeanFactory的使用是非常简单的,但这个类已经在Spring3.1中被标明废弃。XmlBeanFactory继承自DefaultListableBeanFactory,是对后者的功能扩展,我们可以通过编程直接使用后者来达到前者的功能,代码如下:

ClassPathResource resource = new ClassPathResource("beans.xml");
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions(resource);

以上代码使用了DefaultListableBeanFactory这一功能功能完整的IOC容器,主要分为以下四个步骤:

1. 创建IOC容器配置文件的资源,这里使用ClassPathResource,即在ClassPath中寻找资源文件。

2. 创建一个BeanFactory,这里使用了DefaultListableBeanFactory。

3. 创建一个BeanDefinition读取器,这里使用XmlBeanDefinitionReader,用于加载以XML文件格式定义的BeanDefinition,并回调给BeanFactory。

4. 从资源中读取Bean的配置信息,并完成Bean的加载和注册。

(二)ApplicationContext系列容器接口

ApplicationContext作为一系列重要的容器产品,一方面继承了BeanFactory基本的IOC容器功能;另一方面,对BeanFactory进行了功能扩展。

如图2所示,ApplicationContext通过继承ApplicationEventPublisher、ResourcePatternResolver、MessageSource、EnvironmentCapable接口,为基本的IOC容器扩展了高级的容器特性,例如:

ApplicationEventPublisher:支持应用事件

MessageSource:支持国际化信息源

ResourcePatternResolver: 允许以路径模式定位Resource

EnvironmentCapable:应用上下文环境检查

图2 ApplicationContext继承关系

FileSystemXmlApplicationContext和ClassPathXmlApplicationContext是常用的应用上下文,根据名字可以看出二者分别实现了从文件系统和类路径读取以XML格式定义的BeanDefinition的功能,下面以FileSystemXmlApplicationContext为例看一看应用上下文的设计原理。

FileSystemXmlApplicationContext的类继承关系如下图所示。

图3 FIleSystemXmlApplicationContext继承关系

关于应用上下文的主要功能已经在其父类AbstractXmlApplicationContext中实现了,而FileSystemXmlApplicationContext主要进行了两方面的功能扩展:

1. 构造应用上下文实例,同时启动IOC容器的refresh()过程,代码如下

/**
 * 创建一个应用上下文,根据应用环境解析路径,并启动refresh()过程
 */
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException {
	super(parent);
	setConfigLocations(configLocations);
	if (refresh) {
		refresh();
	}
}

refresh()中具体的处理过程已经定义在AbstractApplicationContext中,在子类应用上下文中只需要显式调用即可。refresh()中进行了刷新准备、创建BeanFactory、注册消息源等一系列操作。

2. 定义从文件路径加载XML资源文件的方法,代码如下:

/**
 * 通过文件系统路径解析资源文件
 */
@Override
protected Resource getResourceByPath(String path) {
	if (path.startsWith("/")) {
		path = path.substring(1);
	}
	return new FileSystemResource(path);
}

FileSystemXmlApplicationContext重写了DefaultResourceLoader中的getResourceByPath(String path)方法,提供了通过文件系统路径定位资源文件的功能。在DefaultResourceLoader中,默认实现是通过ClassPath加载资源文件的,代码如下所示:

protected Resource getResourceByPath(String path) {
	return new ClassPathContextResource(path, getClassLoader());
}

参考资料:《Spring技术内幕

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值