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对象中