IOC容器的设计与初始化
标签:Spring

IOC原理分析

1. IoC容器和依赖反转模式

依赖反转:依赖对象的获得被反转了
在面向对象的系统中,对象封装了数据和对数据处理,对象的依赖关系常常体现在对数据和方法的依赖上。这些依赖关系可以通过把对象依赖注入交给框架或IoC容器来完成。
依赖反转有很多实现方式,在Spring中,IoC容器是实现这个模式的载体,他可以在对象生成或初始化时直接将数据注入到对象中,也可以将对象引用注入到对象数据域中的方式来注入对方法调用的依赖。
通过使用IoC容器,对象依赖关系的管理被反转了,转到IoC容器中来了,对象之间的相关依赖关系由IoC容器进行管理,并由IoC容器完成对象的注入。把在面向对象编程中需要执行的诸如新建对象,为对象引用赋值等操作交由容器统一完成。

2. IoC容器的设计与实现

2.1 BeanFactory和ApplicationContext

BeanFactory接口是一个简单容器系列,这系列容器只实现了容器的基本功能;ApplicationContext应用上下文,作为容器的高级形态存在,在简单容器的基础上增加了许多面向框架的特性,同时对应用环境做了适配。

2.2 BeanFactory的应用场景

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

理解BeanFactory和FactoryBean:
一个是Factory,也就是IoC容器或对象工厂,一个是Bean。在Spring中,所有的Bean都是由BeanFactory(也就是IoC容器)来进行管理的。但对FactoryBean,则是一个能产生或者修饰对象生成的工厂Bean,他的实现与设计模式中的工厂模式和修饰器模式类似。

2.3 BeanFactory容器的设计原理

在Spring中,实际上是把DefaultListableBeanFactory作为一个默认的功能完整的IOC容器来使用的。 例如XMLBeanFactory在继承了DefaultListableBeanFactory作为一个默认的功能完整的IoC容器来使用的,并在这个这个基础上实现了其他诸如读取XML的附加功能。

编程式使用IoC容器:

// 创建IoC配置文件的抽象资源,这个抽象资源包含了BeanDefinition的定义信息
ClassPathResource res=new ClassPathResource("beans.xml")
// 创建一个BeanFactory,这里使用DefaultListableBeanFactory
DefaultListableBeanFactory factory=new DefaultListableBeanFactory();
// 创建一个载入BeanDefinition的读取器,这里使用XMLBeanDefinitionReader
// 来载入XML文件形式的BeanDefinition通过一个回调配置给BeanFactory
XmlBeanDefinitionReader reader=new XmlBeanDefinitionReader(factory);
// 从定义好的资源位置读入配置信息,具体解析过程由XMLBeanDefinitionReader来完成
// 完成整个载入和注册Bean定义以后,需要的IoC容器就建立起来了,这个时候就可以直接使用IoC容器
reader.loadBeanDefinitions(res);

3. IoC容器的初始化过程

注意这个初始化过程不包含Bean的依赖注入,在Spring IoC的设计中,Bean的定义和依赖注入是两个独立的过程,Bean的注入一般发生在应用第一次通过getBean向容器索取Bean的时候。但有一个例外,在使用IoC容器时有一个预实例化配置,对Bean设置了 lazyinit 属性,那么Bean的依赖注入在IoC容器初始化时就预先完成了,而不需要等到整个初始化完成。

3.1 Resource定位过程

指的是BeanDefinition的资源定位,它由ResourceLoader通过统一的Resource接口来完成,这个Resource对各种形式的BeanDefinition的使用都提供了统一的接口,即得先找到Bean的资源,比如在文件(FileSystemResource)或者在类路径(ClassPathResource)

在ApplicationContext中,Spring已经为我们提供了一系列加载不同Resource的读取器的实现,比如 FileSystemXMLApplicationContext可以从文件系统载入Resource,ClassPathXMLApplicationContext 可以从Class Path载入Resource,XMLWebApplicationContext 可以在web容器中载入Resource。

FileSystemXMLApplicationContext的继承体系:

FileSystemApplicationContext是一个支持XML定义BeanDefinition的ApplicationContext,并且可以指定文件形式的BeanDefinition的读入,这些文件可以使用文件路径和URL来表示,在测试和独立应用环境中,这个ApplicationContext非常有用。

通过读 FileSystemApplicationContext 可知它实现了一个 getResourceByPath 将得到的资源路径返回,对于其他的比如 ClassPathResource、ServletContextResource也类似。

通过返回的Resource对象来进行BeanDefinition的载入。

3.2 BeanDefinition的载入和解析

把用户定义好的bean表示成IoC容器内部的数据结构,这个这个内部的数据结构就是BeanDefinition(相当于POJO在IoC容器中的抽象)

具体包含解析xml文件中的的 <bean> </bean>的各个属性值,并生产相应的BeanDefinition,现在暂时看着有点难受 :cry:

FileSystemApplicationContext 调用 refresh()方法之后,就开始创建BeanFactory,再向上 loadBeanDefinition()去加载 XMLBeanDefinitionReader(因为这里使用的是XML方式,使用其他方式的要使用不同的读取器) 然后把这个IOC容器设置好,最后启动读取器来完成 BeanDefinition 在IoC容器的载入。

BeanDefinition的载入分为两个部分,首先是调用XML解析器得到document对象,但这些对象并没有按照spring的bean规则进行解析,在完成统一的XML解析之后,再按照spring的bean规则进行解析。

具体的Spring BeanDefinition解析是在 BeanDefinitionParserDelegate 完成的,里面包含了对各种Spring Bean定义规则的处理。把这些元素值从XML中读出来后设置到生成的BeanDefinitionHolder(包含了BeanDefinition对象和其他与使用BeanDefinition相关的信息)中去,在 AbstractBeanDefinition 中定义了很多我们平时设置Bean的一些属性,通过BeanDefinitionParserDelegateparseBeanDefinitionElement解析之后将会返回一个AbstractBeanDefinition

在对Bean中已经有同名的Property进行解析的时候,将不会再次解析,起作用的是第一个。

在对property进行解析的时候,Array、List、Set都会生成对应的数据对象,如ManagedArray等类似的

3.3 向IoC容器注册这些BeanDefinition

通过调用BeanDefinitionRegistry接口来完成,在IoC容器内部将BeanDefinition注入到一个HashMap中去,IoC容器就是通过这个HashMap来持有这些BeanDefinition数据的。

前面已经完成了对BeanDefinition在IoC容器中的载入和解析的过程,在这些动作完成以后,用户定义的BeanDefinition信息已经在IoC容器中内建立起自己的数据结构以及相应的数据表示,但现在还不能使用,需要在IoC容器进行注册,在 DefaultListableBeanFactory是通过一个HashMap来载入BeanDefinition的:

	/** Map of bean definition objects, keyed by bean name */
	private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

完成Bean的注册就可以使用了,容器的作用就是对这些数据进行处理和维护。

DefaultListableBeanFactory有实现了BeanDefinitionRegistry接口,在DefaultListableBeanFactory完成注册,详细代码:

@Override
	public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
			throws BeanDefinitionStoreException {

		Assert.hasText(beanName, "Bean name must not be empty");
		Assert.notNull(beanDefinition, "BeanDefinition must not be null");

		if (beanDefinition instanceof AbstractBeanDefinition) {
			try {
				((AbstractBeanDefinition) beanDefinition).validate();
			}
			catch (BeanDefinitionValidationException ex) {
				throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
						"Validation of bean definition failed", ex);
			}
		}

		BeanDefinition oldBeanDefinition;

		oldBeanDefinition = this.beanDefinitionMap.get(beanName);
//		这里检查是否有相同的BeanDefinition已经在IoC容器中注册了,如果有相同名字的BeanDefinition
//		且不允许被覆盖的话,将会抛出异常
		if (oldBeanDefinition != null) {
			if (!isAllowBeanDefinitionOverriding()) {
				throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
						"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
						"': There is already [" + oldBeanDefinition + "] bound.");
			}
			else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
				// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
				if (this.logger.isWarnEnabled()) {
					this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +
							"' with a framework-generated bean definition: replacing [" +
							oldBeanDefinition + "] with [" + beanDefinition + "]");
				}
			}
			else if (!beanDefinition.equals(oldBeanDefinition)) {
				if (this.logger.isInfoEnabled()) {
					this.logger.info("Overriding bean definition for bean '" + beanName +
							"' with a different definition: replacing [" + oldBeanDefinition +
							"] with [" + beanDefinition + "]");
				}
			}
			else {
				if (this.logger.isDebugEnabled()) {
					this.logger.debug("Overriding bean definition for bean '" + beanName +
							"' with an equivalent definition: replacing [" + oldBeanDefinition +
							"] with [" + beanDefinition + "]");
				}
			}
			this.beanDefinitionMap.put(beanName, beanDefinition);
		}
		else {
			if (hasBeanCreationStarted()) {
				// Cannot modify startup-time collection elements anymore (for stable iteration)
				synchronized (this.beanDefinitionMap) {
					this.beanDefinitionMap.put(beanName, beanDefinition);
					List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
					updatedDefinitions.addAll(this.beanDefinitionNames);
					updatedDefinitions.add(beanName);
					this.beanDefinitionNames = updatedDefinitions;
					if (this.manualSingletonNames.contains(beanName)) {
						Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
						updatedSingletons.remove(beanName);
						this.manualSingletonNames = updatedSingletons;
					}
				}
			}
			else {
				// Still in startup registration phase
//				正常注册BeanDefinition方过程,beanName为key,beanDefinition为value
				this.beanDefinitionMap.put(beanName, beanDefinition);
				this.beanDefinitionNames.add(beanName);
				this.manualSingletonNames.remove(beanName);
			}
			this.frozenBeanDefinitionNames = null;
		}

		if (oldBeanDefinition != null || containsSingleton(beanName)) {
			resetBeanDefinition(beanName);
		}
	}

上面那个注册的方法,我看老版本的是需要同步的,但这没有采取同步处理来保证数据的一致性。

  • 9 min read

CONTRIBUTORS


  • 9 min read