有三个类 A、B、C其中A 依赖B,B依赖C,C依赖A,这就形成了一个循环依赖。
循环依赖又分两种:
构造器循环依赖:
按照一般编程方式,此种依赖无解,但是Spring 提供了懒加载机制(创建代理对象 - @Lazy
)来保证构造器循环依赖场景下,程序依然能够工作。
属性循环依赖:
这种情况Spring采用了三个Map(earlySingletonObjects、singletonFactories、singletonObjects),来保证程序工作
@Lazy
修饰假设我们有以下代码:
BeanA
@Component
public class BeanA {
private BeanB b;
//BeanA 在执行构造函数时依赖BeanB
public BeanA(BeanB b) {
this.b = b;
}
}
BeanB
@Component
public class BeanB {
private BeanA a;
//BeanB构造时依赖BeanA ,但是构造参数使用@Lazy注解修饰
public BeanB(@Lazy BeanA a) {
this.a = a;
}
}
经过执行我们发现Spring 并未抛出异常,接下来我们分析其原理。
当我们构造 BeanA
实例时,会进入下面的代码
//AbstractAutowireCapableBeanFactory#createBeanInstance
//获取 BeanA,的构造函数
Constructor<?>[] ctors =
determineConstructorsFromBeanPostProcessors(beanClass, beanName);
if (ctors != null
|| mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR
|| mbd.hasConstructorArgumentValues()
|| !ObjectUtils.isEmpty(args)) {
return autowireConstructor(beanName, mbd, ctors, args);
}
接着使用 ConstructorResolver
类的autowireConstructor()
方法,根据构造器参数,从IoC容器中找到对应的实例。该类并不具备主动解析的能力,还是依赖AutowireCapableBeanFactory.resolveDependency
进行解析。
protected BeanWrapper autowireConstructor(String beanName,
RootBeanDefinition mbd,
@Nullable Constructor<?>[] ctors,
@Nullable Object[] explicitArgs) {
return new ConstructorResolver(this)
.autowireConstructor(beanName, mbd, ctors, explicitArgs);
}
需要注意的是,AutowireCapableBeanFactory.resolveDependency
的第一个入参是 new DependencyDescriptor(param, true)
,其中的true
代表的含义是:该依赖项是强依赖,即required
,如果找不到,会抛出NoSuchBeanDefinitionException
。这也是Spring4
以后官方推荐使用构造器注入的原因之一: 表明强依赖强系
// org.springframework.beans.factory.support.
// ConstructorResolver#resolveAutowiredArgument
protected Object resolveAutowiredArgument(
MethodParameter param,
String beanName,
@Nullable Set<String> autowiredBeanNames,
TypeConverter typeConverter,
boolean fallback) {
// beanName = BeanA
// paramType = BeanB.class
Class<?> paramType = param.getParameterType();
if (InjectionPoint.class.isAssignableFrom(paramType)) {
InjectionPoint injectionPoint = currentInjectionPoint.get();
if (injectionPoint == null) {
throw new IllegalStateException(
"No current InjectionPoint available for " + param);
}
return injectionPoint;
}
// 走到熟悉的beanFactory.resolveDependency方法
return this.beanFactory.resolveDependency(
new DependencyDescriptor(param, true),
beanName, autowiredBeanNames, typeConverter);
// ... (省略)
}
接着,便来到了熟悉的AutowireCapableBeanFactory#resolveDependency
,此处会处理@Lazy
注解,通过方法名getLazyResolutionProxyIfNecessary
我们可以大胆猜测:使用代理的方式处理@Lazy
// org.springframework.beans.factory.support.
// DefaultListableBeanFactory#resolveDependency
public Object resolveDependency(DependencyDescriptor descriptor,
@Nullable String requestingBeanName,
@Nullable Set<String>
autowiredBeanNames,
@Nullable TypeConverter typeConverter)
throws BeansException {
// ...(省略)
// 处理@Lazy的Case
Object result = getAutowireCandidateResolver().
getLazyResolutionProxyIfNecessary(
descriptor, requestingBeanName);
if (result == null) {
result = doResolveDependency(descriptor,
requestingBeanName,
autowiredBeanNames,
typeConverter);
}
return result;
}
```
接着,先判断依赖项是否含有@Lazy
注解,如果含有,通过buildLazyResolutionProxy
方法生成代理对象返回
// org.springframework.context.annotation.
// ContextAnnotationAutowireCandidateResolver
// #getLazyResolutionProxyIfNecessary
public Object getLazyResolutionProxyIfNecessary(
DependencyDescriptor descriptor,
@Nullable String beanName) {
return (isLazy(descriptor) ?
buildLazyResolutionProxy(descriptor, beanName) : null);
}
// 判断依赖项里是否含有@Lazy注解
protected boolean isLazy(DependencyDescriptor descriptor) {
for (Annotation ann : descriptor.getAnnotations()) {
Lazy lazy = AnnotationUtils.getAnnotation(ann, Lazy.class);
if (lazy != null && lazy.value()) {
return true;
}
}
MethodParameter methodParam = descriptor.getMethodParameter();
if (methodParam != null) {
Method method = methodParam.getMethod();
if (method == null || void.class == method.getReturnType()) {
Lazy lazy = AnnotationUtils.getAnnotation(methodParam.
getAnnotatedElement(), Lazy.class);
if (lazy != null && lazy.value()) {
return true;
}
}
}
return false;
}
// 对依赖项生成代理对象
protected Object buildLazyResolutionProxy(final DependencyDescriptor
descriptor,
final @Nullable String beanName) {
Assert.state(getBeanFactory() instanceof DefaultListableBeanFactory,
"BeanFactory needs to be a DefaultListableBeanFactory");
final DefaultListableBeanFactory beanFactory =
(DefaultListableBeanFactory) getBeanFactory();
// 稍微记一下TargetSource[目标源],下文还要用到
TargetSource ts = new TargetSource() {
// ...(省略)
};
ProxyFactory pf = new ProxyFactory();
pf.setTargetSource(ts);
Class<?> dependencyType = descriptor.getDependencyType();
if (dependencyType.isInterface()) {
pf.addInterface(dependencyType);
}
// 通过ProxyFactory生成代理对象
return pf.getProxy(beanFactory.getBeanClassLoader());
}
我们得到一个结论:通过在构造器参数中标识@Lazy
注解,Spring 生成并返回了一个代理对象,因此给BeanA
注入的BeanB
并非真实对象而是其代理
虽然构造器注入的循环依赖解决了,程序也能正常启动,但是程序执行的时候是不是我们想要的效果呢?也即是说,BeanA
中的代理对象 BeanB
如何与真实的 BeanB
对象关联起来的呢?
我们已经在 BeanB
中添加了一个方法 - log()
执行BeanA.BeanB.log
方法,由于 BeanB
是个代理对象,必然先进入代理逻辑。由于 BeanB
并非接口,不能通过JDK代理,因此是通过Cglib代理,如下示:
// org.springframework.aop.framework.CglibAopProxy.
// DynamicAdvisedInterceptor#intercept
private static class DynamicAdvisedInterceptor implements
MethodInterceptor, Serializable {
private final AdvisedSupport advised;
public DynamicAdvisedInterceptor(AdvisedSupport advised) {
this.advised = advised;
}
@Override
@Nullable
public Object intercept(Object proxy, Method method,
Object[] args, MethodProxy methodProxy)
throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
Object target = null;
// 上文提到的TargetSource
TargetSource targetSource = this.advised.getTargetSource();
// Get as late as possible to minimize the time we "own" the
// target, in case it comes from a pool...[可以仔细品品该注释]
// 通过TargetSource来获取被代理的对象target
target = targetSource.getTarget();
// ...(省略)
if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
Object[] argsToUse = AopProxyUtils
.adaptArgumentsIfNecessary(method, args);
// 通过反射调用被代理对象的方法
retVal = methodProxy.invoke(target, argsToUse);
}
// ...(省略)
}
}
代码中,通过 TargetSource#getTarget
来获取被代理的对象 target
,然后通过反射完成被代理对象的方法调用
我们回过头来看此处的 TargetSource
是什么,在上面的 buildLazyResolutionProxy
方法中,构造了 TargetSource
,把省略的代码展开:
TargetSource ts = new TargetSource() {
@Override
public Class<?> getTargetClass() {
// Bar.class
return descriptor.getDependencyType();
}
@Override
public boolean isStatic() {
return false;
}
@Override
public Object getTarget() {
// 通过beanFactory去真正解析依赖(Bar),将Spring IoC里真实的Bar返回
Object target = beanFactory.doResolveDependency(
descriptor, beanName, null, null);
if (target == null) {
Class<?> type = getTargetClass();
if (Map.class == type) {
return Collections.emptyMap();
}
else if (List.class == type) {
return Collections.emptyList();
}
else if (Set.class == type || Collection.class == type) {
return Collections.emptySet();
}
throw new NoSuchBeanDefinitionException(
descriptor.getResolvableType(),
"Optional dependency not present for lazy injection point");
}
return target;
}
@Override
public void releaseTarget(Object target) {
}
};
最关键的一行代码是Object target = beanFactory.doResolveDependency(descriptor, beanName, null, null);
,它的作用是通过 beanFactory
去真正解析依赖 (BeanB)
,将 Spring IoC
里真实的 BeanB
返回,如此,就拿到了真正的BeanB
对象
此处我们可以得到一个结论:代理对象 BeanB
与真实的 BeanB
对象,是通过 TargetSouce
关联起来的,每次执行被代理对象的方法时,都会先通过 TargetSouce
去拿到真实的对象DefaultListableBeanFactory.doResolveDependency
,然后通过反射进行调用。
想要查看 BeanA
的代理类 可以使用下面代码来查看 CGLib
的生成类
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "文件夹路径");
Spring
构造器注入循环依赖的解决方案是 @Lazy
,其基本思路是:对于强依赖的对象,一开始并不注入对象本身,而是注入其代理对象,以便顺利完成实例的构造,形成一个完成的对象,这样与其它应用层对象就不会形成互相依赖的关系;当需要调用真实对象的方法时,通过 TargetSouce
,在容器中拿到真实的对象DefaultListableBeanFactory.doResolveDependency()
,然后通过反射完成调用.
需要注意的是,在 BeanA
的构造阶段,并不能去调用 BeanB
,这是因为当前注入的是 BeanB
的代理对象,真实对象还没有生成,所以这时调用就会抛出异常,终止Spring 的加载。
流程图地址
https://gitmind.cn/app/flowchart/f2c901532
假设我们有以下类
BeanA
@Component
public class BeanA {
@Autowired
private BeanB b;
public BeanA() {
System.out.println("init A ");
System.out.println(this);
}
}
BeanB
@Component
public class BeanB {
@Autowired
private BeanA a;
public BeanB() {
System.out.println("init B");
System.out.println(this);
}
}
在 Bean A、B
它们互相持有对方的引用,这就构成了循环依赖。
Spring 通过提前暴露创建中的单例对象,来解决这个问题。
为了实现提前暴露对象,Spring 使用了三个 Map
缓存创建以及创建中的对象,DefaultSingletonBeanRegistry
:我们需要记住这三个Map
//单例Bean - 也就是完全创建好,并且初始化好的Bean
/** Cache of singleton objects: bean name --> bean instance */
private final Map<String, Object> singletonObjects =
new ConcurrentHashMap<String, Object>(256);
//单例工厂(也可能存放刚刚创建的单例对象,或者代理类)
/** Cache of singleton factories: bean name --> ObjectFactory */
private final Map<String, ObjectFactory<?>> singletonFactories =
new HashMap<String, ObjectFactory<?>>(16);
//提前暴露的缓存Map 承接从工厂缓存的对象 ,
/** Cache of early singleton objects: bean name --> bean instance */
private final Map<String, Object> earlySingletonObjects =
new HashMap<String, Object>(16);
//单例对象,注册set - 当单例对象放到 singletonObjects 中时 记录该对象
/** Set of registered singletons, containing the bean names in registration order. */
private final Set<String> registeredSingletons = new LinkedHashSet<>(256);
接下来我们正式进入属性值循环依赖的解决:
从 getBean(A)
开始,我们首先从缓存中加载 A
//AbstractBeanFactory.doGetBean(...)
// Object sharedInstance = getSingleton(beanName);
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//从 singletonObject (完整的单例对象集合)中取出单例对象
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
//从提前曝光的 singletonObject 集合取出对象
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
//从对象工厂 singletonFactories 取出对象
ObjectFactory<?> singletonFactory =
this.singletonFactories.get(beanName);
if (singletonFactory != null) {
//将对象工厂的对象转移到提前曝光的集合
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
由于上一步的并未有 Bean
返回,所以进入 Bean
的创建
//AbstractBeanFactory.doGetBean(...)
// Create bean instance.
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
我们来看一下 getSingleton
方法
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
synchronized (this.singletonObjects) {
//继续从缓存中获取,这是为了在多线程情况下保证实例只被创建一次
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
。。。。
boolean newSingleton = false;
。。。。
try {
//传入的回调对象,使用它创建真实的单例
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
。。。。
if (newSingleton) {
//在这里我们将创建好的对象,放入到 单例缓存中
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
查看 getSingleton
方法中的匿名函数 createBean(beanName, mbd, args);
@Override
protected Object createBean(String beanName,
RootBeanDefinition mbd,
@Nullable Object[] args)
throws BeanCreationException {
......
RootBeanDefinition mbdToUse = mbd;
。。。。。
try {
//进入创建对象的步骤
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
if (logger.isTraceEnabled()) {
logger.trace("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
}
....
}
doCreateBean
protected Object doCreateBean(String beanName,
RootBeanDefinition mbd,
@Nullable Object[] args)
throws BeanCreationException {
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
//从单例缓存中移除 实例
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
//创建实例(如果对象使用@Lazy修饰,则返回代理)
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}
..........
//将创建包的Bean放入Bean工厂缓存
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
boolean earlySingletonExposure = (mbd.isSingleton()
&& this.allowCircularReferences
&& isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
//重点
addSingletonFactory(beanName, ()
-> getEarlyBeanReference(beanName, mbd, bean));
}
// Initialize the bean instance.
Object exposedObject = bean;
try {
//填充属性
//执行Aware
//执行BeanPostProcessor
populateBean(beanName, mbd, instanceWrapper);
//调用自定义的Init方法
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
。。。。。。。。
//返回
return exposedObject;
}
populateBean
protected void populateBean(String beanName,
RootBeanDefinition mbd,
@Nullable BeanWrapper bw) {
........
PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
int resolvedAutowireMode = mbd.getResolvedAutowireMode();
if (resolvedAutowireMode == AUTOWIRE_BY_NAME
|| resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
//通过getBean 获取 依赖
// Add property values based on autowire by name if applicable.
if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
autowireByName(beanName, mbd, bw, newPvs);
}
// Add property values based on autowire by type if applicable.
if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
autowireByType(beanName, mbd, bw, newPvs);
}
pvs = newPvs;
}
.........
if (pvs != null) {
//依赖注入
applyPropertyValues(beanName, mbd, bw, pvs);
}
}
随后回到 doCreateBean
方法 ,执行init 方法
exposedObject = initializeBean(beanName, exposedObject, mbd);
接下来回到 getSingleton
方法
addSingleton(beanName, singletonObject);
将创建好的对象,放入单例缓存,并清除提现曝光缓存和工厂缓存
我们上面说了 构造循环依赖 和 原型属性循环依赖 的解决办法,那么 Spring
是如何检测到循环依赖呢?
Spring
采用了,采用了 ThreadLocal
来保存正在创建的对象, 当我们创建对象时就把对象的标识放入到 ThreadLocal
中。 - final ThreadLocal<Object> prototypesCurrentlyInCreation
//AbstractBeanFactory.doGetBean()
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
我们看一下 beforePrototypeCreation
方法
protected void beforePrototypeCreation(String beanName) {
Object curVal = this.prototypesCurrentlyInCreation.get();
if (curVal == null) {
this.prototypesCurrentlyInCreation.set(beanName);
}
else if (curVal instanceof String) {
Set<String> beanNameSet = new HashSet<>(2);
beanNameSet.add((String) curVal);
beanNameSet.add(beanName);
this.prototypesCurrentlyInCreation.set(beanNameSet);
}
else {
Set<String> beanNameSet = (Set<String>) curVal;
beanNameSet.add(beanName);
}
}
createBean(beanName, mbd, args);
没有什么好解释的,就是创建 Bean
,如果使用 @Lazy
修饰的话,则创建代理对象。后续如果 有 AOP 代理的话再次创建代理对象
接下来 我们来件一下 afterPrototypeCreation(beanName);
protected void afterPrototypeCreation(String beanName) {
Object curVal = this.prototypesCurrentlyInCreation.get();
if (curVal instanceof String) {
this.prototypesCurrentlyInCreation.remove();
}
else if (curVal instanceof Set) {
Set<String> beanNameSet = (Set<String>) curVal;
beanNameSet.remove(beanName);
if (beanNameSet.isEmpty()) {
this.prototypesCurrentlyInCreation.remove();
}
}
}
上面的代码也很简单,就是从 ThreadLocal
中移除创建好的对象 (new 、填充属性、aware、beanPostprocessor、init 方法)
检测循环依赖 - 其实检测在前面,但是先说存放易于理解
//AbstractBeanFactory.doGetBean()
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
来看一下 isPrototypeCurrentlyInCreation
方法
protected boolean isPrototypeCurrentlyInCreation(String beanName) {
Object curVal = this.prototypesCurrentlyInCreation.get();
return (curVal != null
&&(curVal.equals(beanName)
|| (curVal instanceof Set && ((Set<?>) curVal).contains(beanName))));
}
我们可以看到当 ThreadLocal
中存在当前的 BeanName
时 返回 true
,随后if
条件成立抛出异常。
我们来梳理下这两步是如何执行的。假设 A、B 类为原型模式,并且相互依赖并被其他位置引用。
从 A
开始创建: getBean(A)
首先检查 A
的循环依赖,发现 ThreadLocal
中并不存在该类,则继续向下执行
将 A
放入到 ThreadLocal
中
开始 A
的创建, 此时发现 A
依赖 B
,那么则进入 getBean(B)
检查 B
的循环依赖,发现 ThreadLocal
中并不存在该类,则继续向下执行
将 B
放入到 ThreadLocal
中
开始 B
的创建, 此时发现 B
依赖 A
,那么则进入 getBean(A)
首先检查 A
的循环依赖,发现 ThreadLocal
中存在该类,抛出异常
所以我们可以看到 Spring
采用了递归方式,来反复执行 ThreadLocal
值的添加和检测,来达到检测循环依赖的目的,非常高明。
这个检测方法其实与原型循环依赖几乎一样, Spring
使用了一个 Set
- 来保存 Bean
所依赖的 Bean
。
当 A
创建时,
首先将A
标记为创建中
在构造时发现依赖 B
,则将 B
放到 Set
中
随后递归创建 B
首先将B
标记为创建中
在构造时发现依赖 A
,则将 A
放到 Set
中
随后递归创建 A
,发现 A
处于构造中,并且在 Set
中存在,随即抛出异常
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。