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实例的其他情形