IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> spring学习——IOC原理 -> 正文阅读

[Java知识库]spring学习——IOC原理

一、DI和IOC的概念

DI和IOC从本质上是从不同角度描述了同一件事情:

  • IOC:将对象的控制权交付给框架,由框架管理对象的生命周期,用户使用对象的时候直接从容器按照名称拿即可;(从容器的角度)
  • DI:程序通过容器使用对象,在使用容器前需要注入相应的对象名称。(从程序的角度)

从我们开发的使用过程中,通常就是去容器中拿对应的对象,那么这个过程是怎么样的呢?本文将逐步分析IOC的整个流程,其中着重分析了:

  1. 多级缓存;
  2. 依赖注入的源码;
  3. 循环依赖解决。

二、spring容器缓存

在Spring默认的单例实例注册实现类(DefaultSingletonBeanRegistry)中,有多个Map存储不同时期的对象实例:

2.1 singletonObjects

/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

在spring容器初始化后,所有的对象都会放入该缓存中。其结构是Map,即beanName为key,Instance为value。

2.2 singletonFactories

/** Cache of singleton factories: bean name to ObjectFactory. */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

存储了多个beanFactory,每个bean有自己对应的beanFactory,这是所谓的二级缓存,该缓存的作用是,当程序在任何地方都找不到该bean对应的实例,此时会调用beanFactory来获取bean实例,并放入earlySingletonObjects因此,有两个很重要的结论

  1. earlySingletonObjects 中的bean都是由singletonFactories中的beanName根据beanFactor来创建放入
  2. 二者的关系是此消彼长,earlySingletonObjects 拿出一个beanFactory,创建对象放入earlySingletonObjects 时就会从自身中把这个bean删除掉。

2.3 earlySingletonObjects

/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);

用于解决循环依赖问题,该缓存中存储的是半成品实例
即这些实例在创建时由于其属性中有其他bean的引用,而引用的对象尚未创建(有依赖),此时需要暂停创建当前对象而且创建引用对象(依赖的对象)。此时当前的对象会放到earlySingletonObjects 中(通过singletonFactories ),提前暴露出来(解决循环依赖)。

2.4 registeredSingletons

将bean注册为单例且完整。(这部分我不是很理解)

/** Set of registered singletons, containing the bean names in registration order. */
private final Set<String> registeredSingletons = new LinkedHashSet<>(256);

	public void registerSingleton(String beanName, Object singletonObject) throws IllegalStateException {
		Assert.notNull(beanName, "Bean name must not be null");
		Assert.notNull(singletonObject, "Singleton object must not be null");
		synchronized (this.singletonObjects) {
			Object oldObject = this.singletonObjects.get(beanName);
			// 如果该对象已经被加入了singletonObjects,那么说明已经创建完成,无需重复注册
			if (oldObject != null) {
				throw new IllegalStateException("Could not register object [" + singletonObject +
						"] under bean name '" + beanName + "': there is already object [" + oldObject + "] bound");
			}
			addSingleton(beanName, singletonObject);
		}
	}
	// 将beanName和实例对象放入singletonObjects和registeredSingletons
	protected void addSingleton(String beanName, Object singletonObject) {
		synchronized (this.singletonObjects) {
			this.singletonObjects.put(beanName, singletonObject);
			this.singletonFactories.remove(beanName);
			this.earlySingletonObjects.remove(beanName);
			this.registeredSingletons.add(beanName);
		}
	}

2.5 singletonsCurrentlyInCreation

/** Names of beans that are currently in creation. */
private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16));

在spring容器创建一个bean时,会首先将beanName 放入该缓存,表示该bean正在被创建。
同理,当创建完成后,也会将该beanName移除该容器。

protected void beforeSingletonCreation(String beanName) {
		if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
			throw new BeanCurrentlyInCreationException(beanName);
		}
	}
	
protected void afterSingletonCreation(String beanName) {
		if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
			throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
		}
	}

三、 spring容器启动过程(默认是单例实例)

  1. 声明该对象正在创建
  2. 加载配置文件,创建beanFactory;
  3. 获取配置文件中的对于Bean的definition,以便于构造对象和注入属性;
  4. fresh beanFactory,同时初始化bean实例(单例);
  5. 注册所有的单例bean并返回可用的容器引用。

3.1 bean的创建

大致流程如下:
将当前的beanName标记为正在创建;

  1. 使用initializeBean方法初始化实例(此时只有一个对象,没有属性注入);
  2. 如果支持循环依赖则生成三级缓存,并且尝试提前暴露(如果此时该beanName实例化完成,那么则不会加入缓存)
  3. 填充bean属性,初始化bean,这里可能会出现循环依赖从而引发别的bean的实例化,但是此时当前的bean已经放入beanFactories中了;
  4. 处理循环依赖,这里需要注意对于动态代理中的循环依赖的处理。在之后会单独讲解。
  5. 此时bean已经可以被使用,并尝试注册bean;
  6. 将bean放入容器中(一级缓存),标记已创建完成并删除二三级缓存的内容。

AbstractAutowireCapableBeanFactory中的createBean方法(做了一些安全性的保证)调用了doCreateBean方法(实际创建的方法):

	protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
			throws BeanCreationException {

		// 创建BeanWrapper和保存bean的class对象到mbd中
		BeanWrapper instanceWrapper = null;
		if (mbd.isSingleton()) {
			// 先从缓存中看看能不能读出BeanWrapper
			instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
		}
		if (instanceWrapper == null) {
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}
		......
		Object bean = instanceWrapper.getWrappedInstance();
		Class<?> beanType = instanceWrapper.getWrappedClass();
		if (beanType != NullBean.class) {
			mbd.resolvedTargetType = beanType;
		}
		.......
		// 决定是否开启二三级缓存解决循环依赖
		boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
				isSingletonCurrentlyInCreation(beanName));
		// 如果允许循环依赖(即会处理循环依赖),会将该beanName提前暴露
		if (earlySingletonExposure) {
			.......
			/* addSingletonFactory的主要逻辑
			synchronized (this.singletonObjects) {
			if (!this.singletonObjects.containsKey(beanName)) {
				this.singletonFactories.put(beanName, singletonFactory);
				this.earlySingletonObjects.remove(beanName);
				this.registeredSingletons.add(beanName);
			}
			*/
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}

		Object exposedObject = bean;
		try {
			// 注入Bean的属性,其实啥都没做,就是返回一个空的实例
			populateBean(beanName, mbd, instanceWrapper);
			/* 初始化bean, 主要调用了invokeAwareMethods,该方法的作用是:
			1. 设置beanName,
			2. 设置类加载器,
			3. 设置beanFactory 
			*/
			exposedObject = initializeBean(beanName, exposedObject, mbd);
		}
		.......
		// 允许提前暴露
		if (earlySingletonExposure) {
			// 如果第二个参数为false
			//getSingleton只会去singletonObjects和earlySingletonObjects中找,不会调用beanFactory创建
			Object earlySingletonReference = getSingleton(beanName, false);
			/* 
			此时如果从getSingleton方法中获取到了实例
			说明该bean是由于产生了循环依赖而导致从beanFactory中生成了bean并暴露出来
			*/
			if (earlySingletonReference != null) {
				// exposedObject == bean是用来解决动态代理类的
				if (exposedObject == bean) {
					exposedObject = earlySingletonReference;
				}
				/* 处理循环依赖
					allowRawInjectionDespiteWrapping :
					在循环引用的情况下,如果注入的bean被代理,是否要注入原始bean实例
				*/
				else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
					String[] dependentBeans = getDependentBeans(beanName);
					Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
					for (String dependentBean : dependentBeans) {
						if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
							actualDependentBeans.add(dependentBean);
						}
					}
					if (!actualDependentBeans.isEmpty()) {
					// 抛出异常
					}
				}
			}
		}

		......
		return exposedObject;
	}

3.2 getSingleton的代码逻辑:

	protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		// 直接尝试从容器中获取bean
		Object singletonObject = this.singletonObjects.get(beanName);
		// 如果该单例对象没有被创建或者正在被创建
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			// 从early reference cache中获取:即该对象是否是由于循环依赖而提前暴露
			singletonObject = this.earlySingletonObjects.get(beanName);
			// 如果还没有获取到,则需要创建该对象,注意此时必须要允许allowEarlyReference
			if (singletonObject == null && allowEarlyReference) {
				// 先上锁,即别的线程无法调用下面的代码
				synchronized (this.singletonObjects) {
					// 此时还需要再次获取一下,因为从第一次快速获取到现在可能别的线程创建了该bean
					singletonObject = this.singletonObjects.get(beanName);
					if (singletonObject == null) {
						// 和上面同理
						singletonObject = this.earlySingletonObjects.get(beanName);
					    // 如果还没有,从singletonFactories缓存中获取创建该bean的bean factory
						if (singletonObject == null) {
							ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
							// 如果获取到了,则通过singletonFactory创建该bean
							if (singletonFactory != null) {
								/*
								1. 从singletonFactory中申请一个对象实例;
								2. 放入earlySingletonObjects中;
								3. 从singletonFactory移除beanName;
								*/
								singletonObject = singletonFactory.getObject();
								this.earlySingletonObjects.put(beanName, singletonObject);
								this.singletonFactories.remove(beanName);
							}
						}
					}
				}
			}
		}
		return singletonObject;
	}

四、获取bean的过程

4.1 getBean(AbstractApplicationContext)

调用BeanFactory的getBean方法。

    public Object getBean(String name) throws BeansException {
    	// 这里会检测BeanFactory是否正在刷新或者已经关闭,如果没有抛出异常说明BeanFactory正常,可以获取bean
        this.assertBeanFactoryActive();
        return this.getBeanFactory().getBean(name);
    }

4.2 调用doGetBean

String beanName = transformedBeanName(name);
// getSingleton方法
Object sharedInstance = getSingleton(beanName);

4.3 调用getSingleton(DefaultSingletonBeanRegistry)

该方法会返回注册的单例对象,并且检查安全性:

  1. 是否是已经创建好的对象;
  2. 对当前创建的单例的早期引用(解决循环引用)。

五、循环依赖解决

5.1 常规场景

说明一个场景:A与B之间存在循环依赖:在这里插入图片描述

5.2 针对AOP过程中出现的循环依赖

其主要的处理逻辑在于下面的这段代码:

if (earlySingletonExposure) {
			Object earlySingletonReference = getSingleton(beanName, false);
			if (earlySingletonReference != null) {
				if (exposedObject == bean) {
					exposedObject = earlySingletonReference;
				}
				else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
					String[] dependentBeans = getDependentBeans(beanName);
					Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
					for (String dependentBean : dependentBeans) {
						if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
							actualDependentBeans.add(dependentBean);
						}
					}
					if (!actualDependentBeans.isEmpty()) {
						throw new BeanCurrentlyInCreationException(beanName,
								"Bean with name '" + beanName + "' has been injected into other beans [" +
								StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
								"] in its raw version as part of a circular reference, but has eventually been " +
								"wrapped. This means that said other beans do not use the final version of the " +
								"bean. This is often the result of over-eager type matching - consider using " +
								"'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");
					}
				}
			}

为什么在获取到了earlySingletonReference (必定存在循环依赖)的时候还要exposedObject == bean?我的理解是:
在进行对象初始化时(exposedObject = initializeBean(xxxx)),如果需要进行aop的话会调用:

wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);

exposedObject 其实指向的是被代理后的对象,而bean引用指向的还是原始对象。

  1. 如果exposedObject == bean,说明此时没有发送AOP,可以直接返回;
  2. exposedObject != bean,说明发生了AOP行为,处理AOP中的循环依赖。下面会继续解释。

5.3 为什么要使用二三级缓存?

二级缓存:earlySingletonObjects
三级缓存:singletonFactories
二级缓存就是用来存储创建到一半的对象,这些对象无法直接使用,但是可以解决循环依赖这个过程,因为已经可以拿到引用了。
二级缓存中的对象由三级缓存中的BeanFactory中创建,也就是三级缓存中只是提供了创建对象的方法。这样的好处是:

  1. 降低了没有必要的对象创建的开销;
  2. 将对bean实例的依赖转化成了对beanFactory的依赖,这样做的好处是可以解决动态代理的过程中出现的循环依赖。

如果两个对象A,B都需要AOP且存在循环依赖,那么过程是先创建A,在创建A的时候触发创建B,B创建并要获取A代理后的对象,此时会去一二级缓存中发现没有,此时再去singletonFactories创建早期对象,即调用getEarlyBeanReference,此时会检查对象是否需要被代理:hasInstantiationAwareBeanPostProcessors。如果需要就提前生成代理后的对象并放入二级缓存中

	protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
		Object exposedObject = bean;
		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
			for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
				exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
			}
		}
		return exposedObject;
	}
  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-08-10 13:17:12  更:2021-08-10 13:18:12 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 18:57:33-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码