- 什么是循环依赖
- 如何检测是否存在循环依赖
- 如何解决循环依赖
- 多例的情况下,循环依赖问题为什么无法解决
- 单例的情况下,虽然可以解决循环依赖,但是还有其他问题吗
- 为什么采用三级缓存解决循环依赖,如果直接将早起bean丢到二级缓存可以吗
什么是循环依赖
这个很好理解,多个bean之间相互依赖,形成了一个闭环。
比如:A依赖于B、B依赖于C、C依赖于A。
如何检测是否存在循环依赖?
检测循环依赖比较简单,使用一个列表来记录正在创建的bean。bean创建之前,先去记录中看一下自己是否已经在列表中,如果在,说明存在循环依赖。如果不在,将其加入到这个列表,bean创建完毕之后,将其从这个列表中移除。
源码方面:
protected void beforeSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
}
singletonsCurrentlyInCreation就是用来记录目前正在创建中的bean名称列表,this.singletonsCurrentlyInCreation.add(beanName)返回false,说明beanName已经在当前列表中了,此时会抛循环依赖的异常BeanCurrentlyInCreationException
Spring如何解决循环依赖的问题
关于bean的生命周期有几个主要的步骤:(不熟悉的,请看下之前的bean生命周期)
- 实例化bean:即调用构造器创建bean实例
- 填充属性,注入依赖的bean,比如通过set方式,@Autowired注解的方式注入bean
- bean的初始化,比如调用init方法等
从上面3个步骤中可以看出,注入依赖的对象,有2中情况:
- 通过步骤1中构造器的方式注入依赖
- 通过步骤2注入依赖
@Component
public class ServiceA {
private ServiceB serviceB;
public ServiceA(ServiceB serviceB) {
this.serviceB = serviceB;
}
}
@Component
public class ServiceB {
private ServiceA serviceA;
public ServiceB(ServiceA serviceA) {
this.serviceA = serviceA;
}
}
构造器的情况比较容易理解,实例化ServiceA的时候,需要有serviceB,而实例化ServiceB的时候需要有serviceA,构造器循环依赖是无法解决的
@Component
public class ServiceA {
private ServiceB serviceB;
@Autowired
public void setServiceB(ServiceB serviceB) {
this.serviceB = serviceB;
}
}
@Component
public class ServiceB {
private ServiceA serviceA;
@Autowired
public void setServiceA(ServiceA serviceA) {
this.serviceA = serviceA;
}
}
如果我们采用硬编码的方式创建上面2个对象,过程如下:
//创建serviceA
ServiceA serviceA = new ServiceA();
//创建serviceB
ServiceB serviceB = new ServiceB();
//将serviceA注入到serviceB中
serviceB.setServiceA(serviceA);
//将serviceB注入到serviceA中
serviceA.setServiceB(serviceB);
下面来看一下spring中set方法创建上面2个bean的过程:
- spring轮询准备创建2个bean:serviceA和serviceB
- spring容器发现singletonObjects中没有serviceA
- 调用serviceA的构造器创建serviceA实例
- serviceA准备注入依赖的对象,发现需要通过setServiceB注入serviceB
- serviceA向spring容器查找serviceB
- spring容器发现singletonObjects中没有serviceB
- 调用serviceB的构造器创建serviceB实例
- serviceB准备注入依赖的对象,发现需要通过setServiceA注入serviceA
- serviceB向spring容器查找serviceA
- 此时又进入步骤2了
上面过程死循环了,怎么才能终结?
可以在第3步后加一个操作:将实例化好的serviceA丢到singletonObjects中,此时问题就解决了。
spring中也采用类似的方式,稍微有点区别,上面使用了一个缓存,而spring内部采用了3级缓存来解决这个问题,我们一起来细看一下。
3级缓存对应的代码:
/** 第一级缓存:单例bean的缓存 */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** 第二级缓存:早期暴露的bean的缓存 */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
/** 第三级缓存:单例bean工厂的缓存 */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
下面来看spring中具体的过程,我们一起来分析源码
开始的时候,获取serviceA,会调用下面代码
// org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
//1.查看缓存中是否已经有这个bean了
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}else {
//若缓存中不存在,准备创建这个bean
if (mbd.isSingleton()) {
//2.下面进入单例bean的创建过程
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
}
return (T) bean;
}
getSingleton方法:
public Object getSingleton(String beanName) {
return getSingleton(beanName, true);
}
然后进入下面方法,会依次尝试从3级缓存中查找bean,注意下面的第2个参数,为ture的时候,才会从第3级中查找,否则只会查找1、2级缓存:
//allowEarlyReference:是否允许从三级缓存singletonFactories中通过getObject拿到bean
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//1.先从一级缓存中找
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
//2.从二级缓存中找
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
//3.二级缓存中没找到 && allowEarlyReference为true的情况下,从三级缓存中找
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
//三级缓存返回的是一个工厂,通过工厂来获取创建bean
singletonObject = singletonFactory.getObject();
//将创建好的bean丢到二级缓存中
this.earlySingletonObjects.put(beanName, singletonObject);
//从三级缓存移除
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
刚开始,3个缓存中肯定是找不到的,会返回null,接着会执行下面代码准备创建serviceA:
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
});
}
进入getSingleton方法,而getSingleton方法代码比较多,为了方便大家理解,无关的代码我给剔除了,如下:
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
//单例bean创建之前调用,将其加入正在创建的列表中,上面有提到过,主要用来检测循环依赖用的
beforeSingletonCreation(beanName);
boolean newSingleton = false;
try {
//调用工厂创建bean
singletonObject = singletonFactory.getObject();//@1
newSingleton = true;
}
finally {
//单例bean创建之前调用,主要是将其从正在创建的列表中移除
afterSingletonCreation(beanName);
}
if (newSingleton) {
//将创建好的单例bean放入缓存中
addSingleton(beanName, singletonObject);//@2
}
}
return singletonObject;
}
}
我们在跟一下createBean(beanName, mbd, args);
最终进去Object beanInstance = doCreateBean(beanName, mbdToUse, args);
方法:
BeanWrapper instanceWrapper = null;
if (instanceWrapper == null) {
//通过反射调用构造器实例化serviceA
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
//变量bean:表示刚刚同构造器创建好的bean示例
final Object bean = instanceWrapper.getWrappedInstance();
//判断是否需要暴露早期的bean,条件为(是否是单例bean && 当前容器允许循环依赖 && bean名称存在于正在创建的bean名称清单中)
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
//若earlySingletonExposure为true,通过下面代码将早期的bean暴露出去
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
什么是早期的bean?刚刚实例化好的bean,此时bean还未进行属性填充,初始化等操作。
我们进入addSingletonFactory
用于将早期的bean暴露出去,主要将其丢到第3级缓存中:
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
//第1级缓存中不存在bean
if (!this.singletonObjects.containsKey(beanName)) {
//将其丢到第3级缓存中
this.singletonFactories.put(beanName, singletonFactory);
//后面的2行代码不用关注
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
上面的方法执行之后,serviceA就被丢到第3级缓存中了。
后续的serviceA开始注入依赖对象,发现需要注入ServiceB,会从容器中获取servuceB,而serviceB的获取又会走上面同样的过程进行实例化,然后将serviceB提前暴露出去,然后serviceB开始注入依赖对象,serviceB发现自己需要注入serviceA,此时去容器中找serviceA,找serviceA会先去缓存中找,会执行getSingleton(“serviceA”,true),此时会走下面代码:
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//1.先从一级缓存中找
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
//2.从二级缓存中找
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
//3.二级缓存中没找到 && allowEarlyReference为true的情况下,从三级缓存中找
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
//三级缓存返回的是一个工厂,通过工厂来获取创建bean
singletonObject = singletonFactory.getObject();
//将创建好的bean丢到二级缓存中
this.earlySingletonObjects.put(beanName, singletonObject);
//从三级缓存移除
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
上面的方法走完之后,serviceA会被放入二级缓存earlySingletonObjects中,会将serviceA返回,此时serviceB中的serviceA注入成功,serviceB继续完成创建,然后将自己返回给serviceA,此时serviceA通过set方法将serviceB注入。
serviceA创建完毕之后,会调用addSingleton方法将其加入到缓存中,这块代码如下:
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
//将bean放入第1级缓存中
this.singletonObjects.put(beanName, singletonObject);
//将其从第3级缓存中移除
this.singletonFactories.remove(beanName);
//将其从第2级缓存中移除
this.earlySingletonObjects.remove(beanName);
}
}
下面捋一捋整个过程:
- 从容器中获取ServiceA
- 容器尝试从3个缓存中找ServiceA,找不到
- 准备创建ServiceA
- 调用serviceA的构造器创建serviceA,得到serviceA实例,此时serviceA还未填充属性,未进行其他任何的初始化操作
- 将早期的serviceA暴露出去:即将其丢到第3级缓存singletonFactories中
- serviceA准备填充属性,发现需要注入serviceB,然后向容器获取serviceB
- 容器尝试从3个缓存中找serviceB,找不到
- 准备创建serviceB
- 调用serviceB的构造器创建serviceB,得到serviceB实例,此时serviceB还未填充属性,未进行其他任何初始化的操作
- 将早期的serviceB暴露出去:即将其丢到第3级缓存singletonFactories中
- serviceB准备填充属性,发现需要注入serviceA,然后向容器获取serviceA
- 容器尝试从3个缓存中找serviceA,发现此时serviceA位于第3级缓存中,经过处理之后,serviceA会从第3级缓存中移除,然后会存到第2级缓存中,然后将其返回给serviceB,此时serviceA通过serviceB中的setServiceA方法被注入到serviceB中
- serviceB继续执行后续的一些操作,最后完成创建工作,然后会调用addSingleton方法,将自己丢到第1级缓存中,并将自己从第2和第3级缓存中移除
- serviceB将自己返回给serviceA
- serviceA通过setServiceB方法将serviceB注入进去
- serviceA继续执行后续的一些操作,最后完成创建工作,然后会调用addSingleton方法,将自己丢到第1级缓存中,并将自己从第2和第3级缓存中移除
循环依赖无法解决的情况
只有单例的bean会通过三级缓存提前暴露来解决循环依赖的问题,而非单例的bean,每次从容器中获取都是一个新的对象,都会重新创建,所以非单例的bean是没有缓存的,不会将其放到三级缓存中。
情况1
条件:
serviceA:多例
serviceB:多例
结果:
此时不管是任何方式都是无法解决循环依赖的问题,最终都会报错,因为每次去获取依赖的bean都会重新创建。
情况2
条件:
serviceA:单例
serviceB:多例
结果:
若使用构造器的方式相互注入,是无法完成注入操作的,会报错。
若采用set方式注入,所有bean都还未创建的情况下,若去容器中获取serviceB,会报错
1.从容器中获取serviceB
2.serviceB由于是多例的,所以缓存中肯定是没有的
3.检查serviceB是在正在创建的bean名称列表中,没有
4.准备创建serviceB
5.将serviceB放入正在创建的bean名称列表中
6.实例化serviceB(由于serviceB是多例的,所以不会提前暴露,必须是单例的才会暴露)
7.准备填充serviceB属性,发现需要注入serviceA
8.从容器中查找serviceA
9.尝试从3级缓存中找serviceA,找不到
10.准备创建serviceA
11.将serviceA放入正在创建的bean名称列表中
12.实例化serviceA
13.由于serviceA是单例的,将早期serviceA暴露出去,丢到第3级缓存中
14.准备填充serviceA的属性,发现需要注入serviceB
15.从容器中获取serviceB
16.先从缓存中找serviceB,找不到
17.检查serviceB是在正在创建的bean名称列表中,发现已经存在了,抛出循环依赖的异常
如果要是先获取serviceA,结果会是怎么样?
为什么需要用3级缓存
问题:如果只使用2级缓存,直接将刚实例化好的bean暴露给二级缓存出是否可以否?
原因:若将刚刚实例化好的bean直接丢到二级缓存中暴露出去,如果后期这个bean对象被更改了,比如可能在上面加了一些拦截器,将其包装为一个代理了,那么暴露出去的bean和最终的这个bean就不一样的,将自己暴露出去的时候是一个原始对象,而自己最终却是一个代理对象,最终会导致被暴露出去的和最终的bean不是同一个bean的,将产生意向不到的效果,而三级缓存就可以发现这个问题,会报错。
案例
需求:在调用service1的每个方法的时候,我们都要进行拦截
@Component
public class Service1 {
public void m1() {
System.out.println("Service1 m1");
}
private Service2 service2;
@Autowired
public void setService2(Service2 service2) {
this.service2 = service2;
}
}
@Component
public class Service2 {
public void m1() {
System.out.println("Service2 m1");
this.service1.m1();//@1
}
private Service1 service1;
@Autowired
public void setService1(Service1 service1) {
this.service1 = service1;
}
public Service1 getService1() {
return service1;
}
}
@Component
public class MethodBeforeInterceptor implements BeanPostProcessor {
@Nullable
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if ("service1".equals(beanName)) {
//代理创建工厂,需传入被代理的目标对象
ProxyFactory proxyFactory = new ProxyFactory(bean);
//添加一个方法前置通知,会在方法执行之前调用通知中的before方法
proxyFactory.addAdvice(new MethodBeforeAdvice() {
@Override
public void before(Method method, Object[] args, @Nullable Object target) throws Throwable {
System.out.println("你好,service1");
}
});
//返回代理对象
return proxyFactory.getProxy();
}
return bean;
}
}
@Test
public void test3() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(MainConfig3.class);
context.refresh();
}
运行报错:
org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'service1': Bean with name 'service1' has been injected into other beans [service2] 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.
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:623)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:516)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:324)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:322)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:897)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:879)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:551)
at org.springframework.context.annotation.AnnotationConfigApplicationContext.<init>(AnnotationConfigApplicationContext.java:89)
at com.spring.test.ClientTest.testDepend(ClientTest.java:80)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)
错误所在:AbstractAutowireCapableBeanFactory.java:623
if (earlySingletonExposure) {
//@1
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
//@2
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
//@3
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 " +
"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
}
}
}
}
上面代码主要用来判断当有循环依赖的情况下,早期暴露给别人使用的bean是否和最终的bean不一样的情况下,会抛出一个异常。
@1:调用getSingleton(beanName, false)方法,这个方法用来从3个级别的缓存中获取bean,但是注意了,这个地方第二个参数是false,此时只会尝试从第1级和第2级缓存中获取bean,如果能够获取到,说明了什么?说明了第2级缓存中已经有这个bean了,而什么情况下第2级缓存中会有bean?说明这个bean从第3级缓存中已经被别人获取过,然后从第3级缓存移到了第2级缓存中,说明这个早期的bean被别人通过getSingleton(beanName, true)获取过
@2:这个地方用来判断早期暴露的bean和最终spring容器对这个bean走完创建过程之后是否还是同一个bean,上面我们的service1被代理了,所以这个地方会返回false,此时会走到@3
@3:allowRawInjectionDespiteWrapping这个参数用来控制是否允许循环依赖的情况下,早期暴露给被人使用的bean在后期是否可以被包装,通俗点理解就是:是否允许早期给别人使用的bean和最终bean不一致的情况,这个值默认是false,表示不允许,也就是说你暴露给别人的bean和你最终的bean需要是一致的
而上面代码注入到service2中的service1是早期的service1,而最终spring容器中的service1变成一个代理对象了,早期的和最终的不一致了,而allowRawInjectionDespiteWrapping又是false,所以报异常了。
那么如何解决这个问题:
很简单,将allowRawInjectionDespiteWrapping设置为true就可以了:
码如下:
@Test
public void test4() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
//创建一个BeanFactoryPostProcessor:BeanFactory后置处理器
context.addBeanFactoryPostProcessor(beanFactory -> {
if (beanFactory instanceof DefaultListableBeanFactory) {
//将allowRawInjectionDespiteWrapping设置为true
((DefaultListableBeanFactory) beanFactory).setAllowRawInjectionDespiteWrapping(true);
}
});
context.register(MainConfig3.class);
context.refresh();
System.out.println("容器初始化完毕");
}
上面代码中将allowRawInjectionDespiteWrapping设置为true了,是通过一个BeanFactoryPostProcessor来实现的,后面会有一篇文章来详解BeanFactoryPostProcessor,目前你只需要知道BeanFactoryPostProcessor可以在bean创建之前用来干预BeanFactory的创建过程,可以用来修改BeanFactory中的一些配置。
再次输出:
@Test
public void testDepend1() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.addBeanFactoryPostProcessor(beanFactory -> {
if (beanFactory instanceof DefaultListableBeanFactory) {
//将allowRawInjectionDespiteWrapping设置为true
((DefaultListableBeanFactory) beanFactory).setAllowRawInjectionDespiteWrapping(true);
}
});
context.register(MainConfig9.class);
context.refresh();
//获取service1
com.spring.beanDepend.Service1 service1 = context.getBean(com.spring.beanDepend.Service1.class);
//获取service2
com.spring.beanDepend.Service2 service2 = context.getBean(com.spring.beanDepend.Service2.class);
System.out.println("-----service2.m1----");
service2.m1(); //@1
System.out.println("----service1.m1-----");
service1.m1(); //@2
System.out.println(service2.getService1() == service1);
}
容器初始化完毕
-----service2.m1----
Service2 m1
Service1 m1
----service1.m1-----
你好,service1
Service1 m1
false
有个问题:在service2#m1方法中,我们有调用serivce1#m1的方法,但是没有进行拦截,但是单独调用service1.m1方法,却起效了,说明service2中注入的service1不是代理对象,所以没有加上拦截器的功能,那是因为service2中注入的是早期的service1,注入的时候service1还不是一个代理对象,所以没有拦截器中的功能。
再看看最后一行输出为false,说明service2中的service1确实和spring容器中的service1不是一个对象了。
如何解决问题呢?
既然最终service1是一个代理对象,那么你提前暴露出去的时候,注入到service2的时候,你也必须得是个代理对象啊,需要确保给别人和最终是同一个对象。
我们看暴露早起bean的源码,注意下面的代码:
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
注意有个getEarlyBeanReference方法,来看一下这个方法是干什么的,源码如下:
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
return exposedObject;
}
从3级缓存中获取bean的时候,会调用上面这个方法来获取bean,这个方法内部会看一下容器中是否有SmartInstantiationAwareBeanPostProcessor这种处理器,然后会依次调用这种处理器中的getEarlyBeanReference方法,那么思路来了,我们可以自定义一个SmartInstantiationAwareBeanPostProcessor,然后在其getEarlyBeanReference中来创建代理不就可以了,聪明,我们来试试,将MethodBeforeInterceptor代码改成下面这样:
@Component
public class MethodBeforeInterceptor implements SmartInstantiationAwareBeanPostProcessor {
@Override
public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
if ("service1".equals(beanName)) {
//代理创建工厂,需传入被代理的目标对象
ProxyFactory proxyFactory = new ProxyFactory(bean);
//添加一个方法前置通知,会在方法执行之前调用通知中的before方法
proxyFactory.addAdvice(new MethodBeforeAdvice() {
@Override
public void before(Method method, Object[] args, @Nullable Object target) throws Throwable {
System.out.println("你好,service1");
}
});
//返回代理对象
return proxyFactory.getProxy();
}
return bean;
}
}
运行结果:
-----service2.m1----
Service2 m1
你好,service1
Service1 m1
----service1.m1-----
你好,service1
Service1 m1
true
单例bean解决了循环依赖,还存在什么问题?
循环依赖的情况下,由于注入的是早期的bean,此时早期的bean中还未被填充属性,初始化等各种操作,也就是说此时bean并没有被完全初始化完毕,此时若直接拿去使用,可能存在有问题的风险。