spring循环依赖问题

spring循环依赖问题

实际业务系统中由于业务的复杂性,可能会导致业务层组件产生服务A依赖服务B,服务B依赖服务C,服务C依赖服务A的循环依赖问题。有时候产生了这种问题可以服务正常运行,有时候spring会提示依赖错误。那为何会有此区别呢?来看下代码
测试代码一如下:

import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;

@Service
@AllArgsConstructor
public class ServiceA {
    private ServiceB serviceB;
}
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;

@Service
@AllArgsConstructor
public class ServiceB {
    private ServiceC serviceC;
}

启动日志如下:

2019-10-13 16:58:48.221  INFO 12528 --- [           main] com.example.demo.DemoApplication         : Starting DemoApplication on jingjing with PID 12528 (D:\workspace\demo\target\classes started by jingy in D:\workspace\demo)
2019-10-13 16:58:48.224  INFO 12528 --- [           main] com.example.demo.DemoApplication         : No active profile set, falling back to default profiles: default
2019-10-13 16:58:49.580  INFO 12528 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2019-10-13 16:58:49.604  INFO 12528 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2019-10-13 16:58:49.604  INFO 12528 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.26]
2019-10-13 16:58:49.712  INFO 12528 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2019-10-13 16:58:49.712  INFO 12528 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 1407 ms
2019-10-13 16:58:49.767  WARN 12528 --- [           main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'serviceA' defined in file [D:\workspace\demo\target\classes\com\example\demo\service\ServiceA.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'serviceB' defined in file [D:\workspace\demo\target\classes\com\example\demo\service\ServiceB.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'serviceC' defined in file [D:\workspace\demo\target\classes\com\example\demo\service\ServiceC.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'serviceA': Requested bean is currently in creation: Is there an unresolvable circular reference?
2019-10-13 16:58:49.770  INFO 12528 --- [           main] o.apache.catalina.core.StandardService   : Stopping service [Tomcat]
2019-10-13 16:58:49.783  INFO 12528 --- [           main] ConditionEvaluationReportLoggingListener : 

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2019-10-13 16:58:49.789 ERROR 12528 --- [           main] o.s.b.d.LoggingFailureAnalysisReporter   : 

***************************
APPLICATION FAILED TO START
***************************

Description:

The dependencies of some of the beans in the application context form a cycle:

┌─────┐
|  serviceA defined in file [D:\workspace\demo\target\classes\com\example\demo\service\ServiceA.class]
↑     ↓
|  serviceB defined in file [D:\workspace\demo\target\classes\com\example\demo\service\ServiceB.class]
↑     ↓
|  serviceC defined in file [D:\workspace\demo\target\classes\com\example\demo\service\ServiceC.class]
└─────┘

spring提示存在循环依赖,容器启动失败
测试代码二如下:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class ServiceA {
    @Autowired
    private ServiceB serviceB;
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class ServiceB {
    @Autowired
    private ServiceC serviceC;
}
@Service
public class ServiceC {
    @Autowired
    private ServiceA serviceA;
}

启动日志如下:

2019-10-13 17:01:36.673  INFO 14232 --- [           main] com.example.demo.DemoApplication         : Starting DemoApplication on jingjing with PID 14232 (D:\workspace\demo\target\classes started by jingy in D:\workspace\demo)
2019-10-13 17:01:36.677  INFO 14232 --- [           main] com.example.demo.DemoApplication         : No active profile set, falling back to default profiles: default
2019-10-13 17:01:38.274  INFO 14232 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2019-10-13 17:01:38.315  INFO 14232 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2019-10-13 17:01:38.316  INFO 14232 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.26]
2019-10-13 17:01:38.431  INFO 14232 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2019-10-13 17:01:38.431  INFO 14232 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 1629 ms
2019-10-13 17:01:38.712  INFO 14232 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2019-10-13 17:01:38.919  INFO 14232 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2019-10-13 17:01:38.925  INFO 14232 --- [           main] com.example.demo.DemoApplication         : Started DemoApplication in 2.871 seconds (JVM running for 4.737)

服务启动正常
两种方式都产生了循环依赖的条件,只是注入方式不同,第一种方式采用构造器注入,第二种通过类型注入。那么spring是怎么处理这两种注入方式产生的循环依赖问题呢?
springboot的入口方法是SpringApplication类的run方法

public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
		return run(new Class<?>[] { primarySource }, args);
}

跟踪代码查看这个方法里面主要干了以下几件事

public ConfigurableApplicationContext run(String... args) {
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		configureHeadlessProperty();
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting();
		try {
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
			ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
			configureIgnoreBeanInfo(environment);
			Banner printedBanner = printBanner(environment);
			context = createApplicationContext();
			exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
			//准备上下文
			prepareContext(context, environment, listeners, applicationArguments, printedBanner);
			//这里会扫描启动类路径下的所有需要加载的组件,转化为元信息,存储在DefaultListableBeanFactory中,这一步很重要
			refreshContext(context);
			afterRefresh(context, applicationArguments);
			stopWatch.stop();
			...省略部分代码...
		return context;
	}

spring实际创建bean的方法为AbstractAutowireCapableBeanFactory类的doCreateBean

// Instantiate the bean.
		BeanWrapper instanceWrapper = null;
		if (mbd.isSingleton()) {
			instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
		}
		if (instanceWrapper == null) {
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}

这里的createBeanInstance方法其实是调用了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");
		}
		//这里实际是bean初始化前先缓存了创建bean的ObjectFactory,这个时候bean其实已经调用默认构造方法通过反射进行了实例化
		addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
	}
	
	// Initialize the bean instance.
	Object exposedObject = bean;
	try {
		//这里对属性进行了填充
		populateBean(beanName, mbd, instanceWrapper);
		exposedObject = initializeBean(beanName, exposedObject, mbd);
	}

由此可见,spring对bean的创建过程可谓有着精心的设计,而我们使用spring所会碰到的问题,spring早已有预见提供了解决方案。如果理解有误的地方,还请不吝指正。学习之路无止境,学习好的代码设计能让我们不断提升自己,诸君共勉。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值