文章目录
- 内容总结
- 循环依赖
- 循环依赖的解决方案设计
- 模拟循环依赖
- 解决循环依赖的关键 缓存
- 仍然存在问题 代理
- 解决代理问题的关键 提前AOP
- 仍然存在问题 AOP全部提前与Bean创建流程设计严重不符
- 解决违背设计问题的关键 尽量提前而非全部提前
- 仍然存在问题 多重循环
内容总结
循环依赖
循环依赖只存在于 Spring 中, 是因为 Spring 创建 Bean 的流程中, 依赖注入阶段, 会先从单例池中找, 没有再从定义池中找, 针对定义池中找到的候选项会通过 getBean 创建其单例并缓存到单例池, 此机制导致了存在循环依赖的问题. 如 A 依赖 B, B 依赖 A, getBean A 的时候, A 还没有入单例池. 发现依赖 B, 又去 getBean B, 又发现依赖 A, 在单例池中没有找到 A, 在定义池中找到了 A, 所以又去 getBean A, …, Spring 通过三级缓存的方式, 解决了这个问题
Spring 完成依赖注入的核心方法是 BeanFactory.resolveDependency, 做先类型后名称的筛选, 取到符合条件的候选项, 候选项如果来自单例池, 则是一个 Bean, 如果来自定义池, 则就是一个 Class 对象, 在这里会对其的 BeanName 做 getBean 操作, 生成 Bean 并缓存到单例池. 这个流程可以简化为, Spring 创建 Bean 的流程中的依赖注入阶段, 会对其依赖做 getBean 操作
其中 findAutowireCandidates 步骤会从单例池和定义池中找符合类型条件的 BeanName, 如果当前依赖的 Bean 还没有被生成单例并缓存, 则在单例池中找不到, 就会去定义池中找, 定义池中通过遍历 beanDefinitionNames 的方式拿到每一个 BeanDefinition, 第一步就是判断是否为 FactoryBean, 内部就会判断 beanClass 字段中的全限定类名是否被加载成为 Class 对象, 没有就会执行类加载, 并缓存结果
循环依赖的解决方案设计
模拟循环依赖
假设有AB两个类, A依赖B且B依赖A, 遍历创建单例Bean时先A再B, A依赖B所以去创建B, B依赖A所以去创建A, 陷入循环
@Component
public class A {@Autowiredprivate B b;
}
@Component
public class B {@Autowiredprivate A a;
}
解决循环依赖的关键 缓存
需要提前暴露A, 让B找的到A, 即增加一个缓存, 即可打破循环依赖. 该缓存就是解决循环依赖的关键
仍然存在问题 代理
假设被aop切到, 最终会生成代理对象, 则现有逻辑会导致, 加入单例池的是代理对象, 被注入的是原始对象, 不符合单例设计
解决代理问题的关键 提前AOP
只要在加入缓存前, 提前执行aop, 就能保证单例. 不管是否需要aop, 最终被注入的和加入单例池的一定是同一个对象
仍然存在问题 AOP全部提前与Bean创建流程设计严重不符
在Spring关于Bean创建流程的设计中, AOP是发生在初始化(后)阶段的, 现在把AOP全部提前到实例化后, 严重违背设计
解决违背设计问题的关键 尽量提前而非全部提前
仍然存在问题 多重循环
假设a依赖bc, b依赖a, c依赖a, 则按照现有逻辑, b和c注入的a不是同一个