测试的Springboot版本: 2.6.4,禁止了循环依赖,但是可以通过application.yml开启(哈哈)

@Lazy注解解决循环依赖

情况一:只有简单属性关系的循环依赖

涉及的Bean:

  • ASerivce及其实现类ASerivceImpl
  • BSerivce及其实现类BSerivceImpl

com.example.demo.service.AService

package com.example.demo.service;

public interface AService {
void zaWaLuDo();
}

com.example.demo.service.impl.AServiceImpl

package com.example.demo.service.impl;

import com.example.demo.service.AService;
import com.example.demo.service.BService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; @Service
public class AServiceImpl implements AService { @Autowired
public BService bService; @Override
public void zaWaLuDo(){
System.out.println("ToKiOToMaLei!");
}
}

com.example.demo.service.BService

package com.example.demo.service;

public interface BService {

}

com.example.demo.service.impl.BServiceImpl

package com.example.demo.service.impl;

import com.example.demo.service.AService;
import com.example.demo.service.BService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; @Service
public class BServiceImpl implements BService {
@Autowired
public AService aService;
}

此时ASerivceBService构成循环依赖的关系:

测试类:com.example.demo.service.AServiceTest

package com.example.demo.service;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest
class AServiceTest {
@Autowired
AService aService; @Test
public void test(){
aService.zaWaLuDo();
} }

此时运行test方法,将会报错:

java.lang.IllegalStateException: Failed to load ApplicationContext

	at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:132)
at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:124)
at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:118)
at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:83)
at org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener.prepareTestInstance(SpringBootDependencyInjectionTestExecutionListener.java:43)
at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:248)
at org.springframework.test.context.junit.jupiter.SpringExtension.postProcessTestInstance(SpringExtension.java:138)
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeTestInstancePostProcessors$8(ClassBasedTestDescriptor.java:363)
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.executeAndMaskThrowable(ClassBasedTestDescriptor.java:368)
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeTestInstancePostProcessors$9(ClassBasedTestDescriptor.java:363)
......省略.....
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'AServiceImpl': Unsatisfied dependency expressed through field 'bService'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'BServiceImpl': Unsatisfied dependency expressed through field 'aService'; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'AServiceImpl': Requested bean is currently in creation: Is there an unresolvable circular reference?
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:659)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:639)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:119)

最重要的一句应该是:

美观处理过:
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'AServiceImpl': Unsatisfied dependency expressed through field 'bService';
nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'BServiceImpl': Unsatisfied dependency expressed through field 'aService';
nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException:
Error creating bean with name 'AServiceImpl': Requested bean is currently in creation:
Is there an unresolvable circular reference?

Spring提醒我们可能存在circular reference,就是大名鼎鼎的循环依赖。

解决办法

在其中任意一个属性注入@Autowired上加入懒加载@Lazy即可跑通,比如在AService的实现类中加入:

package com.example.demo.service.impl;

import com.example.demo.service.AService;
import com.example.demo.service.BService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service; @Service
public class AServiceImpl implements AService { @Autowired
@Lazy //懒加载
public BService bService; @Override
public void zaWaLuDo(){
System.out.println("ToKiOToMaLei!");
}
}

此时,运行测试方法test()的运行结果就是:

ToKiOToMaLei!

说明aService.zaWaLuDo()方法执行成功

源码分析

参考:https://www.zhihu.com/question/438247718

主要是靠Spring中(人为定义)的三级缓存有关:

org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean)

第一级缓存:**Map<String, Object> singletonObjects**

第一级缓存的作用?

  • 用于存储单例模式下创建的Bean实例(已经创建完毕)。
  • 该缓存是对外使用的,指的就是使用Spring框架的程序员。

存储什么数据?

  • K:bean的名称
  • V:bean的实例对象, 或者说:“成品”对象(有代理对象则指的是代理对象,已经创建完毕)

第二级缓存:**Map<String, Object> earlySingletonObjects**

第二级缓存的作用?

  • 用于存储单例模式下创建的Bean实例(该Bean被提前暴露的引用,该Bean还在创建中)。
  • 该缓存是对内使用的,指的就是Spring框架内部逻辑使用该缓存。

存储的数据:

  • K:bean的名称
  • V:bean的实例对象,“半成品”对象(有代理对象则指的是代理对象,该Bean还在创建中)

第三级缓存:**Map<String, ObjectFactory<?>> singletonFactories**

第三级缓存的作用?

  • 通过ObjectFactory对象来存储单例模式下提前暴露的Bean实例的引用(正在创建中)。
  • 该缓存是对内使用的,指的就是Spring框架内部逻辑使用该缓存。
  • 此缓存是解决循环依赖最大的功臣

存储什么数据?

  • K:bean的名称
  • V:ObjectFactory,该对象持有提前暴露的bean的引用

一、注入AService时,首先进入org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean:

				// Create bean instance.
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
...
}

看看getSingleton方法的原型,org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, org.springframework.beans.factory.ObjectFactory<?>)

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory)

所以此时doGetBean方法会进入lambda方法中的,调用createBean方法来得到一个ObjectFactory

接着我们进入到org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactorydoCreateBean方法, 打上断点看看:

  1. beanName='AServiceImpl'的时候,先根据反射创建了一个Object类的AServiceImpl的bean,里面的BServicenull
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args){
...省略...
Object bean = instanceWrapper.getWrappedInstance(); //ASericeImpl@4686
Class<?> beanType = instanceWrapper.getWrappedClass(); //beanType = "class com.example.demo.service.impl.AServiceImpl"
...省略...
}
  1. 判断该bean是否已经被提前暴露
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
...省略... //判断该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));
//如果是,就调用addSingletonFactory方法,
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
...省略...
}
  1. 若没有被提前暴露,就进入到语句:
		// Initialize the bean instance.
Object exposedObject = bean;
try {
//调用populateBean方法后,AService中的BService属性就不再是null,而是一个$Proxy@4981$,
//应该是个代理的对象,解决注入的燃眉之急
populateBean(beanName, mbd, instanceWrapper);
//做一些初始化的操作
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
  1. 将该bean暴露
		// Register bean as disposable.
try {
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
  1. 接着就将其返回
return exposedObject;

此时,exposedObject对象里的bService还是$Proxy$

二、回到org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean方法:

				if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
//这时候回到了这里,lambda表达式会得到上面的exposedObject
return createBean(beanName, mbd, args);
}

此时会回到getSingleton方法中,进入getSingleton方法内部:

				try {
//其中的singletonFactory调用getObject就是lambda表达式返回的exposedObject,也就是里面的bService还是$Proxy$
singletonObject = singletonFactory.getObject();
//标记为新的单例bean
newSingleton = true;
}

最后我们看看,this.singletonObjects中的AService



可以看到用bService中注入了一个神秘的$Proxy$,然后写入了一级缓存中,经调试后发现是在getSingleton方法中,调用addSingleton方法写入的,这时候二级、三级缓存全程都没写入过数据。

				if (newSingleton) {
addSingleton(beanName, singletonObject);
}

三、回到test()方法:

    @Test
public void test(){
aService.zaWaLuDo();
}

此时,aService中的bService还是个&Proxy$



这时候继续就会正常执行aService.zaWaLuDo_()_,程序正常结束。

总结下就是这种情况下,aService会由doCreateBean方法创建,而bService是某种代理的东西存在其中。

四、我们改写一下两个Service,使AService需要调用BService的方法:

  1. AServiceImpl
package com.example.demo.service.impl;

import com.example.demo.service.AService;
import com.example.demo.service.BService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service; @Service
public class AServiceImpl implements AService { @Autowired
@Lazy
public BService bService; @Override
public void zaWaLuDo(){
System.out.println("ToKiOToMaLei!");
bService.starPuLaXin();
}
}
  1. BServiceImpl
package com.example.demo.service.impl;

import com.example.demo.service.AService;
import com.example.demo.service.BService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; @Service
public class BServiceImpl implements BService {
@Autowired
public AService aService; @Override
public void starPuLaXin() {
System.out.println("Za WaLuDo!");
}
}

我们先在执行aServuce,zaWaLuDo()之前打个断点看看此时的aService是什么情况:





可以看到跟上面的情况是一样的。

这个时候我们在org.springframework.beans.factory.support.AbstractBeanFactory#getBean(java.lang.String)方法打个断点看看,这时候会进入org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean方法,执行:

		// Eagerly check singleton cache for manually registered singletons.
Object sharedInstance = getSingleton(beanName);

sharedInstance是这样的:



里面的bService还是一个$Proxy$,我们一直调试到getBean方法结束,他最终会进入到jdk代理org.springframework.aop.framework.JdkDynamicAopProxy中执行方法:

//方法参数
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
//target根据参数argsToUse执行方法method的结果
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);

于是就会执行结果:

ToKiOToMaLei!
Za WaLuDo!

五、研究一下aServicebService的注入过程,二者都会进入doCreateBean方法,aService会入上面的过程一样被创建,我们研究一下bService的创建过程,当执行到:

		try {
populateBean(beanName, mbd, instanceWrapper);
exposedObject = initializeBean(beanName, exposedObject, mbd);
}

执行完populateBean方法,exposedObject(即将成型的bService)就被注入了aService



但是这个aService中的bService实际上还只是个$Proxy$,在接下来的过程中,aService中的bService一直都还只是个$Proxy$,就像bService中的aService也一直都还是个$Proxy$,所以可以推断,这种情况下Spring不关心二者是否真的存了个“成品”对象,只是有个“半成品”对象利用通过jdk动态代理AOP执行bService的方法而已, 最终会到:org.springframework.aop.framework.JdkDynamicAopProxy#invoke中执行bService的方法.

情况二:构造器注入循环依赖示例

  • com.example.demo.service.CService
package com.example.demo.service;

public interface CService {
void goldExperience();
}
  • com.example.demo.service.DService
package com.example.demo.service;

public interface DService {
}
  • com.example.demo.service.impl.CServiceImpl
package com.example.demo.service.impl;

import com.example.demo.service.CService;
import com.example.demo.service.DService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; @Service
public class CServiceImpl implements CService { private DService dService; @Autowired
public CServiceImpl(DService dService) {
this.dService = dService;
} @Override
public void goldExperience() {
System.out.println("MUDAMUDAMUDAMUDA!!!!");
}
}
  • com.example.demo.service.impl.DServiceImpl
package com.example.demo.service.impl;

import com.example.demo.service.CService;
import com.example.demo.service.DService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; @Service
public class DServiceImpl implements DService {
private CService cService; @Autowired
public DServiceImpl(CService cService) {
this.cService = cService;
}
}
  • com.example.demo.service.CServiceTest
package com.example.demo.service;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest; import static org.junit.jupiter.api.Assertions.*; @SpringBootTest
class CServiceTest {
@Autowired
CService cService; @Test
public void test(){
cService.goldExperience();
}
}

运行测试方法,同样报循环依赖的错误。

解决方法

在参数里添加@Lazy方法:

package com.example.demo.service.impl;

import com.example.demo.service.CService;
import com.example.demo.service.DService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service; @Service
public class CServiceImpl implements CService { private DService dService; @Autowired
public CServiceImpl(@Lazy DService dService) { //参数上添加了@Lazy方法
this.dService = dService;
} @Override
public void goldExperience() {
System.out.println("MUDAMUDAMUDAMUDA!!!!");
}
}

源码分析

跟情况一一样,也是通过注入一个"假"的对象解决:



将代码改成情况一的调用dService的方法也是通过jdk动态代理AOP解决。

Springboot解决循环依赖的源码阅读

application.yml中开启:

spring:
main:
allow-circular-references: true

我们先调试一下看看:



发现cService中的dService是货真价实的

我们尝试调试看看能不能搞清楚Springboot到底是怎么注入的,在老地方org.springframework.beans.factory.support.AbstractBeanFactory#getBean(java.lang.String)打个断点,研究一下dService是怎么做的:

	@Override
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}

一直调试到org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor才终于有点眉目:

			if (value != null) {
ReflectionUtils.makeAccessible(field);
field.set(bean, value);
}

此时的各变量是这样的:



field.set(bean, value)方法内部是getFieldAccessor_(_obj_)_.set_(_obj, value_)_;后面的set_(_obj, value_)_就是已编译好的字节码了,执行下一步后,dService中缺乏的cService就有东西了,所以可以推测是一个写入过程。

我们看看其巨长无比的调用栈:



在这句所在的inject方法头部打个注解,看看有没有头绪,cService是从哪来的,重新启动一下



当创建dService时,会进入到该方法体,初始的时候value啥也没有,接着会进到:

...
value = resolveFieldValue(field, bean, beanName); //进入本句
...
if (value != null) {
ReflectionUtils.makeAccessible(field);
field.set(bean, value);
}

一直调用到org.springframework.beans.factory.config.DependencyDescriptor#resolveCandidate方法:

	public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory)
throws BeansException { return beanFactory.getBean(beanName); //beanName="CServiceImpl"
}

此时由进入到了老朋友org.springframework.beans.factory.support.AbstractBeanFactory#getBean(java.lang.String)方法中,返回了一个CServiceImpl对象给上面的resolveFieldValue(field, bean, beanName);接着就进入到field.set(bean, value);中将其注入,那么神奇的事情肯定是发生在beanFactory.getBean(beanName);

老办法,再打个断点,回到取CServiceImpl对象的时候看看:



接着他会进入老朋友org.springframework.beans.factory.support.AbstractBeanFactory#getBean(java.lang.String)方法中,我们step into,在执行完Object sharedInstance = getSingleton(beanName)后就有了CServiceImpl对象,只不过他的dService还是null:

protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
throws BeansException { String beanName = transformedBeanName(name);
Object beanInstance; // Eagerly check singleton cache for manually registered singletons.
Object sharedInstance = getSingleton(beanName);
....

最后还是会field.set(bean, value);dService先注入。

看到这里感觉非常混乱,感觉还是按那幅图来看吧:

  1. 在创建cService调用doCreateBean方法,执行了addSingletonFactory
{
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// Initialize the bean instance.
Object exposedObject = bean;
try {
populateBean(beanName, mbd, instanceWrapper);
exposedObject = initializeBean(beanName, exposedObject, mbd);
}

三级缓存this.singletonFactories 中便存入了“半成品”对象的自己:

  1. cService执行到populateBean的时候,旋即进入到了dServicedoCreateBean
  2. dService通过addSingletonFactory也往三级缓存this.singletonFactories 中便存入了“半成品”对象的自己,此时c、d都在三级缓存this.singletonFactories里:

  3. dService执行了下一句,即populateBean之后,cService从三级缓存换入到了二级缓存this.earlySingletonObjects



    此时其内部的dService为空:

  4. 到这一步,我们再理清一下调用链:

    dService执行到getSingleton后:
// Create bean instance.
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}

getSingleton内部执行了addSingleton_(_beanName, singletonObject_)_之后,便把自己写入了三级缓存this.singletonObjects中,并把半成品的cService注入到自己中,形如:

  1. 之后就回到了cService->populateBean的执行,最终去到了field.set_(_bean, value_)_中,此时bean为cService, value为dService(内部的cServicedService仍未空),执行完之后,就链接上了!神奇!:
  2. cService ->populateBean执行结束后,回到cService -> doGetBean的执行,进行完cService -> getSingleton后,二级缓存this.earlySingletonObjects中的cService也移入了一级缓存this.singletonObjects之中:

    此时,基本上解决了循环引用的问题。

总结一下

  1. 我们认为一个对象的创建可分为两步:

    1. 实例化:可认为是一个在内存中划分空间的过程
    2. 初始化:为该对象的属性赋值的过程
  2. cService在创建的时候,先只进行了实例化,此时是一个“半成品”对象,写入三级缓存中存储;
  3. 旋即进行其所需要的dService对象的创建,而dService会进行实例化之后,也将“半成品”的自己写入三级缓存中,
  4. 此时cService会尝试进行初始化(为属性赋值),他需要写入dService,但事实上会为dService赋值为null,然后写入二级缓存,此时的cService仍然是个“半成品”。
  5. 接着又回到dService的创建,这时候他也会进行一个初始化,将二级队列中的完成了“假”的初始化的“半成品”对象cService,给自己所需的属性注入,完成了初始化过程,并写入了一级缓存。
  6. 然后就回到了cService还在进行中的创建过程,这个时候cService是个“假”的“半成品”,在二级缓存,而dService是个真的成品,他确实拥有了cService对象。
  7. cService这时候也会将一级缓存中的dService,一个真正完成了初始化的对象,注入到自己的属性中,这个时候二者终于完成了互相注入,cService也完成了初始化,进入了一级缓存,循环依赖得以解决。

为什么需要三级缓存?

我大概总结了一下流程:



可以看出,互相依赖的两个对象有三种状态:

  1. 只有“存在”,没有内部的“半成品”形态一对象
  2. 注入了“半成品”形态一对象的“半成品”形态二对象
  3. 注入了“半成品”形态二对象的完全体“成品”对象

因有客观上有三种状态的对象,所以才利用三级缓存来分别存储,比较科学的说明如下:

三级缓存

singletonObjects

一级缓存, Cache of singleton objects bean name --> bean instance。 存放完整对象。

earlySingletonObjects

二级缓存, Cache of early singleton objects bean name --> bean instance 提前曝光的BEAN缓存。 存放半成品对象。

singletonFactories

三级缓存, Cache of singleton factories bean name --> ObjectFactory。需要的对象被代理时,就必须使用三级缓存(否则二级就够了)。解决循环依赖中存在aop的问题 存放 lambda 表达式和对象名称的映射。

作者:JavaEdge

链接:https://www.zhihu.com/question/438247718/answer/2331910821

来源:知乎

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

参考

  1. https://www.zhihu.com/question/438247718
  2. https://blog.csdn.net/qq_18298439/article/details/88818418

本文仅为分享学习经历,欢迎批评指正

Springboot循环依赖实践纪实的更多相关文章

  1. Springboot循环依赖

    背景 最近在使用Springboot做项目的时候,遇到了一个循环依赖的 问题.那什么是循环依赖呢,常见的一种情形就是在ServiceA中注入了ServiceB,在ServiceB中也注入了Servic ...

  2. Mybatis源码手记-从缓存体系看责任链派发模式与循环依赖企业级实践

    一.缓存总览 Mybatis在设计上处处都有用到的缓存,而且Mybatis的缓存体系设计上遵循单一职责.开闭原则.高度解耦.及其精巧,充分的将缓存分层,其独到之处可以套用到很多类似的业务上.这里将主要 ...

  3. springboot bean的循环依赖实现 源码分析

    springboot bean的循环依赖实现 源码分析 本文基于springboot版本2.5.1 <parent> <groupId>org.springframework. ...

  4. 手把手教你调试SpringBoot启动 IoC容器初始化源码,spring如何解决循环依赖

    授人以鱼不如授人以渔,首先声明这篇文章并没有过多的总结和结论,主要内容是教大家如何一步一步自己手动debug调试源码,然后总结spring如何解决的循环依赖,最后,操作很简单,有手就行. 本次调试 是 ...

  5. 折腾了我两天的springboot数据源datasource循环依赖问题,都被搞疯掉了

    在做项目重构的时候增加了两个功能 1.多数据源. 2.token的验证从以前的数据库验证,移到了redis端. 1.多数据源使用 druid-spring-boot-starter 套件 其核心代码如 ...

  6. Springboot源码分析之Spring循环依赖揭秘

    摘要: 若你是一个有经验的程序员,那你在开发中必然碰到过这种现象:事务不生效.或许刚说到这,有的小伙伴就会大惊失色了.Spring不是解决了循环依赖问题吗,它是怎么又会发生循环依赖的呢?,接下来就让我 ...

  7. Springboot Bean循环依赖问题

    参考博客原文地址: https://www.jb51.net/article/168398.htm https://www.cnblogs.com/mianteno/p/10692633.html h ...

  8. SpringBoot项目意外出现 循环依赖和注入的对象意外是Null的问题 Requested bean is currently in creation: Is there an unresolvable circular reference? 或 nested exception is java.lang.NullPointerException

    1.Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name ...

  9. 开涛spring3(3.2) - DI之循环依赖

    3.2.1  什么是循环依赖 循环依赖就是循环引用,就是两个或多个Bean相互之间的持有对方,比如CircleA引用CircleB,CircleB引用 CircleC,CircleC引用CircleA ...

随机推荐

  1. JavaScripts调用摄像头【MediaDevices.getUserMedia()】

    h5调用摄像头(允许自定义界面)[MediaDevices.getUserMedia()] <!DOCTYPE html> <html lang="en"> ...

  2. Redis学习笔记(三)redis 的键管理

    Redis 的键管理 一.Redis 数据库管理 Redis 是一个键值对(key-value pair)的数据库服务器,其数据保存在 src/server.h/redisDb 中(网上很多帖子说在 ...

  3. SqlServer基础语法

    历史 有很多软件公司开发了数据库产品,其中微软公司的数据库产品命名为 SQL Server,也称 MS SQL Server. 1989年 Ashton-Tate/Microsoft SQL Serv ...

  4. UIView与核心动画对比?

    1.UIView和核心动画区别?        核心动画只能添加到CALayer        核心动画一切都是假象,并不会改变真实的值.             2.什么时候使用UIView的动画? ...

  5. php导出excel xml word

    转载请注明来源:https://www.cnblogs.com/hookjc/ Excel: <?php header("Content-Type: application/vnd.m ...

  6. Java 自定义窗体(标题栏、窗体背景)

    感谢大佬:https://blog.csdn.net/ltx06/article/details/28996839 最近在书上看到这篇文章,觉得挺有意思.与大家分享一下,具体可以参见明日科技出版的&l ...

  7. 7.3php编译安装最终版

    进过无数次折腾后发现编译php安装的有些难, 问题可能是自己对linux不太了解吧. [root@third src]# cd php-7.3.6 [root@third php-7.3.6]# ./ ...

  8. .NET 云原生架构师训练营(权限系统 RGCA 开发任务)--学习笔记

    目录 目标 模块拆分 OPM 开发任务 目标 基于上一讲的模块划分做一个任务拆解,根据任务拆解实现功能 模块拆分 模块划分已经完成了边界的划分,边界内外职责清晰 OPM 根据模块拆分画出 OPM(Ob ...

  9. Dockerfile镜像实例

    Dockerfile镜像实例 目录 Dockerfile镜像实例 一.构建SSH镜像 1. 建立工作目录 2. 生成镜像 3. 启动容器并修改root密码 二.systemctl镜像 1. 建立工作目 ...

  10. sbt修改源(国内优先)

    [repositories] local aliyun: https://maven.aliyun.com/repository/public oschina: http://maven.oschin ...