写在前面
私以为自己对于spring的循环依赖理解的已经很不错了,直到前段时间遇到了一个让我思考了良久的问题,即“循环依赖所注入的实例竟不是容器中的实例”。于是又从头仔细撸了一遍源码,才最终找到原因。因为我觉的整个过程涉及到的知识点还蛮多的,所以准备用6-7篇来针对“循环依赖所注入的实例竟不是容器中的实例”这个问题进行剖析。
问题引出
废话不多说,先来看看这个问题是什么? 有两个Service具有循环引用关系,分别是ServiceA和ServiceB,如下。 ServiceA.class
@Service
public class ServiceA {
@Autowired
// 可能有人会疑问,为何定义成public还要再写get方法,这个我们后面的文章会说,先卖个关子
public ServiceB serviceB;
@Async
public String process() {
return "success";
}
public ServiceB getServiceB() {
return serviceB;
}
}
ServiceB.class
@Service
public class ServiceB implements ApplicationContextAware {
public ServiceA serviceA;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
Map<String, ServiceA> beansOfType = applicationContext.getBeansOfType(ServiceA.class);
serviceA = beansOfType.get("serviceA");
}
public ServiceA getServiceA() {
return serviceA;
}
}
配置类
@EnableAsync
@EnableAspectJAutoProxy
@Configuration
@ComponentScan("com.xyh.study.provider.spring.AOP.第六小节.第四章A")
public class AppConfig {
}
可以看到,ServiceA持有ServiceB的引用,同时ServiceB也持有ServiceA的引用。两个Service相互持有对方的引用。ServiceA通过@Autowired注入ServiceB,ServiceB通过ApplicationContextAware来注入ServiceA。
有以下几点需要特别注意一下
1.我的表述是ServiceB持有ServiceA的引用,而不是ServiceB依赖ServiceA的。我们当然可以认为ServiceB依赖了ServiceA,但是我用“引用”的原因是想与spring中对于依赖的定义进行区分。以上例子,spring认为ServiceA依赖ServiceB,而并不认为ServiceB依赖ServiceA。
2.注意下,我们的ServiceA中有一个异步方法。
以上就是我们整个系列要分析的。例子虽然简单,但是对应的知识点可以不少的。 到此我们先思考下,ServiceA实例所持有的ServiceB实例与容器中的ServiceB是同一个bean吗?以及ServiceB实例所持有的ServiceA实例与容器中的ServiceA是同一个bean吗? 也就是以下代码输出是什么?
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
System.out.println(((ServiceA) (applicationContext.getBean("serviceA"))).getServiceB() == applicationContext.getBean("serviceB"));
System.out.println(((ServiceB) (applicationContext.getBean("serviceB"))).getServiceA() == applicationContext.getBean("serviceA"));
}
}
暂停思考一下
暂停思考一下
暂停思考一下
------------------------------------分隔线------------------------------------
如果你能很清晰的分辨出,输出结果是true、false的话,那么你就不需要后续了,如果你有些疑问的话,不妨我们一起探究这其中的奥秘。
Spring中的依赖关系
作为以上问题解析的第一篇,本篇我准备先说一下spring中的“依赖”。
@Autowired是spring中依赖关系的载体。当A通过@Autowired注入了B,spring就认为A依赖了B,B被A依赖了。spring中的DefaultSingletonBeanRegistry会记录这种关系,是通过两个类型为Map<String, Set<String>>的变量进行依赖关系存储的。上源码(隐去了无关的部分):
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
/** Map between dependent bean names: bean name to Set of dependent bean names. */
// 该map记录了被依赖关系,即key对应被依赖的bean name,value对应依赖了key的bean name集合,可以理解为“谁依赖我”
private final Map<String, Set<String>> dependentBeanMap = new ConcurrentHashMap<>(64);
/** Map between depending bean names: bean name to Set of bean names for the bean's dependencies. */
// 改map记录了依赖关系,与dependentBeanMap正好相反,可以理解为“我依赖谁”
private final Map<String, Set<String>> dependenciesForBeanMap = new ConcurrentHashMap<>(64);
/**
* Register a dependent bean for the given bean,
* to be destroyed before the given bean is destroyed.
* @param beanName the name of the bean
* @param dependentBeanName the name of the dependent bean
*/
/**
* 注册依赖关系
* baneName:需要记录依赖关系的beanName
* dependentBeanName:依赖的bean的name
*
**/
public void registerDependentBean(String beanName, String dependentBeanName) {
String canonicalName = canonicalName(beanName);
synchronized (this.dependentBeanMap) {
Set<String> dependentBeans =
this.dependentBeanMap.computeIfAbsent(canonicalName, k -> new LinkedHashSet<>(8));
if (!dependentBeans.add(dependentBeanName)) {
return;
}
}
synchronized (this.dependenciesForBeanMap) {
Set<String> dependenciesForBean =
this.dependenciesForBeanMap.computeIfAbsent(dependentBeanName, k -> new LinkedHashSet<>(8));
dependenciesForBean.add(canonicalName);
}
}
}
举个例子大家应该能很好理解,假如A通过@Autowired依赖了B,B通过@Autowired依赖了C,A通过@Autowired依赖了C,那么,A、B、C对应的依赖关系可以表示为如下:
dependentBeanMap={ "b": ["a"], "c": ["b", "a"] } dependenciesForBeanMap={ "a": ["b", "c"], "b": ["c"] }
既然有建立依赖关系,那必然有销毁依赖关系,对应的逻辑比较简单,我就不再赘述了。
总结
本文引出了“循环依赖所注入的实例竟不是容器中的实例”问题,并简要介绍了一下spring中的依赖关系,作为系列的开篇,后续我们将分析:
1.spring中三级缓存解决循环依赖
2.循环依赖于AOP的关系
3.@Async对循环依赖造成了怎样的影响。