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框架的基礎(chǔ)設(shè)施,面向Spring本身
- ApplicationContext : 面向使用Spring框架的開發(fā)者,**幾乎所有的應(yīng)用場合都直接使用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)
這里看一個基礎(chǔ)的實現(xiàn)
AnnotationConfigApplicationContext
類圖分析

根據(jù)類圖看,ApplicationContext也是一個bean工廠的實現(xiàn),繼承自ResourceLoader也說明其具備加載文件,掃描bean的能力。ApplicationEventPublisher事件發(fā)布的能力
- BeanFactory:Spring 管理 Bean 的頂層接口,ApplicationContext 繼承了 BeanFactory 的兩個子類:HierarchicalBeanFactory 和 ListableBeanFactory。HierarchicalBeanFactory 是一個具有層級關(guān)系的 BeanFactory,擁有屬性 parentBeanFactory。 ListableBeanFactory 實現(xiàn)了枚舉方法可以列舉出當(dāng)前 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()中被調(diào)用,用來初始化一些國際化的屬性。
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 + "]");
}
}
}什么是國際化?
當(dāng)我們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文件內(nèi)容就和簡體中文文件一致,如果未設(shè)置Locale參數(shù),默認就為該文件內(nèi)容,此文件也可不用
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() 用于通知在此應(yīng)用程序中注冊的所有的監(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ā)對應(yīng)的監(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 的基礎(chǔ)上增加了 getResources(String locationPattern),以支持根據(jù)路徑匹配模式返回多個 Resource 實例,同時也新增了一種新的協(xié)議前綴 classpath*:,該協(xié)議前綴由其子類負責(zé)實現(xiàn)。
PathMatchingResourcePatternResolver 為 ResourcePatternResolver 最常用的子類,它除了支持 ResourceLoader 和 ResourcePatternResolver 新增的 classpath*: 前綴外,還支持 Ant 風(fēng)格的路徑匹配模式(類似于 **/*.xml)。
EnvironmentCapable
提供當(dāng)前系統(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 實例對象為空,則調(diào)用 createEnvironment() 創(chuàng)建一個。
protected ConfigurableEnvironment createEnvironment() {
return new StandardEnvironment();
}
StandardEnvironment 是一個適用于非 WEB 應(yīng)用的 Environment。
Lifecycle
一個用于管理聲明周期的接口。
在 AbstractApplicationContext 中存在一個 LifecycleProcessor 類型的實例對象 lifecycleProcessor,AbstractApplicationContext 中關(guān)于 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 接口用于關(guān)閉和釋放資源,提供了 close() 以釋放對象所持有的資源。在 ApplicationContext 體系中由AbstractApplicationContext 實現(xiàn),用于關(guā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) {
}
}
}
}調(diào)用 doClose() 發(fā)布 ContextClosedEvent 事件,銷毀所有 bean(單例),關(guān)閉 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);
}總結(jié)
ApplicationContext集大成者
在實現(xiàn)對應(yīng)接口功能時都是使用對應(yīng)的實現(xiàn)類去做,而不是自己實現(xiàn),這點在ApplicationContext上十分常見,繼承自功能接口,這樣很容易看出ApplicationContext具備的功能,但是自身并不實現(xiàn),而調(diào)用對應(yīng)的實現(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,利用反射調(diào)用無參構(gòu)造器進行實例化。
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下是對對應(yīng)的類,來判斷類型
根據(jù)webApplicationType加載對應(yīng)的類
調(diào)用其構(gòu)造函數(shù)進行初始化我這里看AnnotationConfigApplicationContext的構(gòu)造過程
接下來通過構(gòu)造方法看ApplicationContext的創(chuàng)建的過程
AbstractApplicationContext
public AbstractApplicationContext() {
this.resourcePatternResolver = getResourcePatternResolver();
}
protected ResourcePatternResolver getResourcePatternResolver() {
return new PathMatchingResourcePatternResolver(this);
}PathMatchingResourcePatternResolver 為 ResourcePatternResolver 最常用的子類,它除了支持 ResourceLoader 和 ResourcePatternResolver 新增的 classpath*: 前綴外,還支持 Ant 風(fēng)格的路徑匹配模式(類似于 **/*.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);
}
構(gòu)造函數(shù)中創(chuàng)建了AnnotatedBeanDefinitionReader和 ClassPathBeanDefinitionScanner
AnnotatedBeanDefinitionReader是一個根據(jù)指定類注冊BeanDefinnation的功能,同時能根據(jù)Condition跳過未達到條件的Bean
ClassPathBeanDefinitionScannery
BeanDeifination掃描類

繼承自RespourceAware和EnviromentCapable
設(shè)置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();
}
設(shè)置資源加載器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());
}
到此這篇關(guān)于SpringBoot ApplicationContext接口深入分析的文章就介紹到這了,更多相關(guān)SpringBoot ApplicationContext內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- springboot如何獲取applicationContext?servletContext
- SpringBoot?容器刷新前回調(diào)ApplicationContextInitializer
- SpringBoot如何使用applicationContext.xml配置文件
- Springboot如何獲取上下文ApplicationContext
- springboot ApplicationContextInitializer的三種使用方法小結(jié)
- SpringBoot如何使用ApplicationContext獲取bean對象
- SpringBoot獲取ApplicationContext的3種方式
- SpringBoot ApplicationContextAware拓展接口使用詳解
相關(guān)文章
新手小白入門必學(xué)JAVA面向?qū)ο笾鄳B(tài)
說到多態(tài),一定離不開其它兩大特性:封裝和繼承,下面這篇文章主要給大家介紹了關(guān)于新手小白入門必學(xué)JAVA面向?qū)ο笾鄳B(tài)的相關(guān)資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下2022-02-02
SpringCloud_Eureka服務(wù)注冊與發(fā)現(xiàn)基礎(chǔ)及構(gòu)建步驟
Eureka服務(wù)注冊中心,主要用于提供服務(wù)注冊功能,當(dāng)微服務(wù)啟動時,會將自己的服務(wù)注冊到Eureka Server,這篇文章主要介紹了SpringCloud中Eureka的配置及詳細使用,需要的朋友可以參考下2023-01-01
Java實現(xiàn)Html轉(zhuǎn)Pdf的方法
這篇文章主要介紹了Java實現(xiàn)Html轉(zhuǎn)Pdf的方法,實例分析了java基于ITextRenderer類操作頁面及系統(tǒng)自帶字體生成pdf文件的相關(guān)技巧,具有一定參考借鑒價值,需要的朋友可以參考下2015-07-07
SpringBoot統(tǒng)一處理功能實現(xiàn)的全過程
最近在做項目時需要對異常進行全局統(tǒng)一處理,主要是一些分類入庫以及記錄日志等,下面這篇文章主要給大家介紹了關(guān)于SpringBoot統(tǒng)一功能處理實現(xiàn)的相關(guān)資料,文中通過圖文以及實例代碼介紹的非常詳細,需要的朋友可以參考下2023-03-03
線程池之newFixedThreadPool定長線程池的實例
這篇文章主要介紹了線程池之newFixedThreadPool定長線程池的實例,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-06-06
SpringMVC視圖轉(zhuǎn)發(fā)重定向區(qū)別及控制器詳解
這篇文章主要為大家介紹了SpringMVC視圖轉(zhuǎn)發(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

