Spring源码--IOC容器实现(1)--IOC容器结构体系 - Go语言中文社区

Spring源码--IOC容器实现(1)--IOC容器结构体系


前言

Github:https://github.com/yihonglei/thinking-in-spring

一 什么是IOC/DI

    依赖反转表示的是依赖对象的获得被反转了,也叫做依赖注入(DI)。

    如果合作对象的引用或依赖关系的管理由具体对象来完成,会导致代码的高耦合和可测试性降低,

对于复杂的面向对象设计非常不利。可以把传统对象依赖注入交由IOC容器来完成,这种从具体对象

手中交出控制的做法就是依赖反转,也就是对象的控制权被反转了,以前是对象--》对象,

现在是对象--》容器--》对象,对象与对象之间不直接依赖注入,而是通过容器控制依赖注入。

这种实现方式可以解耦代码,同时提高代码的可测试性。

    而IOC(Invesion Of Control)容器是依赖反转这种思想的一个具体实现,是Spring框架的核心。

二 Spring IOC结构

1、 Spring IOC容器序列

    在Spring IOC容器设计中,主要有两个容器序列,一个是实现BeanFactory接口的简单容器序列,

这个容器是最基本的容器,因为只是实现了容器最基本的功能;另外一个容器是ApplicationContext

引用上下文,这是一个高级形态容器,因为它实现了具有更多功能的接口,功能更丰富。

    BeanFactory和ApplicationContext是容器的具体表现形式。深入Spring中可以看到各种各样的实现,

代表着各种各样的容器产品,各有特色。就像生活中的水桶一样,有金属的、塑料的等等,各色各样,

但这些水桶都具备一个特征,就是能装水,只要能装水,它就是一只可以用来售卖的水桶。在Spring中

也一样,Spring实现了各种各样的容器,供用户选择和使用。用什么样的容器,取决于用户需要。

但是在使用前,了解容器的基本情况,对我们理解和使用容器都有很大的帮助。就像买水桶一样,货比三家,

多看看桶的情况,才能买到物美价廉的水桶。只有了解Spring容器,才能得心应手,恰到好处的使用容器。

2、Spring IOC容器设计

    在深入理解IOC容器实现前,需要先了解下IOC容器重要的接口设计图:

以一下对这些接口进行简单说明。

1)从接口BeanFactory到HierarchicalBeanFactory,再到ConfigurableBeanFactory,是一条主要的BeanFactory设计路径。

在这条接口设计路径中,BeanFactory定义了基本的IOC容器规范。

BeanFactory接口

    该接口中包含了getBean()这样的IOC容器的基本方法;

HierarchicalBeanFactory接口

    该接口继承了BeanFactory接口,增加了getParentBeanFactory()方法,使BeanFactory具备了双亲IOC的管理功能。

ConfigurableBeanFactory接口

    该接口继承了HierarchicalBeanFactory,主要定义了一些对BeanFactory的配置功能,比如通过setParentBeanFactory()

设置双亲IOC容器,通过addBeanPostProcessor()配置Bean的后置处理器等等。

通过这些接口设计的叠加,定义了BeanFactory就是简单IOC容器的基本功能。

2)以ApplicationContext为核心的接口设计,为第二条接口设计路线,主要涉及接口从BeanFactory到ListableBeanFactory,

再到ApplicationContext,再到WebApplicationContext或者ConfigurableApplicationContext接口。

ApplicationContext源码:

public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
		MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
		......
}

在这个接口体系中,ListableBeanFactory和HierarchicalBeanFactory两个接口,连接BeanFactory接口和

ApplicationContext接口定义。ListableBeanFactory细化了BeanFactory接口功能,比如getBeanDefinitionNames()

获取bean数组。同时,ApplicationContext接口继承了MessageSource等,增加了很多高级特性。

3)这里涉及的是主要的接口关系,而具体的IOC容器都是在这些接口下实现的,比如DefaultListableBeanFactory,

这个接口间接实现了ConfigurableBeanFactory接口,从而成为一个简单IOC容器实现。可以理解为主要接口定义了

IOC的骨架,相应IOC容器去做实现。

4)这个接口体系以BeanFactory和ApplicationContext为核心。

BeanFactory为IOC容器最基本的接口,ApplicationContext为IOC容器的高级形态接口。

以下分别去分析BeanFactory和ApplicationContext。

3、BeanFactory

3.1 BeanFactory应用场景

    BeanFactory接口定义了IOC容器最基本的形式,并且提供了IOC容器所应该遵守的最基本的服务契约和

最基本的编程规范,这些接口勾画出IOC的基本轮廓。

BeanFactory如何定义IOC容器的基本接口?

用户使用容器时,可以通过转义符"&"来得到FactoryBean本身,用来区分通过容器来获取FactoryBean产生的对象

和获取FactoryBean本身。

关于BeanFactory和FactoryBean区别参考:Spring中BeanFactory和FactoryBean区别

先看下BeanFactory的源代码:

package org.springframework.beans.factory;
import org.springframework.beans.BeansException;
import org.springframework.core.ResolvableType;
public interface BeanFactory {
	String FACTORY_BEAN_PREFIX = "&";
	/**
	 * 根据Bean的名字从IOC容器获取Bean。
	Object getBean(String name) throws BeansException;
	/**
	 * 根据Bean的名字和Bean的Class类型从IOC容器获取Bean。
	 */
	<T> T getBean(String name, Class<T> requiredType) throws BeansException;
	/**
	 * 根据Bean的Class类型从IOC容器获取Bean。
	 */
	<T> T getBean(Class<T> requiredType) throws BeansException;
	/**
	 * 根据Bean的名字和Bean构造器参数类型从IOC容器获取Bean。
	 */
	Object getBean(String name, Object... args) throws BeansException;
	/**
	 * 根据Bean的Class类型和Bean构造器参数类型从IOC容器获取Bean。
	 */
	<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
	/**
	 * 根据Bean的名字判断IOC容器是否含有该Bean。
	 */
	boolean containsBean(String name);
	/**
	 * 根据Bean的名字判断是否是Singleton类型。
	 */
	boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
	/**
	 * 根据Bean的名字判断是否是Prototype类型。
	 */
	boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
	/**
	 * 根据Bean的名字和Bean的泛型类型,判断Bean是否是指定的Class类型。
	 */
	boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
	/**
	 * 根据Bean的名字和Bean的Class类型,判断Bean是否是指定的Class类型。
	 */
	boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
	/**
	 * 根据Bean的名字获取Class类型。
	 */
	Class<?> getType(String name) throws NoSuchBeanDefinitionException;
	/**
	 * 根据Bean的名字获取Bean对应的所有别名。
	 */
	String[] getAliases(String name);
}

BeanFactory接口方法勾画了IOC容器的基本特性。通过这一序列的BeanFactory接口,可以使用不同的Bean的检索

方法,很方便的从IOC容器中得到需要的Bean,这些检索方法代表了IOC最基本的容器入口。

3.2 BeanFactory容器设计原理

    BeanFactory接口提供了使用IOC容器的规范。在这个基础上提供了一系列的容器实现供使用。

这里以XmlBeanFactory为例,设计关系图如下:

XmlBeanFactory源码:

package org.springframework.beans.factory.xml;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.core.io.Resource;
@Deprecated
@SuppressWarnings({"serial", "all"})
public class XmlBeanFactory extends DefaultListableBeanFactory {
	private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
	public XmlBeanFactory(Resource resource) throws BeansException {
		this(resource, null);
	}
	public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
		super(parentBeanFactory);
		this.reader.loadBeanDefinitions(resource);
	}
}

XmlBeanFactory创建过程:

1)基于将要读取的xml文件,构建一个Resource对象作为XmlBeanFactory的构造器参数创建XmlBeanFactory。

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

2)通过XmlBeanDefinitionReader的loadBeanDefinitions()调用处理从Resource中载入BeanDefinitions的过程,

loadBeanDefinitions是IOC容器初始化的重要组成部分,最后IOC容器会基于BeanDefinitions完成容器初始化和

依赖注入的过程,这里只是简单介绍下,以下会深入源码分析IOC容器的初始化和依赖注入细节。

IOC容器建立的基本步骤:

先看下通过编程式方式使用IOC容器过程:

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

参考代码可以看到使用IOC容器主要经历了以下几个步骤:

1)创建IOC配置文件的抽象资源,资源包含了BeanDefinition的定义信息。

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

3)创建一个载入BeanDefinition的读取器,这里使用XmlBeanDefinitionReader来载入XML文件形式的BeanDefinition。

4)XmlBeanDefinitionReader解析资源,完成Bean的载入和注册,建立起IOC容器,这个时候就可用了。

4、ApplicationContext

4.1 ApplicationContext的应用场景

ApplicationContext是必BeanFactory更高级形态的容器,提供了很多BeanFactory不具备的功能。

当你决定要自己实现一个IOC容器之前,先看看Spring容器树上有没有心仪的对象。

ApplicationContext类结构图:

基于类图,说明ApplicationContext新特性:

1)支持不同的信息源。扩展了MessageSource接口,支持国际化的实现,为开发多语言版本提供服务。

2)访问资源。扩展了ResourcePatternResolver,而ResourcePatternResolver扩展自ResourceLoader,

而ResourceLoader含Resource属性,通过这两个类的支持,我们可以从不同的地方获取Bean定义资源。

3)支持应用事件。扩展了ApplicationEventPublisher,从而在上下文中引入了事件机制。这些事件机制

与Bean的生命周期的结合为Bean管理提供了便利。

ApplicationContext与BeanFactory相比,功能更丰富,应用开发中一般以ApplicationContext作为容器的

基本形式。

4.2 ApplicationContext容器的设计原理

以ClassPathXmlApplicationContext为例应用:
// 根据配置文件创建spring容器
ApplicationContext context =
	new ClassPathXmlApplicationContext("applicationContext.xml");
// 从容器中获取Bean
ConferenceServiceImpl conferenceService = (ConferenceServiceImpl)context.getBean("conferenceService");
// 调用Bean方法
conferenceService.conference();

ClassPathXmlApplicationContext源码:

package org.springframework.context.support;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;

public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext {
	private Resource[] configResources;

	public ClassPathXmlApplicationContext() {
	}

	public ClassPathXmlApplicationContext(ApplicationContext parent) {
		super(parent);
	}

	public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
		this(new String[] {configLocation}, true, null);
	}

	public ClassPathXmlApplicationContext(String... configLocations) throws BeansException {
		this(configLocations, true, null);
	}

	public ClassPathXmlApplicationContext(String[] configLocations, ApplicationContext parent) throws BeansException {
		this(configLocations, true, parent);
	}

	public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh) throws BeansException {
		this(configLocations, refresh, null);
	}

	public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
			throws BeansException {

		super(parent);
		setConfigLocations(configLocations);
		if (refresh) {
			refresh();
		}
	}
	
	public ClassPathXmlApplicationContext(String path, Class<?> clazz) throws BeansException {
		this(new String[] {path}, clazz);
	}
	
	public ClassPathXmlApplicationContext(String[] paths, Class<?> clazz) throws BeansException {
		this(paths, clazz, null);
	}
	
	public ClassPathXmlApplicationContext(String[] paths, Class<?> clazz, ApplicationContext parent)
			throws BeansException {

		super(parent);
		Assert.notNull(paths, "Path array must not be null");
		Assert.notNull(clazz, "Class argument must not be null");
		this.configResources = new Resource[paths.length];
		for (int i = 0; i < paths.length; i++) {
			this.configResources[i] = new ClassPathResource(paths[i], clazz);
		}
		refresh();
	}
	
	@Override
	protected Resource[] getConfigResources() {
		return this.configResources;
	}
}

构造器传入XML最后会调用到如下构造器:

public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
			throws BeansException {
	super(parent);
	setConfigLocations(configLocations);
	if (refresh) {
	    refresh();
	}
}

最后会调用refresh()方法,这个方法就是IOC容器启动的入口,IOC容器里面进行了一序列复杂的操作,

这也是通往IOC容器核心实现原理的入口。

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/yhl_jxy/article/details/80963352
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2020-02-25 00:37:55
  • 阅读 ( 1348 )
  • 分类:

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢