SpringBoot?ApplicationContext接口深入分析
ApplicationContext簡述
ApplicationContext代表IOC容器,在SpringIOC容器中讀取Bean配置創(chuàng)建Bean實例之前,必須對它進行實例化,只有在容器實例化后才可以從IOC容器里獲取Bean實例并使用。
Spring IOC容器實現(xiàn)方式
Spring 提供了兩種類型的IOC容器實現(xiàn):
- BeanFactory:IOC容器的基本實現(xiàn)。
- ApplicationContext:提供了更多的高級特性,是BeanFactory的子接口。
兩種方式比較:
- BeanFactory:BeanFactory是Spring框架的基礎設施,面向Spring本身
- ApplicationContext : 面向使用Spring框架的開發(fā)者,**幾乎所有的應用場合都直接使用ApplicationContext而非底層的BeanFactory。**無論使用何種方式,配置文件是相同的。
ApplicationContext接口梳理
protected ConfigurableApplicationContext createApplicationContext() { Class<?> contextClass = this.applicationContextClass; if (contextClass == null) { try { switch (this.webApplicationType) { case SERVLET: contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS); break; case REACTIVE: contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS); break; default: contextClass = Class.forName(DEFAULT_CONTEXT_CLASS); } } catch (ClassNotFoundException ex) { throw new IllegalStateException( "Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex); } } return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass); }
根據(jù)不同的環(huán)境創(chuàng)建不同的ApplicationContext實現(xiàn)
這里看一個基礎的實現(xiàn)
AnnotationConfigApplicationContext
類圖分析
根據(jù)類圖看,ApplicationContext也是一個bean工廠的實現(xiàn),繼承自ResourceLoader也說明其具備加載文件,掃描bean的能力。ApplicationEventPublisher事件發(fā)布的能力
- BeanFactory:Spring 管理 Bean 的頂層接口,ApplicationContext 繼承了 BeanFactory 的兩個子類:HierarchicalBeanFactory 和 ListableBeanFactory。HierarchicalBeanFactory 是一個具有層級關系的 BeanFactory,擁有屬性 parentBeanFactory。 ListableBeanFactory 實現(xiàn)了枚舉方法可以列舉出當前 BeanFactory 中所有的 bean 對象而不必根據(jù) name 一個一個的獲取。
- ApplicationEventPublisher:用于封裝事件發(fā)布功能的接口,向事件監(jiān)聽器(Listener)發(fā)送事件消息。
- ResourceLoader:Spring 加載資源的頂層接口,用于加載資源文件。ApplicationContext 繼承 ResourceLoader 的子類 ResourcePatternResolver,該接口是將 location 解析為 Resource 對象的策略接口。
- MessageSource :解析 message 的策略接口,用于支撐國際化等功能。
- EnvironmentCapable :用于獲取 Environment 的接口。
下面就這些接口來一一分析
MessageSource
MessageSource 定義了獲取 message 的策略方法 getMessage(),在 ApplicationContext 體系中,該方法 AbstractApplicationContext 實現(xiàn),在 AbstractApplicationContext 中,它持有一個 MessageSource 實例,將 getMessage() 的實現(xiàn)給該實例來實現(xiàn),如下:
private MessageSource messageSource; public String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException { return this.getMessageSource().getMessage(resolvable, locale); } private MessageSource getMessageSource() throws IllegalStateException { if (this.messageSource == null) { throw new IllegalStateException("MessageSource not initialized - call 'refresh' before accessing messages via the context: " + this); } else { return this.messageSource; } }
此外還有一個 initMessageSource()方法,在 refresh()中被調用,用來初始化一些國際化的屬性。
protected void initMessageSource() { ConfigurableListableBeanFactory beanFactory = this.getBeanFactory(); if (beanFactory.containsLocalBean("messageSource")) { this.messageSource = (MessageSource)beanFactory.getBean("messageSource", MessageSource.class); if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) { HierarchicalMessageSource hms = (HierarchicalMessageSource)this.messageSource; if (hms.getParentMessageSource() == null) { hms.setParentMessageSource(this.getInternalParentMessageSource()); } } if (this.logger.isTraceEnabled()) { this.logger.trace("Using MessageSource [" + this.messageSource + "]"); } } else { DelegatingMessageSource dms = new DelegatingMessageSource(); dms.setParentMessageSource(this.getInternalParentMessageSource()); this.messageSource = dms; beanFactory.registerSingleton("messageSource", this.messageSource); if (this.logger.isTraceEnabled()) { this.logger.trace("No 'messageSource' bean, using [" + this.messageSource + "]"); } } }
什么是國際化?
當我們web項目涉及到國外部署或者國外用戶使用時,需要展示不同語言信息,所以就需要國際化支持,下面將講解Springboot國際化支持操作
例如拋異常,我門希望在不同的環(huán)境能夠顯示不同的報錯
修改Springboot application.yml配置
spring:
messages:
basename: i18n/messages #配置國際化資源文件路徑
創(chuàng)建國際化資源文件
messages.properties不帶后綴為默認語言資源
簡體中文 messages_zh_CN.properties
unknown.exception=未知異常,請聯(lián)系管理員!
user.login.notExists={0} 用戶不存在!
英文 messages_en_US.properties
unknown.exception=Unknown error,Please contact the administrator!
user.login.notExists={0} user not exists!
messages.properties文件內容就和簡體中文文件一致,如果未設置Locale參數(shù),默認就為該文件內容,此文件也可不用
unknown.exception=未知異常,請聯(lián)系管理員!
user.login.notExists={0} 用戶不存在!
獲取錯誤信息通過messageSource獲取
public class MessageUtils { /** * 根據(jù)消息鍵和參數(shù) 獲取消息 委托給spring messageSource * @param code 消息鍵 * @param args 參數(shù) * @return 獲取國際化翻譯值 */ public static String message(String code, Object... args) { MessageSource messageSource = SpringUtils.getBean(MessageSource.class); return messageSource.getMessage(code, args, LocaleContextHolder.getLocale()); } }
ApplicationEventPublisher
用于封裝事件發(fā)布功能的接口,向事件監(jiān)聽器(Listener)發(fā)送事件消息。
該接口提供了一個 publishEvent() 用于通知在此應用程序中注冊的所有的監(jiān)聽器。該方法在 AbstractApplicationContext 中實現(xiàn)。
public void publishEvent(ApplicationEvent event) { this.publishEvent(event, (ResolvableType)null); } public void publishEvent(Object event) { this.publishEvent(event, (ResolvableType)null); } protected void publishEvent(Object event, @Nullable ResolvableType eventType) { Assert.notNull(event, "Event must not be null"); Object applicationEvent; if (event instanceof ApplicationEvent) { applicationEvent = (ApplicationEvent)event; } else { applicationEvent = new PayloadApplicationEvent(this, event); if (eventType == null) { eventType = ((PayloadApplicationEvent)applicationEvent).getResolvableType(); } } if (this.earlyApplicationEvents != null) { this.earlyApplicationEvents.add(applicationEvent); } else { this.getApplicationEventMulticaster().multicastEvent((ApplicationEvent)applicationEvent, eventType); } if (this.parent != null) { if (this.parent instanceof AbstractApplicationContext) { ((AbstractApplicationContext)this.parent).publishEvent(event, eventType); } else { this.parent.publishEvent(event); } } }
根據(jù)不同的事件類型觸發(fā)對應的監(jiān)聽器,并且會觸發(fā)父容器的發(fā)布發(fā)布事件
ResourcePatternResolver
ResourcePatternResolver 接口繼承 ResourceLoader 接口,為將 location 解析為 Resource 對象的策略接口。它提供的 getResources() 在 AbstractApplicationContext 中實現(xiàn),在 AbstractApplicationContext 中它持有一個 ResourcePatternResolver 的實例對象。 其定義如下:
public Resource[] getResources(String locationPattern) throws IOException { return this.resourcePatternResolver.getResources(locationPattern); }
該方法的具體實現(xiàn)在 PathMatchingResourcePatternResolve
ResourcePatternResolver 在 ResourceLoader 的基礎上增加了 getResources(String locationPattern),以支持根據(jù)路徑匹配模式返回多個 Resource 實例,同時也新增了一種新的協(xié)議前綴 classpath*:,該協(xié)議前綴由其子類負責實現(xiàn)。
PathMatchingResourcePatternResolver 為 ResourcePatternResolver 最常用的子類,它除了支持 ResourceLoader 和 ResourcePatternResolver 新增的 classpath*: 前綴外,還支持 Ant 風格的路徑匹配模式(類似于 **/*.xml)。
EnvironmentCapable
提供當前系統(tǒng)環(huán)境 Environment 組件。提供了一個 getEnvironment() 用于返回 Environment 實例對象,該方法在 AbstractApplicationContext 實現(xiàn)。
public ConfigurableEnvironment getEnvironment() { if (this.environment == null) { this.environment = this.createEnvironment(); } return this.environment; }
如果持有的 environment 實例對象為空,則調用 createEnvironment() 創(chuàng)建一個。
protected ConfigurableEnvironment createEnvironment() { return new StandardEnvironment(); }
StandardEnvironment 是一個適用于非 WEB 應用的 Environment。
Lifecycle
一個用于管理聲明周期的接口。
在 AbstractApplicationContext 中存在一個 LifecycleProcessor 類型的實例對象 lifecycleProcessor,AbstractApplicationContext 中關于 Lifecycle 接口的實現(xiàn)都是委托給 lifecycleProcessor 實現(xiàn)的。如下:
public void start() { this.getLifecycleProcessor().start(); this.publishEvent((ApplicationEvent)(new ContextStartedEvent(this))); } public void stop() { this.getLifecycleProcessor().stop(); this.publishEvent((ApplicationEvent)(new ContextStoppedEvent(this))); } public boolean isRunning() { return this.lifecycleProcessor != null && this.lifecycleProcessor.isRunning(); }
在啟動、停止的時候會分別發(fā)布 ContextStartedEvent 和 ContextStoppedEvent 事件。
Closeable
Closeable 接口用于關閉和釋放資源,提供了 close() 以釋放對象所持有的資源。在 ApplicationContext 體系中由AbstractApplicationContext 實現(xiàn),用于關閉 ApplicationContext 銷毀所有 bean ,此外如果注冊有 JVM shutdown hook,同樣要將其移除。如下:
public void close() { synchronized(this.startupShutdownMonitor) { this.doClose(); if (this.shutdownHook != null) { try { Runtime.getRuntime().removeShutdownHook(this.shutdownHook); } catch (IllegalStateException var4) { } } } }
調用 doClose() 發(fā)布 ContextClosedEvent 事件,銷毀所有 bean(單例),關閉 BeanFactory 。如下:
protected void doClose() { if (this.active.get() && this.closed.compareAndSet(false, true)) { if (this.logger.isDebugEnabled()) { this.logger.debug("Closing " + this); } LiveBeansView.unregisterApplicationContext(this); try { this.publishEvent((ApplicationEvent)(new ContextClosedEvent(this))); } catch (Throwable var3) { this.logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", var3); } if (this.lifecycleProcessor != null) { try { this.lifecycleProcessor.onClose(); } catch (Throwable var2) { this.logger.warn("Exception thrown from LifecycleProcessor on context close", var2); } } this.destroyBeans(); this.closeBeanFactory(); this.onClose(); if (this.earlyApplicationListeners != null) { this.applicationListeners.clear(); this.applicationListeners.addAll(this.earlyApplicationListeners); } this.active.set(false); } }
BeanFactory
applicationContext對beanFactory的實現(xiàn)實際上基本上都是通過成員變量來實現(xiàn)的DefaultListableBeanFactory
在applicationContext的實現(xiàn)GenericApplicationContext中
@Override public void registerBeanDefinition(String beanName,BeanDefinition beanDefinition) throws BeanDefinitionStoreException { this.beanFactory.registerBeanDefinition(beanName,beanDefinition); } @Override public void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException{ this.beanFactory. removeBeanDefinition(beanName); } @Override public BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException{? return this.beanFactory.getBeanDefinition(beanName); }
總結
ApplicationContext集大成者
在實現(xiàn)對應接口功能時都是使用對應的實現(xiàn)類去做,而不是自己實現(xiàn),這點在ApplicationContext上十分常見,繼承自功能接口,這樣很容易看出ApplicationContext具備的功能,但是自身并不實現(xiàn),而調用對應的實現(xiàn)類。繼承了很多的功能。
ApplicaionContext創(chuàng)建
protected ConfigurableApplicationContext createApplicationContext() { Class<?> contextClass = this.applicationContextClass; if (contextClass == null) { try { switch (this.webApplicationType) { case SERVLET: contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS); break; case REACTIVE: contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS); break; default: contextClass = Class.forName(DEFAULT_CONTEXT_CLASS); } } catch (ClassNotFoundException ex) { throw new IllegalStateException( "Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex); } } return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass); }
springBoot會創(chuàng)建一個ConfigurableApplicationContext
通過webApplicationType判斷創(chuàng)建什么類型的Context,如果是SERVLET那么實例化
AnnotationConfigServletWebServerApplicationContext,利用反射調用無參構造器進行實例化。
webApplicationType推斷方法
static WebApplicationType deduceFromClasspath() { if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null) && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) { return WebApplicationType.REACTIVE; } for (String className : SERVLET_INDICATOR_CLASSES) { if (!ClassUtils.isPresent(className, null)) { return WebApplicationType.NONE; } } return WebApplicationType.SERVLET; }
根據(jù)classPath下是對對應的類,來判斷類型
根據(jù)webApplicationType加載對應的類
調用其構造函數(shù)進行初始化我這里看AnnotationConfigApplicationContext的構造過程
接下來通過構造方法看ApplicationContext的創(chuàng)建的過程
AbstractApplicationContext
public AbstractApplicationContext() { this.resourcePatternResolver = getResourcePatternResolver(); } protected ResourcePatternResolver getResourcePatternResolver() { return new PathMatchingResourcePatternResolver(this); }
PathMatchingResourcePatternResolver 為 ResourcePatternResolver 最常用的子類,它除了支持 ResourceLoader 和 ResourcePatternResolver 新增的 classpath*: 前綴外,還支持 Ant 風格的路徑匹配模式(類似于 **/*.xml)。用于掃描類路徑下的類
GenericApplicationContext
public GenericApplicationContext() { this.beanFactory = new DefaultListableBeanFactory(); }
創(chuàng)建bean工廠,初始化一個空的bean工廠,后續(xù)注冊獲取bean都依靠這個bean工廠進行實現(xiàn)
AnnotationConfigApplicationContext
public AnnotationConfigApplicationContext() { this.reader = new AnnotatedBeanDefinitionReader(this); this.scanner = new ClassPathBeanDefinitionScanner(this); }
構造函數(shù)中創(chuàng)建了AnnotatedBeanDefinitionReader和 ClassPathBeanDefinitionScanner
AnnotatedBeanDefinitionReader是一個根據(jù)指定類注冊BeanDefinnation的功能,同時能根據(jù)Condition跳過未達到條件的Bean
ClassPathBeanDefinitionScannery
BeanDeifination掃描類
繼承自RespourceAware和EnviromentCapable
設置environment
private static Environment getOrCreateEnvironment(BeanDefinitionRegistry registry) { Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); if (registry instanceof EnvironmentCapable) { return ((EnvironmentCapable) registry).getEnvironment(); } return new StandardEnvironment(); }
設置資源加載器resourceLoader通過資源加載,和環(huán)境完成bean的掃描
@Override public void setResourceLoader(@Nullable ResourceLoader resourceLoader) { this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader); this.metadataReaderFactory = new CachingMetadataReaderFactory(resourceLoader); this.componentsIndex = CandidateComponentsIndexLoader.loadIndex(this.resourcePatternResolver.getClassLoader()); }
到此這篇關于SpringBoot ApplicationContext接口深入分析的文章就介紹到這了,更多相關SpringBoot ApplicationContext內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
- springboot如何獲取applicationContext?servletContext
- SpringBoot?容器刷新前回調ApplicationContextInitializer
- SpringBoot如何使用applicationContext.xml配置文件
- Springboot如何獲取上下文ApplicationContext
- springboot ApplicationContextInitializer的三種使用方法小結
- SpringBoot如何使用ApplicationContext獲取bean對象
- SpringBoot獲取ApplicationContext的3種方式
- SpringBoot ApplicationContextAware拓展接口使用詳解
相關文章
SpringCloud_Eureka服務注冊與發(fā)現(xiàn)基礎及構建步驟
Eureka服務注冊中心,主要用于提供服務注冊功能,當微服務啟動時,會將自己的服務注冊到Eureka Server,這篇文章主要介紹了SpringCloud中Eureka的配置及詳細使用,需要的朋友可以參考下2023-01-01SpringBoot統(tǒng)一處理功能實現(xiàn)的全過程
最近在做項目時需要對異常進行全局統(tǒng)一處理,主要是一些分類入庫以及記錄日志等,下面這篇文章主要給大家介紹了關于SpringBoot統(tǒng)一功能處理實現(xiàn)的相關資料,文中通過圖文以及實例代碼介紹的非常詳細,需要的朋友可以參考下2023-03-03線程池之newFixedThreadPool定長線程池的實例
這篇文章主要介紹了線程池之newFixedThreadPool定長線程池的實例,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-06-06SpringMVC視圖轉發(fā)重定向區(qū)別及控制器詳解
這篇文章主要為大家介紹了SpringMVC視圖轉發(fā)重定向區(qū)別及控制器示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-05-05詳解Springboot快速搭建跨域API接口的步驟(idea社區(qū)版2023.1.4+apache-maven-3.9.
這篇文章主要介紹了Springboot快速搭建跨域API接口(idea社區(qū)版2023.1.4+apache-maven-3.9.3-bin),本文通過圖文并茂的形式給大家介紹的非常詳細,需要的朋友可以參考下2023-07-07