一、基础概念入门:什么是Spring Bean?它在容器中的核心地位是什么?

1.1 Spring Bean的本质与容器关系

:我们常说的Spring Bean到底是什么?它和普通Java对象有什么区别?
:Spring Bean本质上是由Spring IoC容器管理的Java对象,但其生命周期完全由容器掌控,这是它与普通Java对象的核心区别。普通Java对象由开发者通过new关键字手动创建,而Spring Bean的创建、初始化、依赖注入、销毁等过程均由容器自动完成。
从代码角度看,一个简单的Bean定义如下:
// 普通Java对象 public class User { private String name; // getter/setter } // Spring Bean(通过@Component注解标识) @Component public class UserService { @Autowired private UserRepository repository; // 业务方法 }
Java
核心差异体现在管理方式上:
  • 普通对象:User user = new User();(开发者全权负责)
  • Spring Bean:由容器通过反射创建,无需手动实例化
:Spring容器是如何识别哪些类需要被管理为Bean的?
:Spring通过Bean定义(BeanDefinition) 来描述Bean的元信息,包括类名、作用域、构造函数参数、属性等。容器启动时会扫描特定路径下的类(如标注@Component@Service等注解的类),将其转换为BeanDefinition并注册到BeanDefinitionRegistry中。
关键处理类:
  • ClassPathBeanDefinitionScanner:负责扫描类路径下的Bean
  • BeanDefinitionReader:解析XML、注解等形式的Bean定义
  • DefaultListableBeanFactory:实现BeanDefinitionRegistry,存储所有Bean定义

1.2 Spring IoC容器的核心接口体系

:Spring IoC容器的核心接口有哪些?它们之间的关系是什么?
:Spring IoC容器的核心接口体系以BeanFactory为基础,逐步扩展功能:
graph TD A[BeanFactory] --> B[HierarchicalBeanFactory] B --> C[ConfigurableBeanFactory] A --> D[ListableBeanFactory] D --> E[ConfigurableListableBeanFactory] C --> E E --> F[DefaultListableBeanFactory] G[ApplicationContext] --> D G --> H[ResourcePatternResolver] G --> I[MessageSource] G --> J[ApplicationEventPublisher] G --> K[EnvironmentCapable] F --> L[XmlBeanFactory]

BeanFactory

HierarchicalBeanFactory

ConfigurableBeanFactory

ListableBeanFactory

ConfigurableListableBeanFactory

DefaultListableBeanFactory

ApplicationContext

ResourcePatternResolver

MessageSource

ApplicationEventPublisher

EnvironmentCapable

XmlBeanFactory

Mermaid
  • BeanFactory:最基础的容器接口,定义了获取Bean、判断Bean是否存在等方法
  • ApplicationContext:继承BeanFactory并扩展了更多功能(如国际化、事件发布、资源加载等),是实际开发中常用的容器接口
  • DefaultListableBeanFactoryBeanFactory的默认实现,是容器的核心,负责BeanDefinition的注册和Bean的创建
关键方法示例BeanFactory接口):
public interface BeanFactory { // 根据名称获取Bean Object getBean(String name) throws BeansException; // 根据类型获取Bean <T> T getBean(Class<T> requiredType) throws BeansException; // 判断Bean是否存在 boolean containsBean(String name); // 判断Bean是否为单例 boolean isSingleton(String name) throws NoSuchBeanDefinitionException; // 获取Bean的类型 Class<?> getType(String name) throws NoSuchBeanDefinitionException; }
Java

二、Bean加载机制:Spring如何从类到可用Bean?

2.1 扫描与注册:BeanDefinition的诞生过程

:Spring容器启动时,是如何发现并注册Bean的?整个扫描过程的关键步骤是什么?
:Spring的Bean加载首先从BeanDefinition的扫描与注册开始,这一过程可分为四个阶段:资源定位、资源解析、BeanDefinition注册、BeanFactory初始化。

2.1.1 资源定位(Resource Location)

Spring通过ResourcePatternResolver定位需要扫描的类路径资源,默认实现为PathMatchingResourcePatternResolver
关键代码(ClassPathBeanDefinitionScanner):
public int scan(String... basePackages) { int beanCountAtScanStart = this.registry.getBeanDefinitionCount(); // 实际执行扫描的方法 doScan(basePackages); // 注册默认的BeanPostProcessor等 if (this.includeAnnotationConfig) { AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry); } return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart); } protected Set<BeanDefinitionHolder> doScan(String... basePackages) { 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()); // 生成Bean名称 String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry); // 处理通用注解(如@Lazy、@Primary等) if (candidate instanceof AbstractBeanDefinition) { postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName); } // 处理@Conditional注解 if (candidate instanceof AnnotatedBeanDefinition) { AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate); } // 检查Bean是否已存在,处理冲突 if (checkCandidate(beanName, candidate)) { BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName); definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); beanDefinitions.add(definitionHolder); // 注册BeanDefinition registerBeanDefinition(definitionHolder, this.registry); } } } return beanDefinitions; }
Java

2.1.2 资源解析(Resource Parsing)

扫描到的资源(.class文件)会被解析为BeanDefinition,不同类型的Bean(如注解标注的Bean、XML配置的Bean)有不同的解析器。
对于注解Bean,AnnotatedBeanDefinitionReader会解析类上的注解(如@Component@Service等),提取Bean的元信息。

2.1.3 BeanDefinition注册(Registration)

解析后的BeanDefinition会被注册到BeanDefinitionRegistry(通常是DefaultListableBeanFactory)中,存储在一个Map<String, BeanDefinition>结构中。
// DefaultListableBeanFactory中的注册逻辑 private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256); @Override public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { // 校验BeanDefinition Assert.hasText(beanName, "Bean name must not be empty"); Assert.notNull(beanDefinition, "BeanDefinition must not be null"); if (beanDefinition instanceof AbstractBeanDefinition) { try { ((AbstractBeanDefinition) beanDefinition).validate(); } catch (BeanDefinitionValidationException ex) { throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Validation of bean definition failed", ex); } } // 检查是否已存在同名BeanDefinition BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName); if (existingDefinition != null) { // 处理冲突(根据配置决定是否覆盖) if (!isAllowBeanDefinitionOverriding()) { throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition); } this.beanDefinitionMap.put(beanName, beanDefinition); } else { // 检查是否已创建过Bean实例 if (hasBeanCreationStarted()) { // 同步操作,避免并发问题 synchronized (this.beanDefinitionMap) { this.beanDefinitionMap.put(beanName, beanDefinition); List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1); updatedDefinitions.addAll(this.beanDefinitionNames); updatedDefinitions.add(beanName); this.beanDefinitionNames = updatedDefinitions; removeManualSingletonName(beanName); } } else { // 未开始创建Bean时直接注册 this.beanDefinitionMap.put(beanName, beanDefinition); this.beanDefinitionNames.add(beanName); removeManualSingletonName(beanName); } this.frozenBeanDefinitionNames = null; } // 重置缓存 if (existingDefinition != null || containsSingleton(beanName)) { resetBeanDefinition(beanName); } }
Java

2.1.4 扫描注册阶段时序图

sequenceDiagram participant 容器启动类 as 容器启动类(如SpringApplication) participant ApplicationContext as ApplicationContext participant Scanner as ClassPathBeanDefinitionScanner participant Reader as AnnotatedBeanDefinitionReader participant Registry as BeanDefinitionRegistry(DefaultListableBeanFactory) participant Resource as 资源文件(.class) 容器启动类->>ApplicationContext: 初始化容器 ApplicationContext->>Scanner: 执行扫描(指定basePackages) Scanner->>Resource: 定位类路径资源 Resource-->>Scanner: 返回.class资源列表 Scanner->>Reader: 解析资源为BeanDefinition Reader-->>Scanner: 返回BeanDefinition Scanner->>Registry: 注册BeanDefinition Registry-->>Scanner: 注册成功 Scanner-->>ApplicationContext: 扫描完成 ApplicationContext-->>容器启动类: 容器初始化完成
资源文件(.class)BeanDefinitionRegistry(DefaultListableBeanFactory)AnnotatedBeanDefinitionReaderClassPathBeanDefinitionScannerApplicationContext容器启动类(如SpringApplication)资源文件(.class)BeanDefinitionRegistry(DefaultListableBeanFactory)AnnotatedBeanDefinitionReaderClassPathBeanDefinitionScannerApplicationContext容器启动类(如SpringApplication)初始化容器执行扫描(指定basePackages)定位类路径资源返回.class资源列表解析资源为BeanDefinition返回BeanDefinition注册BeanDefinition注册成功扫描完成容器初始化完成
Mermaid

2.2 Bean的实例化与依赖注入:从定义到可用对象

:BeanDefinition注册完成后,Spring是如何将其转换为实际的Bean对象的?依赖注入是在哪个阶段完成的?
:Bean的实例化与依赖注入是容器的核心功能,主要通过AbstractAutowireCapableBeanFactory实现,整个过程可分为以下关键步骤:

2.2.1 实例化前准备(Pre-instantiation)

容器在实例化Bean前,会先检查是否需要提前实例化(如单例Bean的预实例化),并处理FactoryBean等特殊Bean。
// AbstractBeanFactory中获取Bean的入口方法 @Override public Object getBean(String name) throws BeansException { return doGetBean(name, null, null, false); } protected <T> T doGetBean( String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException { // 解析Bean名称(处理别名等) String beanName = transformedBeanName(name); Object beanInstance; // 检查是否已有缓存的单例Bean Object sharedInstance = getSingleton(beanName); if (sharedInstance != null && args == null) { // 处理FactoryBean beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null); } else { // 处理原型Bean的循环依赖(原型Bean不支持循环依赖) if (isPrototypeCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } // 检查父容器 BeanFactory parentBeanFactory = getParentBeanFactory(); if (parentBeanFactory != null && !containsBeanDefinition(beanName)) { String nameToLookup = originalBeanName(name); if (parentBeanFactory instanceof AbstractBeanFactory) { return ((AbstractBeanFactory) parentBeanFactory).doGetBean( nameToLookup, requiredType, args, typeCheckOnly); } else if (args != null) { return (T) parentBeanFactory.getBean(nameToLookup, args); } else if (requiredType != null) { return parentBeanFactory.getBean(nameToLookup, requiredType); } else { return (T) parentBeanFactory.getBean(nameToLookup); } } // 标记Bean为正在创建 if (!typeCheckOnly) { markBeanAsCreated(beanName); } try { // 获取合并后的BeanDefinition RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); checkMergedBeanDefinition(mbd, beanName, args); // 实例化依赖的Bean(依赖的Bean会先于当前Bean实例化) String[] dependsOn = mbd.getDependsOn(); if (dependsOn != null) { for (String dep : dependsOn) { if (isDependent(beanName, dep)) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'"); } registerDependentBean(dep, beanName); try { getBean(dep); } catch (NoSuchBeanDefinitionException ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "'" + beanName + "' depends on missing bean '" + dep + "'", ex); } } } // 实例化Bean if (mbd.isSingleton()) { sharedInstance = getSingleton(beanName, () -> { try { return createBean(beanName, mbd, args); } catch (BeansException ex) { destroySingleton(beanName); throw ex; } }); beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } else if (mbd.isPrototype()) { // 原型Bean每次获取都会创建新实例 Object prototypeInstance = null; try { beforePrototypeCreation(beanName); prototypeInstance = createBean(beanName, mbd, args); } finally { afterPrototypeCreation(beanName); } beanInstance = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd); } else { // 其他作用域(如request、session等) String scopeName = mbd.getScope(); final Scope scope = this.scopes.get(scopeName); if (scope == null) { throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'"); } try { Object scopedInstance = scope.get(beanName, () -> { beforePrototypeCreation(beanName); try { return createBean(beanName, mbd, args); } finally { afterPrototypeCreation(beanName); } }); beanInstance = getObjectForBeanInstance(scopedInstance, name, beanName, mbd); } catch (IllegalStateException ex) { throw new BeanCreationException(beanName, "Scope '" + scopeName + "' is not active for the current thread; consider " + "defining a scoped proxy for this bean if you intend to refer to it from a singleton", ex); } } } catch (BeansException ex) { cleanupAfterBeanCreationFailure(beanName); throw ex; } } // 检查类型是否匹配 if (requiredType != null && !requiredType.isInstance(beanInstance)) { try { T convertedBean = getTypeConverter().convertIfNecessary(beanInstance, requiredType); if (convertedBean == null) { throw new BeanNotOfRequiredTypeException(name, requiredType, beanInstance.getClass()); } return convertedBean; } catch (TypeMismatchException ex) { throw new BeanNotOfRequiredTypeException(name, requiredType, beanInstance.getClass()); } } return (T) beanInstance; }
Java

2.2.2 实例化(Instantiation)

通过createBean方法创建Bean实例,核心逻辑在AbstractAutowireCapableBeanFactorydoCreateBean方法中:
  1. 创建Bean实例:通过构造函数反射创建对象
      • 处理构造函数注入(@Autowired标注的构造函数)
      • 选择合适的构造函数(根据参数匹配)
  1. 处理循环依赖:对于单例Bean,通过三级缓存解决循环依赖
      • 一级缓存:存储已初始化完成的Bean
      • 二级缓存:存储已实例化但未初始化完成的Bean
      • 三级缓存:存储Bean的工厂对象,用于提前暴露Bean引用
// AbstractAutowireCapableBeanFactory的doCreateBean方法 protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { // 实例化Bean BeanWrapper instanceWrapper = null; if (mbd.isSingleton()) { instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); } if (instanceWrapper == null) { instanceWrapper = createBeanInstance(beanName, mbd, args); } Object bean = instanceWrapper.getWrappedInstance(); Class<?> beanType = instanceWrapper.getWrappedClass(); if (beanType != NullBean.class) { mbd.resolvedTargetType = beanType; } // 处理合并后的BeanDefinition synchronized (mbd.postProcessingLock) { if (!mbd.postProcessed) { try { applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); } catch (Throwable ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Post-processing of merged bean definition failed", ex); } mbd.postProcessed = true; } } // 提前暴露Bean引用,解决循环依赖 boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { if (logger.isTraceEnabled()) { logger.trace("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references"); } addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); } // 初始化Bean Object exposedObject = bean; try { // 依赖注入(属性注入) populateBean(beanName, mbd, instanceWrapper); exposedObject = initializeBean(beanName, exposedObject, mbd); } catch (Throwable ex) { if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) { throw (BeanCreationException) ex; } else { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex); } } // 处理循环依赖检查 if (earlySingletonExposure) { Object earlySingletonReference = getSingleton(beanName, false); if (earlySingletonReference != null) { if (exposedObject == bean) { exposedObject = earlySingletonReference; } else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) { String[] dependentBeans = getDependentBeans(beanName); Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length); for (String dependentBean : dependentBeans) { if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) { actualDependentBeans.add(dependentBean); } } if (!actualDependentBeans.isEmpty()) { throw new BeanCurrentlyInCreationException(beanName, "Bean with name '" + beanName + "' has been injected into other beans [" + StringUtils.collectionToCommaDelimitedString(actualDependentBeans) + "] in its raw version as part of a circular reference, but has eventually been " + "wrapped. This means that said other beans do not use the final version of the " + "bean. This is often the result of over-eager type matching - consider using " + "'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example."); } } } } // 注册Bean的销毁回调 try { registerDisposableBeanIfNecessary(beanName, bean, mbd); } catch (BeanDefinitionValidationException ex) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex); } return exposedObject; }
Java

2.2.3 初始化(Initialization)

Bean实例化后,会进行初始化处理,包括:
  1. 属性注入:通过反射设置Bean的属性(字段注入和setter注入)
      • 处理@Autowired@Value等注解
      • 自动装配(根据类型或名称匹配依赖)
  1. 初始化方法调用
      • 实现InitializingBean接口的afterPropertiesSet方法
      • 自定义初始化方法(通过@PostConstruct注解或XML配置的init-method
  1. BeanPostProcessor处理
      • postProcessBeforeInitialization:初始化前调用
      • postProcessAfterInitialization:初始化后调用(可用于AOP代理创建)

2.2.4 实例化与初始化阶段时序图

sequenceDiagram participant 容器 as 容器(AbstractAutowireCapableBeanFactory) participant 定义 as BeanDefinition participant 实例 as Bean实例 participant 依赖 as 依赖Bean participant 后置处理器 as BeanPostProcessor 容器->>容器: 检查缓存,无则准备实例化 容器->>定义: 获取BeanDefinition 定义-->>容器: 返回BeanDefinition 容器->>容器: 选择构造函数 容器->>实例: 反射创建实例(无参/有参构造) 实例-->>容器: 返回实例(未初始化) 容器->>容器: 提前暴露实例(解决循环依赖) 容器->>依赖: 实例化并注入依赖Bean 依赖-->>容器: 依赖注入完成 容器->>实例: 设置属性(依赖注入) 容器->>后置处理器: 调用postProcessBeforeInitialization 后置处理器-->>容器: 处理后的实例 容器->>实例: 调用InitializingBean.afterPropertiesSet() 容器->>实例: 调用自定义初始化方法(@PostConstruct) 容器->>后置处理器: 调用postProcessAfterInitialization(可能创建代理) 后置处理器-->>容器: 最终Bean实例(可能是代理) 容器->>容器: 存入缓存(单例) 容器-->>调用者: 返回可用Bean实例
调用者BeanPostProcessor依赖BeanBean实例BeanDefinition容器(AbstractAutowireCapableBeanFactory)调用者BeanPostProcessor依赖BeanBean实例BeanDefinition容器(AbstractAutowireCapableBeanFactory)检查缓存,无则准备实例化获取BeanDefinition返回BeanDefinition选择构造函数反射创建实例(无参/有参构造)返回实例(未初始化)提前暴露实例(解决循环依赖)实例化并注入依赖Bean依赖注入完成设置属性(依赖注入)调用postProcessBeforeInitialization处理后的实例调用InitializingBean.afterPropertiesSet()调用自定义初始化方法(@PostConstruct)调用postProcessAfterInitialization(可能创建代理)最终Bean实例(可能是代理)存入缓存(单例)返回可用Bean实例
Mermaid

2.3 特殊Bean的处理:FactoryBean与Aware接口

:FactoryBean和普通Bean有什么区别?Spring是如何处理实现了Aware接口的Bean的?
:FactoryBean和Aware接口是Spring中两种特殊的Bean处理机制,用于满足复杂场景的需求。

2.3.1 FactoryBean:工厂Bean

FactoryBean是一种特殊的Bean,它本身是一个工厂,用于创建其他Bean。当容器获取FactoryBean类型的Bean时,默认返回的是其生产的Bean(通过getObject方法),而非FactoryBean本身。
public interface FactoryBean<T> { // 获取生产的Bean @Nullable T getObject() throws Exception; // 获取生产的Bean的类型 @Nullable Class<?> getObjectType(); // 是否为单例 default boolean isSingleton() { return true; } }
Java
使用场景:复杂对象的创建(如数据源、MyBatis的SqlSessionFactory等)
容器处理逻辑
// AbstractBeanFactory的getObjectForBeanInstance方法 protected Object getObjectForBeanInstance( Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) { // 如果不是FactoryBean,直接返回 if (!(beanInstance instanceof FactoryBean)) { return beanInstance; } Object object = null; if (mbd != null) { mbd.isFactoryBean = true; } else { // 从缓存中获取FactoryBean生产的Bean object = getCachedObjectForFactoryBean(beanName); } if (object == null) { FactoryBean<?> factory = (FactoryBean<?>) beanInstance; // 检查BeanDefinition是否存在 if (mbd == null && containsBeanDefinition(beanName)) { mbd = getMergedLocalBeanDefinition(beanName); } boolean synthetic = (mbd != null && mbd.isSynthetic()); // 调用FactoryBean的getObject方法获取Bean object = getObjectFromFactoryBean(factory, beanName, !synthetic); } return object; }
Java

2.3.2 Aware接口:感知容器信息

Aware接口用于让Bean获取容器的相关信息(如Bean名称、类加载器等),常用的Aware接口包括:
  • BeanNameAware:获取Bean在容器中的名称
  • BeanClassLoaderAware:获取类加载器
  • BeanFactoryAware:获取BeanFactory
  • ApplicationContextAware:获取ApplicationContext
处理时机:在属性注入后、初始化方法调用前,由ApplicationContextAwareProcessor处理。
// ApplicationContextAwareProcessor的postProcessBeforeInitialization方法 @Override @Nullable public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (!(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware || bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware || bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)) { return bean; } AccessControlContext acc = null; if (System.getSecurityManager() != null) { acc = this.applicationContext.getBeanFactory().getAccessControlContext(); } if (acc != null) { AccessController.doPrivileged((PrivilegedAction<Object>) () -> { invokeAwareInterfaces(bean); return null; }, acc); } else { invokeAwareInterfaces(bean); } return bean; } private void invokeAwareInterfaces(Object bean) { if (bean instanceof EnvironmentAware) { ((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment()); } if (bean instanceof EmbeddedValueResolverAware) { ((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver); } if (bean instanceof ResourceLoaderAware) { ((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext); } if (bean instanceof ApplicationEventPublisherAware) { ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext); } if (bean instanceof MessageSourceAware) { ((MessageSourceAware) bean).setMessageSource(this.applicationContext); } if (bean instanceof ApplicationContextAware) { ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext); } }
Java
 

三、Bean的生命周期:从创建到销毁的完整旅程

:Spring Bean的完整生命周期包含哪些阶段?每个阶段有哪些关键操作和回调方法?
:Spring Bean的生命周期是容器管理Bean的全过程,从BeanDefinition的加载到Bean的销毁,可分为以下11个关键阶段:

3.1 生命周期总览与关键阶段解析

3.1.1 生命周期阶段划分

  1. BeanDefinition加载与注册:容器扫描并解析类,将其注册为BeanDefinition
  1. 实例化前准备:检查Bean的作用域、合并BeanDefinition等
  1. 实例化(Instantiation):通过构造函数反射创建Bean实例
  1. 属性注入前:处理Aware接口,让Bean感知容器信息
  1. 属性注入(Populate):设置Bean的属性,完成依赖注入
  1. 初始化前(PostProcessBeforeInitialization):BeanPostProcessor的前置处理
  1. 初始化(Initialization)
      • 调用InitializingBean.afterPropertiesSet()
      • 调用自定义初始化方法(@PostConstructinit-method
  1. 初始化后(PostProcessAfterInitialization):BeanPostProcessor的后置处理(如AOP代理)
  1. Bean就绪:Bean可被容器使用
  1. 销毁前(Pre-destruction)
      • 调用DisposableBean.destroy()
      • 调用自定义销毁方法(@PreDestroydestroy-method
  1. 销毁(Destruction):Bean被销毁,资源释放

3.1.2 生命周期架构图

notion image

3.2 各阶段详细解析与源码示例

3.2.1 实例化阶段(Instantiation)

核心操作:通过反射调用构造函数创建Bean实例,关键类为InstantiationStrategy,默认实现为CglibSubclassingInstantiationStrategy(支持构造函数注入)。
// SimpleInstantiationStrategy的instantiate方法(简化版) public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner, @Nullable Constructor<?> ctor, Object... args) { // 如果有指定的构造函数,使用该构造函数创建实例 if (ctor != null) { return BeanUtils.instantiateClass(ctor, args); } // 否则使用默认构造函数 for (Constructor<?> constructor : bd.getBeanClass().getDeclaredConstructors()) { if (constructor.getParameterCount() == 0) { return BeanUtils.instantiateClass(constructor); } } throw new BeanCreationException("No default constructor found"); }
Java

3.2.2 属性注入阶段(Populate)

核心操作:为Bean的字段或setter方法设置值,处理@Autowired@Value等注解,关键类为AutowiredAnnotationBeanPostProcessor
// AutowiredAnnotationBeanPostProcessor的postProcessProperties方法 @Override public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) { InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs); try { metadata.inject(bean, beanName, pvs); } catch (BeanCreationException ex) { throw ex; } catch (Throwable ex) { throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex); } return pvs; }
Java

3.2.3 初始化阶段(Initialization)

关键代码AbstractAutowireCapableBeanFactoryinitializeBean方法):
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) { if (System.getSecurityManager() != null) { AccessController.doPrivileged((PrivilegedAction<Object>) () -> { invokeAwareMethods(beanName, bean); return null; }, getAccessControlContext()); } else { // 处理Aware接口 invokeAwareMethods(beanName, bean); } Object wrappedBean = bean; if (mbd == null || !mbd.isSynthetic()) { // 初始化前处理 wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); } try { // 调用初始化方法 invokeInitMethods(beanName, wrappedBean, mbd); } catch (Throwable ex) { throw new BeanCreationException( (mbd != null ? mbd.getResourceDescription() : null), beanName, "Invocation of init method failed", ex); } if (mbd == null || !mbd.isSynthetic()) { // 初始化后处理 wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); } return wrappedBean; } // 调用初始化方法 protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd) throws Throwable { // 检查是否实现InitializingBean接口 boolean isInitializingBean = (bean instanceof InitializingBean); if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) { if (logger.isTraceEnabled()) { logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'"); } if (System.getSecurityManager() != null) { try { AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> { ((InitializingBean) bean).afterPropertiesSet(); return null; }, getAccessControlContext()); } catch (PrivilegedActionException pae) { throw pae.getException(); } } else { // 调用afterPropertiesSet方法 ((InitializingBean) bean).afterPropertiesSet(); } } // 调用自定义初始化方法 if (mbd != null && bean.getClass() != NullBean.class) { String initMethodName = mbd.getInitMethodName(); if (StringUtils.hasLength(initMethodName) && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) && !mbd.isExternallyManagedInitMethod(initMethodName)) { invokeCustomInitMethod(beanName, bean, mbd); } } }
Java

3.2.4 销毁阶段(Destruction)

核心操作:当容器关闭时,销毁单例Bean,释放资源,关键类为DisposableBeanAdapter
// DisposableBeanAdapter的destroy方法(简化版) public void destroy() { if (this.invokeDisposableBean) { try { if (logger.isTraceEnabled()) { logger.trace("Invoking destroy() on bean with name '" + this.beanName + "'"); } // 调用DisposableBean的destroy方法 ((DisposableBean) this.bean).destroy(); } catch (Throwable ex) { logger.error("Destroy method on bean with name '" + this.beanName + "' threw an exception", ex); } } // 调用自定义销毁方法 if (this.destroyMethod != null) { invokeCustomDestroyMethod(this.destroyMethod); } else if (this.destroyMethodName != null) { Method methodToCall = determineDestroyMethod(this.destroyMethodName); if (methodToCall != null) { invokeCustomDestroyMethod(methodToCall); } } // 销毁依赖的Bean for (Runnable destroyCallback : this.destroyCallbacks) { destroyCallback.run(); } }
Java

3.2.5 初始化与销毁阶段关键注解与接口示例

@Component public class LifecycleDemoBean implements InitializingBean, DisposableBean, BeanNameAware { private String message; // 构造函数(实例化阶段调用) public LifecycleDemoBean() { System.out.println("1. 构造函数调用:实例化Bean"); } // BeanNameAware接口方法(属性注入前调用) @Override public void setBeanName(String name) { System.out.println("3. BeanNameAware.setBeanName:" + name); } // 属性注入(属性注入阶段调用) @Autowired public void setMessage(@Value("${demo.message:default}") String message) { this.message = message; System.out.println("4. 属性注入:message = " + message); } // 初始化前(BeanPostProcessor前置处理) @PostConstruct public void postConstruct() { System.out.println("6. @PostConstruct:初始化前处理"); } // InitializingBean接口方法(初始化阶段调用) @Override public void afterPropertiesSet() throws Exception { System.out.println("7. InitializingBean.afterPropertiesSet:初始化处理"); } // 自定义初始化方法(初始化阶段调用) public void customInit() { System.out.println("8. 自定义init-method:额外初始化处理"); } // 初始化后(BeanPostProcessor后置处理,如AOP代理) public void postProcessAfterInitialization() { System.out.println("9. 初始化后:Bean就绪"); } // 自定义销毁方法(销毁阶段调用) @PreDestroy public void preDestroy() { System.out.println("10. @PreDestroy:销毁前处理"); } // DisposableBean接口方法(销毁阶段调用) @Override public void destroy() throws Exception { System.out.println("11. DisposableBean.destroy:销毁处理"); } // 自定义销毁方法(销毁阶段调用) public void customDestroy() { System.out.println("12. 自定义destroy-method:额外销毁处理"); } } // 配置类指定初始化和销毁方法 @Configuration public class LifecycleConfig { @Bean(initMethod = "customInit", destroyMethod = "customDestroy") public LifecycleDemoBean lifecycleDemoBean() { return new LifecycleDemoBean(); } } // BeanPostProcessor实现类 @Component public class DemoBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof LifecycleDemoBean) { System.out.println("5. BeanPostProcessor.postProcessBeforeInitialization"); } return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof LifecycleDemoBean) { System.out.println("9. BeanPostProcessor.postProcessAfterInitialization"); } return bean; } }
Java
输出结果
1. 构造函数调用:实例化Bean 3. BeanNameAware.setBeanName:lifecycleDemoBean 4. 属性注入:message = default 5. BeanPostProcessor.postProcessBeforeInitialization 6. @PostConstruct:初始化前处理 7. InitializingBean.afterPropertiesSet:初始化处理 8. 自定义init-method:额外初始化处理 9. BeanPostProcessor.postProcessAfterInitialization 9. 初始化后:Bean就绪 (容器关闭时) 10. @PreDestroy:销毁前处理 11. DisposableBean.destroy:销毁处理 12. 自定义destroy-method:额外销毁处理
Plain text

3.3 不同作用域的生命周期差异

:不同作用域的Bean在生命周期上有什么区别?单例Bean和原型Bean的生命周期管理有哪些关键差异?
:作用域决定了Bean的生命周期边界和实例创建策略,不同作用域的Bean在生命周期上有显著差异:

3.3.1 单例(singleton)Bean

  • 生命周期:与容器生命周期一致,容器启动时创建(懒加载除外),容器关闭时销毁
  • 实例数量:容器中只有一个实例
  • 管理特点:容器负责完整的生命周期管理(创建、初始化、销毁)
  • 适用场景:无状态组件(如Service、DAO)
懒加载配置:通过@Lazy注解或XML配置lazy-init="true",使单例Bean在首次获取时才实例化。

3.3.2 原型(prototype)Bean

  • 生命周期:容器仅负责实例化和初始化,不负责销毁,实例由开发者管理
  • 实例数量:每次获取时创建新实例
  • 管理特点
    • 不支持@PreDestroyDisposableBean的自动调用
    • 不会被容器缓存,每次getBean都创建新实例
  • 适用场景:有状态组件(如命令对象、请求参数封装对象)

3.3.3 请求(request)Bean

  • 生命周期:与HTTP请求生命周期一致,请求开始时创建,请求结束时销毁
  • 实例数量:每个请求一个实例
  • 管理特点
    • 存储在HttpServletRequest的属性中
    • 依赖RequestContextHolderThreadLocal存储上下文
  • 适用场景:存储请求相关数据(如请求参数、用户信息)

3.3.4 会话(session)Bean

  • 生命周期:与用户会话生命周期一致,会话创建时创建,会话失效时销毁
  • 实例数量:每个会话一个实例
  • 管理特点:存储在HttpSession中,可能存在序列化问题
  • 适用场景:存储用户会话相关数据(如购物车、登录状态)

3.3.5 应用(application)Bean

  • 生命周期:与Web应用生命周期一致,应用启动时创建,应用关闭时销毁
  • 实例数量:整个Web应用一个实例
  • 适用场景:存储应用级配置(如全局缓存、应用统计信息)

3.3.6 不同作用域生命周期对比表

作用域
实例创建时机
生命周期边界
容器管理销毁
线程安全性默认保证
典型使用场景
singleton
首次获取/容器启动
容器启动至销毁
❌(需手动保证)
无状态服务、工具类
prototype
每次获取
获取后由用户管理
❌(完全依赖用户)
有状态命令对象、请求参数封装
request
首次在请求中使用
HTTP请求开始至响应完成
✅(线程隔离)
请求级上下文、用户快照
session
首次在会话中使用
用户会话创建至失效
⚠️(多请求共享)
用户登录状态、购物车
application
首次在应用中使用
Web应用启动至关闭
❌(需手动保证)
应用级缓存、配置信息

3.3.7 单例与原型Bean生命周期对比时序图

sequenceDiagram participant 容器 as 容器 participant 单例 as 单例Bean participant 原型 as 原型Bean participant 请求 as 请求 Note over 容器,单例: 单例Bean生命周期 容器->>单例: 实例化(一次) 单例-->>容器: 返回实例 容器->>单例: 初始化(一次) 请求->>容器: 获取单例Bean 容器->>单例: 返回同一实例 单例-->>请求: 处理请求 请求->>容器: 再次获取单例Bean 容器->>单例: 返回同一实例 单例-->>请求: 处理请求 容器->>单例: 销毁(容器关闭时) Note over 容器,原型: 原型Bean生命周期 请求->>容器: 获取原型Bean 容器->>原型: 实例化(新实例) 原型-->>容器: 返回实例 容器->>原型: 初始化(新实例) 容器-->>请求: 返回新实例 请求->>原型: 使用实例 请求->>原型: 手动销毁(开发者负责) 请求->>容器: 再次获取原型Bean 容器->>原型: 实例化(另一个新实例) 原型-->>容器: 返回实例 容器->>原型: 初始化(另一个新实例) 容器-->>请求: 返回新实例
请求原型Bean单例Bean容器请求原型Bean单例Bean容器单例Bean生命周期原型Bean生命周期实例化(一次)返回实例初始化(一次)获取单例Bean返回同一实例处理请求再次获取单例Bean返回同一实例处理请求销毁(容器关闭时)获取原型Bean实例化(新实例)返回实例初始化(新实例)返回新实例使用实例手动销毁(开发者负责)再次获取原型Bean实例化(另一个新实例)返回实例初始化(另一个新实例)返回新实例
Mermaid

四、Bean作用域的底层实现:从定义到实例管理

4.1 Scope接口体系与核心实现类

:Spring中定义作用域的核心接口是什么?不同作用域的实现类有哪些?它们是如何被容器管理的?
:Spring通过Scope接口定义作用域的基本行为,所有作用域实现都需遵循该接口规范。容器通过ScopeRegistry注册作用域,并在获取Bean时根据BeanDefinitionscope属性选择对应的Scope实现。

4.1.1 Scope接口核心方法

public interface Scope { // 从作用域中获取Bean实例,若不存在则通过ObjectFactory创建 Object get(String name, ObjectFactory<?> objectFactory); // 从作用域中移除Bean实例 @Nullable Object remove(String name); // 注册销毁回调,当Bean从作用域中移除时调用 void registerDestructionCallback(String name, Runnable callback); // 解析作用域中的上下文对象(如RequestScope返回HttpServletRequest) @Nullable Object resolveContextualObject(String key); // 获取作用域的会话ID(可选,用于日志等) @Nullable String getConversationId(); }
Java

4.1.2 核心作用域实现类

  1. SingletonScope:单例作用域实现
      • 核心逻辑:通过DefaultSingletonBeanRegistry的三级缓存管理单例Bean
      • 特殊处理:容器启动时预实例化(懒加载除外),全局唯一实例
      public class SingletonScope implements Scope { @Override public Object get(String name, ObjectFactory<?> objectFactory) { // 委托给SingletonBeanRegistry管理 return ((DefaultSingletonBeanRegistry) this.beanFactory).getSingleton(name, objectFactory); } @Override public Object remove(String name) { ((DefaultSingletonBeanRegistry) this.beanFactory).destroySingleton(name); return null; } // 其他方法省略... }
      Java
  1. PrototypeScope:原型作用域实现
      • 核心逻辑:每次调用get方法都通过ObjectFactory创建新实例
      • 特殊处理:不注册销毁回调(容器不管理原型Bean的销毁)
      public class PrototypeScope implements Scope { @Override public Object get(String name, ObjectFactory<?> objectFactory) { // 直接返回新实例,不缓存 return objectFactory.getObject(); } @Override public Object remove(String name) { return null; // 原型Bean无缓存,无需移除 } @Override public void registerDestructionCallback(String name, Runnable callback) { // 原型Bean的销毁回调由用户手动触发,容器不自动处理 if (logger.isDebugEnabled()) { logger.debug("Cannot register destruction callback for prototype bean '" + name + "': Prototype beans do not support destruction callbacks"); } } // 其他方法省略... }
      Java
  1. RequestScope:请求作用域实现(Web环境)
      • 核心逻辑:将Bean存储在HttpServletRequest的属性中
      • 上下文依赖:通过RequestContextHolder获取当前请求
      public class RequestScope extends AbstractRequestAttributesScope { @Override protected int getScope() { return RequestAttributes.SCOPE_REQUEST; } @Override public String getConversationId() { return RequestContextHolder.currentRequestAttributes().getSessionId() + "-" + RequestContextHolder.currentRequestAttributes().getRequestId(); } }
      Java
  1. SessionScope:会话作用域实现(Web环境)
      • 核心逻辑:将Bean存储在HttpSession
      • 销毁机制:会话过期或失效时触发销毁回调
      public class SessionScope extends AbstractRequestAttributesScope { @Override protected int getScope() { return RequestAttributes.SCOPE_SESSION; } @Override public String getConversationId() { return RequestContextHolder.currentRequestAttributes().getSessionId(); } }
      Java
  1. ApplicationScope:应用作用域实现(Web环境)
      • 核心逻辑:将Bean存储在ServletContext
      • 生命周期:与Web应用生命周期一致

4.1.3 作用域注册与容器集成

容器通过ConfigurableBeanFactoryregisterScope方法注册作用域:
// 注册自定义作用域示例 @Configuration public class CustomScopeConfig { @Bean public static CustomScopeConfigurer customScopeConfigurer() { CustomScopeConfigurer configurer = new CustomScopeConfigurer(); Map<String, Object> scopes = new HashMap<>(); scopes.put("customScope", new CustomScope()); // 自定义Scope实现 configurer.setScopes(scopes); return configurer; } }
Java
注册时序图
sequenceDiagram participant 配置类 as 配置类(CustomScopeConfig) participant 配置器 as CustomScopeConfigurer participant 容器 as ConfigurableBeanFactory participant 作用域 as 自定义Scope实现 配置类->>配置器: 创建CustomScopeConfigurer 配置类->>配置器: 设置自定义Scope(scopeName->Scope实例) 配置器->>容器: 初始化时调用postProcessBeanFactory 容器->>容器: 遍历配置器中的scopes 容器->>容器: 调用registerScope(scopeName, scopeInstance) 容器->>作用域: 注册Scope实例至内部缓存
自定义Scope实现ConfigurableBeanFactoryCustomScopeConfigurer配置类(CustomScopeConfig)自定义Scope实现ConfigurableBeanFactoryCustomScopeConfigurer配置类(CustomScopeConfig)创建CustomScopeConfigurer设置自定义Scope(scopeName->Scope实例)初始化时调用postProcessBeanFactory遍历配置器中的scopes调用registerScope(scopeName, scopeInstance)注册Scope实例至内部缓存
Mermaid

4.2 单例(Singleton)作用域深度解析

:单例Bean的实例是如何被容器创建和缓存的?三级缓存是如何解决循环依赖问题的?单例Bean在并发环境下有哪些需要注意的问题?
:单例Bean是Spring中默认且最常用的作用域,其核心实现依赖DefaultSingletonBeanRegistry的缓存机制和循环依赖处理策略。

4.2.1 单例Bean的创建与缓存机制

单例Bean的缓存体系由三级缓存构成:
// DefaultSingletonBeanRegistry中的三级缓存 // 1. 一级缓存:存储完全初始化完成的单例Bean private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); // 2. 二级缓存:存储早期暴露的单例Bean(已实例化但未初始化) private final Map<String, Object> earlySingletonObjects = new HashMap<>(16); // 3. 三级缓存:存储单例Bean的工厂对象,用于创建早期引用 private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
Java
获取单例Bean的核心逻辑
@Nullable protected Object getSingleton(String beanName, boolean allowEarlyReference) { // 先从一级缓存获取 Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { // 一级缓存未命中且Bean正在创建中,从二级缓存获取 synchronized (this.singletonObjects) { singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null && allowEarlyReference) { // 二级缓存未命中,从三级缓存获取工厂并创建早期引用 ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { singletonObject = singletonFactory.getObject(); // 移至二级缓存,避免重复创建 this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } return singletonObject; }
Java

4.2.2 循环依赖解决方案

循环依赖:A依赖B,B依赖A的情况。单例Bean通过三级缓存解决:
  1. A实例化后,将自身工厂放入三级缓存
  1. A注入依赖时发现需要B,开始创建B
  1. B实例化后,注入依赖时发现需要A,从三级缓存获取A的早期引用
  1. B初始化完成,注入A的早期引用
  1. A获取B的实例完成注入,继续初始化
  1. A初始化完成,移至一级缓存
循环依赖处理时序图
sequenceDiagram participant A as 单例Bean A participant B as 单例Bean B participant 容器 as 容器(三级缓存) A->>容器: 实例化A(未初始化) A->>容器: 注册A的工厂至三级缓存 A->>容器: 需要注入B,请求创建B B->>容器: 实例化B(未初始化) B->>容器: 注册B的工厂至三级缓存 B->>容器: 需要注入A,查询缓存 容器->>容器: 从三级缓存获取A的工厂 容器->>A: 通过工厂获取A的早期引用 容器->>容器: 将A移至二级缓存 B->>B: 注入A的早期引用,完成初始化 B->>容器: B初始化完成,移至一级缓存 A->>A: 注入B的实例,完成初始化 A->>容器: A初始化完成,移至一级缓存(从二级缓存移除)
容器(三级缓存)单例Bean B单例Bean A容器(三级缓存)单例Bean B单例Bean A实例化A(未初始化)注册A的工厂至三级缓存需要注入B,请求创建B实例化B(未初始化)注册B的工厂至三级缓存需要注入A,查询缓存从三级缓存获取A的工厂通过工厂获取A的早期引用将A移至二级缓存注入A的早期引用,完成初始化B初始化完成,移至一级缓存注入B的实例,完成初始化A初始化完成,移至一级缓存(从二级缓存移除)
Mermaid

4.2.3 单例Bean的并发问题与线程安全

潜在问题
  • 单例Bean默认无状态,但如果包含可修改的共享状态,会引发线程安全问题
  • 初始化阶段的并发创建风险(通过synchronized同步块解决)
解决方案
  1. 设计为无状态Bean(推荐)
  1. 使用ThreadLocal存储线程私有状态
  1. 对共享资源加锁(synchronizedReentrantLock
源码级线程安全保障
// 单例创建的同步控制 public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) { Assert.notNull(beanName, "Bean name must not be null"); synchronized (this.singletonObjects) { Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { // 标记Bean为正在创建 beforeSingletonCreation(beanName); boolean newSingleton = false; try { singletonObject = singletonFactory.getObject(); newSingleton = true; } finally { // 取消创建标记 afterSingletonCreation(beanName); } if (newSingleton) { // 添加至一级缓存 addSingleton(beanName, singletonObject); } } return singletonObject; } }
Java

4.3 原型(Prototype)作用域深度解析

:原型Bean的创建流程与单例有何本质区别?为什么容器不管理原型Bean的销毁?使用原型Bean时需要注意哪些内存泄漏风险?
:原型Bean的核心特性是“每次获取创建新实例”,容器仅负责实例化和初始化,不跟踪后续生命周期,这导致其销毁管理需开发者手动处理。

4.3.1 原型Bean的实例化流程

与单例的关键差异:
  • 不进入三级缓存,每次getBean都触发完整实例化流程
  • 无缓存,实例创建后直接返回给调用者
核心源码PrototypeScopeget方法):
@Override public Object get(String name, ObjectFactory<?> objectFactory) { // 直接通过工厂创建新实例,不缓存 return objectFactory.getObject(); }
Java
实例化时序图
sequenceDiagram participant 调用者 as 调用者(getBean("prototypeBean")participant 容器 as AbstractBeanFactory participant 作用域 as PrototypeScope participant 工厂 as ObjectFactory 调用者->>容器: 请求获取原型Bean 容器->>容器: 检查作用域为prototype 容器->>作用域: 调用get(name, objectFactory) 作用域->>工厂: 调用objectFactory.getObject() 工厂->>工厂: 完整实例化流程(构造函数+依赖注入+初始化) 工厂->>作用域: 返回新实例 作用域->>容器: 返回新实例 容器->>调用者: 返回新实例(不缓存)
ObjectFactoryPrototypeScopeAbstractBeanFactory调用者(getBean("prototypeBean"))ObjectFactoryPrototypeScopeAbstractBeanFactory调用者(getBean("prototypeBean"))请求获取原型Bean检查作用域为prototype调用get(name, objectFactory)调用objectFactory.getObject()完整实例化流程(构造函数+依赖注入+初始化)返回新实例返回新实例返回新实例(不缓存)
Mermaid

4.3.2 原型Bean的销毁管理缺失问题

容器不管理销毁的原因
  • 原型Bean的生命周期不由容器控制,无法确定何时销毁
  • 多实例场景下,容器无法跟踪所有实例的引用
手动销毁方案
  1. 通过BeanFactory获取原型Bean后,手动调用销毁方法
  1. 结合@PreDestroyDisposableBean,但需手动触发:
// 手动销毁原型Bean示例 @Service public class PrototypeManagerService { @Autowired private ConfigurableBeanFactory beanFactory; private List<DisposableBean> prototypesToDestroy = new ArrayList<>(); public <T> T getPrototypeBean(Class<T> clazz) { T bean = beanFactory.getBean(clazz); if (bean instanceof DisposableBean) { prototypesToDestroy.add((DisposableBean) bean); } return bean; } @PreDestroy public void destroyPrototypes() { for (DisposableBean bean : prototypesToDestroy) { try { bean.destroy(); } catch (Exception e) { // 处理异常 } } } }
Java

4.3.3 原型Bean与单例Bean的依赖陷阱

问题场景:单例Bean依赖原型Bean时,原型Bean不会被重新创建,导致单例Bean始终持有同一个原型实例。
解决方案
  1. 使用ObjectProvider延迟获取:
@Component public class SingletonService { @Autowired private ObjectProvider<PrototypeBean> prototypeBeanProvider; public void doSomething() { // 每次调用获取新实例 PrototypeBean prototypeBean = prototypeBeanProvider.getObject(); prototypeBean.execute(); } }
Java
  1. 实现ApplicationContextAware手动获取:
@Component public class SingletonService implements ApplicationContextAware { private ApplicationContext context; public void doSomething() { PrototypeBean prototypeBean = context.getBean(PrototypeBean.class); prototypeBean.execute(); } @Override public void setApplicationContext(ApplicationContext applicationContext) { this.context = applicationContext; } }
Java

4.4 Web环境作用域(Request/Session/Application)解析

:Web环境下的Request、Session作用域是如何与HTTP上下文绑定的?它们的线程安全性如何保证?在分布式环境下有哪些挑战?
:Web作用域通过RequestContextHolderThreadLocal机制绑定当前请求/会话上下文,实现线程隔离。但在分布式环境中,会话共享需额外处理。

4.4.1 RequestScope的实现原理

核心机制
  • 将Bean存储在HttpServletRequestattribute
  • 通过RequestContextHolder获取当前请求:
// RequestContextHolder的核心实现 public abstract class RequestContextHolder { private static final ThreadLocal<RequestAttributes> requestAttributesHolder = new NamedThreadLocal<>("Request attributes"); private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder = new NamedInheritableThreadLocal<>("Request context"); // 设置当前请求属性 public static void setRequestAttributes(@Nullable RequestAttributes attributes) { setRequestAttributes(attributes, false); } // 获取当前请求属性 @Nullable public static RequestAttributes getRequestAttributes() { RequestAttributes attributes = requestAttributesHolder.get(); if (attributes == null) { attributes = inheritableRequestAttributesHolder.get(); } return attributes; } }
Java
RequestScope的get方法
@Override public Object get(String name, ObjectFactory<?> objectFactory) { RequestAttributes attributes = RequestContextHolder.currentRequestAttributes(); Object scopedObject = attributes.getAttribute(name, getScope()); if (scopedObject == null) { scopedObject = objectFactory.getObject(); attributes.setAttribute(name, scopedObject, getScope()); } return scopedObject; }
Java
请求作用域时序图
sequenceDiagram participant 请求 as HttpServletRequest participant 过滤器 as RequestContextFilter participant 持有者 as RequestContextHolder participant 作用域 as RequestScope participant 容器 as BeanFactory 请求->>过滤器: 进入请求 过滤器->>持有者: 设置当前请求至ThreadLocal 持有者->>作用域: 提供当前请求上下文 容器->>作用域: 获取request作用域Bean 作用域->>请求: 检查属性中是否存在Bean 请求-->>作用域: 不存在 作用域->>容器: 创建新实例 容器->>作用域: 返回新实例 作用域->>请求: 存储实例至request属性 作用域->>容器: 返回实例 请求->>过滤器: 请求处理完成 过滤器->>持有者: 清除ThreadLocal中的请求
BeanFactoryRequestScopeRequestContextHolderRequestContextFilterHttpServletRequestBeanFactoryRequestScopeRequestContextHolderRequestContextFilterHttpServletRequest进入请求设置当前请求至ThreadLocal提供当前请求上下文获取request作用域Bean检查属性中是否存在Bean不存在创建新实例返回新实例存储实例至request属性返回实例请求处理完成清除ThreadLocal中的请求
Mermaid

4.4.2 SessionScope的实现与序列化问题

核心机制
  • 将Bean存储在HttpSession
  • 会话过期时自动触发销毁回调
序列化挑战
  • 会话Bean必须实现Serializable接口,否则会话序列化时会出错
  • 跨服务器部署(如集群)时,需配置会话共享(如Redis)
会话作用域的线程安全
  • 单个会话内的请求串行处理,默认线程安全
  • 多个会话间的Bean相互隔离,无共享状态

4.4.3 Web作用域的配置与使用限制

必需配置(非Spring Boot环境):
<!-- 注册Web作用域的监听器和过滤器 --> <listener> <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class> </listener> <filter> <filter-name>requestContextFilter</filter-name> <filter-class>org.springframework.web.filter.RequestContextFilter</filter-class> </filter> <filter-mapping> <filter-name>requestContextFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
XML
使用限制
  • 只能在Web环境中使用,否则抛出IllegalStateException
  • 非Web线程(如异步任务)中获取Web作用域Bean需特殊处理:
// 异步任务中使用Web作用域Bean @Service public class AsyncService { @Async public void asyncTask() { // 复制父线程的请求上下文 RequestAttributes attributes = RequestContextHolder.getRequestAttributes(); RequestContextHolder.setRequestAttributes(attributes, true); // 第二个参数为true表示可继承 try { // 使用request/session作用域Bean } finally { RequestContextHolder.resetRequestAttributes(); } } }
Java

4.5 自定义作用域实现与实战案例

:如何实现一个自定义的Spring作用域?需要覆盖哪些核心方法?有哪些实际应用场景?
:自定义作用域需实现Scope接口,核心是定义getremove方法的逻辑。常见应用场景包括:批处理任务作用域、用户会话集群作用域等。

4.5.1 自定义作用域实现步骤

  1. 实现Scope接口
public class BatchScope implements Scope { // 使用ThreadLocal存储批处理任务内的Bean private ThreadLocal<Map<String, Object>> batchScopedObjects = new ThreadLocal<>(); // 存储销毁回调 private ThreadLocal<Map<String, Runnable>> destructionCallbacks = new ThreadLocal<>(); @Override public Object get(String name, ObjectFactory<?> objectFactory) { Map<String, Object> scopedObjects = getScopedObjects(); Object object = scopedObjects.get(name); if (object == null) { object = objectFactory.getObject(); scopedObjects.put(name, object); } return object; } @Override public Object remove(String name) { Map<String, Object> scopedObjects = getScopedObjects(); Map<String, Runnable> callbacks = getDestructionCallbacks(); callbacks.remove(name); return scopedObjects.remove(name); } @Override public void registerDestructionCallback(String name, Runnable callback) { getDestructionCallbacks().put(name, callback); } @Override public Object resolveContextualObject(String key) { return null; } @Override public String getConversationId() { return Thread.currentThread().getName(); } // 辅助方法:获取当前批处理的对象存储 private Map<String, Object> getScopedObjects() { Map<String, Object> scopedObjects = batchScopedObjects.get(); if (scopedObjects == null) { scopedObjects = new HashMap<>(); batchScopedObjects.set(scopedObjects); } return scopedObjects; } // 手动触发批处理结束时的销毁 public void endBatch() { Map<String, Runnable> callbacks = destructionCallbacks.get(); if (callbacks != null) { for (Runnable callback : callbacks.values()) { callback.run(); } } batchScopedObjects.remove(); destructionCallbacks.remove(); } }
Java
  1. 注册自定义作用域
@Configuration public class BatchScopeConfig { @Bean public static CustomScopeConfigurer batchScopeConfigurer() { CustomScopeConfigurer configurer = new CustomScopeConfigurer(); configurer.setScopes(Collections.singletonMap("batch", new BatchScope())); return configurer; } }
Java
  1. 使用自定义作用域
@Component @Scope("batch") public class BatchContext { private List<String> processedItems = new ArrayList<>(); public void addProcessedItem(String item) { processedItems.add(item); } @PreDestroy public void logSummary() { System.out.println("Processed " + processedItems.size() + " items"); } }
Java

4.5.2 自定义作用域的使用场景与注意事项

适用场景
  • 批处理任务:每个任务一个上下文实例
  • 分布式会话:基于Redis的跨节点会话共享
  • 测试环境:每个测试方法一个独立实例
注意事项
  • 必须确保作用域的上下文正确初始化和销毁
  • 多线程环境下需保证ThreadLocal的清理(避免内存泄漏)
  • 与AOP结合时需注意代理对象的作用域一致性
 

五、Bean加载机制的高级特性:条件、延迟与动态注册

5.1 条件注解(@Conditional)的底层实现

:Spring的条件注解(如@ConditionalOnClass@ConditionalOnBean)是如何决定Bean是否被加载的?其底层的条件判断逻辑在哪个阶段执行?
:条件注解通过Condition接口实现动态判断,在BeanDefinition注册阶段(ConfigurationClassPostProcessor处理时)执行条件校验,不符合条件的BeanDefinition会被排除。这一机制允许根据环境、类存在性等动态控制Bean的加载。

5.1.1 条件注解的核心接口与注解体系

核心接口Condition,定义条件判断方法:
@FunctionalInterface public interface Condition { // context:条件上下文,包含BeanDefinitionRegistry、Environment等 // metadata:注解元数据 boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata); }
Java
常用条件注解(均组合@Conditional):
  • @ConditionalOnClass:类路径存在指定类时生效
  • @ConditionalOnMissingClass:类路径不存在指定类时生效
  • @ConditionalOnBean:容器中存在指定Bean时生效
  • @ConditionalOnMissingBean:容器中不存在指定Bean时生效
  • @ConditionalOnProperty:配置属性满足条件时生效
  • @ConditionalOnResource:指定资源存在时生效

5.1.2 条件判断的执行时机与流程

条件判断发生在BeanDefinition注册阶段,具体流程如下:
  1. ConfigurationClassParser解析@Configuration类时,遇到条件注解会记录条件
  1. ConfigurationClassBeanDefinitionReader注册BeanDefinition前,调用ConditionEvaluator评估条件
  1. 条件不满足时,跳过当前BeanDefinition的注册
核心源码ConditionEvaluator):
public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) { if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) { return false; } if (phase == null) { // 处理类级别的条件 if (metadata instanceof AnnotationMetadata && ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata) metadata)) { return shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION); } return shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN); } // 获取所有@Conditional注解的条件 List<Condition> conditions = new ArrayList<>(); for (String[] conditionClasses : getConditionClasses(metadata)) { for (String conditionClass : conditionClasses) { Condition condition = getCondition(conditionClass, this.context.getClassLoader()); conditions.add(condition); } } // 排序条件(按@Order注解) AnnotationAwareOrderComparator.sort(conditions); // 执行条件判断 for (Condition condition : conditions) { ConfigurationPhase requiredPhase = null; if (condition instanceof ConfigurationCondition) { requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase(); } if ((requiredPhase == null || requiredPhase == phase) && !condition.matches(this.context, metadata)) { return true; // 条件不满足,跳过注册 } } return false; }
Java

5.1.3 典型条件注解的实现逻辑(以@ConditionalOnClass为例)

@ConditionalOnClass的条件判断由OnClassCondition实现:
@Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { // 获取注解的value属性(需要存在的类名) MultiValueMap<String, Object> attributes = metadata.getAllAnnotationAttributes(ConditionalOnClass.class.getName()); if (attributes == null) { return true; } // 检查每个类是否存在 for (Object value : attributes.get("value")) { String[] classNames = (String[]) value; for (String className : classNames) { if (!ClassUtils.isPresent(className, context.getClassLoader())) { return false; // 类不存在,条件不满足 } } } return true; }
Java

5.1.4 条件注解处理时序图

sequenceDiagram participant 配置类 as @Configuration类 participant 解析器 as ConfigurationClassParser participant 评估器 as ConditionEvaluator participant 条件 as OnClassCondition(示例) participant 注册器 as BeanDefinitionRegistry 解析器->>配置类: 解析配置类 解析器->>配置类: 发现@ConditionalOnClass注解 解析器->>评估器: 调用shouldSkip() 评估器->>条件: 调用matches() 条件->>条件: 检查类路径是否存在指定类 条件-->>评估器: 返回true(条件满足)/false(不满足) 评估器-->>解析器: 返回是否跳过 alt 条件满足 解析器->>注册器: 注册BeanDefinition else 条件不满足 解析器->>解析器: 跳过BeanDefinition注册 end
BeanDefinitionRegistryOnClassCondition(示例)ConditionEvaluatorConfigurationClassParser@Configuration类BeanDefinitionRegistryOnClassCondition(示例)ConditionEvaluatorConfigurationClassParser@Configuration类alt[条件满足][条件不满足]解析配置类发现@ConditionalOnClass注解调用shouldSkip()调用matches()检查类路径是否存在指定类返回true(条件满足)/false(不满足)返回是否跳过注册BeanDefinition跳过BeanDefinition注册
Mermaid

5.2 延迟加载(@Lazy)的实现机制与使用场景

@Lazy注解是如何实现Bean的延迟加载的?单例Bean和原型Bean的延迟加载有何区别?延迟加载可能带来哪些性能影响?
@Lazy通过延迟Bean的实例化时机实现懒加载:单例Bean在首次被使用时才实例化(而非容器启动时),原型Bean的延迟加载无实际意义(本身就是每次获取时创建)。其底层通过代理模式或ObjectFactory延迟初始化逻辑实现。

5.2.1 单例Bean的延迟加载实现

核心原理
  • 非延迟单例:容器启动时通过preInstantiateSingletons()预实例化
  • 延迟单例:注册为LazyInitTargetSource,首次调用时才触发实例化
源码解析AbstractBeanFactory处理@Lazy):
protected Object getSingleton(String beanName, boolean allowEarlyReference) { Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { // 处理循环依赖... } else if (singletonObject == null && !this.lazyInit) { // 非延迟加载:立即创建 singletonObject = singletonFactory.getObject(); } return singletonObject; } // 延迟加载的Bean在注册时标记为lazyInit @Override public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) { if (beanDefinition.isLazyInit()) { this.lazyInitBeans.add(beanName); } else { // 非延迟单例可能触发预实例化 if (isSingleton(beanDefinition) && this.allowEagerInit) { preInstantiateSingletons(); } } }
Java

5.2.2 延迟加载与代理模式的结合

@Lazy@Autowired结合使用时,Spring会为依赖创建代理对象,延迟目标Bean的实例化:
// 延迟注入的代理创建逻辑(简化版) private Object createLazyProxy(InjectionPoint injectionPoint, Class<?> beanType) { ProxyFactory proxyFactory = new ProxyFactory(); proxyFactory.setTargetSource(new LazyInitTargetSource() { @Override public Object getTarget() { return getBean(beanType); // 首次调用时获取实际Bean } }); return proxyFactory.getProxy(); }
Java

5.2.3 延迟加载的优缺点与适用场景

优点
  • 减少容器启动时间(避免启动时创建所有Bean)
  • 节省内存(不使用的Bean不会被实例化)
缺点
  • 首次访问Bean时可能有性能开销(实例化耗时操作)
  • 初始化异常延迟暴露(容器启动时无法发现)
适用场景
  • 资源密集型Bean(如数据库连接池,非启动必需)
  • 很少被使用的Bean(如后台任务处理器)
  • 循环依赖场景(配合@Lazy可打破循环依赖)

5.2.4 延迟加载与非延迟加载对比时序图

sequenceDiagram participant 容器 as ApplicationContext participant 单例A as 非延迟单例Bean participant 单例B as 延迟单例Bean participant 调用者 as 调用者 容器->>容器: 启动 容器->>单例A: 预实例化(非延迟) 单例A-->>容器: 实例化完成 容器-->>容器: 启动完成(单例B未实例化) 调用者->>容器: 获取单例B 容器->>单例B: 首次实例化(延迟) 单例B-->>容器: 实例化完成 容器-->>调用者: 返回单例B实例
调用者延迟单例Bean非延迟单例BeanApplicationContext调用者延迟单例Bean非延迟单例BeanApplicationContext启动预实例化(非延迟)实例化完成启动完成(单例B未实例化)获取单例B首次实例化(延迟)实例化完成返回单例B实例
Mermaid

5.3 动态Bean注册的高级方式(BeanDefinitionRegistryPostProcessor)

:除了注解和XML配置,Spring还支持哪些动态注册Bean的方式?BeanDefinitionRegistryPostProcessorBeanFactoryPostProcessor有何区别?动态注册的Bean如何参与完整的生命周期?
:动态注册Bean的核心接口是BeanDefinitionRegistryPostProcessor,它允许在容器启动阶段通过编程方式注册BeanDefinition。相比BeanFactoryPostProcessor,它更侧重于BeanDefinition的注册(而非修改),动态注册的Bean会像静态配置的Bean一样参与完整生命周期。

5.3.1 动态注册核心接口与执行时机

接口定义
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor { // 动态注册BeanDefinition的核心方法 void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException; }
Java
执行时机
  • BeanFactoryPostProcessor之前执行
  • 在所有静态BeanDefinition注册完成后,Bean实例化前执行

5.3.2 动态注册示例:编程方式创建BeanDefinition

@Component public class DynamicBeanRegistrar implements BeanDefinitionRegistryPostProcessor { @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { // 创建BeanDefinition RootBeanDefinition definition = new RootBeanDefinition(DynamicService.class); // 设置属性 definition.getPropertyValues().add("message", "Dynamic Bean"); // 设置作用域 definition.setScope(BeanDefinition.SCOPE_SINGLETON); // 设置初始化方法 definition.setInitMethodName("init"); // 注册BeanDefinition registry.registerBeanDefinition("dynamicService", definition); } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { // 可选:修改已注册的BeanDefinition } } // 动态注册的Bean类 public class DynamicService { private String message; public void setMessage(String message) { this.message = message; } public void init() { System.out.println("DynamicService initialized with message: " + message); } }
Java

5.3.3 动态注册与静态注册的Bean生命周期对比

动态注册的Bean与静态Bean(注解/XML配置)的生命周期完全一致:
  1. 均通过BeanDefinition描述元信息
  1. 共享相同的实例化、依赖注入、初始化流程
  1. 相同的作用域管理策略

5.3.4 动态注册时序图

sequenceDiagram participant 容器 as ApplicationContext participant 注册器 as DynamicBeanRegistrar(实现BeanDefinitionRegistryPostProcessor) participant 注册表 as BeanDefinitionRegistry participant 工厂 as ConfigurableListableBeanFactory 容器->>容器: 初始化BeanFactory 容器->>容器: 注册静态BeanDefinition(注解/XML) 容器->>注册器: 调用postProcessBeanDefinitionRegistry 注册器->>注册表: 动态注册BeanDefinition 注册表-->>注册器: 注册完成 容器->>注册器: 调用postProcessBeanFactory(可选) 注册器->>工厂: 修改BeanFactory配置(可选) 容器->>容器: 执行BeanFactoryPostProcessor 容器->>容器: 实例化所有非延迟单例Bean(包括动态注册的)
ConfigurableListableBeanFactoryBeanDefinitionRegistryDynamicBeanRegistrar(实现BeanDefinitionRegistryPostProcessor)ApplicationContextConfigurableListableBeanFactoryBeanDefinitionRegistryDynamicBeanRegistrar(实现BeanDefinitionRegistryPostProcessor)ApplicationContext初始化BeanFactory注册静态BeanDefinition(注解/XML)调用postProcessBeanDefinitionRegistry动态注册BeanDefinition注册完成调用postProcessBeanFactory(可选)修改BeanFactory配置(可选)执行BeanFactoryPostProcessor实例化所有非延迟单例Bean(包括动态注册的)
Mermaid

5.4 FactoryBean的高级用法与代理模式结合

FactoryBean除了创建复杂对象,还有哪些高级应用场景?它与AOP代理结合时如何保证代理对象的正确创建?FactoryBeanisSingleton()方法对返回实例的作用域有何影响?
FactoryBean的高级应用包括动态代理创建、依赖注入增强、复杂对象池管理等。与AOP结合时,FactoryBean返回的对象会被BeanPostProcessor(如AnnotationAwareAspectJAutoProxyCreator)进一步处理生成代理。其isSingleton()方法决定返回实例是否被容器缓存(单例/原型)。

5.4.1 FactoryBean与AOP代理的协同工作

执行流程
  1. FactoryBeangetObject()返回原始对象
  1. BeanPostProcessor(如AbstractAutoProxyCreator)在postProcessAfterInitialization中为原始对象创建代理
  1. 容器缓存的是代理对象(若isSingleton()返回true)
示例:通过FactoryBean创建带事务的服务代理
public class TransactionalServiceFactoryBean implements FactoryBean<TransactionalService> { private TransactionalService target; @Override public TransactionalService getObject() throws Exception { // 创建目标对象 target = new TransactionalService(); // 手动添加事务代理(实际由Spring AOP自动处理) return (TransactionalService) Proxy.newProxyInstance( TransactionalService.class.getClassLoader(), new Class[]{TransactionalService.class}, new TransactionInvocationHandler(target) ); } @Override public Class<?> getObjectType() { return TransactionalService.class; } @Override public boolean isSingleton() { return true; // 返回单例代理对象 } } // 目标服务类 public class TransactionalService { public void execute() { // 业务逻辑 } }
Java

5.4.2 FactoryBean的作用域控制

  • isSingleton()返回truegetObject()返回的实例会被容器缓存(单例)
  • isSingleton()返回false:每次getBean()都调用getObject()(原型)
注意FactoryBean自身的作用域与返回对象的作用域是独立的:
  • FactoryBean本身默认是单例
  • 其返回对象的作用域由isSingleton()决定

5.4.3 FactoryBean的高级应用场景

  1. 对象池管理:通过FactoryBean管理数据库连接池、线程池等
public class ConnectionPoolFactoryBean implements FactoryBean<DataSource> { private DataSource dataSource; @Override public DataSource getObject() throws Exception { if (dataSource == null) { HikariConfig config = new HikariConfig(); config.setJdbcUrl("jdbc:mysql://localhost:3306/test"); dataSource = new HikariDataSource(config); } return dataSource; } @Override public Class<?> getObjectType() { return DataSource.class; } @Override public boolean isSingleton() { return true; // 连接池单例 } // 销毁方法:关闭连接池 @PreDestroy public void destroy() { ((HikariDataSource) dataSource).close(); } }
Java
  1. 动态实现接口:根据配置动态生成接口实现类
public class DynamicInterfaceFactoryBean implements FactoryBean<DynamicInterface> { @Override public DynamicInterface getObject() throws Exception { // 使用JDK动态代理生成接口实现 return (DynamicInterface) Proxy.newProxyInstance( DynamicInterface.class.getClassLoader(), new Class[]{DynamicInterface.class}, (proxy, method, args) -> { // 动态实现方法逻辑 return "Dynamic implementation: " + method.getName(); } ); } @Override public Class<?> getObjectType() { return DynamicInterface.class; } @Override public boolean isSingleton() { return false; // 每次获取新实例 } }
Java

5.4.4 FactoryBean与AOP协同时序图

sequenceDiagram participant 容器 as AbstractBeanFactory participant 工厂 as TransactionalServiceFactoryBean participant 后置处理器 as AbstractAutoProxyCreator participant 目标 as 原始TransactionalService participant 代理 as 事务代理对象 容器->>工厂: 调用getObject() 工厂->>目标: 创建原始对象 工厂-->>容器: 返回原始对象 容器->>后置处理器: 调用postProcessAfterInitialization(原始对象) 后置处理器->>代理: 为原始对象创建事务代理 代理-->>后置处理器: 返回代理对象 后置处理器-->>容器: 返回代理对象 alt FactoryBean.isSingleton()=true 容器->>容器: 缓存代理对象 end 容器-->>调用者: 返回代理对象
调用者事务代理对象原始TransactionalServiceAbstractAutoProxyCreatorTransactionalServiceFactoryBeanAbstractBeanFactory调用者事务代理对象原始TransactionalServiceAbstractAutoProxyCreatorTransactionalServiceFactoryBeanAbstractBeanFactoryalt[FactoryBean.-isSingleton()=t-rue]调用getObject()创建原始对象返回原始对象调用postProcessAfterInitialization(原始对象)为原始对象创建事务代理返回代理对象返回代理对象缓存代理对象返回代理对象
Mermaid

六、Bean生命周期与作用域的常见问题与解决方案

6.1 循环依赖的各种场景与解决方案深度解析

:除了单例Bean之间的循环依赖,还有哪些循环依赖场景?为什么原型Bean之间的循环依赖无法被Spring解决?如何通过代码配置避免循环依赖?
:循环依赖的场景包括单例之间、单例与原型之间、原型之间,其中只有单例Bean的循环依赖能被Spring的三级缓存解决。原型Bean的循环依赖因每次获取都创建新实例,会导致无限递归,无法被容器自动处理。解决需通过设计优化或手动注入打破循环。

6.1.1 循环依赖场景分类与处理结果

循环依赖场景
Spring是否能自动解决
原因分析
单例A ←→ 单例B
三级缓存提前暴露未初始化实例,依赖注入时使用早期引用
单例A ←→ 原型B
否(抛异常)
原型B每次创建都依赖单例A,单例A依赖原型B会导致B被重复创建,触发循环依赖
原型A ←→ 原型B
否(抛异常)
每次获取都创建新实例,形成无限递归创建(A→B→A→B...)
单例A ←→ request作用域B
是(需代理)
通过ScopedProxyMode.TARGET_CLASS为B创建代理,延迟B的实例化

6.1.2 原型Bean循环依赖的源码级分析

异常触发点AbstractBeanFactorydoGetBean方法检测到原型Bean循环依赖:
protected <T> T doGetBean(...) { // ... if (isPrototypeCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } // ... if (mbd.isPrototype()) { Object prototypeInstance = null; try { beforePrototypeCreation(beanName); // 标记原型Bean正在创建 prototypeInstance = createBean(beanName, mbd, args); } finally { afterPrototypeCreation(beanName); // 移除创建标记 } } // ... } // AbstractBeanFactory的beforePrototypeCreation方法 protected void beforePrototypeCreation(String beanName) { Object curVal = this.prototypesCurrentlyInCreation.get(); if (curVal == null) { this.prototypesCurrentlyInCreation.set(beanName); } else if (curVal instanceof String) { Set<String> beanNames = new HashSet<>(); beanNames.add((String) curVal); beanNames.add(beanName); this.prototypesCurrentlyInCreation.set(beanNames); } else { Set<String> beanNames = (Set<String>) curVal; beanNames.add(beanName); } }
Java
循环依赖检测逻辑isPrototypeCurrentlyInCreation检查当前原型Bean是否在创建中,若已存在则触发异常。

6.1.3 解决循环依赖的设计模式与代码方案

  1. 构造函数注入改为字段注入
      • 构造函数注入在实例化阶段就需要依赖,容易触发循环依赖
      • 字段注入在实例化后执行,可配合@Lazy延迟依赖获取
// 问题代码(构造函数注入循环依赖) @Component public class SingletonA { private final PrototypeB prototypeB; @Autowired public SingletonA(PrototypeB prototypeB) { // 实例化A时就需要B,触发循环 this.prototypeB = prototypeB; } } // 解决方案(字段注入+@Lazy) @Component public class SingletonA { @Autowired @Lazy // 延迟B的实例化,直到首次使用 private PrototypeB prototypeB; }
Java
  1. 使用@Lazy创建代理
      • 为依赖创建代理对象,延迟实际实例化
  1. 引入中间层打破循环
      • 将A和B的共享依赖抽取到C,A和B都依赖C,而非彼此依赖
  1. 手动获取依赖
      • 通过ApplicationContext.getBean()在需要时手动获取,避免构造/注入阶段依赖

6.1.4 跨作用域循环依赖的解决方案(单例与request)

问题场景:单例Bean依赖request作用域Bean,容器启动时无法获取request上下文。
解决方案:为request作用域Bean创建代理:
@Component @Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS) public class RequestScopedBean { // ... } @Component public class SingletonBean { @Autowired private RequestScopedBean requestBean; // 注入的是代理对象 public void doSomething() { // 实际使用时才通过代理获取真实实例(此时已存在request上下文) requestBean.process(); } }
Java
代理创建逻辑ScopedProxyUtils生成代理类,代理的getObject()方法在实际调用时从request作用域获取实例。

6.1.5 循环依赖解决方案时序图(单例+request作用域)

sequenceDiagram participant 单例A as 单例Bean A participant 代理B as RequestScopedBean的代理 participant 真实B as 真实RequestScopedBean participant 容器 as ApplicationContext participant 请求 as HttpServletRequest 容器->>单例A: 实例化A(非延迟单例) 单例A->>容器: 需要注入B 容器->>容器: 检测B为request作用域,且A为单例 容器->>代理B: 创建代理对象(不依赖request上下文) 代理B-->>容器: 返回代理对象 容器->>单例A: 注入B的代理对象 单例A-->>容器: 初始化完成 请求->>单例A: 调用A的方法 单例A->>代理B: 调用B的方法 代理B->>请求: 从当前request获取真实B实例 请求->>真实B: 返回真实实例 代理B->>真实B: 调用process() 真实B-->>代理B: 处理结果 代理B-->>单例A: 处理结果 单例A-->>请求: 响应结果
HttpServletRequestApplicationContext真实RequestScopedBeanRequestScopedBean的代理单例Bean AHttpServletRequestApplicationContext真实RequestScopedBeanRequestScopedBean的代理单例Bean A实例化A(非延迟单例)需要注入B检测B为request作用域,且A为单例创建代理对象(不依赖request上下文)返回代理对象注入B的代理对象初始化完成调用A的方法调用B的方法从当前request获取真实B实例返回真实实例调用process()处理结果处理结果响应结果
Mermaid

6.2 BeanPostProcessor的执行顺序与冲突解决

:多个BeanPostProcessor的执行顺序由什么决定?如果不同BeanPostProcessor对同一Bean的处理逻辑冲突(如AOP代理与自定义代理),如何保证正确的执行顺序?
BeanPostProcessor的执行顺序由@Order注解或Ordered接口决定,数值越小执行越早。若存在逻辑冲突(如多个代理处理器),需通过调整顺序保证最终代理正确生成(通常AOP代理应最后执行)。Spring通过PriorityOrderedOrdered接口实现两级排序。

6.2.1 BeanPostProcessor的排序机制

排序接口层级
  1. PriorityOrdered:最高优先级,先于所有Ordered执行
  1. Ordered:次优先级,按getOrder()返回值排序(值越小越先执行)
  1. 无接口:最低优先级,最后执行
排序执行源码PostProcessorRegistrationDelegateregisterBeanPostProcessors方法:
private static void registerBeanPostProcessors( ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) { // 1. 注册PriorityOrdered的BeanPostProcessor List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<>(); for (BeanPostProcessor pp : beanPostProcessors) { if (pp instanceof PriorityOrdered) { priorityOrderedPostProcessors.add(pp); } } sortPostProcessors(priorityOrderedPostProcessors, beanFactory); registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors); // 2. 注册Ordered的BeanPostProcessor List<BeanPostProcessor> orderedPostProcessors = new ArrayList<>(); // ... 筛选并排序 ... registerBeanPostProcessors(beanFactory, orderedPostProcessors); // 3. 注册无排序接口的BeanPostProcessor List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<>(); // ... 筛选 ... registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors); // 4. 注册特殊的BeanPostProcessor(如ApplicationListenerDetector) // ... }
Java

6.2.2 常见BeanPostProcessor的执行顺序

BeanPostProcessor实现类
排序优先级
作用
ApplicationContextAwareProcessor
PriorityOrdered
处理Aware接口
ConfigurationClassPostProcessor
PriorityOrdered
处理@Configuration
AutowiredAnnotationBeanPostProcessor
Ordered
处理@Autowired注入
RequiredAnnotationBeanPostProcessor
Ordered
处理@Required注解
AbstractAutoProxyCreator
Ordered
创建AOP代理(如AnnotationAwareAspectJAutoProxyCreator
自定义无排序接口的BeanPostProcessor
最低
自定义处理逻辑

6.2.3 解决BeanPostProcessor冲突的最佳实践

  1. 明确指定顺序
      • 为自定义BeanPostProcessor实现Ordered接口或添加@Order注解
      • AOP代理相关的处理器应设置较高优先级(值较小),确保最后执行
  1. 避免重复代理
      • 多个处理器都可能创建代理时,通过ProxyUtils判断是否已为代理对象
  1. 使用postProcessBeforeInitializationpostProcessAfterInitialization区分时机
      • 初始化前:适合修改Bean属性、设置标记
      • 初始化后:适合创建代理(确保所有初始化完成)
  1. 通过BeanFactory获取处理器顺序
      • 调试时可通过beanFactory.getBeanPostProcessors()查看实际顺序

6.2.4 BeanPostProcessor执行时序图(含排序)

sequenceDiagram participant 容器 as ApplicationContext participant 注册器 as PostProcessorRegistrationDelegate participant P1 as PriorityOrdered处理器(如ApplicationContextAwareProcessor) participant P2 as Ordered处理器(如AutowiredAnnotationBeanPostProcessor) participant P3 as 无排序处理器(自定义) participant Bean as 目标Bean 容器->>注册器: 调用registerBeanPostProcessors() 注册器->>注册器: 按PriorityOrdered→Ordered→无排序的顺序注册 容器->>Bean: 实例化Bean 容器->>P1: 调用postProcessBeforeInitialization(Bean) P1-->>容器: 返回处理后的Bean 容器->>P2: 调用postProcessBeforeInitialization(Bean) P2-->>容器: 返回处理后的Bean 容器->>P3: 调用postProcessBeforeInitialization(Bean) P3-->>容器: 返回处理后的Bean 容器->>Bean: 执行初始化方法 容器->>P1: 调用postProcessAfterInitialization(Bean) P1-->>容器: 返回处理后的Bean 容器->>P2: 调用postProcessAfterInitialization(Bean) P2-->>容器: 返回处理后的Bean 容器->>P3: 调用postProcessAfterInitialization(Bean) P3-->>容器: 返回处理后的Bean(可能是代理)
目标Bean无排序处理器(自定义)Ordered处理器(如AutowiredAnnotationBeanPostProcessor)PriorityOrdered处理器(如ApplicationContextAwareProcessor)PostProcessorRegistrationDelegateApplicationContext目标Bean无排序处理器(自定义)Ordered处理器(如AutowiredAnnotationBeanPostProcessor)PriorityOrdered处理器(如ApplicationContextAwareProcessor)PostProcessorRegistrationDelegateApplicationContext调用registerBeanPostProcessors()按PriorityOrdered→Ordered→无排序的顺序注册实例化Bean调用postProcessBeforeInitialization(Bean)返回处理后的Bean调用postProcessBeforeInitialization(Bean)返回处理后的Bean调用postProcessBeforeInitialization(Bean)返回处理后的Bean执行初始化方法调用postProcessAfterInitialization(Bean)返回处理后的Bean调用postProcessAfterInitialization(Bean)返回处理后的Bean调用postProcessAfterInitialization(Bean)返回处理后的Bean(可能是代理)
Mermaid

6.3 作用域滥用导致的内存泄漏与线程安全问题

:错误使用Bean作用域可能导致哪些内存泄漏问题?不同作用域的Bean在多线程环境下的线程安全保证是什么?如何通过代码检测和避免这些问题?
:作用域滥用的典型问题包括:单例持有request作用域Bean导致内存泄漏、会话Bean未序列化导致集群问题、原型Bean未及时销毁导致资源耗尽。线程安全方面,单例Bean需手动保证线程安全,request/session作用域因线程隔离默认安全,原型Bean的线程安全由使用者负责。

6.3.1 单例持有request作用域Bean导致的内存泄漏

问题场景
@Component public class SingletonHolder { @Autowired private RequestScopedBean requestBean; // 错误:单例持有request作用域Bean // 实际使用时,requestBean已与原请求解绑,可能引用过期数据或导致内存泄漏 }
Java
内存泄漏原因
  • request作用域Bean本应随请求销毁,但被单例长期持有,导致HttpServletRequest相关资源无法释放
  • 若Bean持有InputStream等资源,会造成资源泄漏
解决方案
  1. 使用代理模式延迟获取:
@Component @Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS) public class RequestScopedBean { ... }
Java
  1. 手动在需要时获取:
@Component public class SingletonHolder { @Autowired private ApplicationContext context; public void doWithRequestBean() { RequestScopedBean requestBean = context.getBean(RequestScopedBean.class); // 使用后无需手动销毁(由request生命周期管理) } }
Java

6.3.2 会话Bean的序列化问题与集群部署挑战

问题场景
  • 会话Bean未实现Serializable,在集群环境下会话复制时抛出NotSerializableException
  • 会话Bean持有不可序列化资源(如Connection),导致序列化失败
解决方案
  1. 实现Serializable接口:
@Component @Scope("session") public class SessionScopedBean implements Serializable { private transient Connection connection; // 标记不可序列化字段(需手动恢复) // 序列化回调 private void writeObject(ObjectOutputStream out) throws IOException { // 自定义序列化逻辑 } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { // 自定义反序列化逻辑,恢复transient字段 } }
Java
  1. 使用分布式会话存储(如Redis):
      • Spring Session可将会话存储在Redis,避免序列化到本地
  1. 减少会话Bean的状态:
      • 仅存储必要数据,避免大对象或资源引用

6.3.3 多线程环境下的作用域线程安全对比

作用域
线程安全默认保证
不安全场景示例
线程安全解决方案
singleton
❌(需手动保证)
包含ArrayList等非线程安全集合的单例Bean
使用ConcurrentHashMap、加锁
prototype
❌(完全依赖用户)
多线程共享同一原型实例
每次使用创建新实例,不共享
request
✅(线程隔离)
手动将requestBean传递到其他线程
使用RequestContextHolder传递上下文
session
⚠️(单会话串行)
同一用户的并发请求操作会话Bean
会话内加锁或使用线程安全集合

6.3.4 检测作用域相关内存泄漏的工具与方法

  1. Spring内置工具
      • RequestContextListener:确保ThreadLocal清理
      • WebApplicationContextUtils:检查上下文是否正确关闭
  1. JVM工具
      • jmap -histo:live <pid>:查看对象存活情况,检测request/session Bean是否未释放
      • jstack <pid>:检查ThreadLocal相关线程是否泄漏
  1. 代码检测
      • 单例Bean中禁止持有HttpServletRequestHttpSession等Web对象
      • 使用@PreDestroy验证Bean是否被正确销毁
  1. 集成测试
      • 模拟多次请求/会话,检测内存是否持续增长

6.4 生命周期回调方法的执行顺序与冲突解决

:当一个Bean同时使用@PostConstructInitializingBeaninit-method时,它们的执行顺序是什么?如果这些方法之间存在逻辑冲突(如重复初始化),如何解决?
:三种初始化方法的执行顺序为:@PostConstructInitializingBean.afterPropertiesSet()init-method。销毁方法顺序为:@PreDestroyDisposableBean.destroy()destroy-method。若存在逻辑冲突,需通过统一初始化入口、设置标记位等方式避免重复执行。

6.4.1 初始化方法执行顺序的源码验证

执行顺序源码AbstractAutowireCapableBeanFactoryinvokeInitMethods方法:
protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd) throws Throwable { boolean isInitializingBean = (bean instanceof InitializingBean); if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) { // 1. 执行InitializingBean.afterPropertiesSet() ((InitializingBean) bean).afterPropertiesSet(); } if (mbd != null && bean.getClass() != NullBean.class) { String initMethodName = mbd.getInitMethodName(); if (StringUtils.hasLength(initMethodName) && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) && !mbd.isExternallyManagedInitMethod(initMethodName)) { // 2. 执行init-method invokeCustomInitMethod(beanName, bean, mbd); } } }
Java
@PostConstruct执行时机:由CommonAnnotationBeanPostProcessorpostProcessBeforeInitialization调用,早于InitializingBean
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { InvocationTargetException ex = null; try { // 执行@PostConstruct方法 this.initAnnotationProcessor.postProcessBeforeInitialization(bean, beanName); } // ... return bean; }
Java

6.4.2 销毁方法执行顺序的验证

执行顺序@PreDestroyDisposableBean.destroy()destroy-method
源码依据DisposableBeanAdapterdestroy方法:
public void destroy() { // 1. 执行@PreDestroy方法 if (!CollectionUtils.isEmpty(this.beanPostProcessors)) { for (DestructionAwareBeanPostProcessor processor : this.beanPostProcessors) { processor.postProcessBeforeDestruction(this.bean, this.beanName); } } // 2. 执行DisposableBean.destroy() if (this.invokeDisposableBean) { ((DisposableBean) this.bean).destroy(); } // 3. 执行destroy-method if (this.destroyMethod != null) { invokeCustomDestroyMethod(this.destroyMethod); } else if (this.destroyMethodName != null) { Method methodToCall = determineDestroyMethod(this.destroyMethodName); if (methodToCall != null) { invokeCustomDestroyMethod(methodToCall); } } }
Java

6.4.3 解决生命周期方法冲突的最佳实践

  1. 统一初始化入口
      • 只使用一种初始化方式(推荐@PostConstruct,注解方式更直观)
      • 若必须多种方式,确保它们的职责明确(如@PostConstruct处理依赖,init-method处理资源)
  1. 设置初始化标记
      • 避免重复初始化:
@Component public class ConflictingInitBean implements InitializingBean { private boolean initialized = false; @PostConstruct public void postConstruct() { if (!initialized) { commonInit(); } } @Override public void afterPropertiesSet() throws Exception { if (!initialized) { commonInit(); } } @Bean(initMethod = "initMethod") public ConflictingInitBean conflictingInitBean() { return new ConflictingInitBean(); } public void initMethod() { if (!initialized) { commonInit(); } } private void commonInit() { // 统一初始化逻辑 initialized = true; } }
Java
  1. 利用@Order控制多个@PostConstruct方法顺序
      • 同一类中的多个@PostConstruct方法执行顺序不确定,需合并为一个方法
  1. 避免在初始化方法中抛出异常
      • 异常会导致Bean初始化失败,容器可能无法启动
      • 应捕获异常并记录,确保初始化流程完成

6.4.4 生命周期方法执行时序图(含冲突解决)

sequenceDiagram participant 容器 as AbstractAutowireCapableBeanFactory participant 后置处理器 as CommonAnnotationBeanPostProcessor participant Bean as 目标Bean(含多种初始化方法) 容器->>Bean: 实例化完成,属性注入完成 容器->>后置处理器: 调用postProcessBeforeInitialization(Bean) 后置处理器->>Bean: 调用@PostConstruct方法 Bean->>Bean: 执行commonInit(),标记initialized=true 后置处理器-->>容器: 返回Bean 容器->>Bean: 调用InitializingBean.afterPropertiesSet() Bean->>Bean: 检查initialized,跳过重复初始化 容器->>Bean: 调用init-method Bean->>Bean: 检查initialized,跳过重复初始化 容器->>容器: Bean初始化完成
目标Bean(含多种初始化方法)CommonAnnotationBeanPostProcessorAbstractAutowireCapableBeanFactory目标Bean(含多种初始化方法)CommonAnnotationBeanPostProcessorAbstractAutowireCapableBeanFactory实例化完成,属性注入完成调用postProcessBeforeInitialization(Bean)调用@PostConstruct方法执行commonInit(),标记initialized=true返回Bean调用InitializingBean.afterPropertiesSet()检查initialized,跳过重复初始化调用init-method检查initialized,跳过重复初始化Bean初始化完成
Mermaid
 

七、Bean生命周期与作用域的最佳实践

7.1 作用域选择的决策指南

:在实际开发中,如何根据业务场景选择合适的Bean作用域?不同作用域的性能开销和资源占用有何差异?
:作用域的选择需平衡业务需求、性能和资源消耗。单例Bean适合无状态服务,原型适合有状态对象,Web作用域适合与请求/会话绑定的数据。错误的选择可能导致线程安全问题或性能瓶颈。

7.1.1 作用域选择决策树

  1. 是否为无状态组件?
      • 是 → 选择单例(singleton)
      • 否 → 进入下一步
  1. 状态是否与当前HTTP请求绑定?
      • 是 → 选择请求(request)
      • 否 → 进入下一步
  1. 状态是否与用户会话绑定?
      • 是 → 选择会话(session)
      • 否 → 进入下一步
  1. 是否需要每次使用都创建新实例?
      • 是 → 选择原型(prototype)
      • 否 → 考虑自定义作用域

7.1.2 各作用域的性能对比与资源消耗

作用域
实例创建开销
内存占用
垃圾回收压力
线程安全保障
典型性能瓶颈
singleton
一次创建
需手动保证
共享资源竞争
prototype
每次创建
频繁创建销毁导致GC频繁
request
每请求一次
天然安全
请求量过大时实例过多
session
每会话一次
中高
会话内安全
会话数过多导致内存溢出

7.1.3 典型业务场景的作用域选择案例

  1. 用户认证服务(UserAuthService)
      • 无状态(仅验证令牌,不存储用户状态)→ 单例
  1. 购物车(ShoppingCart)
      • 与用户会话绑定 → 会话作用域
  1. 订单创建命令(CreateOrderCommand)
      • 有状态(包含订单详情),每次创建新订单 → 原型
  1. 请求日志上下文(RequestLogContext)
      • 与当前请求绑定 → 请求作用域
  1. 全局缓存管理器(GlobalCacheManager)
      • 应用级单例 → 单例(或应用作用域)

7.1.4 作用域选择的反模式与陷阱

  1. 过度使用单例
      • 将有状态逻辑放入单例Bean,导致线程安全问题
      • 示例:单例Bean包含SimpleDateFormat(非线程安全)
  1. 滥用原型Bean
      • 频繁创建重量级原型Bean(如数据库连接),未复用
      • 解决方案:结合对象池(如Apache Commons Pool)
  1. 会话Bean存储大对象
      • 存储大量数据(如文件内容)导致会话体积过大
      • 解决方案:仅存储ID,使用时从数据库加载
  1. 在非Web环境使用request/session作用域
      • 导致IllegalStateException(无Web上下文)
      • 解决方案:使用@ConditionalOnWebApplication条件注解

7.2 生命周期管理的最佳实践

:在实际开发中,如何合理使用Bean的生命周期方法?初始化和销毁方法中适合执行哪些操作?应避免哪些危险操作?
:生命周期方法应专注于资源管理(初始化时获取资源,销毁时释放),避免复杂业务逻辑。需确保方法幂等、无副作用,且不依赖容器状态。

7.2.1 初始化方法(@PostConstruct/InitializingBean)的适用操作

  1. 资源获取
      • 数据库连接池初始化
      • 缓存预热(加载基础数据)
      • 网络连接建立(如WebSocket连接)
@PostConstruct public void init() { // 初始化连接池 HikariConfig config = new HikariConfig(); config.setJdbcUrl("jdbc:mysql://localhost:3306/test"); dataSource = new HikariDataSource(config); // 缓存预热 loadBaseDataToCache(); }
Java
  1. 配置验证
      • 检查必要配置项是否存在
      • 验证依赖服务是否可用
  1. 定时器启动
      • 启动后台定时任务(如数据同步)

7.2.2 销毁方法(@PreDestroy/DisposableBean)的适用操作

  1. 资源释放
      • 关闭数据库连接
      • 释放文件句柄
      • 停止线程池
@PreDestroy public void destroy() { if (dataSource instanceof HikariDataSource) { ((HikariDataSource) dataSource).close(); // 关闭连接池 } executorService.shutdown(); // 关闭线程池 }
Java
  1. 状态保存
      • 将内存中的临时数据持久化到磁盘
  1. 清理操作
      • 移除临时文件
      • 注销监听器

7.2.3 生命周期方法中的危险操作与禁忌

  1. 避免在初始化方法中调用其他Bean的方法
      • 被调用Bean可能尚未初始化完成,导致空指针
  1. 禁止在初始化方法中启动长时间阻塞操作
      • 会导致容器启动卡住(如无限循环、未设置超时的网络请求)
  1. 销毁方法中避免抛出异常
      • 异常会被容器捕获,但可能导致资源未完全释放
  1. 不依赖生命周期方法的执行顺序
      • 不同Bean的初始化/销毁顺序未明确定义,不应相互依赖
  1. 避免在原型Bean中使用@PreDestroy
      • 容器不会自动调用,需手动触发

7.2.4 生命周期管理的进阶技巧

  1. 使用@DependsOn控制初始化顺序
@Component @DependsOn("databaseInitializer") // 确保databaseInitializer先初始化 public class CacheManager { @PostConstruct public void init() { // 依赖数据库初始化完成 } }
Java
  1. 结合SmartLifecycle实现阶段式启动
      • 支持启动/停止的顺序控制,适合复杂系统
  1. 使用ApplicationListener监听容器事件
      • ContextRefreshedEvent:容器初始化完成后触发
      • ContextClosedEvent:容器关闭前触发
@Component public class StartupListener implements ApplicationListener<ContextRefreshedEvent> { @Override public void onApplicationEvent(ContextRefreshedEvent event) { // 容器完全初始化后执行(所有Bean都已就绪) } }
Java

7.3 加载机制优化与性能调优

:在大型Spring应用中,Bean加载机制可能成为性能瓶颈,有哪些优化手段?如何减少容器启动时间和内存占用?
:加载机制优化可从减少扫描范围、合理使用条件注解、优化依赖注入、控制预实例化等方面入手。大型应用可通过模块化、懒加载、排除不必要Bean等方式提升启动性能。

7.3.1 减少组件扫描范围

问题@ComponentScan默认扫描指定包及其子包,范围过大会导致扫描时间长、BeanDefinition过多。
优化方案
  1. 精确指定扫描包
@SpringBootApplication(scanBasePackages = { "com.example.service", "com.example.repository" }) // 仅扫描必要包 public class Application { ... }
Java
  1. 排除不需要的组件
@ComponentScan(excludeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Controller.class) }) // 排除Controller(如在服务层模块)
Java
  1. 使用@Indexed加速扫描
      • Spring 5.0+支持,编译时生成索引文件META-INF/spring.components
      • 减少运行时类路径扫描时间
@Indexed // 为组件生成索引 @Component public class MyService { ... }
Java

7.3.2 优化依赖注入与减少循环依赖

优化方案
  1. 优先使用构造函数注入
      • 明确依赖关系,避免字段注入的隐藏依赖
      • 帮助在编译时发现缺少的依赖
  1. 减少依赖层级
      • 过长的依赖链(A→B→C→D)会增加初始化时间
      • 拆分大Bean,减少单个Bean的依赖数量
  1. 通过设计消除循环依赖
      • 引入中间接口或事件驱动模式
      • 例:A和B相互依赖 → 改为A发布事件,B订阅事件

7.3.3 控制单例Bean的预实例化

优化方案
  1. 对非关键Bean使用@Lazy
      • 仅在首次使用时初始化,减少启动时间
      • 适合后台任务、报表生成等非启动必需Bean
  1. 批量处理预实例化
      • 对启动必需的单例Bean,确保其依赖的Bean也非延迟加载
      • 避免启动时的串行初始化,改为并行(需注意线程安全)
  1. 使用spring.main.lazy-initialization=true全局延迟
      • Spring Boot配置,所有Bean默认延迟加载(按需初始化)

7.3.4 条件注解与动态注册的性能优化

优化方案
  1. 尽早排除不符合条件的Bean
      • 使用@ConditionalOnClass在类加载阶段排除
      • 避免无效BeanDefinition的后续处理
  1. 动态注册Bean时的懒加载
      • 通过BeanDefinitionRegistryPostProcessor注册的Bean,设置lazyInit=true
      • 仅在需要时才实例化
  1. 缓存条件判断结果
      • 自定义Condition时,缓存重复的判断结果(如文件是否存在)

7.3.5 加载机制性能调优的监控与工具

  1. Spring Boot Actuator
      • beans端点:查看Bean数量、作用域、依赖关系
      • conditions端点:查看条件注解的匹配结果
  1. JVM工具
      • jvisualvm:监控容器启动时的类加载和实例化时间
      • jprofiler:分析Bean初始化的热点方法
  1. 自定义启动监听器
      • 记录关键阶段的耗时:
@Component public class StartupTimeListener implements ApplicationListener<ApplicationEvent> { private long scanStartTime; private long instantiateStartTime; @Override public void onApplicationEvent(ApplicationEvent event) { if (event instanceof ApplicationStartingEvent) { scanStartTime = System.currentTimeMillis(); } else if (event instanceof ContextRefreshedEvent) { long totalTime = System.currentTimeMillis() - scanStartTime; System.out.println("容器启动总耗时:" + totalTime + "ms"); } } }
Java

7.4 与Spring Boot自动配置的结合实践

:Spring Boot的自动配置是如何管理Bean的生命周期和作用域的?如何通过自定义自动配置覆盖默认行为?
:Spring Boot自动配置通过@Conditional注解动态注册Bean,其生命周期和作用域与手动配置的Bean一致。可通过@AutoConfigureBefore/@AutoConfigureAfter控制顺序,或使用@Primary覆盖默认Bean。

7.4.1 自动配置中的Bean生命周期管理

自动配置的Bean定义方式
@Configuration @ConditionalOnClass(DataSource.class) public class DataSourceAutoConfiguration { @Bean @ConditionalOnMissingBean public DataSource dataSource() { return new HikariDataSource(); } @Bean public DataSourceInitializer dataSourceInitializer(DataSource dataSource) { DataSourceInitializer initializer = new DataSourceInitializer(); initializer.setDataSource(dataSource); return initializer; } }
Java
生命周期管理
  • 自动配置的Bean同样支持@PostConstruct@PreDestroy等注解
  • 可通过@Bean(initMethod = "init", destroyMethod = "close")指定生命周期方法

7.4.2 自动配置中的作用域设置

示例:自动配置请求作用域的Bean:
@Configuration @ConditionalOnWebApplication public class WebAutoConfiguration { @Bean @Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS) public RequestContext requestContext() { return new RequestContext(); } }
Java
覆盖默认作用域
  • 自定义配置类中重新定义Bean,指定不同作用域:
@Configuration public class CustomWebConfig { @Bean @Scope("prototype") // 覆盖自动配置的request作用域 public RequestContext requestContext() { return new CustomRequestContext(); } }
Java

7.4.3 自定义自动配置的最佳实践

  1. 使用@Conditional确保条件明确
      • 避免无条件注册Bean,导致冲突
      • 例:@ConditionalOnMissingBean确保仅在用户未自定义时生效
  1. 控制自动配置顺序
@Configuration @AutoConfigureAfter(DataSourceAutoConfiguration.class) // 确保数据源先配置 public class MyJpaAutoConfiguration { ... }
Java
  1. 通过META-INF/spring.factories注册自动配置
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\\ com.example.autoconfigure.MyAutoConfiguration
Plain text
  1. 允许用户覆盖配置
      • 始终使用@ConditionalOnMissingBean
      • 提供@ConfigurationProperties绑定用户配置
@ConfigurationProperties(prefix = "my.service") public class MyServiceProperties { private int timeout = 5000; // 默认值 // getter/setter } @Configuration @EnableConfigurationProperties(MyServiceProperties.class) public class MyServiceAutoConfiguration { @Bean public MyService myService(MyServiceProperties properties) { MyService service = new MyService(); service.setTimeout(properties.getTimeout()); return service; } }
Java

7.4.4 自动配置与自定义Bean的集成时序图

sequenceDiagram participant 启动类 as @SpringBootApplication participant 自动配置类 as DataSourceAutoConfiguration participant 自定义配置 as CustomDataSourceConfig participant 注册器 as BeanDefinitionRegistry 启动类->>启动类: 启用自动配置@EnableAutoConfiguration 启动类->>自动配置类: 加载自动配置类 自动配置类->>注册器: 尝试注册DataSource(@ConditionalOnMissingBean) 自定义配置->>注册器: 注册自定义DataSource(优先级更高) 注册器->>自动配置类: 检测到已存在DataSource,跳过注册 注册器->>自定义配置: 完成自定义DataSource注册 启动类->>启动类: 所有Bean注册完成,开始实例化
BeanDefinitionRegistryCustomDataSourceConfigDataSourceAutoConfiguration@SpringBootApplicationBeanDefinitionRegistryCustomDataSourceConfigDataSourceAutoConfiguration@SpringBootApplication启用自动配置@EnableAutoConfiguration加载自动配置类尝试注册DataSource(@ConditionalOnMissingBean)注册自定义DataSource(优先级更高)检测到已存在DataSource,跳过注册完成自定义DataSource注册所有Bean注册完成,开始实例化
Mermaid

八、Spring Bean机制的未来发展与趋势

8.1 Spring 6.x与Spring Boot 3.x中的Bean机制变化

:Spring 6.x和Spring Boot 3.x作为最新版本,在Bean的生命周期、作用域和加载机制上有哪些重要更新?这些变化对开发者有何影响?
:Spring 6.x和Spring Boot 3.x基于Jakarta EE 9+,在保持核心机制稳定的同时,引入了对GraalVM原生镜像的支持、响应式Bean处理增强、以及更严格的类型检查。这些变化要求开发者更注重Bean的初始化效率和无状态设计,同时为云原生环境提供了更好的适配。

8.1.1 GraalVM原生镜像对Bean加载机制的影响

核心变化
  • * Ahead-of-Time(AOT)编译**:在构建时预先生成BeanDefinition和初始化逻辑,替代运行时的反射处理
  • 减少反射依赖:要求Bean的构造函数、方法和字段必须可访问(非private),否则需通过@RegisterReflectionForBinding显式注册
  • 初始化时机提前:许多运行时操作(如组件扫描、条件判断)移至构建时,减少启动时间
对Bean加载的优化
// AOT模式下,@Configuration类会被编译为字节码,直接注册BeanDefinition @Configuration public class AotConfig { @Bean public MyService myService() { return new MyService(); } } // AOT处理后生成的代码(简化): public class AotConfig__BeanDefinitions { public static void registerBeanDefinitions(BeanDefinitionRegistry registry) { RootBeanDefinition def = new RootBeanDefinition(MyService.class); registry.registerBeanDefinition("myService", def); } }
Java
开发者注意事项
  • 避免在Bean初始化中使用动态反射(如Class.forName()
  • 自定义BeanPostProcessor需在AOT处理中注册
  • 原型Bean和@Lazy在原生镜像中仍受支持,但初始化逻辑更严格

8.1.2 Jakarta EE迁移对Bean生命周期的影响

核心变化
  • javax包迁移至jakarta包(如jakarta.annotation.PostConstruct替代javax.annotation.PostConstruct
  • @PreDestroy@PostConstruct的处理逻辑保持不变,但需更新依赖
迁移示例
// Spring 5.x import javax.annotation.PostConstruct; // Spring 6.x import jakarta.annotation.PostConstruct; // 包名变更,功能不变 @Component public class JakartaMigrationBean { @PostConstruct public void init() { ... } }
Java
影响
  • 第三方库需同步迁移至Jakarta EE,否则可能导致注解不生效
  • 自定义BeanPostProcessor若依赖javax相关类,需更新为jakarta对应类

8.1.3 响应式编程对Bean生命周期的扩展

核心变化
  • 响应式初始化支持:允许@PostConstruct方法返回MonoFlux,容器会等待响应式流完成后再标记Bean就绪
  • Reactive Scope:实验性的响应式作用域,结合Context管理Bean实例,适合WebFlux应用
示例
@Component public class ReactiveInitBean { private final WebClient webClient; private List<String> data; @Autowired public ReactiveInitBean(WebClient webClient) { this.webClient = webClient; } @PostConstruct public Mono<Void> init() { // 响应式初始化:从远程加载数据 return webClient.get() .uri("/initial-data") .retrieve() .bodyToFlux(String.class) .collectList() .doOnNext(list -> this.data = list) .then(); } }
Java
处理逻辑
  • 容器通过ReactiveBeanPostProcessor处理响应式初始化方法
  • 等待Mono完成后再执行后续生命周期步骤
  • 若初始化失败,会触发ContextClosedEvent并销毁已创建的Bean

8.1.4 未来版本可能的演进方向

  1. 更智能的条件注解:结合上下文感知优化条件判断逻辑,自动识别不必要的Bean
  1. 动态作用域增强:支持基于时间、请求参数的动态作用域定义
  1. 内存高效的原型Bean管理:引入对象池机制优化原型Bean的创建销毁开销
  1. 与云原生环境深度集成:如Kubernetes Pod作用域、服务网格上下文感知
 

8.2 响应式编程与Bean生命周期的协同

:在响应式编程模型(如Spring WebFlux)中,Bean的生命周期管理与传统Spring MVC有何不同?响应式Bean的作用域如何设计才能保证线程安全和性能?
:响应式编程模型下,Bean的生命周期需适应非阻塞、事件驱动的特点,初始化和销毁方法可返回响应式类型(Mono/Flux)。响应式Bean通常设计为无状态,作用域以单例为主,结合Context传递请求/会话信息,避免使用线程绑定的Web作用域。

8.2.1 响应式Bean的初始化与销毁

核心特性
  • 非阻塞初始化@PostConstruct方法可返回Mono<Void>,容器会订阅并等待完成
  • 资源释放的响应式处理@PreDestroy方法返回Mono<Void>,确保异步资源(如HttpClient)正确关闭
示例
@Component public class ReactiveResourceBean { private final HttpClient httpClient; public ReactiveResourceBean() { this.httpClient = HttpClient.create(); } @PostConstruct public Mono<Void> connect() { return httpClient.connect() .doOnSubscribe(s -> System.out.println("连接建立")) .then(); } @PreDestroy public Mono<Void> disconnect() { return httpClient.dispose() .doOnTerminate(() -> System.out.println("连接关闭")); } }
Java
处理时序
  1. 容器实例化Bean后,调用connect()并订阅返回的Mono
  1. 等待Mono完成(连接建立),Bean标记为就绪
  1. 容器关闭时,调用disconnect()并等待资源释放完成

8.2.2 响应式环境下的作用域设计

推荐实践
  • 优先使用单例:响应式Bean通常无状态,单例可避免频繁创建开销
  • 避免使用request/session作用域:WebFlux基于Netty事件循环,无线程绑定的请求上下文
  • 使用Context传递请求信息:替代request作用域,通过Reactor Context在响应式流中传递上下文
示例
@Service public class ReactiveUserService { public Mono<User> getUser() { // 从Reactor Context获取用户ID(替代request作用域) return Mono.deferContextual(ctx -> { String userId = ctx.get("USER_ID"); return fetchUserById(userId); }); } private Mono<User> fetchUserById(String userId) { // 从数据库获取用户 } } // 控制器中设置Context @RestController public class UserController { @GetMapping("/user") public Mono<ResponseEntity<User>> getUser(ServerWebExchange exchange) { String userId = exchange.getRequest().getHeaders().getFirst("X-USER-ID"); return reactiveUserService.getUser() .contextWrite(Context.of("USER_ID", userId)) .map(ResponseEntity::ok); } }
Java

8.2.3 响应式与命令式Bean的混合管理

挑战
  • 命令式Bean(如@Controller)依赖响应式Bean(如@Service返回Mono
  • 响应式Bean中调用阻塞操作可能导致事件循环阻塞
解决方案
  1. 使用publishOn切换线程池
@Service public class MixedService { private final BlockingRepository repository; public Mono<Data> getData() { // 在IO线程池执行阻塞操作 return Mono.fromCallable(() -> repository.blockingGet()) .publishOn(Schedulers.boundedElastic()); } }
Java
  1. 通过@Lazy延迟依赖注入
      • 避免响应式Bean初始化时依赖未就绪的命令式Bean
  1. 使用ReactiveAdapterRegistry适配类型
      • 自动转换命令式返回值为响应式类型

8.2.4 响应式Bean生命周期时序图

sequenceDiagram participant 容器 as WebFluxApplicationContext participant 后置处理器 as ReactiveBeanPostProcessor participant Bean as 响应式Bean(含Mono初始化方法) participant 资源 as 异步资源(如HttpClient) 容器->>Bean: 实例化完成,属性注入完成 容器->>后置处理器: 调用postProcessBeforeInitialization(Bean) 后置处理器->>Bean: 调用@PostConstruct方法(返回Mono) Bean->>资源: 发起异步连接请求 资源-->>Bean: 返回连接结果(Mono完成) Bean-->>后置处理器: Mono完成信号 后置处理器-->>容器: 返回就绪的Bean 容器->>容器: 响应式Bean可用 容器->>Bean: 接收关闭信号 容器->>Bean: 调用@PreDestroy方法(返回Mono) Bean->>资源: 发起异步断开请求 资源-->>Bean: 返回断开结果(Mono完成) Bean-->>容器: Mono完成信号 容器->>容器: 容器关闭
异步资源(如HttpClient)响应式Bean(含Mono初始化方法)ReactiveBeanPostProcessorWebFluxApplicationContext异步资源(如HttpClient)响应式Bean(含Mono初始化方法)ReactiveBeanPostProcessorWebFluxApplicationContext实例化完成,属性注入完成调用postProcessBeforeInitialization(Bean)调用@PostConstruct方法(返回Mono)发起异步连接请求返回连接结果(Mono完成)Mono完成信号返回就绪的Bean响应式Bean可用接收关闭信号调用@PreDestroy方法(返回Mono)发起异步断开请求返回断开结果(Mono完成)Mono完成信号容器关闭
Mermaid

8.3 云原生环境下的Bean机制适配

:在云原生环境(如Kubernetes)中,Spring Bean的生命周期和作用域需要做哪些调整?如何保证Bean在动态扩缩容、服务漂移场景下的稳定性?
:云原生环境要求Bean具备轻量、可迁移、弹性伸缩的特性。需通过缩短初始化时间、避免本地状态、使用分布式作用域、以及增强健康检查与优雅关闭机制来适配。Spring Cloud提供的@RefreshScope等特性进一步增强了Bean在动态环境下的灵活性。

8.3.1 缩短Bean初始化时间的优化策略

核心需求
  • 容器启动时间直接影响Kubernetes的就绪探针和扩缩容速度
  • 过长的初始化可能导致Pod被Kill(liveness探针失败)
优化手段
  1. 并行初始化
      • Spring Boot 2.6+支持spring.main.lazy-initialization=false时的并行Bean初始化
      • 通过@DependsOn控制依赖,无依赖的Bean并行启动
  1. 延迟初始化非核心Bean
      • 对监控、日志等非核心组件使用@Lazy,优先初始化业务Bean
  1. 预加载与缓存预热异步化
      • 初始化方法仅启动预热任务,不等待完成
@Component public class CloudNativeBean { @PostConstruct public void init() { // 异步执行缓存预热,不阻塞初始化 CompletableFuture.runAsync(this::preloadCache); } private void preloadCache() { // 耗时的缓存加载逻辑 } }
Java

8.3.2 分布式作用域与配置刷新

@RefreshScope的实现机制
  • 特殊作用域,当配置变更时自动刷新Bean实例
  • 通过RefreshScopeRefresher监听配置变更事件
  • 实现原理:创建代理对象,配置变更时销毁旧实例,下次访问时创建新实例
示例
@RestController @RefreshScope // 配置变更时自动刷新 public class ConfigurableController { @Value("${app.message:default}") private String message; @GetMapping("/message") public String getMessage() { return message; } }
Java
分布式会话作用域
  • 使用spring-session-data-redis@SessionScope的Bean存储在Redis
  • 实现跨Pod的会话共享,避免服务漂移导致的状态丢失

8.3.3 优雅关闭与资源释放

核心需求
  • Kubernetes删除Pod时,需保证Bean有足够时间完成当前请求并释放资源
  • 避免在关闭过程中接收新请求
实现手段
  1. 配置优雅关闭超时
# application.yml server: shutdown: graceful spring: lifecycle: timeout-per-shutdown-phase: 30s # 关闭阶段超时时间
YAML
  1. 通过@PreDestroy释放分布式资源
@Component public class KubernetesAwareBean { private final KubernetesClient client; @PreDestroy public void releaseResources() { // 通知Kubernetes删除相关资源(如Job) client.batch().v1().jobs() .withName("my-job") .delete(); } }
Java
  1. 监听ContextClosedEvent执行最终清理
@Component public class ShutdownListener implements ApplicationListener<ContextClosedEvent> { @Override public void onApplicationEvent(ContextClosedEvent event) { // 执行最终清理(如发送metrics) } }
Java

8.3.4 云原生Bean的健康检查与自我修复

集成Kubernetes探针
  1. 就绪探针(Readiness):依赖Bean的初始化状态
@Component public class ReadinessIndicator implements HealthIndicator { private volatile boolean ready = false; @PostConstruct public void init() { // 初始化完成后标记为就绪 this.ready = true; } @Override public Health health() { if (ready) { return Health.up().build(); } return Health.down().withDetail("reason", "initializing").build(); } }
Java
  1. 存活探针(Liveness):监控Bean的运行状态
@Component public class LivenessIndicator implements HealthIndicator { private final AtomicBoolean healthy = new AtomicBoolean(true); public void markUnhealthy() { healthy.set(false); } @Override public Health health() { return healthy.get() ? Health.up().build() : Health.down().build(); } }
Java

九、总结与展望

9.1 核心知识点梳理

:经过对Spring Bean生命周期、作用域和加载机制的深入剖析,哪些核心知识点是开发者必须掌握的?这些知识点如何帮助开发者编写更高效、更稳定的Spring应用?
:核心知识点包括:Bean生命周期的11个阶段及关键回调、三级缓存解决单例循环依赖的原理、作用域的底层实现与线程安全特性、Bean加载的扫描-注册-实例化流程、以及BeanPostProcessorFactoryBean的高级用法。掌握这些知识能帮助开发者:避免循环依赖和线程安全问题、优化容器启动性能、合理设计Bean的状态管理、以及快速定位和解决与Bean相关的疑难问题。

9.1.1 生命周期核心要点

  1. 阶段划分:从BeanDefinition注册到销毁,需牢记初始化前(@PostConstruct)、初始化(InitializingBean)、销毁(@PreDestroy)三个关键节点。
  1. 回调顺序@PostConstructInitializingBeaninit-method@PreDestroyDisposableBeandestroy-method
  1. 与作用域的关联:单例Bean的生命周期与容器一致,原型Bean需手动管理销毁,Web作用域与请求/会话生命周期绑定。

9.1.2 作用域选择核心要点

  1. 单例:优先用于无状态服务,需手动保证线程安全。
  1. 原型:用于有状态对象,注意避免频繁创建导致的性能问题。
  1. Web作用域:需配合代理模式(ScopedProxyMode)在单例Bean中使用。
  1. 自定义作用域:通过Scope接口实现,适用于批处理、分布式会话等场景。

9.1.3 加载机制核心要点

  1. 扫描与注册@ComponentScanBeanDefinitionRegistryPostProcessor是BeanDefinition的两大来源。
  1. 实例化与依赖注入:三级缓存解决单例循环依赖,AutowiredAnnotationBeanPostProcessor处理自动注入。
  1. 条件与延迟@Conditional控制Bean是否注册,@Lazy延迟单例Bean的实例化。

9.2 最佳实践总结

  1. 作用域选择
      • 无状态组件用单例,有状态组件用原型或Web作用域。
      • 避免单例持有Web作用域Bean,必要时使用代理。
  1. 生命周期管理
      • 初始化方法只做资源获取,销毁方法只做资源释放。
      • 避免在初始化中执行复杂业务逻辑或远程调用。
  1. 性能优化
      • 缩小@ComponentScan范围,使用@Indexed加速扫描。
      • 非核心Bean使用@Lazy,减少启动时间。
      • 避免不必要的循环依赖,通过设计优化而非依赖三级缓存。
  1. 云原生适配
      • 缩短初始化时间,支持优雅关闭。
      • 使用@RefreshScope应对配置动态变更。
      • 实现健康探针,适配Kubernetes的生命周期管理。

9.3 未来学习路径建议

  1. 深入源码
      • 阅读AbstractAutowireCapableBeanFactorydoCreateBean方法,理解实例化全流程。
      • 分析DefaultSingletonBeanRegistry的三级缓存实现,掌握循环依赖解决细节。
  1. 实践项目
      • 实现自定义作用域(如"定时任务作用域")。
      • 开发BeanPostProcessor处理特定注解(如自定义@LogExecution记录方法执行时间)。
      • 基于Spring Boot 3.x构建GraalVM原生镜像应用,解决AOT编译问题。
  1. 扩展技术栈
      • 学习Spring Cloud的@RefreshScope和配置中心集成。
      • 研究Spring WebFlux的响应式Bean生命周期管理。
      • 了解Jakarta EE 9+与Spring的兼容性处理。
通过对Spring Bean机制的深入理解和实践,开发者能更好地驾驭Spring框架,编写更高效、更稳定、更易维护的企业级应用。随着Spring生态的持续演进,Bean作为核心概念,其机制也将不断优化,为云原生、响应式等新兴场景提供更强大的支持。
复杂业务场景下利用Spring Bean机制的设计与实践深入排查:@Scope("prototype")与@RequestScope字段篡改问题全链路分析
Loading...
目录
0%
Honesty
Honesty
花有重开日,人无再少年.
统计
文章数:
120
目录
0%