第1章:Spring Bean生命周期概述
1.1 什么是Spring Bean生命周期?
定義:Spring Bean生命周期是指從Bean的創建、初始化、使用到銷毀的完整過程,由Spring容器嚴格管理 。核心思想是Spring容器通過IoC(控制反轉)和DI(依賴注入)機制,控制Bean的創建、配置和銷毀,開發者可通過擴展點(如BeanPostProcessor
、Aware接口
)插入自定義邏輯。
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初始化方法(如@PostConstruct
、InitializingBean.afterPropertiesSet()
或自定義init-method
)之前,Spring容器調用所有BeanPostProcessor
的postProcessBeforeInitialization()
方法,允許開發者修改Bean屬性或狀態 。
關鍵點:
- 全局性:對容器中的所有Bean生效
- 執行順序:由
BeanPostProcessor
的實現類決定,可通過實現PriorityOrdered
或Ordered
接口控制執行順序? - 可修改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的初始化方法,包括以下三種方式:
注解方式:
@PostConstruct
:標注在方法上,方法執行在屬性賦值之后、BeanPostProcessor的postProcessAfterInitialization()
之前@PreDestroy
:標注在方法上,方法執行在Bean銷毀之前
接口方式:
InitializingBean
:實現afterPropertiesSet()
方法DisposableBean
:實現destroy()
方法
配置方式:
- XML配置中的
init-method
屬性 - Java配置中的
@Bean(initMethod="...")
- XML配置中的
關鍵點:
- 執行順序:
@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容器調用所有BeanPostProcessor
的postProcessAfterInitialization()
方法,允許開發者對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()
方法負責調用BeanPostProcessor
的postProcessAfterInitialization()
:
// 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的銷毀回調方法,包括以下三種方式:
注解方式:
@PreDestroy
:標注在方法上,方法執行在Bean銷毀之前
接口方式:
DisposableBean
:實現destroy()
方法
配置方式:
- XML配置中的
destroy-method
屬性 - Java配置中的
@Bean(destroyMethod="...")
- XML配置中的
關鍵點:
- 單例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
方法時,遵循以下順序:
- 實現
PriorityOrdered
接口的處理器(按優先級排序) - 實現
Ordered
接口的處理器(按順序排序) - 未實現排序接口的處理器(按注冊順序排序)
代碼示例:自定義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);
}
銷毀方法觸發方式:
- 手動調用:在使用完原型Bean后,手動調用其銷毀方法
- 作用域代理:通過
@Scope(proxyMode=TargetClass)
啟用代理,使原型Bean支持銷毀方法? - 第三方工具:如使用對象池管理原型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;
}
銷毀機制:
- request作用域:通過
RequestContextListener
監聽HTTP請求結束,自動銷毀Bean? - 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通過三級緩存(singletonObjects
、earlySingletonObjects
、singletonFactories
)解決循環依賴問題。
三級緩存作用:
- 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
中關閉網絡連接
排查工具:
- YourKit Java Profiler:實時監控內存使用情況,創建多個堆快照進行對比分析?
- Eclipse Memory Analyzer (MAT):分析堆轉儲文件,找出未釋放的資源
- lsof命令:檢查進程打開的文件句柄數?
解決方案:
- 確保銷毀方法被調用:對于單例Bean,確保容器正常關閉;對于原型Bean,手動調用銷毀方法或啟用作用域代理?
- 使用try-with-resources:確保資源在使用后自動關閉?
- 實現 DisposableBean接口:在
destroy()
方法中釋放資源? - 使用@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或注解配置錯誤
- 資源訪問失敗:如無法連接數據庫
調試技巧:
- 日志分析:啟用Spring的DEBUG級別日志,查看Bean初始化的詳細過程
- 斷點調試:在IDE中設置斷點,跟蹤Bean初始化的執行流程
- 異常堆棧分析:查看拋出的異常信息,定位具體問題
- 使用@DependsOn:明確指定Bean的依賴順序
- 使用@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
,因為它們代碼侵入性低 - 避免實現接口:如
InitializingBean
和DisposableBean
,因為它們代碼侵入性高 - 慎用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 避免在構造函數中訪問未注入的依賴
問題描述:在構造函數中訪問未注入的依賴會導致NullPointerException
或BeanCurrentlyInCreationException
異常 。
原因分析:依賴注入在構造函數之后進行,因此在構造函數中無法訪問通過依賴注入的字段。
解決方案:
- 避免在構造函數中訪問依賴:將依賴訪問邏輯移到初始化方法中
- 使用構造器注入:通過
@Autowired
注解在構造函數參數上,確保依賴在實例化時注入 - 使用
@PostConstruct
方法:在初始化方法中訪問依賴 - 使用
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
配置方式:
- XML配置:
<bean lazy-init="true">
- 注解配置:
@Bean(lazyInit = true)
- 全局配置:
<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的生命周期管理需要考慮模塊之間的依賴關系和作用域。
解決方案:
- 模塊化配置:將每個模塊的Bean配置放在獨立的配置類中
- 作用域管理:根據模塊需求選擇合適的作用域
- 依賴管理:使用
@Import
或@ComponentScan
明確指定模塊之間的依賴關系 - 初始化順序控制:使用
@DependsOn
或@Order
控制Bean的初始化順序 - 銷毀順序控制:使用
@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。
實現方式:
- @Conditional注解:根據條件決定是否創建Bean
- 環境變量檢查:在初始化方法中檢查環境變量
- 屬性文件配置:通過屬性文件決定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.TARGET
或ProxyMode.動靜態
創建代理對象 - 支持銷毀方法:通過代理對象觸發銷毀方法
- 適用場景:需要在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的生命周期。
關鍵策略:
- 單例Bean:使用
@PreDestroy
或disposableBean
接口,確保容器關閉時釋放資源 - 原型Bean:使用作用域代理,或手動調用銷毀方法
- request作用域Bean:使用
RequestContextListener
,確保請求結束后釋放資源 - 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的銷毀機制?