欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-07-12 13:39:30
...

目录

一、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容器的功能,另一方面可以方便特定容器的定制实现。

Spring源码学习【一】初识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:应用上下文环境检查

Spring源码学习【一】初识IOC容器图2 ApplicationContext继承关系

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

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

Spring源码学习【一】初识IOC容器图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技术内幕

相关标签: Spring IOC容器