Interview preparation -- Spring-IOC容器初始化过程

Spring IOC
IOC容器初始化过程
  • 第一个过程Resource定位过程:指的是BeanDefinition的资源定位。由ResourceLoader通过统一的Resource接口完成,同BeanDefinition可以存在各种形式,例如我们文件系统可以用FileSystemResource,类路径系统中可以用ClassPathResource
  • 第二个过程BeanDefinition的载入:将用户在resource文件中定义号的Bean表示成IOC容器内部的数据结构,这个数据结构就是BeanDefinition
  • 第三个过程向IOC容器注册这些BeanDefinition的过程,通过BeanDefinitionRegisty接口完成注册,最终将BeanDefinition注入到一个ConcurrentHashMap中,IOC容器通过这个ConcurrentHashMap来持有这些BeanDefition数据
BeanDefition 的Resource的定位源码分析
  • 以下源码版本: spring-context-5.2.2-RELEASE
  • 源码入口我们用一个比较典型的载入方式来分析:
FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext("123.xml");
  • FileSystemXmlApplicationContext 通过继承 AbstractApplicationContext 获得了ResourceLoader 加载BeanDefinition的能力,AbstractApplicationContext父类是DefaultResourceLoader
  • 我们直接看FileSystemXmlApplicationContext 的核心构造方法
	public FileSystemXmlApplicationContext(
			String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
			throws BeansException {

		super(parent);
		setConfigLocations(configLocations);
		if (refresh) {
		//核心方法,定位 + 加载资源
			refresh();
		}
	}
  • 以上构造方法是对象制定目录Xml文件的对象初始化过程,refresh启动了BeanDefinition的载入过程
  • 继续追源码如下:
//进入refresh,找到 AbstractApplicationContext 类中refresh实现
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
//找到AbstractRefreshableApplicationContext 中的 obtainFreshBeanFactory
refreshBeanFactory();
//找到AbstractRefreshableApplicationContext 中的 refreshBeanFactory
loadBeanDefinitions(beanFactory);
//找到AbstractXmlApplicationContext 中的 loadBeanDefinitions
loadBeanDefinitions(beanDefinitionReader);
//最后到 AbstractBeanDefinitionReader 中loadBeanDefinitions 具体实现,关键方法如下
int count = loadBeanDefinitions(resources);
//关注这个方法
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
//实现方法在PathMatchingResourcePatternResolver 中getResources ,
// 处理带有classpath*:" 表示的路径
//处理其他方式,例如war包之类的,
public Resource[] getResources(String locationPattern) throws IOException {
	}
//查看DefaultResourceLoader
//处理带URL表示的Resource的定位
//既不是classPath,也不是url也不是war,则交给getResourceByPath,就是直接按文件路径获取
@Override
	public Resource getResource(String location) {
	}
//最后的查询方式,安文件路径直接获取
	@Override
	protected Resource getResourceByPath(String path) {
		if (path.startsWith("/")) {
			path = path.substring(1);
		}
		return new FileSystemResource(path);
	}
  • 资源定位总结:Resource资源定位的过程已经完成,做法就是利用AbstractApplicationContext 中对DefaultResourceLoader 类继承的getResource方法 对传入的Path进行解析,然后生成一个FileSystemResource对象并返回
  • 如果是其他类型的ApplicationContext,就生成对应类型的Resource
BeanDefinition的Resource 载入
  • 载入的过程自然是在定位resource文件之后,还在根据以上流程,我们找到定位Resource的代码,其中如下点位开始
//最后到 AbstractBeanDefinitionReader 中loadBeanDefinitions 具体实现,关键方法如下
//定位resource
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
//加载解析Resource并生成BeanDefinition,同时注册到IOC容器中(关键方法)
int count = loadBeanDefinitions(resources);
  • 我们对关键方法 loadBeanDefinitions进行分析
//继续跟loadBeanDefinition,进入XmlBeanDefinitionReader 类中的loadBeanDefinitions,获取文件流信息,同时进行BeanDefinition的正式加载入口
	InputStream inputStream = encodedResource.getResource().getInputStream();
	InputSource inputSource = new InputSource(inputStream);
	return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
//接着进入XmlBeanDefinitionReader 的doLoadBeanDefinitions,首先解析xml文件得到一个Document数据结构
Document doc = doLoadDocument(inputSource, resource);
//利用Resource生成的Document进行BeanDefinition生成,在BeanDefinitionDocumentReader 类中的registerBeanDefinitions 方法
int count = registerBeanDefinitions(doc, resource);
//接着 DefaultBeanDefinitionDocumentReader 中的 doRegisterBeanDefinitions 利用Document中的元素信息身材BeanDefinition,此处我们能看到我们XML文件中的各种固定Element信息,此处进行递归解析
parseBeanDefinitions(root, this.delegate);
//解析过程比较复杂,最后通过parseBeanDefinitionElement解析完后会将新建一个BeanDefinitionHolder,通过
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
  • 总结,Resource文件的载入过程到这已经结束,
  • 依据上一步骤得到的FileSystemResource,在BeanDefinitionReader 接口中定义了资源文件的读取固定模板方法
  • 本案例中的实现方法在XmlBeanDefinitionReader,获取source文件的文件流信息,调用XML的解析器得到一个Document对象
  • 解析Document文档内容,分析每一个Document中的element,其中就会解析我们在xml文件中配置的每一个< bean >标签对,将XML配置的标签信息读取出来设置到AbstractBeanDefinition 中
  • 最后将BeanDefinition 设置到BeanDefinitionHolder中,它是BeanMetadataElement接口 的一个实现,也就是Bean的元数据类
BeanDefinition 注册到IOC容器源码解析
  • 在获取到BeanDefinitionHolder后,回到DefaultBeanDefinitionDocumentReader 类中processBeanDefinition 方法查看加载过程
//进入
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
//跟如方法中,来到DefaultListableBeanFactory中registerBeanDefinition 方法
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
			throws BeanDefinitionStoreException {
			//合法性判断
		if (beanDefinition instanceof AbstractBeanDefinition) {
		.....
				((AbstractBeanDefinition) beanDefinition).validate();
.....
		}
		//查看是否加载过这个同名的类
		BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
		if (existingDefinition != null) {
			if (!isAllowBeanDefinitionOverriding()) {
				throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
			}
			//各种合法性判断
			....
			//将beanDefinition 加入到ConcurrentHashMap 中,key是ml文件配置的Bean的名字
			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;
					removeManualSingletonName(beanName);
				}
			}
			else {
				// Still in startup registration phase
				this.beanDefinitionMap.put(beanName, beanDefinition);
				this.beanDefinitionNames.add(beanName);
				removeManualSingletonName(beanName);
			}
			this.frozenBeanDefinitionNames = null;
		}

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

  • 总结:此处完成了BeanDefinition在IOC容器中的注册
  • 在得到BeanDefinitionHolder之后,的注册过程只需要将对应的BeanDefinition添加到DefaultListableBeanFactory 中定义的CurrentHashMap中
  • 设置到Map中注意,key值就是我们在XML文件中配置的bean名称
  • 如果发现之前CurrentHashMap中已经存在同名的Bean,则依据allowBeanDefinitionOverriding配置来决定是否覆盖
IOC 容器的依赖注入过程
  • 我们在一家完成资源定位,解析,注册后,在IOC容器中简历了BeanDefinition数据映射的时候,我们就可以开始IOC容器的依赖注入过程
  • 依赖注入的过程是用户第一次向IOC容器获取Bean时触发的,我们在IOC容器接口BeanFactory中有对getBean接口定义,此处就是触发依赖注入的地方。追代码
//AbstractApplicationContext 中 getBean方法
return getBeanFactory().getBean(name);
//追到AbstractBeanFactory中doGetBean 方法
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
			@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
//先从缓存中取Bean:判断是否单例bean,如果是则直接从concurrentHashMap中获取返回,对这种Bean请求不需要重复创建
Object sharedInstance = getSingleton(beanName);
		if (sharedInstance != null && args == null) {
		......
		}
//对IOC容器中的BeanDefinition是否存在进行检查,检查是否能在当前的BeanFactory中取得对应的Bean。如果当前的工厂中无法获取,那么通过递归的方式到双亲BeanFactory中取。
	BeanFactory parentBeanFactory = getParentBeanFactory();
	if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
		.....
	}
	//通过Bean的名称获取BeanDefinition
	final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
	//获取当前Bean的所有依赖Bean,这样会触发GetBean的递归调用,直到找到一个没有其他Bean引用关系的Bean为止
	String[] dependsOn = mbd.getDependsOn();
	for (String dep : dependsOn) {
	......
		getBean(dep);
	}
	// 最后核心方法,通过调研CreateBean方法,创建Singleton Bean实例,调用ObjectFactory的createBean方法,接下来进入createBean
	return createBean(beanName, mbd, args);
}
  • 以上就是依赖注入的入口
    • getBean是起点,先从缓存获取Bean
    • 接着判断Bean是否已尽创建过,在当前BeanFactory或者递归双亲BeanFactory中是否有当前要生成的Bean
    • 然后获取当前Bean的所有依赖Bean,并且递归的触发getBean,直到有一个没有任何其他Bean引用的Bean为止
    • 最后就是CreateBean方法。
//进入AbstractAutowireCapableBeanFactory 中的createBean方法
//BeanWrapper持有创建出来的Bean对象
BeanWrapper instanceWrapper = null;
//首先如果是单例模式的Bean,先将缓存中的同名Bean清除
if (mbd.isSingleton()) {
			instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
		}
		//此处是创建Bean的核心方法,由createBeanInstance 完成
		if (instanceWrapper == null) {
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}
//创建完Bean后,会通过PopulateBean方法对Bean进行初始化,依赖注入就是在这个步骤完成,最后exposedObject会作为依赖注入完成后的Bean返回
		Object exposedObject = bean;
		try {
			populateBean(beanName, mbd, instanceWrapper);
			exposedObject = initializeBean(beanName, exposedObject, mbd);
		}
  • 接着分析一下createBeanInstance 以及populateBean 两个核心方法
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
//先确认需要创建的Bean实例的类可以实例化,做一次校验
Class<?> beanClass = resolveBeanClass(mbd, beanName);
//使用工厂方法对Bean进行实例化
if (mbd.getFactoryMethodName() != null) {
			return instantiateUsingFactoryMethod(beanName, mbd, args);
		}
		//使用构造函数进行实例化
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
		if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
				mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
			return autowireConstructor(beanName, mbd, ctors, args);
		}
//最后使用默认的构造函数对Bean实例化
		return instantiateBean(beanName, mbd);
}
//接着进入instantiateBean,使用的是默认的实例化策略对Bean进行实例化,
//默认情况是用CglibSubclassingInstantiationStrategy,也就是使用CGLIB对Bean进行实例化
beanInstance = AccessController.doPrivileged((PrivilegedAction<Object>) () ->
						getInstantiationStrategy().instantiate(mbd, beanName, parent),
						getAccessControlContext());
//进入getInstantiationStrategy().instantiate 方法中,有两种实例化java对象的方式,如下
//第一种用BeanUtil的方法进行实例化,这个BeanUtils实例化通过Constructor来实例化Bean,最终就是用的JVM的放射功能
return BeanUtils.instantiateClass(constructorToUse);
//第二种情况用的CGLIB来生成
return instantiateWithMethodInjection(bd, beanName, owner);
  • 由上源码,总结一下Bean的实例化过程,首先要知道的是Bean的实例化就是生成Bean中所包含的所有的Java对象,进行新建并且做init,此处的init并不是通过构造方法,而是赋予初始值,例如0:

    • 在createBeanInstance方法中 我们通过BeanDefinition中的属性来决定使用某一种实例化方法
    • 其中包括使用工厂方法,构造函数,默认构造函数
    • 如果没有特殊的设置最终使用默认构造的情况来生成Bean的实例,这里使用的方法有两种
      • 默认一种是BeanUtil利用JVM的反射机制生成Java对象
      • 默认第二种是使用CGLIB字节码增强技术对Bean进行实例化
  • 最后populateBean 完成具体属性的解析以及Bean属性注入的过程。

//populateBean 参数,Bean名称,BeanDefinition基础数据,实例化之后的Bean持有对象BeanWrapper
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
}
  • populateBean通过递归对BeanDefinition对象中的属性进行解析,然后将解析后的值设置到Bean对象中
最后IOC 基本原理总结
  • BeanDefition 的Resource的定位:

    • 利用AbstractApplicationContext 中对DefaultResourceLoader 类继承的getResource方法 对传入的Path进行解析,然后生成一个FileSystemResource对象并返回。如果是其他类型的ApplicationContext,就生成对应类型的Resource
  • BeanDefinition的Resource 载入:

    • 依据上一步骤得到的FileSystemResource,在BeanDefinitionReader 接口中定义了资源文件的读取固定模板方法
    • 本案例中的实现方法在XmlBeanDefinitionReader,获取source文件的文件流信息,调用XML的解析器得到一个Document对象
    • 解析Document文档内容,分析每一个Document中的element,整个解析过程,就是对Spring配置文件文件中配置的每一个< bean >标签对的一个遍历,将XML配置的标签信息读取出来设置到AbstractBeanDefinition 中
    • 最后将BeanDefinition 设置到BeanDefinitionHolder中,它是BeanMetadataElement接口 的一个实现,也就是Bean的元数据类
  • BeanDefinition 注册到IOC容器

    • 在得到BeanDefinitionHolder之后,的注册过程只需要将对应的BeanDefinition添加到DefaultListableBeanFactory 中定义的CurrentHashMap中
    • 其中key值就是我们在XML文件中配置的bean名称
    • 如果发现之前CurrentHashMap中已经存在同名的Bean,则依据allowBeanDefinitionOverriding配置来决定是否覆盖
  • IOC 容器的依赖注入

    • getBean是IOC依赖注入的起点,会先从缓存获取Bean,接着判断Bean是否已创建过,通过递归调用的方式在当前和双亲BeanFactoryIOC容器中查找是否有当前的Bean
    • 再次通过递归调用的方式获取当前Bean的所有依赖Bean,并且递归的同时触发getBean,直到得到一个没有任何其他Bean引用的Bean为止
    • 最后就是CreateBean方法。最终生成一个BeanWrapper的Bean实例引用对象
  • Bean属性注入:

    • populateBean通过递归对BeanDefinition对象中的属性进行解析,然后将解析后的值设置到Bean对象中
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值