Spring IoC容器的实现

疯狂的Bug 2018-10-08

IoC原理在不同的语言中都有许多实现,例如c++,java等。其中spring是java中最著名的一个产品,IoC也是spring框架解决的核心问题。

在Spring IoC容器的设计中,主要有两条线路,一是实现BeanFactory接口的简单容器系列,这系列只实现了容器的基本功能,另一个是ApplicationContext应用上下文,它是容器的高级形态,增加了许多面向框架的特性,同时对应用环境做了许多适配。

IoC容器是怎样设计的呢?

下图描述了IoC容器中的的主要接口设计:

Spring IoC容器的实现

最常用的以Application应用上下文为核心的接口设计,主要涉及的接口有:ApplicationContext,

WebApplicaionContext,

ConfigurableAppicationContext。

我们常用的应用上下文基本上都是ConfigurableApplicationContext或者WebApplicationContext的实现。

在Spring中,实际上是把DefaultListableBeanFactory作为一个默认的功能完整的IoC容器来使用的。我们通过编程式使用IoC容器可以更好地理解其原理:

ClassPathResource res = new ClassPathResource(“beans.xml”);

DefaultListableBeanFactory factory = new DefaultListableBeanFactory();

XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);

Reader.loadBeanDefinition(res);

可以看到使用IoC容器主要包含4个步骤:

  1. 创建配置文件的抽象资源,包含了BeanDefinition的定义信息。
  2. 创建一个BeanFactory,这里使用DefaultListableBeanFactory。
  3. 创建一个载入BeanDefinition的读取器。
  4. 从定义好的资源位置读入配置信息,解析过程由BeanDefinitionReader来实现。

首先,初始化容器包括BeanDefinition的Resource定位,载入和注册三个基本过程。资源定位统一通过Resource接口来完成,Spring实现了多种定位方式,例如文件系统中的信息可以使用FileSystemResource来定位,类路径中的信息可以通过ClassPathResource来定位等。

其次,在定位到配置文件后,就会将配置信息转换成BeanDefinition,BeanDefinition就是POJO对象在IoC容器中的抽象表示,通过这种数据结构,使得IoC容器能够对Bean进行管理。

第三个过程是向IoC容器注册这些BeanDefinition,这个步骤必须基于BeanDefinition信息来完成。通过调用BeanDefinitionRegistry接口,把载入过程中解析得到的BeanDefinition向IoC容器进行注册。

通过这3步,容器的初始化就完成了,如果使用的是BeanFactory或者开启了懒加载,依赖注入通常发生在第一次调用getBean()向容器索取Bean的时候。而使用ApplicationContext则会在容器初始化的时候就进行依赖注入。

定位

在Spring中,资源的定位主要由ResourceLoader模块实现。

Spring IoC容器的实现

ResourceLoader 的默认实现是DefaultResourceLoader,而我们可以看到,AbstractApplicationContext通过继承DefaultResourceLoader已经具备了读入BeanDefinition的能力。

查看DefaultResourceLoader重要方法的代码实现:

protected Resource getResourceByPath(String path) {

return new ClassPathContextResource(path, getClassLoader());

}

这是一个模板方法,作用是读取Resource资源, FileSystemXmlApplicationContext类重写了这个方法,返回一个FileSystemResource对象,通过这个对象,Spring可以进行相关的I/0操作,完成BeanDefinition的定位。如果是其他的ApplicationContext,那么会对应生成其他种类的Resource,比如ClassPathResource等。

如果应用直接使用ApplicationContext,以FileSystemXmlApplicationContext为例,在代码实现中可以看到其构造方法:

public FileSystemXmlApplicationContext(

String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)

throws BeansException {

super(parent);

setConfigLocations(configLocations);

if (refresh) {

refresh();

}

}

refresh()函数的定义在

AbstractApplicationContext中

实现如下:

public void refresh() throws BeansException, IllegalStateException {

synchronized (this.startupShutdownMonitor) {

// Prepare this context for refreshing.

prepareRefresh();

// Tell the subclass to refresh the internal bean factory.

ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

// Prepare the bean factory for use in this context.

prepareBeanFactory(beanFactory);

try {

// Allows post-processing of the bean factory in context subclasses.

postProcessBeanFactory(beanFactory);

// Invoke factory processors registered as beans in the context.

invokeBeanFactoryPostProcessors(beanFactory);

// Register bean processors that intercept bean creation.

registerBeanPostProcessors(beanFactory);

// Initialize message source for this context.

initMessageSource();

// Initialize event multicaster for this context.

initApplicationEventMulticaster();

// Initialize other special beans in specific context subclasses.

onRefresh();

// Check for listener beans and register them.

registerListeners();

// Instantiate all remaining (non-lazy-init) singletons.

finishBeanFactoryInitialization(beanFactory);

// Last step: publish corresponding event.

finishRefresh();

}

catch (BeansException ex) {

if (logger.isWarnEnabled()) {

logger.warn("Exception encountered during context initialization - " +

"cancelling refresh attempt: " + ex);

}

// Destroy already created singletons to avoid dangling resources.

destroyBeans();

// Reset 'active' flag.

cancelRefresh(ex);

// Propagate exception to caller.

throw ex;

}

finally {

// Reset common introspection caches in Spring's core, since we

// might not ever need metadata for singleton beans anymore...

resetCommonCaches();

}

}

}

可以看到,refresh()函数即IoC容器启动时的一系列复杂操作。在refresh()方法中调用了obtainFreshBeanFactory(),追踪这个方法可以发现它调用了loadBeanDefinitions(),这个方法的作用即定位载入Resource,在这个方法中,调用了getResourceByPath(),不同的子类具有不同的定位资源的方式实现。

载入和解析

载入的过程,相当于把定义的BeanDefinition在IoC容器中转化成一个Spring内部表示的数据结构的过程。这些BeanDefinition数据在IoC容器中通过一个HashMap来保持和维护。

容器的启动,主要由refresh()完成,这个方法描述了应用上下文的初始化过程。refreshBeanFactory()方法创建了BeanFactory,通过这个方法,可以了解bean载入的过程。

protected final void refreshBeanFactory() throws BeansException {

if (hasBeanFactory()) {

destroyBeans();

closeBeanFactory();

}

try {

//创建IoC容器,这里使用的是DefaultListableBeanFactory

DefaultListableBeanFactory beanFactory = createBeanFactory();

beanFactory.setSerializationId(getId());

customizeBeanFactory(beanFactory);

//启动对BeanDefinition的载入

loadBeanDefinitions(beanFactory);

synchronized (this.beanFactoryMonitor) {

this.beanFactory = beanFactory;

}

}

catch (IOException ex) {

throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);

}

}

loadBeanDefinitons是一个抽象方法,根据不同的子类实现不同资源的读取和载入。例如AbstractXmlApplicationContext中的实现:

@Override

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {

// Create a new XmlBeanDefinitionReader for the given BeanFactory.

XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

// Configure the bean definition reader with this context's

// resource loading environment.

beanDefinitionReader.setEnvironment(this.getEnvironment());

beanDefinitionReader.setResourceLoader(this);

beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

// Allow a subclass to provide custom initialization of the reader,

// then proceed with actually loading the bean definitions.

initBeanDefinitionReader(beanDefinitionReader);

loadBeanDefinitions(beanDefinitionReader);

}

由于AbstractXmlApplicationContext类是通过Xml读入资源的,因此初始化读取器XmlBeanDefinitionReader,并将它在容器中设置好,最后启动这个读取器来完成BeanDefinition在IoC容器中的载入。

注册

通过前面的动作,用户定义的BeanDefinition信息已经在IoC容器中建立起了数据结构,但此时这些数据还不能提供IoC容器直接使用,需要在容器中对这些BeanDefinition数据进行注册。在DefaultListableBeanFactory中可以看到持有Bean的容器,是一个HashMap

private final MapbeanDefinitionMap = new ConcurrentHashMap(256);

该类实现了BeanDefinitionRegistry接口,具有注册的功能,通过实现registerBeanDefinition方法,实现注册的具体逻辑,将bean放入Map中。

调用关系如下图:

Spring IoC容器的实现

此时,IoC容器完成了初始化的过程,在容器中已经建立了整个Bean的配置信息,并且这些Bean可以被容器使用了,他们都在beanDefinitionMap里被检索和使用。

Spring IoC容器的实现

[ShareSDK] 轻松实现社会化功能 强大的社交分享

[SMSSDK] 快速集成短信验证 联结通讯录社交圈

[MobLink] 打破App孤岛 实现Web与App无缝链接

[MobPush] 快速集成推送服务 应对多样化推送场景

[AnalySDK] 精准化行为分析 + 多维数据模型 + 匹配全网标签 + 垂直行业分析顾问

BBSSDK | ShareREC | MobAPI | MobPay | ShopSDK | MobIM | App工厂

截止2018 年4 月,Mob 开发者服务平台全球设备覆盖超过84 亿,SDK下载量超过3,300,000+次,服务超过380,000+款移动应用

相关推荐