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 创建一般流程
- 智能选择构造器创建原始对象
- 依赖注入
- 填充属性,有则获取,无则创建
- 在填充过程中,如果发现循环依赖,则提前创建半成品 bean (原始对象或代理对象) 并缓存
- beanPostProcessors
- aop (AbstractAutoProxyCreator):如果已经 aop,则跳过,否则进行 aop 增强生成代理对象
- 其他后处理器
- 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
在非失效情形下:
Lazy 模式注入非 LazyBean
会立刻创建代理对象进行注入,而且会延迟 bean 的创建至 preInstantiateSingletons 阶段
Lazy 模式注入 LazyBean
会立刻创建代理对象进行注入,而且会延迟 bean 的创建至运行时 bean 的方法初次调用时刻
非 Lazy 模式注入 LazyBean
会立刻创建 bean,Lazy 失效
失效情形
- 非延迟加载的类中注入了延迟加载的类会导致延迟加载提前
- 提前获取延迟加载类 bean 实例的其他情形