轉載請註明出處:
2.建立SpringApplication 應用,在建構函式中推斷啟動應用型別,並進行spring boot自動裝配
3.1 執行prepareEnvironment 方法,準備應用環境
3.1.1 getOrCreateEnvironment 方法原始碼
3.1.2 configureEnvironment() 方法獲取配置環境
3.2.createApplicationContext方法
3.2.1AnnotationConfigApplicationContext() 的構造方法
3.4 檢視 refreshContext中refresh方法
3.4.2 obtainFreshBeanFactory()
3.4.4 invokeBeanFactoryPostProcessors()
3.4.5 registerBeanPostProcessors()
3.4.6 重點 finishBeanFactoryInitialization() 實現SpringBean生命週期過程
1.SpringBoot 原始碼執行流程圖
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { return (new SpringApplication(primarySources)).run(args); }
檢視 SpringApplication 建構函式
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { this.sources = new LinkedHashSet(); // 獲取banner列印模式 this.bannerMode = Mode.CONSOLE; this.logStartupInfo = true; // 新增命令列系統屬性 this.addCommandLineProperties = true; this.addConversionService = true; this.headless = true; this.registerShutdownHook = true; this.additionalProfiles = Collections.emptySet(); this.isCustomEnvironment = false; // 是否懶載入 this.lazyInitialization = false; // 預設賦值 applicationContextFactory 工廠物件 this.applicationContextFactory = ApplicationContextFactory.DEFAULT; this.applicationStartup = ApplicationStartup.DEFAULT; this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); this.primarySources = new LinkedHashSet(Arrays.asList(primarySources)); // 根據類載入路徑推斷webApplicaitonType型別:SERVLET;REACTIVE this.webApplicationType = WebApplicationType.deduceFromClasspath(); // 進行Spring的自動裝配,主要用來SpringFactoriesLoader類進行裝載工廠 this.bootstrappers = new ArrayList(this.getSpringFactoriesInstances(Bootstrapper.class)); this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class)); // 設定監聽器 this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = this.deduceMainApplicationClass(); }
檢視run方法原始碼
public ConfigurableApplicationContext run(String... args) { // 應用啟動計時器 StopWatch stopWatch = new StopWatch(); stopWatch.start(); DefaultBootstrapContext bootstrapContext = this.createBootstrapContext(); // 初始化應用上下文 ConfigurableApplicationContext context = null; this.configureHeadlessProperty(); // 獲取啟動監聽器 SpringApplicationRunListeners listeners = this.getRunListeners(args); //監聽器使用類似於生產-消費模式進行訊息監聽 listeners.starting(bootstrapContext, this.mainApplicationClass); try { //構建應用引數 ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); //準備應用環境 ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments); this.configureIgnoreBeanInfo(environment); //列印banner Banner printedBanner = this.printBanner(environment); //建立ApplicationContext context = this.createApplicationContext(); context.setApplicationStartup(this.applicationStartup); //準備context this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner); //重點:重新整理context:實現IOC容器啟動的整個過程 this.refreshContext(context); //後置工作 this.afterRefresh(context, applicationArguments); stopWatch.stop(); if (this.logStartupInfo) { (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch); } listeners.started(context); this.callRunners(context, applicationArguments); } catch (Throwable var10) { this.handleRunFailure(context, var10, listeners); throw new IllegalStateException(var10); } }
獲取啟動監聽器。實際上獲取的是一個EventPublishingRunListener物件,這個類能通過一個SimpleApplicationEventMulticaster物件廣播事件,用到了Executor多執行緒非同步執行框架;
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) { // 根據前面推斷的web環境型別建立推Environment物件 ConfigurableEnvironment environment = this.getOrCreateEnvironment(); // 進行環境配置 this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs()); ConfigurationPropertySources.attach((Environment)environment); listeners.environmentPrepared(bootstrapContext, (ConfigurableEnvironment)environment); DefaultPropertiesPropertySource.moveToEnd((ConfigurableEnvironment)environment); this.configureAdditionalProfiles((ConfigurableEnvironment)environment); this.bindToSpringApplication((ConfigurableEnvironment)environment); if (!this.isCustomEnvironment) { environment = (new EnvironmentConverter(this.getClassLoader())).convertEnvironmentIfNecessary((ConfigurableEnvironment)environment, this.deduceEnvironmentClass()); } ConfigurationPropertySources.attach((Environment)environment); return (ConfigurableEnvironment)environment; }
prepareEnvironment 方法主要用來準備應用環境,進行property配置檔案解析,profile 環境解析以及獲取系統屬性和系統環境等;
private ConfigurableEnvironment getOrCreateEnvironment() { if (this.environment != null) { return this.environment; } else { switch(this.webApplicationType) { case SERVLET: return new StandardServletEnvironment(); case REACTIVE: return new StandardReactiveWebEnvironment(); default: return new StandardEnvironment(); } } }
configureEnvironment() 原始碼:
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) { if (this.addConversionService) { ConversionService conversionService = ApplicationConversionService.getSharedInstance(); environment.setConversionService((ConfigurableConversionService)conversionService); } // 配置屬性源 this.configurePropertySources(environment, args); //獲取啟用的 profile this.configureProfiles(environment, args); }
createApplicationContext 方法原始碼
protected ConfigurableApplicationContext createApplicationContext() { return this.applicationContextFactory.create(this.webApplicationType); }
進入 applicationContextFactory.create 方法的類中,存在一個根據webApplicationType獲取容器型別的變數方法如下:
@FunctionalInterface public interface ApplicationContextFactory { // 根據webApplicationType返回指定的容器例項 applicationContextFactoryApplicationContextFactory DEFAULT = (webApplicationType) -> { try { switch(webApplicationType) { case SERVLET: return new AnnotationConfigServletWebServerApplicationContext(); case REACTIVE: return new AnnotationConfigReactiveWebServerApplicationContext(); default: return new AnnotationConfigApplicationContext(); } } catch (Exception var2) { throw new IllegalStateException("Unable create a default ApplicationContext instance, you may need a custom ApplicationContextFactory", var2); } }; ConfigurableApplicationContext create(WebApplicationType webApplicationType); }
通過以上方式獲取到具體的ApplicationContext例項,並通過建構函式進行例項化,檢視建構函式的過程:
構造方法原始碼如下:
public AnnotationConfigApplicationContext() { // 啟動上面獲取的容器型別例項 StartupStep createAnnotatedBeanDefReader = this.getApplicationStartup().start("spring.context.annotated-bean-reader.create"); // 構建AnnotatedBeanDefinitionReader物件 this.reader = new AnnotatedBeanDefinitionReader(this); createAnnotatedBeanDefReader.end(); // 構建 ClassPathBeanDefinitionScanner物件 this.scanner = new ClassPathBeanDefinitionScanner(this); }
AnnotationConfigApplicationContext的父類GenericApplicationContext的預設構造器會構造一個DefaultListableBeanFactory 物件,這樣應用上下文持有一個bean factory的引用,大部分應用只需與應用上下文提供的介面打交道就是因為它對bean factory進行了一層封裝。至此,一個Spring容器已經構造出來了,但是目前這個容器還什麼都沒有,需要根據使用者的配置檔案進行配置才能按照使用者邏輯進行工作。
prepareContext 方法原始碼
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { // 使context持有應用環境的引用,同時將應用環境的引用賦給reader和scanner context.setEnvironment(environment); // 實現應用上下文的後置處理:主要是註冊BeanNameGenerator型別的bean並設定應用上下文的資源載入器和類載入器 this.postProcessApplicationContext(context); // 應用初始化器--新增監聽器、logger、warnning、以及spring啟動加解密等元件 this.applyInitializers(context); listeners.contextPrepared(context); bootstrapContext.close(context); if (this.logStartupInfo) { this.logStartupInfo(context.getParent() == null); this.logStartupProfileInfo(context); } ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); // 新增啟動相關的bean beanFactory.registerSingleton("springApplicationArguments", applicationArguments); // 註冊列印banner的bean if (printedBanner != null) { beanFactory.registerSingleton("springBootBanner", printedBanner); } // 註冊可定義重寫的bean if (beanFactory instanceof DefaultListableBeanFactory) { ((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); } // 新增懶載入的bean工廠後置處理器 if (this.lazyInitialization) { context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor()); } // Load the sources Set<Object> sources = this.getAllSources(); Assert.notEmpty(sources, "Sources must not be empty"); // 重點:將source bean裝載到應用上下文 this.load(context, sources.toArray(new Object[0])); // 日誌配置 listeners.contextLoaded(context); }
通過 load 方法將所有的bean註冊到 容器中;該方法實現的呼叫鏈如下:
SpringApplication.load(ApplicationContext context, Object[] sources)--------> BeanDefinitionLoader.load()---->BeanDefinitionLoader.load(Object source)---> BeanDefinitionLoader.load(Class<?> source)----->AnnotatedBeanDefinitionReader.register(Class<?>... componentClasses)---> AnnotatedBeanDefinitionReader.registerBean(Class<?> beanClass)----> AnnotatedBeanDefinitionReader.doRegisterBean ---> BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
private final Map<String, BeanDefinition> beanDefinitionMap;
檢視refreshContext 中 refresh 方法原始碼
public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. // 為應用上下文的重新整理做準備--設定時間、記錄重新整理日誌、初始化屬性源中的佔位符和驗證必要的屬性等 prepareRefresh(); // Tell the subclass to refresh the internal bean factory. // 使用CAS讓子類重新整理內部的bean factory ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. // 準備在這個應用上下文中使用的bean factory prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. // bean factory 後置處理 postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. // 呼叫應用上下文中作為bean註冊的工廠處理器 invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. // 註冊攔截建立bean的bean處理器 registerBeanPostProcessors(beanFactory); // Initialize message source for this context. // 初始化訊息源 initMessageSource(); // Initialize event multicaster for this context. // 初始化事件廣播 initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. // 初始化特定上下文子類中的其它bean onRefresh(); // Check for listener beans and register them. // 註冊監聽器bean registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. // 例項化所有的單例bean finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. // 釋出相應的事件 finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } }
AbstractApplicationContext#prepareRefresh() 原始碼:
protected void prepareRefresh() { //記錄啟動時間 this.startupDate = System.currentTimeMillis(); //標誌位設定 this.closed.set(false); this.active.set(true); //日誌記錄一下 if (logger.isInfoEnabled()) { logger.info("Refreshing " + this); } // Initialize any placeholder property sources in the context environment // 初始化資源佔位符 initPropertySources(); // Validate that all properties marked as required are resolvable // see ConfigurablePropertyResolver#setRequiredProperties // 驗證所有必要的屬效能通過getProperty()解析,不能則丟擲異常 getEnvironment().validateRequiredProperties(); // Allow for the collection of early ApplicationEvents, // to be published once the multicaster is available... this.earlyApplicationEvents = new LinkedHashSet<ApplicationEvent>(); }
建立environment並載入System.properties()及System.getenv()到environment中
因為AbstractApplication沒有引用bean factory,只定義了重新整理bean factory相關的方法,重新整理bean factory的具體實現在子類的GenericApplicationContext#refreshBeanFactory()中實現,具體程式碼和說明如下:
protected final void refreshBeanFactory() throws IllegalStateException { // 只支援重新整理一次 if (!this.refreshed.compareAndSet(false, true)) { throw new IllegalStateException("GenericApplicationContext does not support multiple refresh attempts: just call 'refresh' once"); } // 設定序列號 this.beanFactory.setSerializationId(getId()); }
可以看到對bean factory的重新整理實際上只是為其設定了一個序列號。
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) { // Tell the internal bean factory to use the context's class loader etc. // 使用應用上下文的類載入器 beanFactory.setBeanClassLoader(getClassLoader()); // 設定bean表示式解析器 beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader())); // 新增屬性編輯器註冊器 beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this,getEnvironment())); // Configure the bean factory with context callbacks. // 使用上下文回撥函式配置bean factory beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this)); beanFactory.ignoreDependencyInterface(EnvironmentAware.class); beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class); beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class); beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class); beanFactory.ignoreDependencyInterface(MessageSourceAware.class); beanFactory.ignoreDependencyInterface(ApplicationContextAware.class); // BeanFactory interface not registered as resolvable type in a plain factory. // MessageSource registered (and found for autowiring) as a bean. // 註冊依賴 beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory); beanFactory.registerResolvableDependency(ResourceLoader.class, this); beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this); beanFactory.registerResolvableDependency(ApplicationContext.class, this); // Register early post-processor for detecting inner beans as ApplicationListeners. // 新增一個用於探測實現了ApplicationListener介面的bean的後置處理器 beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this)); // Detect a LoadTimeWeaver and prepare for weaving, if found. // 探測LoadTimeWeaver並準備織入,與AOP相關 if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) { beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory)); // Set a temporary ClassLoader for type matching. beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader())); } // Register default environment beans. // 將預設環境作為bean註冊 if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) { beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment()); } if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) { beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties()); } if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) { beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment()); } }
addBeanPostProcessor()會新增一個ApplicationContextAwareProcessor處理器,這個類實現了BeanPostProcessor介面,同時由於應用上下文持有其它*Aware等的引用,因此在後面的程式碼中忽略了這些依賴
該方法會掃描到指定包下標有註解的類,然後將其變成BeanDefinition物件,然後放到一個Spring的Map中,用於後面建立Spring bean的時候使用這個BeanDefinition
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) { // BeanFactory後置處理器的具體實現 PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors()); // Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime // (e.g. through an @Bean method registered by ConfigurationClassPostProcessor) if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) { beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory)); beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader())); } }
Spring委託PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors實現後置處理,它的具體實現很長,系統啟動時就註冊了幾個後置處理器,如SharedMetadataReaderFactoryContextInitializer,CachingMetadataReaderFactoryPostProcessor等。
程式碼的執行思路是:先將後置處理器進行分類,分別是BeanFactoryPostProcessor和BeanDefinitionRegistryPostProcessor,同時將BeanDefinitionRegistry註冊為一個BeanDefinition並呼叫登錄檔後置處理器的相關方法(與登錄檔相關);接著,按PriorityOrdered, Ordered和其它的順序呼叫手動新增(Spring Boot)的後置處理器。Spring Boot在之前註冊過一個ConfigurationClassPostProcessor後置處理器;
最終這個後置處理器會呼叫ConfigurationClassPostProcessor#processConfigBeanDefinitions()對配置類進行處理。在處理時需要建立 ConfigurationClassParser物件進行解析,同時會建立一個 ComponentScanAnnotationParser 物件,在這個類中會掃描獲取所有帶有註解的bean類;
ConfigurationClassPostProcessor#processConfigBeanDefinitions()具體的思路是先獲取所有的bean definition,並找出配置類對應的bean definition。接著對容器進行一下轉換並例項化一個ConfigurationClassParser配置類解析器物件parser,呼叫parser的parse()對配置類進行解析。ConfigurationClassParser#parse()的具體實現如下:
public void parse(Set<BeanDefinitionHolder> configCandidates) { this.deferredImportSelectors = new LinkedList<DeferredImportSelectorHolder>(); for (BeanDefinitionHolder holder : configCandidates) { BeanDefinition bd = holder.getBeanDefinition(); try { //如果bean是註解的,則解析註解---Spring Boot基於註解配置 if (bd instanceof AnnotatedBeanDefinition) { parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName()); } // 如果是抽象bean並且有bean類 else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) { parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName()); } else { // 普通解析 parse(bd.getBeanClassName(), holder.getBeanName()); } } catch (BeanDefinitionStoreException ex) { throw ex; } catch (Throwable ex) { throw new BeanDefinitionStoreException( "Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex); } } // 處理延遲匯入的選擇器 processDeferredImportSelectors(); }
對@ImportResource註解的處理
處理@Bean註解的方法不會註冊bean,只在配置類中註冊相應的方法。
處理超類
processDeferredImportSelectors()的具體實現:
private void processDeferredImportSelectors() { List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors; this.deferredImportSelectors = null; Collections.sort(deferredImports, DEFERRED_IMPORT_COMPARATOR); for (DeferredImportSelectorHolder deferredImport : deferredImports) { ConfigurationClass configClass = deferredImport.getConfigurationClass(); try { // 獲取importSelector---在自動配置源資料中刪除不符合要求或者無法例項化的物件 String[] imports = deferredImport.getImportSelector().selectImports(configClass.getMetadata()); //處理import---迭代處理,最終呼叫processConfigurationClass處理自動配置的類 processImports(configClass, asSourceClass(configClass), asSourceClasses(imports), false); } catch (BeanDefinitionStoreException ex) { throw ex; } catch (Throwable ex) { throw new BeanDefinitionStoreException("Failed to process import candidates for configuration class [" + configClass.getMetadata().getClassName() + "]", ex); } } }
至此,Spring Boot的自動配置基本完成
3.4.5 registerBeanPostProcessors()
根據實現了PropertyOrdered,Order介面,排序後註冊所有的BeanPostProcessor後置處理器,主要用於建立bean時,執行這些後置處理器的方法,這也是Spring 提供的擴充套件點,讓我們能夠插手Spring bean的建立過程。
完成所所有單例bean的建立和例項化,其方法呼叫鏈如下
AbstractApplicationContext.finishBeanFactoryInitialization(方法最後一行)-----〉 AbstractBeanFactory.getBean---->AbstractBeanFactory.doGetBean-------> AbstractAutowireCapableBeanFactory.createBean-------> 重點:AbstractAutowireCapableBeanFactory.doCreateBean(這個方法進行bean的生命週期)
在例項化Bean的過程中,會按照順序執行,如下:
resolveBeforeInstantiation會找到型別為InstantiationAwareBeanPostProcessor,且在Bean初始化前對Bean執行操作,例項化 ----》 AbstractAutowireCapableBeanFactory.doCreateBean() populateBean注入屬性initalizeBean方法呼叫擴充套件,順序如下:
1)如果Bean是BeanNameAware,BeanClassLoaderAware,BeanFactoryAware,會執行者幾個的方法Aware的對應方法
2)先執行所有BeanPostProcessor.postProcessBeforeInitialization()
3) 反射呼叫init方法,如果Bean是InitializingBean,會先執行InitializingBean的afterPropertiesSet方法, 然後在執行自動以的init方法 、
4)呼叫所有BeanPostProcessor.applyBeanPostProcessorsAfterInitialization()方法 @PostConstruct標記的方法,是在BeanPostProcessor.postProcessBeforeInitialization() 呼叫時執行,也就是有一個BeanPostProcessor用於處理標記了該註解的方法(InitDestroyAnnotationBeanPostProcessor),定時器等註解,原理也是一樣的,在解析BeanDefinition時,會將這些註解都解析成BeanDefinition的一個屬性
可以簡述為以下九步
-
例項化bean物件(通過構造方法或者工廠方法)
-
設定物件屬性(setter等)(依賴注入)
-
如果Bean實現了BeanNameAware介面,工廠呼叫Bean的setBeanName()方法傳遞Bean的ID。(和下面的一條均屬於檢查Aware介面)
-
如果Bean實現了BeanFactoryAware介面,工廠呼叫setBeanFactory()方法傳入工廠自身
-
將Bean例項傳遞給Bean的前置處理器的postProcessBeforeInitialization(Object bean, String beanname)方法
-
呼叫Bean的初始化方法; Spring檢測物件如果實現InitializingBean這個介面,就會執行他的afterPropertiesSet()方法,定製初始化邏輯。以及進行@PostConstruct註解邏輯實現
-
將Bean例項傳遞給Bean的後置處理器的postProcessAfterInitialization(Object bean, String beanname)方法
-
使用Bean
-
容器關閉之前,呼叫Bean的銷燬方法