Abstract
好久没写文章了, 最近喜欢上了springboot, 也发现这个开发起来确实很方便和快捷。 包括spring kafka, jdbc, & actuator. 一天, 比较惊讶于spring如何优雅的解决循环依赖的问题的, 因为自己代码有时候也没注意, @Autowired
到处都是, 但是发现spring竟然可以正常工作,于是好奇的想了解下其实现。 所以就有了这个文章。
Spring如何解决循环依赖的?
官方文档:here
Circular dependencies
If you use predominantly constructor injection, it is possible to create an unresolvable circular dependency scenario.
For example: Class A requires an instance of class B through constructor injection, and class B requires an instance of class A through constructor injection. If you configure beans for classes A and B to be injected into each other, the Spring IoC container detects this circular reference at runtime, and throws a BeanCurrentlyInCreationException.
One possible solution is to edit the source code of some classes to be configured by setters rather than constructors. Alternatively, avoid constructor injection and use setter injection only. In other words, although it is not recommended, you can configure circular dependencies with setter injection.
Unlike the typical case (with no circular dependencies), a circular dependency between bean A and bean B forces one of the beans to be injected into the other prior to being fully initialized itself (a classic chicken/egg scenario).
这里面提到2点:
(1) 确实可能存在鸡生蛋 蛋生鸡的问题。 如果有 则抛出异常 BeanCurrentlyInCreationException
(2) 可以试用set/post process来解决(不使用构造器依赖)。
然后百度了一下, 这篇文章写的很不错:here
总结来说就是: 递归调用+一个巧妙地early exposed map.
动手实现一个简单的解决循环依赖的版本
我们主要的目录结构如下: 完整源码在here
BeanA & BeanB 两个测试bean。
package com.example.beans.beans;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class BeanA {
@Autowired
private BeanB b;
public BeanA() {
System.out.println("A's no args construtor");
}
public void print(){
System.out.println("bean a post construct is called");
System.out.println("Is bean b is null in bean a 's post " + (b == null) + " bean a in current b(" + b.getBeana());
b.printMe();
}
}
package com.example.beans.beans;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class BeanB {
@Autowired
private BeanA beana;
public BeanA getBeana() {
return beana;
}
public BeanB() {
System.out.println("B's no args construtor");
System.out.println("Is a null in b's constructor???" + (beana == null));
}
public void printMe(){
System.out.println("Is a null in b's print me???" + (beana == null));
System.out.println("print me is called in Bean B");
}
}
这两个主要是BeanA 和BeanB相互引用。
MySpringImpl里面有我们的主要实现。
package com.example.beans.impl;
import com.example.beans.beans.BeanA;
import com.example.beans.beans.BeanB;
import org.springframework.beans.factory.annotation.Autowired;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class MySpringImpl {
public static void main(String[] args) throws Exception{
String testPackage = "com.example.beans.beans";
Context c = new Context(testPackage);
System.out.println("Below is A==============");
BeanA beanA = (BeanA) c.getBean(testPackage + "." + "BeanA");
beanA.print();
System.out.println("Below is b==============");
BeanB beanB = (BeanB) c.getBean(testPackage + "." + "BeanB");
beanB.printMe();
}
public static class Context {
private Map<String, Object> earlyExposed = new HashMap<>();
private Map<String, Object> created = new HashMap<>();
private Map<String, ObjectFactory> objectFactoryMap = new HashMap<>();
private Set<String> creating = new HashSet<>();
private Map<String, Class> defines = new HashMap<>();
public Context(String pack) {
// scans all packages under this classes....
try {
Class[] clzList = ReflectionUtils.getClasses(pack);
// assume all has a @Service tag
// init all objects
for (Class c : clzList) {
defines.put(c.getName(), c);
}
}
catch (Exception e) {
e.printStackTrace();
}
}
public Object getBean(String beanName) throws Exception {
Object o = getSingleton(beanName);
if (o == null) {
o = getSingleton(beanName, new ObjectFactory() {
@Override
public Object getObject(String c) {
try {
return creatBean(beanName);
}
catch (Exception e) {
e.printStackTrace();
throw new IllegalArgumentException(e);
}
}
});
}
return o;
}
private Object creatBean(String beanName) throws Exception {
Object shared = Class.forName(beanName).newInstance();
if (creating.contains(beanName)) {
objectFactoryMap.put(beanName, new ObjectHolder(shared));
}
// get declared objects
List<Field> fs = ReflectionUtils.getFieldsByAnnotation(Class.forName(beanName), Autowired.class);
for (Field f : fs) {
f.setAccessible(true);
Object v = getBean(f.getType().getName());
f.set(shared, v);
}
creating.remove(beanName);
objectFactoryMap.remove(beanName);
return shared;
}
private Object getSingleton(String beanName, ObjectFactory objectFactory) throws Exception {
return objectFactory.getObject(beanName);
}
private Object getSingleton(String beanName) throws Exception {
if (!defines.containsKey(beanName)) {
throw new IllegalArgumentException("not register - " + beanName);
}
Object target = created.get(beanName);
if (target != null) {
return target;
}
if (creating.contains(beanName)) {
target = earlyExposed.get(beanName);
if (target == null) {
ObjectFactory objectFactory = objectFactoryMap.get(beanName);
if (objectFactory != null) {
target = objectFactory.getObject(beanName);
objectFactoryMap.remove(beanName);
earlyExposed.put(beanName, target);
}
}
} else {
creating.add(beanName);
}
return target;
}
}
public interface ObjectFactory {
Object getObject(String c);
}
public static class ObjectHolder implements ObjectFactory {
private Object o;
public ObjectHolder(Object o) {
this.o = o;
}
@Override
public Object getObject(String c) {
return o;
}
}
}
除去注释, 大概只有100行代码。 建议单步Debug这个代码来加深对spring的理解(结合前文)。 如果想通了发现也不是很复杂。
总结
调用构造器, 放入到earlyExposedMap, 然后递归调用对象依赖的属性方法。