Spring框架中Bean的生命周期:源碼解析與最佳實踐

第1章:Spring Bean生命周期概述

1.1 什么是Spring Bean生命周期?

定義:Spring Bean生命周期是指從Bean的創建、初始化、使用到銷毀的完整過程,由Spring容器嚴格管理 。核心思想是Spring容器通過IoC(控制反轉)和DI(依賴注入)機制,控制Bean的創建、配置和銷毀,開發者可通過擴展點(如BeanPostProcessorAware接口)插入自定義邏輯。

Spring管理的Bean生命周期與普通Java對象有本質區別。普通Java對象由程序員直接new創建,其生命周期完全由JVM垃圾回收機制管理,開發者無法精確控制初始化和銷毀時機。而Spring容器管理的Bean,其生命周期的每個關鍵節點都有明確的控制機制,開發者可以指定初始化方法和銷毀方法,或者通過實現特定接口來干預Bean的創建過程。

關鍵區別:Spring容器管理的Bean具有明確的創建和銷毀階段,且在這些階段提供了多個擴展點,允許開發者在特定時機執行自定義邏輯,例如資源初始化、權限校驗、日志記錄等。

1.2 為什么需要了解Bean生命周期?

必要性

  • 在正確時機執行關鍵操作:如初始化數據庫連接池、加載緩存、配置資源等,這些操作通常需要在Bean完全初始化后執行。
  • 避免隱式錯誤:了解生命周期有助于避免在構造函數中訪問未注入的依賴,防止NullPointerException
  • 理解擴展點機制:掌握BeanPostProcessor、Aware接口等擴展點的調用順序,有助于正確編寫自定義邏輯。
  • 解決常見問題:如循環依賴、資源泄漏、初始化失敗等問題,都需要基于對生命周期的理解來解決。

實際案例

  • 資源泄漏:如果未在@PostConstruct中關閉數據庫連接,可能導致資源泄漏。
  • 循環依賴:在單例Bean中使用@Autowired注入相互依賴的Bean時,Spring通過三級緩存機制解決循環依賴問題。
  • 作用域管理:在Web應用中,request作用域的Bean需要在請求結束時銷毀,而session作用域的Bean需要在會話結束時銷毀。
1.3 Spring容器的角色

IoC容器:Spring通過BeanFactory(基礎容器)和ApplicationContext(應用上下文,功能更豐富)管理Bean的生命周期。DI機制(依賴注入)是IoC的核心體現,它將對象的創建和依賴關系的管理交給Spring容器,而非由程序員顯式控制。

容器類型對比

容器類型特點適用場景
BeanFactory輕量級,僅提供基礎IoC功能非Web環境,資源受限場景
ApplicationContext功能豐富,支持國際化、事件發布、AOP等Web應用,需要完整Spring功能的場景

源碼片段:Spring容器的核心是AbstractApplicationContext類,其refresh()方法負責容器的完整初始化流程:

// AbstractApplicationContext.java
public void refresh() throws BeansException,惡性代碼

總結:Spring容器通過分層抽象(BeanFactory、ApplicationContext)和核心類(如DefaultListableBeanFactory)實現Bean生命周期管理,開發者可以通過擴展點(如BeanPostProcessor)在特定時機插入自定義邏輯。

第2章:生命周期的核心階段詳解

2.1 實例化(Instantiation)

定義:Spring容器通過反射或工廠方法創建Bean的原始對象(未填充屬性)。關鍵點包括:

  • 支持多種實例化方式
    • 無參構造函數
    • 帶參構造函數
    • 工廠方法(靜態或實例方法)
  • 作用域影響
    • 單例(Singleton)Bean在容器啟動時實例化
    • 原型(Prototype)Bean在每次調用getBean()時實例化
    • Web作用域(Request/Session)Bean在請求或會話開始時實例化

代碼示例

// 使用無參構造函數實例化
@Component
public class UserService {public UserService() {System.out.println("1. 實例化UserService");}
}// 使用工廠方法實例化
@Component
public class UserRepository {public static UserRepository create() {System.out.println("1. 工廠方法創建UserRepository");return new UserRepository();}
}

源碼解析:在AbstractAutowireCapableBeanFactory類中,doCreateBean()方法負責Bean的完整創建流程,其中createBeanInstance()方法負責實例化:

// AbstractAutowireCapableBeanFactory.java
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {// 確定實例化策略(無參構造、帶參構造或工廠方法)if (mbd.getFactoryBeanName() != null) {// 工廠方法實例化return EagerInitializationPolicy.eagerlyInitializeBean(mbd, beanName, args, getBeanFactory());}// 無參構造或帶參構造實例化return BeanUtils.instantiateClass(mbd ResolvedBeanClass, args, getBeanFactory().getBeanClassLoader());
}

面試考點

  • 構造函數注入與字段注入的區別?
  • 為什么單例Bean在容器啟動時實例化?
  • 如何自定義Bean的實例化方式?
2.2 屬性賦值(Populate Properties)

定義:為Bean的字段或方法注入依賴對象(如@Autowired@Value)。關鍵點包括:

  • 依賴注入方式
    • 構造器注入(推薦,避免循環依賴)
    • 字段注入(通過@Autowired注解)
    • Setter方法注入(通過@Autowired或XML配置)
  • 屬性賦值時機
    • 在實例化完成后、初始化方法執行前
    • 對于循環依賴,通過三級緩存機制提前暴露半成品Bean
  • 支持嵌套Bean的遞歸注入:Spring能夠處理Bean之間的復雜依賴關系

代碼示例

// 使用字段注入
@Component
public class UserRepository {@Value("${database.url}")private String url;
}// 使用Setter方法注入
@Component
public class UserService {private UserRepository userRepository;@Autowiredpublic void setUserRepository(UserRepository userRepository) {this.userRepository = userRepository;System.out.println("2. 字段注入UserRepository");}
}

源碼解析:在AbstractAutowireCapableBeanFactory類中,populateBean()方法負責屬性賦值:

// AbstractAutowireCapableBeanFactory.java
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {// 解析屬性值并注入PropertyValues pvs = (mbd.getResolvedLazyInit() ? new LazyPropertyValues() : mbd.getPropertyValues());applyPropertyValues(beanName, mbd, bw, pvs);// 處理循環依賴if (mbd.isSingleton() && this beanName != null && !mbd.isLazyInit()) {// 將半成品Bean放入三級緩存Object earlySingleton = getEarlySingletonReference(beanName, mbd, bw);// 將半成品Bean放入二級緩存this.earlySingletonObjects.put IfAbsent (beanName, earlySingleton);// 將半成品Bean放入三級緩存this singletonFactories.put IfAbsent (beanName, new ObjectFactory () {@Overridepublic Object.getObject() {return getSingleton beanName;}});}
}

常見問題

  • 循環依賴:當兩個Bean互相依賴時,Spring如何解決?
  • 依賴缺失:如果依賴的Bean尚未實例化,會拋出NoSuchBeanDefinitionException
  • 屬性賦值失敗:如果屬性類型不匹配或值無法解析,會拋出BeanInitializationException
2.3 初始化前處理(BeanPostProcessor Before)

定義:在Bean初始化方法(如@PostConstructInitializingBean.afterPropertiesSet()或自定義init-method之前,Spring容器調用所有BeanPostProcessorpostProcessBeforeInitialization()方法,允許開發者修改Bean屬性或狀態 。

關鍵點

  • 全局性:對容器中的所有Bean生效
  • 執行順序:由BeanPostProcessor的實現類決定,可通過實現PriorityOrderedOrdered接口控制執行順序?
  • 可修改Bean:返回值可以替換原始Bean(如AOP代理)
  • 可阻斷初始化:通過返回null阻斷后續初始化流程

代碼示例

// 自定義BeanPostProcessor
@Component
public class LoggingBeanPostProcessor implements BeanPostProcessor, PriorityOrdered {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) {System.out.println("3.1 [" + beanName + "] 初始化前處理");return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) {System.out.println("3.2 [" + beanName + "] 初始化后處理");return bean;}@Overridepublic int gettingOrder() {return HIGHEST_PRECEDENCE + 1; // 最高優先級}
}

源碼解析:在AbstractAutowireCapableBeanFactory類中,initializeBean()方法負責初始化流程,其中包含對BeanPostProcessor的調用:

// AbstractAutowireCapableBeanFactory.java
protected Object initializeBean(String beanName, Object bean, RootBeanDefinition mbd) {// 調用Aware接口if (mbd.getSingleton() != null) {// 調用Aware接口方法applyAwareInterfaces(bean, mbd);}// 調用BeanPostProcessor的postProcessBeforeInitializationboolean continueWithInitialization = true;if (mbd postProcessed) {continueWithInitialization = applyBeanPostProcessorsBeforeInitialization (bean, beanName);}if (continueWithInitialization) {// 執行初始化方法invokingInitMethods (bean, beanName, mbd);}// 調用BeanPostProcessor的postProcessAfterInitializationreturn applyBeanPostProcessorsAfterInitialization (bean, beanName);
}

面試考點

  • BeanPostProcessor的執行順序?
  • postProcessBeforeInitialization和postProcessAfterInitialization的區別?
  • BeanPostProcessor如何實現AOP代理?
2.4 初始化(Initialization)

定義:執行Bean的初始化方法,包括以下三種方式:

  1. 注解方式

    • @PostConstruct:標注在方法上,方法執行在屬性賦值之后、BeanPostProcessor的postProcessAfterInitialization()之前
    • @PreDestroy:標注在方法上,方法執行在Bean銷毀之前
  2. 接口方式

    • InitializingBean:實現afterPropertiesSet()方法
    • DisposableBean:實現destroy()方法
  3. 配置方式

    • XML配置中的init-method屬性
    • Java配置中的@Bean(initMethod="...")

關鍵點

  • 執行順序@PostConstruct?>?InitializingBean?>?init-method?
  • 單例Bean:初始化在容器啟動時完成
  • 非單例Bean:初始化在每次獲取Bean時完成

代碼示例

// 使用多種初始化方式
@Component
public class UserService implements InitializingBean {@Value("${system.version}")private String version;@PostConstructpublic void start() {System.out.println("4.1 @PostConstruct方法執行");}@Overridepublic void afterPropertiesSet() throws Exception {System.out.println("4.2 InitializingBean接口方法執行");}@Bean(initMethod = "init")public void init() {System.out.println("4.3 init-method配置方法執行");}
}

源碼解析:在AbstractAutowireCapableBeanFactory類中,invokingInitMethods()方法負責執行初始化方法:

// AbstractAutowireCapableBeanFactory.java
private void invokingInitMethods(String beanName, Object bean, RootBeanDefinition mbd) {// 調用初始化方法if (mbd.getInitMethod() != null) {// 調用自定義init-methodinvoker invoker = new invoker (mbd.getInitMethod(), bean.getClass());invoker invoker (bean);}// 調用InitializingBean接口方法if (bean instanceof initializingBean) {((initializingBean) bean).afterPropertiesSet();}// 調用@PostConstruct注解方法if (mbd.getPostConstructMethod() != null) {invoker invoker = new invoker (mbd.getPostConstructMethod(), bean.getClass());invoker invoker (bean);}
}

常見問題

  • 初始化失敗:如果初始化方法拋出異常,Bean將無法使用
  • 循環依賴:在初始化階段處理循環依賴
  • 作用域限制:非單例Bean的初始化方法在每次獲取Bean時執行
2.5 初始化后處理(BeanPostProcessor After)

定義:在Bean初始化方法執行完畢后,Spring容器調用所有BeanPostProcessorpostProcessAfterInitialization()方法,允許開發者對Bean進行最終修改或增強 。

關鍵點

  • AOP代理生成:Spring AOP通過postProcessAfterInitialization()方法為Bean生成代理對象
  • 全局性:對容器中的所有Bean生效
  • 可修改Bean:返回值可以替換原始Bean(如動態代理)
  • 可阻斷使用:通過返回null阻斷Bean的使用

代碼示例

// AOP代理示例
@Component
public class AuditProxyPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) {if (bean instanceof UserService) {// 創建代理對象return Proxy.newProxyInstance(bean.getClass().getClassLoader(),bean.getClass().getInterfaces(),(proxy, method, args) -> {System.out.println("5.1 [" + beanName + "] AOP代理方法執行");return method.invoke bean, args;});}return bean;}
}

源碼解析:在AbstractAutowireCapableBeanFactory類中,applyBeanPostProcessorsAfterInitialization()方法負責調用BeanPostProcessorpostProcessAfterInitialization()

// AbstractAutowireCapableBeanFactory.java
protected Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException {// 遍歷所有BeanPostProcessorfor (BeanPostProcessor processor : getBeanPostProcessors()) {// 調用postProcessAfterInitialization方法Object current = processor.postProcessAfterInitialization(existingBean, beanName);if (current == null) {return existingBean;}existingBean = current;}return existingBean;
}

面試考點

  • BeanPostProcessor的執行順序?
  • postProcessBeforeInitialization和postProcessAfterInitialization的區別?
  • 如何通過BeanPostProcessor實現AOP代理?
2.6 注冊銷毀回調(Destruction Callbacks)

定義:在Bean初始化完成后,Spring容器注冊Bean的銷毀回調方法,包括以下三種方式:

  1. 注解方式

    • @PreDestroy:標注在方法上,方法執行在Bean銷毀之前
  2. 接口方式

    • DisposableBean:實現destroy()方法
  3. 配置方式

    • XML配置中的destroy-method屬性
    • Java配置中的@Bean(destroyMethod="...")

關鍵點

  • 單例Bean:銷毀回調在容器關閉時觸發
  • 非單例Bean:銷毀回調通常由開發者手動觸發
  • 執行順序@PreDestroy?>?DisposableBean?>?destroy-method?

代碼示例

// 使用多種銷毀方式
@Component
public class DatabasePool implements DisposableBean {@PreDestroypublic void close() {System.out.println("6.1 @PreDestroy方法執行");}@Overridepublic void destroy() throws Exception {System.out.println("6.2 DisposableBean接口方法執行");}@Bean(destroyMethod = "shutdown")public void shutdown() {System.out.println("6.3 destroy-method配置方法執行");}
}

源碼解析:在AbstractBeanFactory類中,registerDestructionCallback()方法負責注冊銷毀回調:

// AbstractBeanFactory.java
public void registerDestructionCallback(String beanName, Runnable callback) {// 將銷毀回調放入銷毀回調Mapthis.destructionCallbacks.put IfAbsent (beanName, callback);
}// AbstractApplicationContext.java
public void close() throws惡性代碼 {// 遍歷所有Bean并執行銷毀方法for (String beanName : getBeanNamesForType(DisposableBean.class)) {DisposableBean disposableBean = (DisposableBean) getBean beanName;disposableBean.destroy();}// 執行自定義destroy-methodfor (String beanName : getBeanNamesForType(Object.class)) {BeanDefinition beanDefinition = getBeanDefinition beanName;if (beanDefinition.getDestroyMethod() != null) {invoker invoker = new invoker (beanDefinition.getDestroyMethod(), getBeanClass beanName);invoker invoker (getBean beanName);}}// 執行@PreDestroy注解方法for (String beanName : getBeanNamesForType(Object.class)) {if (isPreDestroyMethod beanName)) {invoker invoker = new invoker (getPreDestroyMethod beanName), getBeanClass beanName);invoker invoker (getBean beanName);}}
}

常見問題

  • 資源泄漏:如果未正確注冊銷毀回調,可能導致資源泄漏
  • 作用域限制:非單例Bean的銷毀回調需要手動觸發
  • 執行順序:銷毀方法的執行順序與創建順序相反
2.7 銷毀(Destruction)

定義:當Spring容器關閉時(如調用close()方法或服務器停止),容器調用已注冊的銷毀方法,釋放Bean占用的資源。

關鍵點

  • 單例Bean:銷毀在容器關閉時自動執行
  • 非單例Bean:銷毀需要手動觸發或通過作用域代理實現
  • 執行順序:與創建順序相反,先創建的Bean后銷毀

代碼示例

// 手動關閉容器
public class App {public static void main(String[] args) {// 創建Spring容器ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");// 獲取Bean并使用UserService userService = context.getBean(UserService.class);userService.saveUser();// 關閉容器,觸發銷毀方法context.close();}
}

源碼解析:在AbstractApplicationContext類中,close()方法負責容器關閉和Bean銷毀:

// AbstractApplicationContext.java
public void close() throws惡性代碼 {// 執行銷毀回調executeDestructionCallbacks();// 銷毀單例BeandestroySingletons();// 銷毀其他資源doClose();
}// 執行銷毀回調
private void executeDestructionCallbacks() {// 遍歷所有Bean并執行銷毀方法for (String beanName : getBeanNamesForType(Object.class)) {// 執行銷毀方法destroyBean beanName, getBean beanName);}
}

面試考點

  • 單例Bean和原型Bean的銷毀機制有何不同?
  • @PreDestroy和 disposableBean接口的執行順序?
  • 如何確保非單例Bean的銷毀方法被調用?

第3章:生命周期的擴展點與自定義

3.1 BeanPostProcessor的深度解析

定義BeanPostProcessor是Spring框架提供的一個擴展接口,允許開發者在Bean初始化的前后插入自定義邏輯 。

接口定義

// BeanPostProcessor.java
public interface BeanPostProcessor {@Nullabledefault Object postProcessBeforeInitialization(Object bean, String beanName) {return bean;}@Nullabledefault Object postProcessAfterInitialization(Object bean, String beanName) {return bean;}
}

關鍵子接口

  • InstantiationAwareBeanPostProcessor:允許在Bean實例化前后進行干預
  • InitializationAwareBeanPostProcessor:允許在Bean初始化前后進行干預
  • DestructionAwareBeanPostProcessor:允許在Bean銷毀前后進行干預

執行順序:Spring容器在調用BeanPostProcessor方法時,遵循以下順序:

  1. 實現PriorityOrdered接口的處理器(按優先級排序)
  2. 實現Ordered接口的處理器(按順序排序)
  3. 未實現排序接口的處理器(按注冊順序排序)

代碼示例:自定義BeanPostProcessor實現日志增強

// 自定義BeanPostProcessor
@Component
public class LoggingBeanPostProcessor implements BeanPostProcessor, PriorityOrdered {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) {System.out.println("[" + beanName + "] 初始化前處理");return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) {System.out.println("[" + beanName + "] 初始化后處理");return bean;}@Overridepublic int gettingOrder() {return HIGHEST_PRECEDENCE + 1; // 最高優先級}
}

源碼解析:在PostProcessorRegistrationDelegate類中,sortPostProcessors()方法負責排序BeanPostProcessor:

// PostProcessorRegistrationDelegate.java
private static List<BeanPostProcessor> sortPostProcessors(List<BeanPostProcessor> postProcessors) {// 創建排序器BeanPostProcessorSorter處理器排序器 = new BeanPostProcessorSorter();// 按優先級排序處理器排序器.sortByPriority postProcessors);// 按順序排序處理器排序器.sortByOrder postProcessors);// 返回排序后的列表return處理器排序器.getSortedPostProcessors();
}

應用場景

  • AOP代理:通過postProcessAfterInitialization()為Bean生成代理對象
  • 日志記錄:在Bean方法調用前后添加日志
  • 性能監控:在Bean方法調用前后記錄執行時間
  • 安全控制:在Bean方法調用前進行權限校驗

面試考點

  • BeanPostProcessor的執行順序?
  • postProcessBeforeInitialization和postProcessAfterInitialization的區別?
  • 如何通過BeanPostProcessor實現AOP代理?
3.2 BeanFactoryPostProcessor的作用與使用場景

定義BeanFactoryPostProcessor是Spring框架提供的一個擴展接口,允許開發者在Bean實例化之前修改Bean的定義(BeanDefinition) 。

接口定義

// BeanFactoryPostProcessor.java
public interface BeanFactoryPostProcessor {void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws惡性代碼;
}

關鍵子接口

  • BeanDefinitionRegistryPostProcessor:允許在Bean定義加載階段修改BeanDefinition

執行時機:在Bean定義加載完成后、Bean實例化之前執行 ,與BeanPostProcessor的執行階段不同。

代碼示例:修改Bean的屬性值

// 自定義BeanFactoryPostProcessor
@Component
public class PropertyModifyingPostProcessor implements BeanFactoryPostProcessor {@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {// 獲取特定Bean的定義BeanDefinition beanDefinition = beanFactory.getBeanDefinition("userService");// 修改Bean的屬性值beanDefinition.setPropertyValues(new PropertyValues(new PropertyValue("version", "6.0.11")));}
}

源碼解析:在AbstractApplicationContext類中,refresh()方法負責調用BeanFactoryPostProcessor:

// AbstractApplicationContext.java
public void refresh() throws惡性代碼 {// ...// 執行BeanFactoryPostProcessorinvokerBeanFactoryPostProcessors getBeanFactory());// ...// 執行BeanPostProcessorinvokerBeanPostProcessors getBeanFactory());// ...
}

應用場景

  • 動態修改Bean定義:如修改Bean的屬性值、作用域等
  • 掃描特定注解:如自動掃描并注冊MyBatis的Mapper接口
  • 條件化裝配:根據環境條件決定是否注冊某些Bean

面試考點

  • BeanFactoryPostProcessor和BeanPostProcessor的區別?
  • BeanDefinitionRegistryPostProcessor的作用?
  • 如何通過BeanFactoryPostProcessor修改Bean的屬性?
3.3 @PostConstruct與@PreDestroy的底層實現

定義@PostConstruct@PreDestroy是Spring框架提供的注解,用于在Bean的初始化和銷毀階段執行特定方法 。

注解定義

// PostConstruct.java
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PostConstruct {
}// PreDestroy.java
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PreDestroy {
}

底層實現:Spring通過CommonAnnotationBeanPostProcessor處理這些注解 :

// CommonAnnotationBeanPostProcessor.java
public class CommonAnnotationBeanPostProcessor extends AbstractBeanPostProcessorimplements BeanNameAware, BeanFactoryAware, InitializeBean, DisposableBean {// 處理PostConstruct注解private void processPostConstructMethods(Object bean) {// 獲取所有PostConstruct注解方法Method[] methods = getPostConstructMethods bean.getClass());for (Method method : methods) {// 執行方法invoker method.invoke bean);}}// 處理PreDestroy注解private void processPreDestroyMethods(Object bean) {// 獲取所有PreDestroy注解方法Method[] methods = getPreDestroyMethods bean.getClass());for (Method method : methods) {// 注冊銷毀回調registerDestructionCallback getBeanName(), () -> {method.invoke bean);});}}
}

執行順序:在Bean生命周期中,@PostConstruct方法的執行順序在InitializingBean.afterPropertiesSet()之后、自定義init-method之前。

代碼示例

// 使用@PostConstruct和@PreDestroy
@Component
public class CacheService {@PostConstructpublic void initCache() {System.out.println("緩存初始化完成");}@PreDestroypublic void clearCache() {System.out.println("緩存清理完成");}
}

面試考點

  • @PostConstruct和InitializingBean接口的執行順序?
  • @PreDestroy和 disposableBean接口的執行順序?
  • @PostConstruct和@PreDestroy在Spring 6.x中的行為有何變化?
3.4 InitializingBean與DisposableBean接口的適用性對比

對比表格

特性InitializingBean接口disposableBean接口@PostConstruct注解@PreDestroy注解init-method/destroy-method配置
實現方式實現接口實現接口使用注解使用注解在配置文件中指定
執行順序在屬性賦值之后、BeanPostProcessor之前在BeanPostProcessor之后在BeanPostProcessor之后在BeanPostProcessor之前在BeanPostProcessor之后
適用場景需要訪問其他Bean的場景需要訪問其他Bean的場景簡單初始化邏輯簡單銷毀邏輯需要明確指定方法名的場景
靈活性有限,只能使用固定方法名有限,只能使用固定方法名高,可以標注在任何方法上高,可以標注在任何方法上中等,需要在配置中指定方法名
代碼侵入性高,需要實現接口高,需要實現接口低,只需要添加注解低,只需要添加注解低,只需要在配置中指定方法名

源碼解析:在AbstractAutowireCapableBeanFactory類中,initializeBean()方法負責執行初始化方法:

// AbstractAutowireCapableBeanFactory.java
protected Object initializeBean(String beanName, Object bean, RootBeanDefinition mbd) {// ...// 調用Aware接口applyAwareInterfaces(bean, mbd);// 調用BeanPostProcessor的postProcessBeforeInitializationapplyBeanPostProcessorsBeforeInitialization (bean, beanName);// 執行初始化方法invokingInitMethods (bean, beanName, mbd);// 調用BeanPostProcessor的postProcessAfterInitializationreturn applyBeanPostProcessorsAfterInitialization (bean, beanName);
}

最佳實踐

  • 優先使用注解:如@PostConstruct@PreDestroy,因為它們代碼侵入性低
  • 避免循環依賴:在初始化方法中不要依賴其他正在初始化的Bean
  • 處理異常:在初始化和銷毀方法中捕獲并處理異常

面試考點

  • @PostConstruct和InitializingBean接口的執行順序?
  • @PreDestroy和 disposableBean接口的執行順序?
  • 如何解決循環依賴問題?
3.5 自定義初始化/銷毀方法(XML與注解配置)

XML配置方式

<!-- spring.xml -->
<bean id="databasePool" class="com.example.DatabasePool"init-method="init" destroy-method="destroy"><property name="url" value="${database.url}"/><property name="username" value="${database.username}"/><property name="password" value="${database.password}"/>
</bean>

注解配置方式

// 使用@Bean注解指定初始化和銷毀方法
@Configuration
public class AppConfig {@Bean(initMethod = "init", destroyMethod = "destroy")public DatabasePool databasePool() {return new DatabasePool();}
}

代碼示例:自定義初始化和銷毀方法

// 自定義Bean
public class DatabasePool {private String url;private String username;private String password;// 初始化方法public void init() {System.out.println("數據庫連接池初始化完成");}// 銷毀方法public void destroy() {System.out.println("數據庫連接池清理完成");}// Getter和Setter方法public String getUrl() {return url;}public void setUrl(String url) {this.url = url;}public String得到用戶名() {return username;}public void設置用戶名(String username) {this.username = username;}public String得到密碼() {return password;}public void設置密碼(String password) {this.password = password;}
}

源碼解析:在AbstractBeanFactory類中,registerBeanDefinition()方法負責注冊Bean的定義,包括初始化和銷毀方法:

// AbstractBeanFactory.java
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)throws惡性代碼 {// 檢查Bean名稱是否重復checkBeanName beanName);// 檢查Bean是否已經注冊check是否存在BeanDefinition beanName);// 注冊BeanDefinitionthis beanDefinitionMap.put beanName, beanDefinition);// 如果是單例Bean,注冊銷毀回調if (beanDefinition.getScope().equals(Scope.SCOPE-singleton)) {// 注冊銷毀回調registerDestructionCallback beanName, () -> {if (beanDefinition.getDestroyMethod() != null) {invoker invoker = new invoker (beanDefinition.getDestroyMethod(), getBeanClass beanName);invoker invoker (getBean beanName);}});}
}

應用場景

  • 資源初始化:如數據庫連接池、文件句柄等
  • 狀態加載:如從文件或數據庫加載配置
  • 監控配置:如初始化監控系統

面試考點

  • XML配置和注解配置銷毀方法的區別?
  • 如何確保自定義銷毀方法被調用?
  • 單例Bean和原型Bean的銷毀方法執行條件?

第4章:Bean作用域與生命周期管理

4.1 單例(Singleton)作用域的生命周期控制

定義:單例作用域是Spring的默認作用域,每個Spring容器中只會創建一個Bean實例,該實例在整個應用程序生命周期內共享。

關鍵點

  • 實例化時機:容器啟動時或首次獲取Bean時(取決于是否延遲初始化)
  • 屬性賦值時機:實例化后立即進行
  • 初始化時機:屬性賦值完成后立即進行
  • 銷毀時機:容器關閉時自動執行銷毀方法?

代碼示例

// 單例Bean配置
@Component
public class UserService {@Autowiredprivate UserRepository userRepository;@PostConstructpublic void init() {System.out.println("單例Bean初始化完成");}@PreDestroypublic void destroy() {System.out.println("單例Bean銷毀完成");}
}

源碼解析:在DefaultSingletonBeanRegistry類中,get Singleton()方法負責獲取單例Bean:

// DefaultSingletonBeanRegistry.java
public Object get Singleton(String beanName, boolean allowEarlyReference) {// 從一級緩存獲取Object singletonObject = this singletonObjects.get beanName;if (singletonObject == null) {// 從二級緩存獲取singletonObject = this earlySingletonObjects.get beanName;if (singletonObject == null && allowEarlyReference) {// 從三級緩存獲取ObjectFactory<?> singletonFactory = (ObjectFactory<?>) this singletonFactories.get beanName;if (singletonFactory != null) {// 創建半成品BeansingletonObject = singletonFactory.getObject();// 將半成品Bean放入二級緩存this.earlySingletonObjects.put beanName, singletonObject;// 將半成品Bean放入三級緩存this singletonFactories.remove beanName);}}}return singletonObject;
}

面試考點

  • 單例Bean的銷毀時機?
  • 如何確保單例Bean的線程安全性?
  • 單例Bean的循環依賴如何解決?
4.2 原型(Prototype)作用域的生命周期限制

定義:原型作用域表示每次請求Bean時,Spring容器都會創建一個新的Bean實例,這些實例之間是完全獨立的。

關鍵點

  • 實例化時機:每次調用getBean()方法時
  • 屬性賦值時機:實例化后立即進行
  • 初始化時機:屬性賦值完成后立即進行
  • 銷毀時機:Spring容器不管理,需要手動觸發銷毀方法?

代碼示例

// 原型Bean配置
@Component
@Scope("prototype")
public class prototypedBean {@PostConstructpublic void init() {System.out.println("原型Bean初始化完成");}@PreDestroypublic void destroy() {System.out.println("原型Bean銷毀完成");}
}

源碼解析:在AbstractBeanFactory類中,getBean()方法負責獲取Bean實例:

// AbstractBeanFactory.java
public <T> T getBean(String name, Class<T> requiredType) {// 如果是原型Bean,直接創建新實例if (getBeanDefinition(name).getScope().equals(Scope.SCOPE-prototype)) {return createBean(name, getBeanDefinition(name), null);}// 否則,獲取單例Beanreturn (T) getSingleton(name);
}

銷毀方法觸發方式

  1. 手動調用:在使用完原型Bean后,手動調用其銷毀方法
  2. 作用域代理:通過@Scope(proxyMode=TargetClass)啟用代理,使原型Bean支持銷毀方法?
  3. 第三方工具:如使用對象池管理原型Bean,由對象池自動觸發銷毀

面試考點

  • 原型Bean的銷毀機制?
  • 如何確保原型Bean的銷毀方法被調用?
  • 原型Bean和單例Bean的循環依賴如何解決?
4.3 Web作用域(Request/Session)的特殊處理

定義:Web作用域是Spring為Web應用提供的特殊作用域,包括request、session和global session。

關鍵點

  • request作用域:每個HTTP請求創建一個Bean實例,請求結束后自動銷毀?
  • session作用域:每個HTTP會話創建一個Bean實例,會話過期后自動銷毀?
  • global session作用域:僅適用于Portlet應用,基于全局會話創建Bean實例?

配置方式:在Web應用中,需要配置相應的監聽器或過濾器來支持Web作用域 :

<!-- web.xml -->
< listener >< listener-class > org.springframework.web.context.request.RequestContextListener </ listener-class >
</ listener >
< listener >< listener-class > org.springframework.web.context.session.SessionContextListener </ listener-class >
</ listener >

代碼示例:request作用域Bean

// request作用域Bean
@Component
@Scope("request")
public class RequestScopeBean {@PostConstructpublic void init() {System.out.println("request作用域Bean初始化完成");}@PreDestroypublic void destroy() {System.out.println("request作用域Bean銷毀完成");}
}

源碼解析:在RequestScope類中,get()方法負責獲取request作用域Bean:

// RequestScope.java
public Object get(String name, ObjectFactory<?> objectFactory) {// 獲取當前HTTP請求RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();// 從請求屬性中獲取BeanObject scopedObject = attributes.getAttribute(name, getScope());if (scopedObject == null) {// 創建新BeanscopedObject = objectFactory.getObject();// 將Bean放入請求屬性attributes.setAttribute(name, scopedObject, getScope());}return scopedObject;
}

銷毀機制

  1. request作用域:通過RequestContextListener監聽HTTP請求結束,自動銷毀Bean?
  2. session作用域:通過SessionContextListener監聽HTTP會話過期,自動銷毀Bean?

面試考點

  • Web作用域Bean的銷毀機制?
  • 如何確保Web作用域Bean的銷毀方法被調用?
  • request作用域和session作用域的區別?

第5章:源碼級解析與常見問題

5.1 核心類與方法(BeanDefinition、BeanFactory等)

BeanDefinition:Spring框架中表示Bean配置的元數據,包含Bean的類名、屬性值、依賴關系等信息 。

關鍵類

  • RootBeanDefinition:表示一個完整的Bean定義,包含所有配置信息
  • BeanDefinitionReader:負責讀取Bean定義的配置文件或注解
  • BeanDefinitionRegistry:管理Bean定義的注冊表,允許動態注冊和修改Bean定義

BeanFactory:Spring的IoC容器接口,負責管理Bean的生命周期 。

關鍵類

  • DefaultListableBeanFactory:Spring的默認BeanFactory實現,提供了完整的Bean管理功能
  • AbstractAutowireCapableBeanFactory:負責Bean的實例化、屬性賦值和初始化的抽象類
  • AbstractApplicationContext:Spring的應用上下文實現,提供了完整的容器管理功能

源碼解析:在AbstractAutowireCapableBeanFactory類中,doCreateBean()方法負責Bean的完整創建流程:

// AbstractAutowireCapableBeanFactory.java
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {// 實例化BeanBeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args);Object bean = instanceWrapper.getWrappedInstance();// 屬性賦值populateBean(beanName, mbd, instanceWrapper);// 初始化exposedObject = initializeBean(beanName, exposedObject, mbd);// 注冊銷毀回調registerDestructionCallback(beanName, () -> {if (exposedObject instanceof DisposableBean) {((DisposableBean) exposedObject).destroy();}if (mbd.getDestroyMethod() != null) {invoker invoker = new invoker (mbd.getDestroyMethod(), exposedObject.getClass());invoker invoker (exposedObject);}});return exposedObject;
}

面試考點

  • BeanDefinition的作用?
  • BeanFactory和ApplicationContext的區別?
  • doCreateBean()方法的執行流程?
5.2 循環依賴的解決機制與生命周期影響

定義:循環依賴是指兩個或多個Bean相互依賴形成閉環的情況,是Spring中一個經典問題 。

解決機制:Spring通過三級緩存(singletonObjectsearlySingletonObjectssingletonFactories)解決循環依賴問題。

三級緩存作用

  • singletonObjects(一級緩存):存儲完全初始化的單例Bean
  • earlySingletonObjects(二級緩存):存儲提前暴露的半成品Bean(已完成實例化但未完成初始化)
  • singletonFactories(三級緩存):存儲Bean工廠,用于生成早期Bean引用

源碼解析:在AbstractAutowireCapableBeanFactory類中,getEarlyBeanReference()方法負責處理循環依賴:

// AbstractAutowireCapableBeanFactory.java
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, BeanWrapper instanceWrapper) {// 獲取半成品BeanObject bean = instanceWrapper.getWrappedInstance();// 如果需要AOP代理if (mbd.isProxy()) {// 創建代理對象bean = getAopProxy(mbd, instanceWrapper, beanName).getProxy();}// 將半成品Bean放入二級緩存this.earlySingletonObjects.put IfAbsent (beanName, bean);// 將半成品Bean放入三級緩存this singletonFactories.put IfAbsent (beanName, new ObjectFactory () {@Overridepublic Object getObject() {return getSingleton beanName;}});return bean;
}

生命周期影響

  • 實例化階段:提前將半成品Bean放入三級緩存
  • 屬性賦值階段:通過三級緩存獲取半成品Bean引用
  • 初始化階段:延遲執行初始化方法,直到所有依賴注入完成

面試考點

  • Spring如何解決循環依賴問題?
  • 三級緩存的具體作用?
  • 構造器注入的循環依賴能否解決?
5.3 資源泄漏的排查與解決(如未關閉數據庫連接)

定義:資源泄漏是指Bean在銷毀時未正確釋放資源,導致資源(如數據庫連接、文件句柄等)無法回收。

常見場景

  • 數據庫連接泄漏:未在@PreDestroy中關閉數據庫連接
  • 文件句柄泄漏:未在@PreDestroy中關閉文件流
  • 網絡連接泄漏:未在@PreDestroy中關閉網絡連接

排查工具

  1. YourKit Java Profiler:實時監控內存使用情況,創建多個堆快照進行對比分析?
  2. Eclipse Memory Analyzer (MAT):分析堆轉儲文件,找出未釋放的資源
  3. lsof命令:檢查進程打開的文件句柄數?

解決方案

  1. 確保銷毀方法被調用:對于單例Bean,確保容器正常關閉;對于原型Bean,手動調用銷毀方法或啟用作用域代理?
  2. 使用try-with-resources:確保資源在使用后自動關閉?
  3. 實現 DisposableBean接口:在destroy()方法中釋放資源?
  4. 使用@PreDestroy注解:在方法上添加注解,確保資源釋放?

代碼示例:資源泄漏修復

// 修復前:資源未關閉
@Component
public class DatabaseService {private Connection connection;@PostConstructpublic void init() {try {connection = dataSource.getConnection();} catch (SQLException e) {e.printStackTrace();}}@PreDestroypublic void destroy() {if (connection != null) {try {connection.close();} catch (SQLException e) {e.printStackTrace();}}}
}

面試考點

  • 如何排查資源泄漏?
  • 如何確保資源在Bean銷毀時釋放?
  • 單例Bean和原型Bean的資源管理有何不同?
5.4 初始化失敗的調試技巧(日志分析與斷點調試)

定義:初始化失敗是指Bean在初始化過程中拋出異常,導致Bean無法使用。

常見原因

  • 依賴缺失:依賴的Bean尚未創建
  • 屬性值錯誤:屬性值無法解析或類型不匹配
  • 循環依賴:兩個Bean互相依賴,無法完成初始化
  • 配置錯誤:XML或注解配置錯誤
  • 資源訪問失敗:如無法連接數據庫

調試技巧

  1. 日志分析:啟用Spring的DEBUG級別日志,查看Bean初始化的詳細過程
  2. 斷點調試:在IDE中設置斷點,跟蹤Bean初始化的執行流程
  3. 異常堆棧分析:查看拋出的異常信息,定位具體問題
  4. 使用@DependsOn:明確指定Bean的依賴順序
  5. 使用@Lazy:延遲初始化Bean,避免循環依賴

源碼解析:在AbstractAutowireCapableBeanFactory類中,initializeBean()方法負責執行初始化方法,如果拋出異常,會記錄日志并拋出BeanInitializationException

// AbstractAutowireCapableBeanFactory.java
protected Object initializeBean(String beanName, Object bean, RootBeanDefinition mbd) {// ...try {// 執行初始化方法invokingInitMethods (bean, beanName, mbd);} catch (惡性代碼 ex) {// 記錄異常String msg = "Initialization of bean failed";throw new惡性代碼 (msg, ex);}// ...
}

面試考點

  • 如何調試Bean初始化失敗的問題?
  • 如何查看Spring容器的初始化日志?
  • 如何解決BeanCurrentlyInCreationException異常?

第6章:實際開發中的最佳實踐

6.1 如何選擇初始化/銷毀方法的實現方式

選擇建議

  • 優先使用注解:如@PostConstruct@PreDestroy,因為它們代碼侵入性低
  • 避免實現接口:如InitializingBeanDisposableBean,因為它們代碼侵入性高
  • 慎用init-method/destroy-method:僅在無法使用注解時使用

適用場景

  • 注解方式:簡單初始化邏輯,如加載配置、初始化資源等
  • 接口方式:需要訪問其他Bean的場景,如依賴注入后的復雜初始化
  • 配置方式:需要明確指定方法名的場景,如第三方庫的Bean

代碼示例:不同方式的使用場景

// 注解方式:簡單初始化
@Component
public class CacheService {@PostConstructpublic void initCache() {System.out.println("緩存初始化完成");}@PreDestroypublic void clearCache() {System.out.println("緩存清理完成");}
}// 接口方式:需要訪問其他Bean
@Component
public class UserService implements InitializingBean, DisposableBean {@Autowiredprivate UserRepository userRepository;@Overridepublic void afterPropertiesSet() throws惡性代碼 {// 使用其他BeanuserRepository.loadUsers();System.out.println("UserService初始化完成");}@Overridepublic void destroy() throws惡性代碼 {// 使用其他BeanuserRepository.close();System.out.println("UserService銷毀完成");}
}// 配置方式:第三方庫的Bean
@Configuration
public class AppConfig {@Bean(initMethod = "init", destroyMethod = "destroy")public ThirdPartyService thirdPartyService() {return new ThirdPartyService();}
}

面試考點

  • @PostConstruct和InitializingBean接口的執行順序?
  • @PreDestroy和 disposableBean接口的執行順序?
  • 如何解決循環依賴問題?
6.2 避免在構造函數中訪問未注入的依賴

問題描述:在構造函數中訪問未注入的依賴會導致NullPointerExceptionBeanCurrentlyInCreationException異常 。

原因分析:依賴注入在構造函數之后進行,因此在構造函數中無法訪問通過依賴注入的字段。

解決方案

  1. 避免在構造函數中訪問依賴:將依賴訪問邏輯移到初始化方法中
  2. 使用構造器注入:通過@Autowired注解在構造函數參數上,確保依賴在實例化時注入
  3. 使用@PostConstruct方法:在初始化方法中訪問依賴
  4. 使用Aware接口:如ApplicationContextAware,在setApplicationContext()方法中訪問依賴

代碼示例:正確使用構造器注入

// 正確做法:使用構造器注入
@Component
public class UserService {private final UserRepository userRepository;@Autowiredpublic UserService(UserRepository userRepository) {this.userRepository = userRepository;}@PostConstructpublic void init() {// 在初始化方法中訪問依賴userRepository.loadUsers();}
}// 錯誤做法:在構造函數中訪問依賴
@Component
public class UserService {private UserRepository userRepository;public UserService() {// 此時 userRepository尚未注入userRepository.loadUsers(); // 拋出NullPointerException}@Autowiredpublic void setUserRepository(UserRepository userRepository) {this.userRepository = userRepository;}
}

面試考點

  • 為什么在構造函數中訪問依賴會導致問題?
  • 如何正確使用構造器注入?
  • 如何避免BeanCurrentlyInCreationException異常?
6.3 高性能場景下的延遲初始化(Lazy-init)應用

定義:延遲初始化是指Bean的初始化方法在首次獲取Bean時執行,而不是在容器啟動時執行。

關鍵點

  • 減少啟動時間:對于啟動時不需要的Bean,可以延遲初始化
  • 避免循環依賴:在某些情況下,延遲初始化可以避免循環依賴
  • 適用場景:大型應用、需要按需加載的Bean

配置方式

  1. XML配置<bean lazy-init="true">
  2. 注解配置@Bean(lazyInit = true)
  3. 全局配置<context:annotation-config lazy-init="true"/>

代碼示例:延遲初始化配置

// 使用延遲初始化
@Configuration
public class AppConfig {@Bean(lazyInit = true)public ExpensiveService expensiveService() {System.out.println("ExpensiveService實例化");return new ExpensiveService();}@Beanpublic UserService userService() {System.out.println("UserService實例化");return new UserService();}
}

輸出順序

UserService實例化
ExpensiveService實例化(當首次獲取ExpensiveService時)

面試考點

  • 延遲初始化對Bean生命周期的影響?
  • 如何配置延遲初始化?
  • 延遲初始化和@Lazy注解的區別?
6.4 多模塊項目中的Bean生命周期管理策略

問題描述:在多模塊項目中,Bean的生命周期管理需要考慮模塊之間的依賴關系和作用域。

解決方案

  1. 模塊化配置:將每個模塊的Bean配置放在獨立的配置類中
  2. 作用域管理:根據模塊需求選擇合適的作用域
  3. 依賴管理:使用@Import@ComponentScan明確指定模塊之間的依賴關系
  4. 初始化順序控制:使用@DependsOn@Order控制Bean的初始化順序
  5. 銷毀順序控制:使用@Order控制Bean的銷毀順序

代碼示例:多模塊配置

// 模塊A配置
@Configuration
public class ModuleAConfig {@Beanpublic ServiceA serviceA() {return new ServiceA();}
}// 模塊B配置
@Configuration
@Import(ModuleAConfig.class)
public class ModuleBConfig {@Beanpublic ServiceB serviceB() {return new ServiceB();}@Beanpublic ServiceC serviceC() {return new ServiceC();}
}// 模塊C配置
@Configuration
@Import({ModuleAConfig.class, ModuleBConfig.class})
public class ModuleCConfig {@Beanpublic ServiceD serviceD() {return new ServiceD();}
}

面試考點

  • 如何管理多模塊項目中的Bean生命周期?
  • 如何控制Bean的初始化順序?
  • 如何確保跨模塊的Bean能夠正確銷毀?

第7章:高級特性與應用場景

7.1 基于條件的Bean生命周期控制

定義:根據特定條件(如環境變量、操作系統、Java版本等)決定是否創建和銷毀Bean。

實現方式

  1. @Conditional注解:根據條件決定是否創建Bean
  2. 環境變量檢查:在初始化方法中檢查環境變量
  3. 屬性文件配置:通過屬性文件決定Bean的創建和銷毀

代碼示例:條件化Bean創建

// 使用@Conditional注解
@Configuration
public class AppConfig {@Bean@Conditional"OnLinux")public LinuxService linuxService() {return new LinuxService();}@Bean@Conditional"OnWindows")public WindowsService windowsService() {return new WindowsService();}
}// 條件判斷器
public class OnLinux implements Condition {@Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {return context.getEnvironment(). propertyNames(). contains("os.name=Linux");}
}public class OnWindows implements Condition {@Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {return context.getEnvironment(). propertyNames(). contains("os.name=Windows");}
}

面試考點

  • @Conditional注解的作用?
  • 如何根據環境變量控制Bean的生命周期?
  • 如何實現跨平臺的Bean生命周期控制?
7.2 基于事件的Bean生命周期控制

定義:通過Spring的事件機制,在特定事件發生時觸發Bean的初始化或銷毀。

關鍵類

  • ApplicationEvent:表示一個應用事件
  • ApplicationListener:監聽應用事件
  • EventDrivenBean:實現事件驅動的Bean

應用場景

  • 緩存預熱:在容器啟動后,監聽ContextRefreshedEvent事件,觸發緩存預熱
  • 資源釋放:在容器關閉前,監聽ContextClosedEvent事件,觸發資源釋放
  • 按需加載:在特定業務事件發生時,動態創建和銷毀Bean

代碼示例:事件驅動的緩存預熱

// 緩存預熱監聽器
@Component
public class CachePreheater implements ApplicationListener<ContextRefreshedEvent> {@Overridepublic void onApplicationEvent(ContextRefreshedEvent event) {// 緩存預熱System.out.println("緩存預熱開始");// 加載數據到緩存// ...System.out.println("緩存預熱完成");}
}// 資源釋放監聽器
@Component
public class ResourceReleaser implements ApplicationListener<ContextClosedEvent> {@Overridepublic void onApplicationEvent(ContextClosedEvent event) {// 資源釋放System.out.println("資源釋放開始");// 關閉數據庫連接等// ...System.out.println("資源釋放完成");}
}

面試考點

  • Spring事件機制如何工作?
  • 如何監聽容器啟動和關閉事件?
  • 如何實現基于事件的Bean生命周期控制?
7.3 基于作用域代理的Bean生命周期管理

定義:作用域代理允許非單例作用域的Bean(如原型、request、session)支持銷毀方法,通過代理對象管理Bean的生命周期。

關鍵點

  • 代理模式:使用ProxyMode.TARGETProxyMode.動靜態創建代理對象
  • 支持銷毀方法:通過代理對象觸發銷毀方法
  • 適用場景:需要在Bean銷毀時執行特定操作的非單例Bean

配置方式

// 啟用作用域代理
@Component
@Scope(value = "prototype", proxyMode = ProxyMode.TARGET)
public class prototypeBean {@PostConstructpublic void init() {System.out.println("原型Bean初始化完成");}@PreDestroypublic void destroy() {System.out.println("原型Bean銷毀完成");}
}

源碼解析:在ScopeProxyFactory類中,createProxy()方法負責創建作用域代理:

// ScopeProxyFactory.java
public Object createProxy(String beanName, Object target, BeanFactory beanFactory) {// 創建代理對象return ProxyFactory.getProxy(target.getClass().getClassLoader(),target.getClass().getInterfaces(),new invokerHandler (beanName, target, beanFactory));
}// invokerHandler.java
public class invokerHandler implements MethodInterceptor {private String beanName;private Object target;private BeanFactory beanFactory;@Overridepublic Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) {// 攔截方法調用if (method.getName().equals("destroy")) {// 觸發銷毀方法destroyBean beanName, target);return null;}// 調用目標方法return methodProxy.invoke(target, args);}private void destroyBean(String beanName, Object target) {// 從容器中移除BeanbeanFactory.destroyBean beanName, target);}
}

面試考點

  • 作用域代理如何工作?
  • 如何為原型Bean啟用銷毀方法?
  • 作用域代理對Bean性能的影響?
7.4 基于作用域的Bean生命周期管理

定義:根據不同作用域(如單例、原型、request、session)管理Bean的生命周期。

關鍵策略

  1. 單例Bean:使用@PreDestroy disposableBean接口,確保容器關閉時釋放資源
  2. 原型Bean:使用作用域代理,或手動調用銷毀方法
  3. request作用域Bean:使用RequestContextListener,確保請求結束后釋放資源
  4. session作用域Bean:使用SessionContextListener,確保會話過期后釋放資源

代碼示例:不同作用域的Bean配置

// 單例Bean
@Component
public class SingletonBean {@PostConstructpublic void init() {System.out.println("單例Bean初始化完成");}@PreDestroypublic void destroy() {System.out.println("單例Bean銷毀完成");}
}// 原型Bean(啟用作用域代理)
@Component
@Scope(value = "prototype", proxyMode = ProxyMode.TARGET)
public class prototypeBean {@PostConstructpublic void init() {System.out.println("原型Bean初始化完成");}@PreDestroypublic void destroy() {System.out.println("原型Bean銷毀完成");}
}// request作用域Bean
@Component
@Scope("request")
public class RequestScopeBean {@PostConstructpublic void init() {System.out.println("request作用域Bean初始化完成");}@PreDestroypublic void destroy() {System.out.println("request作用域Bean銷毀完成");}
}

面試考點

  • 不同作用域Bean的生命周期有何不同?
  • 如何確保非單例Bean的銷毀方法被調用?
  • Web作用域Bean的銷毀機制?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/91640.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/91640.shtml
英文地址,請注明出處:http://en.pswp.cn/web/91640.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【51單片機6位數碼管密碼鎖】2022-10-15

緣由六位密碼器設計連接LED-嵌入式-CSDN問答 矩陣51單片機密碼鎖,回復:https://bbs.csdn.net/topics/392713242_智者知已應修善業的博客-CSDN博客 #include "REG52.h" unsigned char code smgduan[]{0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x…

?我的第一個開源項目:躍動的心

還是一個編程初學者時&#xff0c;我懷著激動的心情完成了人生第一個開源項目——一個用HTML5 Canvas制作的動態跳動愛心效果。這個項目雖然簡單&#xff0c;卻讓我深刻體會到了開源分享的快樂和技術創造的魅力。 壹、項目靈感 這個項目的靈感來源于瀏覽網頁時&#xff0c;被各…

技術演進中的開發沉思-53 DELPHI VCL系列:windows的消息(下):TApplication窗體

今天我們梳理下關于TApplication的窗體消息下半部分的內容。前面也說過&#xff0c;在 Delphi 的世界里&#xff0c;TApplication 就像一位經驗豐富的總工程師&#xff0c;而主窗體則是它傾注心血打造的核心建筑。如果你第一次在實驗室里敲出 Delphi 代碼時&#xff0c;屏幕上彈…

cesium FBO(四)自定義相機渲染到Canvas(離屏渲染)

前面幾節的例子是將Cesium默認的相機渲染到紋理&#xff08;RTT&#xff09;或Canvas&#xff0c;這片文章講解如何將自定義的一個camera的畫面渲染到Canvas上&#xff0c;有了前面幾篇的基礎了&#xff0c;也能將自定義的畫面渲染紋理、也可以灰度處理&#xff0c;原理是一樣的…

雙機并聯無功環流抑制虛擬阻抗VSG控制【simulink仿真模型實現】

雙機并聯虛擬同步發電機&#xff08;VSG&#xff09;系統中&#xff0c;因線路阻抗不匹配及參數差異&#xff0c;易引發無功環流。本方案在傳統VSG控制基礎上&#xff0c;引入自適應虛擬阻抗環節。其核心在于&#xff1a;實時檢測兩機間無功環流分量&#xff0c;據此動態調節各…

python測試總結

測試題的基礎知識點總結 1.循環求和 for循環步長&#xff08;range(2,101,2)&#xff09; while循環條件判斷&#xff08;i%20&#xff09; 生成器表達式&#xff08;sum(i for i in range )&#xff09; 所以&#xff1a;sum(range(1,101,2))&#xff08;奇數和&#xff09;和…

識別和分類惡意軟件樣本的工具YARA

YARA 是一個用于識別和分類惡意軟件樣本的工具,廣泛應用于惡意軟件分析、威脅情報、入侵檢測等領域。它通過編寫規則(YARA Rules)來匹配文件中的特定字符串、十六進制模式、正則表達式等特征。 一、YARA 的基本使用方法 1. 安裝 YARA Linux(Ubuntu/Debian) sudo apt-ge…

GaussDB 約束的語法

1 約束的作用約束是作用于數據表中列上的規則&#xff0c;用于限制表中數據的類型。約束的存在保證了數據庫中數據的精確性和可靠性。約束有列級和表級之分&#xff0c;列級約束作用于單一的列&#xff0c;而表級約束作用于整張數據表。下面是 GaussDB SQL 中常用的約束。NOT …

SecurityContextHolder 管理安全上下文的核心組件詳解

SecurityContextHolder 管理安全上下文的核心組件詳解在 Spring Security 中&#xff0c;SecurityContextHolder 是??安全上下文&#xff08;Security Context&#xff09;的核心存儲容器??&#xff0c;其核心作用是??在當前線程中保存當前用戶的認證信息&#xff08;如用…

c++詳解系列(引用指針)

目錄 1.什么是引用 2.引用的定義 3.引用的特性 4.引用的使用 4.1引用傳參 4.2傳引用返回 5.const引用&#xff08;在引用的定義前用const修飾&#xff09; 5.1對于引用 5.2對于指針 6.引用&指針 總結 1.什么是引用 引用就是給變量起別名&#xff0c;一個變量可以…

深度學習loss總結(二)

對于目前深度學習主流任務學習,loss的設置至關重要。下面就不同任務的loss設置進行如下總結: (1)目標檢測 2D/3D目標檢測中的 Loss(損失函數)是訓練模型時優化目標的核心,通常包括位置、類別、尺寸、方向等多個方面。以下是目前 常見的 2D 和 3D 目標檢測 Loss 分類與…

【Linux網絡】netstat 的 -anptu 各個參數各自表示什么意思?

netstat 是一個網絡統計工具&#xff0c;它可以顯示網絡連接、路由表、接口統計、偽裝連接和多播成員資格。在 netstat 命令中&#xff0c;不同的參數可以用來定制輸出的內容。 你提到的 -anptu 參數組合各自的功能如下&#xff1a; -a (all): 顯示所有活動的連接和監聽端口。它…

[硬件電路-115]:模擬電路 - 信號處理電路 - 功能放大器工作分類、工作原理、常見芯片

功能放大器是以特定功能為核心的集成化放大電路&#xff0c;通過將運算放大器與外圍電阻、電容等元件集成在單一芯片中&#xff0c;實現標準化、高性能的信號放大功能。其核心優勢在于簡化設計流程、提升系統穩定性&#xff0c;并針對特定應用場景優化性能參數。以下從定義、分…

雙網卡UDP廣播通信機制詳解

UDP廣播通信機制詳解 一、通信流程分析 發送階段 通過Client.Bind(192.168.0.3, 60000)將UDP套接字綁定到指定網卡和端口設置RemoteHost "255.255.255.255"實現全網段廣播數據流向&#xff1a;192.168.0.3:60000 → 255.255.255.255:50000 接收階段 設備響應數據應返…

從遮擋難題到精準測量:激光頻率梳技術如何實現深孔 3D 輪廓的 2um 級重復精度?

一、深孔 3D 輪廓測量的遮擋困境深孔結構&#xff08;如航空發動機燃油噴嘴孔、模具冷卻孔&#xff09;因孔深大&#xff08;常超 100mm&#xff09;、深徑比高&#xff08;&#xff1e;10:1&#xff09;&#xff0c;其 3D 輪廓測量長期受限于光學遮擋難題。傳統光學測量技術&a…

.NET 依賴注入(DI)全面解析

文章目錄一、依賴注入核心原理1. 控制反轉(IoC)與DI關系2. .NET DI核心組件二、服務生命周期1. 三種生命周期類型三、DI容器實現原理1. 服務注冊流程2. 服務解析流程四、高級實現方法1. 工廠模式注冊2. 泛型服務注冊3. 多實現解決方案五、ASP.NET Core中的DI集成1. 控制器注入2…

K8S部署ELK(二):部署Kafka消息隊列

目錄 1. Kafka 簡介 1.1 Kafka 核心概念 &#xff08;1&#xff09;消息系統 vs. 流處理平臺 &#xff08;2&#xff09;核心組件 1.2 Kafka 核心特性 &#xff08;1&#xff09;高吞吐 & 低延遲 &#xff08;2&#xff09;持久化存儲 &#xff08;3&#xff09;分…

Rust進階-part1-智能指針概述-box指針

Rust進階[part1]_智能指針概述&box指針 智能指針概述 在Rust中,智能指針是一類特殊的數據結構,它們不僅像普通指針一樣可以引用數據,還帶有額外的元數據和功能。與普通指針不同,智能指針通常使用結構體實現,并且會實現 Deref 和 Drop 等特定的trait,以提供更強大的…

C++擴展 --- 并發支持庫(補充1)

C擴展 --- 并發支持庫&#xff08;下&#xff09;https://blog.csdn.net/Small_entreprene/article/details/149606406?fromshareblogdetail&sharetypeblogdetail&sharerId149606406&sharereferPC&sharesourceSmall_entreprene&sharefromfrom_link atom…

在Three.js中導入和添加自定義網格的最佳實踐 - 綜合指南

探索在Three.js中導入和添加自定義網格的最佳實踐。本指南涵蓋增強 3D 項目的技術、技巧和實際示例。 添加圖片注釋&#xff0c;不超過 140 字&#xff08;可選&#xff09; 強烈建議使用 GLTF 格式來集成 3D 幾何體&#xff0c;提供簡化的流程&#xff0c;并固有地支持動畫、…