Spring Bean 的 Scope、生命周期和循环依赖分析
Categories: 编程
版本说明
💡 本文使用的版本为:
- JDK: 17
- Spring Boot: 3.1.7
生命周期
整体流程:
graph TD
BEGIN[Spring 加载 Bean] --> A[1. 实例化 Bean]
A -->B[2. 填充 Bean 属性]
B -->C[3. 初始化 Bean]
C -->D[4. 销毁 Bean]
几点说明:
- ` 实例化 Bean`:此时 Bean 只实例化,并没有进行 @Autowired 属性填充。
- ` 填充 Bean 属性 `:如果 Bean 的属性有 @Autowired 注解,会进行属性填充。
- ` 初始化 Bean`
- ` 销毁 Bean`:如果 Bean 实现了 DisposableBean 接口,会调用 destroy 方法。
分析一下 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean 方法,主体代码如下:
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
// 1:默认调用无参构造实例化 Bean
// 如果是只有带参数的构造方法,构造方法里的参数依赖注入,就是发生在这一步
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// Initialize the bean instance.
Object exposedObject = bean;
try {
// 2:填充属性(DI 依赖注入发生在此步骤)
populateBean(beanName, mbd, instanceWrapper);
// 3:调用初始化方法,完成 bean 的初始化操作(AOP 的第三个入口)
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
// ...
}
// ...
首先通过一个测试用例,了解 Spring Bean 的生命周期。下面定义了一个 LifeBean:
@Component
@Data
@Slf4j
public class LifeBean implements BeanNameAware, BeanClassLoaderAware, InitializingBean, DisposableBean {
private int i;
@PostConstruct
public void init() {
log.info("LifeBean init ...");
}
@Override
public void setBeanName(String s) {
log.info("LifeBean setBeanName {}", s);
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
log.info("LifeBean setBeanClassLoader {}", classLoader);
}
@Override
public void afterPropertiesSet() {
log.info("LifeBean afterPropertiesSet i = {}", i);
}
@Override
public void destroy() {
log.info("LifeBean destroy ...");
}
}
单元测试代码:
@Test
public void testLife() {
LifeBean bean = context.getBean(LifeBean.class);
bean.setI(1);
}
IDE 增加 debug 断点,并加上 Condition:
"lifeBean".equalsIgnoreCase(beanName)

log 输出:
LifeBean setBeanName lifeBean
LifeBean setBeanClassLoader jdk.internal.loader.ClassLoaders$AppClassLoader@55f96302
LifeBean init ...
LifeBean afterPropertiesSet i = 0
LifeBean destroy ...
初始化 Bean 详细流程

invokeAwareMethods
private void invokeAwareMethods(String beanName, Object bean) {
if (bean instanceof Aware) {
// 1. setBeanName
if (bean instanceof BeanNameAware beanNameAware) {
beanNameAware.setBeanName(beanName);
}
// 2. setBeanClassLoader
if (bean instanceof BeanClassLoaderAware beanClassLoaderAware) {
ClassLoader bcl = getBeanClassLoader();
if (bcl != null) {
beanClassLoaderAware.setBeanClassLoader(bcl);
}
}
// 3. setBeanFactory
if (bean instanceof BeanFactoryAware beanFactoryAware) {
beanFactoryAware.setBeanFactory(AbstractAutowireCapableBeanFactory.this);
}
}
}
applyBeanPostProcessorsBeforeInitialization
@Override
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
Object current = processor.postProcessBeforeInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}
invokeInitMethods
protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd)
throws Throwable {
boolean isInitializingBean = (bean instanceof InitializingBean);
// 1. afterPropertiesSet
if (isInitializingBean && (mbd == null || !mbd.hasAnyExternallyManagedInitMethod("afterPropertiesSet"))) {
if (logger.isTraceEnabled()) {
logger.trace("Invoking afterPropertiesSet() on bean with name'" + beanName + "'");
}
((InitializingBean) bean).afterPropertiesSet();
}
// 2. 指定 init-method 方法
if (mbd != null && bean.getClass() != NullBean.class) {
String[] initMethodNames = mbd.getInitMethodNames();
if (initMethodNames != null) {
for (String initMethodName : initMethodNames) {
if (StringUtils.hasLength(initMethodName) &&
!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
!mbd.hasAnyExternallyManagedInitMethod(initMethodName)) {
invokeCustomInitMethod(beanName, bean, mbd, initMethodName);
}
}
}
}
}
applyBeanPostProcessorsAfterInitialization
@Override
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
Object current = processor.postProcessAfterInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}
BeanFactory
BeanFactory 接口文件上的注释如下。里面包含了 bean 的生命周期以及对应的顺序。

Spring 官方文档:Customizing the Nature of a Bean

Scope

通过官方文档可以看到,一共有 6 种类型的 Scope:
singleton:(默认)每个 Spring IoC 容器中仅有一个实例(单例)。prototype:每次注入都会新建一个对象,Spring IoC 容器并不会缓存 prototype 的 bean。request:仅在 web-aware 时生效,每次HTTP请求都会新建一个对象。session:仅在 web-aware 时生效,每次HTTP Session都会新建一个对象。application:仅在 web-aware 时生效,每次ServletContext都会新建一个对象。websocket:仅在 web-aware 时生效,每次WebSocket都会新建一个对象。
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
/**
* Scope identifier for the standard singleton scope: {@value}.
* <p>Note that extended bean factories might support further scopes.
* @see #setScope
* @see ConfigurableBeanFactory#SCOPE_SINGLETON
*/
String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
/**
* Scope identifier for the standard prototype scope: {@value}.
* <p>Note that extended bean factories might support further scopes.
* @see #setScope
* @see ConfigurableBeanFactory#SCOPE_PROTOTYPE
*/
String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;
Scope 使用示例
SingletonBean 是一个 Singleton Scope 的 bean,里面的 Scope 注解不设置也可以,默认是 Singleton 的。
@Component
@Scope
@Data
@Slf4j
public class SingletonBean {
private int i;
@PostConstruct
public void init() {
log.info("SingletonBean init ...");
}
}
ProtoTypeBean 是一个 Prototype Scope 的 bean。
@Component
@Scope(scopeName = SCOPE_PROTOTYPE)
@Data
@Slf4j
public class ProtoTypeBean {
private int i;
@PostConstruct
public void init() {
log.info("ProtoTypeBean init ...");
}
}
测试代码中对于每个 bean,分别从容器中获取 2 次,看 log 输出。
@RunWith(SpringRunner.class)
@Slf4j
@SpringBootTest(classes = Application.class)
public class BeanTest {
@Autowired
private ApplicationContext context;
@Test
public void testScope() {
context.getBean(SingletonBean.class);
context.getBean(SingletonBean.class);
context.getBean(ProtoTypeBean.class);
context.getBean(ProtoTypeBean.class);
}
}
最终输出了 1 次 SingletonBean,2 次 ProtoTypeBean,说明 SingletonBean 是单例的,ProtoTypeBean 是每次注入都会新建一个对象。
SingletonBean init ...
ProtoTypeBean init ...
ProtoTypeBean init ...
源码分析
在 Scope 注解定义上查找引用,发现 Scope 作为实例变量只有 AnnotationScopeMetadataResolver 中。
public class AnnotationScopeMetadataResolver implements ScopeMetadataResolver {
private final ScopedProxyMode defaultProxyMode;
protected Class<? extends Annotation> scopeAnnotationType = Scope.class;
@Override
public ScopeMetadata resolveScopeMetadata(BeanDefinition definition) {
ScopeMetadata metadata = new ScopeMetadata();
if (definition instanceof AnnotatedBeanDefinition) {
AnnotatedBeanDefinition annDef = (AnnotatedBeanDefinition) definition;
AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(
annDef.getMetadata(), this.scopeAnnotationType);
if (attributes != null) {
metadata.setScopeName(attributes.getString("value"));
ScopedProxyMode proxyMode = attributes.getEnum("proxyMode");
if (proxyMode == ScopedProxyMode.DEFAULT) {
proxyMode = this.defaultProxyMode;
}
metadata.setScopedProxyMode(proxyMode);
}
}
return metadata;
}
为了观察 Spring 如何处理 SingletonBean 的 Scope 注解,我们可以在这里加一个断点调试,Condition 是 Objects.equals("yano.spring.bean.SingletonBean", annDef.getBeanClassName())。

接着一步步断点向上调试,最终会调用到 org.springframework.context.annotation.ClassPathBeanDefinitionScanner#doScan。具体代码就不分析了,只要理解 Spring 框架,就能看懂源码。
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
for (String basePackage : basePackages) {
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
if (checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
关于 Spring IoC 容器的详细分析,见 最简 Spring IOC 容器源码分析
循环依赖
💡 Spring Boot 在 2.6.0 版本开始默认不允许出现循环引用,出现循环引用肯定是程序的问题。
如果需要允许循环引用,需要在配置文件中添加
spring.main.allow-circular-references=true。
` 循环依赖 ` 就是循环引用,两个或多个 bean 相互之间持有对方。那么 Spring 是如何解决循环依赖的?
循环依赖的 3 种类型
在 Spring 中循环依赖一共有 3 种情况:
- 构造器循环依赖:无法解决
- setter/field 循环依赖:
只能解决单例作用域的 bean 循环依赖 - prototype 范围的依赖处理:无法解决
构造器循环依赖
构造器循环依赖是无法解决的,因为一个 bean 创建时首先要经过构造器,但是构造器相互依赖,就相当于 Java 中多线程死锁。
@Service
public class CircleBean1 {
private CircleBean2 circleBean2;
public CircleBean1(CircleBean2 circleBean2) {
this.circleBean2 = circleBean2;
}
}
@Service
public class CircleBean2 {
private CircleBean1 circleBean1;
public CircleBean2(CircleBean1 circleBean1) {
this.circleBean1 = circleBean1;
}
}
启动后会报下面的错误:
The dependencies of some of the beans in the application context form a cycle:
┌─────┐
| circleBean1
↑ ↓
| circleBean2
└─────┘
setter/field 循环依赖
setter 注入循环依赖:
@Service
public class CircleBean1 {
private CircleBean2 circleBean2;
public CircleBean2 getCircleBean2() {
return circleBean2;
}
@Autowired
public void setCircleBean2(CircleBean2 circleBean2) {
this.circleBean2 = circleBean2;
}
}
@Service
public class CircleBean2 {
private CircleBean1 circleBean1;
public CircleBean1 getCircleBean1() {
return circleBean1;
}
@Autowired
public void setCircleBean1(CircleBean1 circleBean1) {
this.circleBean1 = circleBean1;
}
}
field 注入循环依赖:
@Service
public class CircleBean1 {
@Autowired
private CircleBean2 circleBean2;
}
@Service
public class CircleBean2 {
@Autowired
private CircleBean1 circleBean1;
}
能够正常启动。
prototype 范围的依赖处理
通过 @Scope(scopeName = SCOPE_PROTOTYPE) 注解,可以将 bean 的作用域设置为 prototype,每次注入都会新建一个对象,Spring IoC 容器并不会缓存 prototype 的 bean。
@Service
@Scope(scopeName = SCOPE_PROTOTYPE)
public class CircleBean1 {
@Autowired
private CircleBean2 circleBean2;
}
@Service
@Scope(scopeName = SCOPE_PROTOTYPE)
public class CircleBean2 {
@Autowired
private CircleBean1 circleBean1;
}
启动后会报下面的错误:
The dependencies of some of the beans in the application context form a cycle:
┌─────┐
| circleBean1
↑ ↓
| circleBean2
└─────┘
Spring 如何解决循环依赖?
三级缓存
参考链接:一文详解Spring Bean循环依赖
// 一级缓存:缓存的是已经实例化、属性注入、初始化后的 Bean 对象。
/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
// 二级缓存:缓存的是实例化后,但未属性注入、初始化的 Bean 对象(用于提前暴露 Bean)。
/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
// 三级缓存:缓存的是一个 ObjectFactory,主要作用是生成原始对象进行 AOP 操作后的代理对象
/** Cache of singleton factories: bean name to ObjectFactory. */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
- ` 一级缓存 singletonObjects`: 主要存放的是已经完成实例化、属性填充和初始化所有步骤的单例 Bean 实例,这样的 Bean 能够直接提供给用户使用,我们称之为终态 Bean 或叫成熟 Bean。
- ` 二级缓存 earlySingletonObjects
: 主要存放的已经完成初始化但属性还没自动赋值 ` 的 Bean,这些 Bean 还不能提供用户使用,只是用于提前暴露的 Bean 实例,我们把这样的 Bean 称之为临时 Bean 或早期的 Bean(半成品 Bean) - ` 三级缓存 singletonFactories`: 存放的是 ObjectFactory 的匿名内部类实例,调用 ObjectFactory.getObject() 最终会调用 getEarlyBeanReference 方法,该方法可以获取提前暴露的单例 bean 引用。
💡假设现在有这样的场景 AService 依赖 BService,BService 依赖 AService。
- 一开始加载 AService Bean 首先依次从一二三级缓存中查找是否存在 beanName=AService 的对象。
- AService 还没创建缓存,所以走到创建 AService 的逻辑,调用方法 getSingleton(String beanName,ObjectFactory objectFactory) 方法,第二个参数传入一个 ObjectFactory 接口的匿名内部类实例。
- AService 实例化后调用 addSingletonFactory(String beanName, ObjectFactory singletonFactory) 方法将以 Key 为 AService,value 是 ObjectFactory 类型一个匿名内部类对象放入三级缓存中,在后续使用 AService 时会依次在一二三级缓存中查找,最终三级缓存中查到这个匿名内部类对象,从而触发匿名内部类中 getEarlyBeanReference() 方法回调。
为什么不直接将 AService 实例直接放入三级缓存呢?因为 AOP 增强逻辑在创建 Bean 第三步:调用初始化方法之后进行的,AOP 增强后生成的新代理类 AServiceProxy 实例对象。假如此时直接把 AService 实例直接放入三级缓存,那么在对 BService Bean 依赖的 aService 属性赋值的就是 AService 实例,而不是增强后的 AServiceProxy 实例对象。
- 在以 Key 为 AService,value 为 ObjectFactory 类型一个匿名内部类对象放入三级缓存后,继续对 AService 进行属性填充(依赖注入),这时发现 AService 依赖 BService。
- 又依次从一二三级缓存中查询BService Bean,没找到,于是又按照上述的流程实例化BService,将以Key为BService,value是ObjectFactory类型一个匿名内部类对象放入三级缓存中,继续对BService进行属性填充(依赖注入),这时发现BService又依赖AService。于是依次在一二三级缓存中查找AService。
- 最终三级缓存中查到之前放入的以Key为AService,value为ObjectFactory类型一个匿名内部类对象,从而触发匿名内部类getEarlyBeanReference()方法回调。getEarlyBeanReference()方法决定返回AService实例到底是AService实例本身还是被AOP增强后的AServiceProxy实例对象。如果没AOP切面对AService进行拦截,这时返回的将是AService实例本身。接着将半成品AService Bean放入二级缓存并将Key为AService从三级缓存中删除,这样实现了提前将AService Bean曝光给BService完成属性依赖注入。继续走BService后续初始化逻辑,最后生产了成熟的BService Bean实例。
- AService 也成功获取到 BService 实例,完成后续的初始化工作,解决了循环依赖问题。

源码分析
并没有深入分析,待后面补充
setter 注入造成的依赖是通过 Spring 容器提前暴露刚完成构造器注入但未完成其他步骤(如 setter 注入)的 bean 来完成的,而且只能解决单例作用域的 bean 循环依赖。通过提前暴露一个单例工厂方法,从而使其他 bean 能引用到该 bean,org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean) 代码如下:
/**
* Return the (raw) singleton object registered under the given name.
* <p>Checks already instantiated singletons and also allows for an early
* reference to a currently created singleton (resolving a circular reference).
* @param beanName the name of the bean to look for
* @param allowEarlyReference whether early references should be created or not
* @return the registered singleton object, or {@code null} if none found
*/
@Nullable
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;
}
Spring Boot 2.6.0 开启循环依赖
💡 并不建议这样做,程序最好就没有循环依赖。
项目启动的时候添加参数 spring.main.allow-circular-references=true,或者在代码中添加:
new SpringApplicationBuilder(Application.class).allowCircularReferences(true).run(args);
GitHub LeetCode 项目
项目 GitHub LeetCode 全解,欢迎大家 star、fork、merge,共同打造最全 LeetCode 题解!
Java 编程思想 - 最全思维导图 - GitHub 下载链接,需要的小伙伴可以自取~!!!