Spring Bean IOC、AOP 循环依赖

循环依赖分为三种,自身依赖于自身、互相循环依赖、多组循环依赖。
但无论循环依赖的数量有多少,循环依赖的本质是一样的。就是你的完整创建依赖于我,而我的完整创建也依赖于你,但我们互相没法解耦,最终导致依赖创建失败。
所以 Spring 提供了除了构造函数注入和原型注入外的,setter循环依赖注入解决方案。那么我们也可以先来尝试下这样的依赖,如果是我们自己处理的话该怎么解决。
2. 问题体现
public class ABTest {

public static void main(String[] args) {
    new ClazzA();
}

}

class ClazzA {

private ClazzB b = new ClazzB();

}

class ClazzB {

private ClazzA a = new ClazzA();

}
这段代码就是循环依赖最初的模样,你中有我,我中有你,运行就报错 java.lang.StackOverflowError
这样的循环依赖代码是没法解决的,当你看到 Spring 中提供了 get/set 或者注解,这样之所以能解决,首先是进行了一定的解耦。让类的创建和属性的填充分离,先创建出半成品Bean,再处理属性的填充,完成成品Bean的提供。
3. 问题处理
在这部分的代码中就一个核心目的,我们来自己解决一下循环依赖,方案如下:

public class CircleTest {

private final static Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

public static void main(String[] args) throws Exception {
    System.out.println(getBean(B.class).getA());
    System.out.println(getBean(A.class).getB());
}

private static <T> T getBean(Class<T> beanClass) throws Exception {
    String beanName = beanClass.getSimpleName().toLowerCase();
    if (singletonObjects.containsKey(beanName)) {
        return (T) singletonObjects.get(beanName);
    }
    // 实例化对象入缓存
    Object obj = beanClass.newInstance();
    singletonObjects.put(beanName, obj);
    // 属性填充补全对象
    Field[] fields = obj.getClass().getDeclaredFields();
    for (Field field : fields) {
        field.setAccessible(true);
        Class<?> fieldClass = field.getType();
        String fieldBeanName = fieldClass.getSimpleName().toLowerCase();
        field.set(obj, singletonObjects.containsKey(fieldBeanName) ? singletonObjects.get(fieldBeanName) : getBean(fieldClass));
        field.setAccessible(false);
    }
    return (T) obj;
}

}

class A {

private B b;

// ...get/set

}

class B {
private A a;

	// ...get/set

}
这段代码提供了 A、B 两个类,互相有依赖。但在两个类中的依赖关系使用的是 setter 的方式进行填充。也就是只有这样才能避免两个类在创建之初不非得强依赖于另外一个对象。

getBean,是整个解决循环依赖的核心内容,A 创建后填充属性时依赖 B,那么就去创建 B,在创建 B 开始填充时发现依赖于 A,但此时 A 这个半成品对象已经存放在缓存到singletonObjects 中了,所以 B 可以正常创建,在通过递归把 A 也创建完整了。

四、源码分析

  1. 说说细节
    通过上面的例子我们大概了解到,A和B互相依赖时,A创建完后填充属性B,继续创建B,再填充属性A时就可以从缓存中获取了,如下:

那这个解决事循环依赖的事放到 Spring 中是什么样呢?展开细节!

虽然,解决循环依赖的核心原理一样,但要放到支撑起整个 Spring 中 IOC、AOP 特性时,就会变得复杂一些,整个处理 Spring 循环依赖的过程如下;

以上就是关于 Spring 中对于一个有循环依赖的对象获取过程,也就是你想要的说说细节
乍一看是挺多流程,但是这些也基本是你在调试代码时候必须经过的代码片段,拿到这份执行流程,再调试就非常方便了。
2. 处理过程
关于本章节涉及到的案例源码分析,已更新到 github:https://github.com/fuzhengwei/interview - interview-31

以下是单元测试中对AB依赖的获取Bean操作,重点在于进入 getBean 的源码跟进;

@Test
public void test_alias() {
BeanFactory beanFactory = new ClassPathXmlApplicationContext(“spring-config.xml”);
Bean_A bean_a = beanFactory.getBean(“bean_a”, Bean_A.class);
logger.info(“获取 Bean 通过别名:{}”, bean_a.getBean_b());
}
org.springframework.beans.factory.support.AbstractBeanFactory.java

@Override
public T getBean(String name, Class requiredType) throws BeansException {
return doGetBean(name, requiredType, null, false);
}
从 getBean 进入后,获取 bean 的操作会进入到 doGetBean。
之所以这样包装一层,是因为 doGetBean 有很多不同入参的重载方法,方便外部操作。
doGetBean 方法

protected T doGetBean(
final String name, final Class requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException {

// 从缓存中获取 bean 实例
Object sharedInstance = getSingleton(beanName);

		// mbd.isSingleton() 用于判断 bean 是否是单例模式
		if (mbd.isSingleton()) {
		  // 获取 bean 实例
			sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
				@Override
				public Object getObject() throws BeansException {
					try {
					  // 创建 bean 实例,createBean 返回的 bean 实例化好的
						return createBean(beanName, mbd, args);
					}
					catch (BeansException ex) {
						destroySingleton(beanName);
						throw ex;
					}
				}
			});
			// 后续的处理操作
			bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
		}
		
// ...

// 返回 bean 实例
return (T) bean;
}
按照在源码分析的流程图中可以看到,这一部分是从 getSingleton 先判断是否有实例对象,对于第一次进入是肯定没有对象的,要继续往下走。
在判断 mbd.isSingleton() 单例以后,开始使用基于 ObjectFactory 包装的方式创建 createBean,进入后核心逻辑是开始执行 doCreateBean 操作。
doCreateBean 方法

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

  // 创建 bean 实例,并将 bean 实例包装到 BeanWrapper 对象中返回
	instanceWrapper = createBeanInstance(beanName, mbd, args);

	// 添加 bean 工厂对象到 singletonFactories 缓存中
	addSingletonFactory(beanName, new ObjectFactory<Object>() {
		@Override
		public Object getObject() throws BeansException {
		  // 获取原始对象的早期引用,在 getEarlyBeanReference 方法中,会执行 AOP 相关逻辑。若 bean 未被 AOP 拦截,getEarlyBeanReference 原样返回 bean。
			return getEarlyBeanReference(beanName, mbd, bean);
		}
	});
	
try {
  // 填充属性,解析依赖关系
	populateBean(beanName, mbd, instanceWrapper);
	if (exposedObject != null) {
		exposedObject = initializeBean(beanName, exposedObject, mbd);
	}
}

// 返回 bean 实例
return exposedObject;

}
在 doCreateBean 方法中包括的内容较多,但核心主要是创建实例、加入缓存以及最终进行属性填充,属性填充就是把一个 bean 的各个属性字段涉及到的类填充进去。
createBeanInstance,创建 bean 实例,并将 bean 实例包装到 BeanWrapper 对象中返回
addSingletonFactory,添加 bean 工厂对象到 singletonFactories 缓存中
getEarlyBeanReference,获取原始对象的早期引用,在 getEarlyBeanReference 方法中,会执行 AOP 相关逻辑。若 bean 未被 AOP 拦截,getEarlyBeanReference 原样返回 bean。
populateBean,填充属性,解析依赖关系。也就是从这开始去找寻 A 实例中属性 B,紧接着去创建 B 实例,最后在返回回来。
getSingleton 三级缓存

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 从 singletonObjects 获取实例,singletonObjects 是成品 bean
Object singletonObject = this.singletonObjects.get(beanName);
// 判断 beanName ,isSingletonCurrentlyInCreation 对应的 bean 是否正在创建中
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
// 从 earlySingletonObjects 中获取提前曝光未成品的 bean
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
// 获取相应的 bean 工厂
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
// 提前曝光 bean 实例,主要用于解决AOP循环依赖
singletonObject = singletonFactory.getObject();

				// 将 singletonObject 放入缓存中,并将 singletonFactory 从缓存中移除
				this.earlySingletonObjects.put(beanName, singletonObject);
				this.singletonFactories.remove(beanName);
			}
		}
	}
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);

}
singletonObjects.get(beanName),从 singletonObjects 获取实例,singletonObjects 是成品 bean
isSingletonCurrentlyInCreation,判断 beanName ,isSingletonCurrentlyInCreation 对应的 bean 是否正在创建中
allowEarlyReference,从 earlySingletonObjects 中获取提前曝光未成品的 bean
singletonFactory.getObject(),提前曝光 bean 实例,主要用于解决AOP循环依赖
USB Microphone https://www.soft-voice.com/
Wooden Speakers https://www.zeshuiplatform.com/
亚马逊测评 www.yisuping.cn
深圳网站建设www.sz886.com

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值