Spring 循环依赖与三级缓存

目录

spring 循环依赖与三级缓存

单例对象三级缓存出处

//org.springframework.beans.factory.support.DefaultSingletonBeanRegistry

	/** Cache of singleton objects: bean name to bean instance. */
	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

	/** Cache of singleton factories: bean name to ObjectFactory. */
	private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

	/** Cache of early singleton objects: bean name to bean instance. */
	private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		// Quick check for existing instance without full singleton lock
		Object singletonObject = this.singletonObjects.get(beanName);
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			singletonObject = this.earlySingletonObjects.get(beanName);
			if (singletonObject == null && allowEarlyReference) {
				synchronized (this.singletonObjects) {
					// Consistent creation of early reference within full singleton lock
					singletonObject = this.singletonObjects.get(beanName);
					if (singletonObject == null) {
						singletonObject = this.earlySingletonObjects.get(beanName);
						if (singletonObject == null) {
							ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
							if (singletonFactory != null) {
								singletonObject = singletonFactory.getObject();
								this.earlySingletonObjects.put(beanName, singletonObject);
								this.singletonFactories.remove(beanName);
							}
						}
					}
				}
			}
		}
		return singletonObject;
	}

一级缓存

singletonObjects: 存储最终成品 bean 对象,保证单例

二级缓存

earlySingletonObjects: 存储提前进行 AOP 的代理对象,以供后续其他 bean 直接注入,避免创建多个代理对象

三级缓存

singletonFactories: 单例对象工厂,内部会根据是否需要 AOP 返回代理对象或原始对象

其他支撑缓存

//org.springframework.beans.factory.support.DefaultSingletonBeanRegistry
/** Names of beans that are currently in creation. */
private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16));
//org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator
private final Map<Object, Object> earlyProxyReferences = new ConcurrentHashMap<>(16);

单例对象初始化流程

以如下代码为例:

A,B 互相依赖,无 AOP

C,D 互相依赖,有 AOP

E 依赖于 C

假设按照定义顺序创建 bean, 实际上创建顺序并不影响流程

@Component
class A{
  @Autowired
  private B b;
}

@Component
class B{
  @Autowired
  private A a;
}

@Component
@Transactional
class C{
  @Autowired
  private D d;
}

@Component
@Transactional
class D{
  @Autowired
  private C c;
}

@Component
class E{
  @Autowired
  private C c;
}

bean 创建一般流程

  1. 智能选择构造器创建原始对象
  2. 依赖注入
    1. 填充属性,有则获取,无则创建
    2. 在填充过程中,如果发现循环依赖,则提前创建半成品 bean (原始对象或代理对象) 并缓存
  3. beanPostProcessors
    1. aop (AbstractAutoProxyCreator):如果已经 aop,则跳过,否则进行 aop 增强生成代理对象
    2. 其他后处理器
  4. initialize

无 AOP 的 bean 循环依赖设计草案

如 AB

A a =new A();
put A into cache1
	B b =new B();
	get A from cache1
	b.a = a;
	beanPostProcessors(...aop...) & skip proxy wrap
	put B into cache2
get B from cache2
a.b = b;
beanPostProcessors(...aop...) & skip proxy wrap
put A into cache2

此时只需要两级缓存:原始对象缓存和成品 bean 缓存

有 AOP 的循环依赖设计草案

如 CDE

C c = new C();
	D d = new D();
	d.c = proxy(c);
	put proxy(c) to cache1
  beanPostProcessors(...aop...) & proxy wrap & put proxy(d) to cache1
  put D to cache2
get D from cache2
c.d = proxy(d)
beanPostProcessors(...aop...)	& already proxy
put C to cache2
E e =new E()
get proxy(c) from cache1
e.c = proxy(c)
beanPostProcessors(...aop...) & skip proxy wrap
put E to cache2

此时需要两级缓存:半成品代理对象缓存和成品 bean 对象缓存

spring 考虑有 AOP 和无 AOP 的综合方案

通过综合上述两种场景及无相互依赖场景:

  • 当无相互依赖时,无论有无 AOP,只需要成品 bean 缓存保证单例
  • 当无 AOP 且有相互依赖时,需要原始对象缓存和成品 bean 缓存
  • 当有 AOP 且有相互依赖时,需要代理对象缓存和成品 bean 缓存

因此,可采用三级缓存统一解决上述所有情况:

成品 bean 缓存:存放最终组装好的 bean

半成品 bean 缓存:存放半成品 bean (原始对象和代理对象)

半成品 bean 对象工厂:生成半成品 bean 的工厂,内部生成逻辑为:如果有 AOP,则返回代理对象,否则为原始对象

三级缓存的必要性

内置解决循环依赖的方案

一级:保证 bean 单例

二级:保证代理对象和原始对象单例

三级:统一半成品 bean 获取方式 (根据规则选择代理对象或原始对象),并打破循环

循环依赖无法解决的情形

使用构造器注入的循环依赖:无法创建中间状态对象来打破循环

可以使用 @Lazy 创建代理对象打破循环,该代理对象不同于普通代理对象,在创建时不需要被代理对象 target (通过 TargetSource 抽象得以实现延迟获取被代理对象,否则无法打破循环依赖),可以在运行时或获取该 bean 时触发创建。

@Lazy 解决循环依赖及其原理

核心代码

private Object buildLazyResolutionProxy(
			final DependencyDescriptor descriptor, final @Nullable String beanName, boolean classOnly) {

		BeanFactory beanFactory = getBeanFactory();
		Assert.state(beanFactory instanceof DefaultListableBeanFactory,
				"BeanFactory needs to be a DefaultListableBeanFactory");
		final DefaultListableBeanFactory dlbf = (DefaultListableBeanFactory) beanFactory;

		TargetSource ts = new TargetSource() {
			@Override
			public Class<?> getTargetClass() {
				return descriptor.getDependencyType();
			}
			@Override
			public boolean isStatic() {
				return false;
			}
			@Override
			public Object getTarget() {
				Set<String> autowiredBeanNames = (beanName != null ? new LinkedHashSet<>(1) : null);
				Object target = dlbf.doResolveDependency(descriptor, beanName, autowiredBeanNames, null);
				if (target == null) {
					Class<?> type = getTargetClass();
					if (Map.class == type) {
						return Collections.emptyMap();
					}
					else if (List.class == type) {
						return Collections.emptyList();
					}
					else if (Set.class == type || Collection.class == type) {
						return Collections.emptySet();
					}
					throw new NoSuchBeanDefinitionException(descriptor.getResolvableType(),
							"Optional dependency not present for lazy injection point");
				}
				if (autowiredBeanNames != null) {
					for (String autowiredBeanName : autowiredBeanNames) {
						if (dlbf.containsBean(autowiredBeanName)) {
							dlbf.registerDependentBean(autowiredBeanName, beanName);
						}
					}
				}
				return target;
			}
			@Override
			public void releaseTarget(Object target) {
			}
		};

		ProxyFactory pf = new ProxyFactory();
		pf.setTargetSource(ts);
		Class<?> dependencyType = descriptor.getDependencyType();
		if (dependencyType.isInterface()) {
			pf.addInterface(dependencyType);
		}
		ClassLoader classLoader = dlbf.getBeanClassLoader();
		return (classOnly ? pf.getProxyClass(classLoader) : pf.getProxy(classLoader));
	}

关键点在于创建代理时使用 setTargetSource 而非 setTarget,可以实现延迟创建被代理对象从而打破循环依赖

实现原理

通过代理对象推迟被代理对象的创建时机以打破循环依赖

Lazy 声明位置和使用位置组合的效果

研究以下代码的输出顺序:

@Service
public class AService {

    private BService bService;

    /**
     * 非Lazy模式注入LazyBean会导致延迟失效,未达到lazy意图的使用方式
     */
    @Autowired
    private CService cService;

    /**
     * Lazy模式注入LazyBean,bean会在使用时初始化,正确使用方式
     */
    @Autowired
    @Lazy
    private DService dService;


    /**
     * lazy模式注入非LazyBean,不会达到使用时初始化的效果但是可以打破循环依赖,将这种bean创建推迟到靠后的阶段
     * Lazy仅标注在使用处会在beanFactory.preInstantiateSingletons()阶段触发bean创建
     * BService本身并不是延迟初始化bean
     *
     * @param bService
     */
    public AService(@Lazy BService bService) {
        System.out.println("init AService");
        this.bService = bService;
    }

    /**
     * 会在运行时执行该方法时创建DService单例bean
     */
    public void runLazy() {
        dService.test();
    }
}

@Service
public class BService {
    private AService aService;

    public BService(AService aService) {
        this.aService = aService;
        System.out.println("init BService");
    }
}

@Service
@Lazy
public class CService {

    public CService() {
        System.out.println("init CService");
    }
}

@Service
@Lazy
public class DService {
    @Autowired
    private AService aService;

    public DService() {
        System.out.println("init DService");
    }

    public void test() {
        System.out.println("run DService test");
    }
}

输出顺序:

init AService
init CService
init BService

在非失效情形下:

  1. Lazy 模式注入非 LazyBean

    会立刻创建代理对象进行注入,而且会延迟 bean 的创建至 preInstantiateSingletons 阶段

  2. Lazy 模式注入 LazyBean

    会立刻创建代理对象进行注入,而且会延迟 bean 的创建至运行时 bean 的方法初次调用时刻

  3. 非 Lazy 模式注入 LazyBean

    会立刻创建 bean,Lazy 失效

失效情形

  1. 非延迟加载的类中注入了延迟加载的类会导致延迟加载提前
  2. 提前获取延迟加载类 bean 实例的其他情形