SpringBoot?ApplicationContext接口深入分析
ApplicationContext簡(jiǎn)述
ApplicationContext代表IOC容器,在SpringIOC容器中讀取Bean配置創(chuàng)建Bean實(shí)例之前,必須對(duì)它進(jìn)行實(shí)例化,只有在容器實(shí)例化后才可以從IOC容器里獲取Bean實(shí)例并使用。
Spring IOC容器實(shí)現(xiàn)方式
Spring 提供了兩種類型的IOC容器實(shí)現(xiàn):
- BeanFactory:IOC容器的基本實(shí)現(xiàn)。
- ApplicationContext:提供了更多的高級(jí)特性,是BeanFactory的子接口。
兩種方式比較:
- BeanFactory:BeanFactory是Spring框架的基礎(chǔ)設(shè)施,面向Spring本身
- ApplicationContext : 面向使用Spring框架的開(kāi)發(fā)者,**幾乎所有的應(yīng)用場(chǎng)合都直接使用ApplicationContext而非底層的BeanFactory。**無(wú)論使用何種方式,配置文件是相同的。
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實(shí)現(xiàn)
這里看一個(gè)基礎(chǔ)的實(shí)現(xiàn)
AnnotationConfigApplicationContext
類圖分析

根據(jù)類圖看,ApplicationContext也是一個(gè)bean工廠的實(shí)現(xiàn),繼承自ResourceLoader也說(shuō)明其具備加載文件,掃描bean的能力。ApplicationEventPublisher事件發(fā)布的能力
- BeanFactory:Spring 管理 Bean 的頂層接口,ApplicationContext 繼承了 BeanFactory 的兩個(gè)子類:HierarchicalBeanFactory 和 ListableBeanFactory。HierarchicalBeanFactory 是一個(gè)具有層級(jí)關(guān)系的 BeanFactory,擁有屬性 parentBeanFactory。 ListableBeanFactory 實(shí)現(xiàn)了枚舉方法可以列舉出當(dāng)前 BeanFactory 中所有的 bean 對(duì)象而不必根據(jù) name 一個(gè)一個(gè)的獲取。
- ApplicationEventPublisher:用于封裝事件發(fā)布功能的接口,向事件監(jiān)聽(tīng)器(Listener)發(fā)送事件消息。
- ResourceLoader:Spring 加載資源的頂層接口,用于加載資源文件。ApplicationContext 繼承 ResourceLoader 的子類 ResourcePatternResolver,該接口是將 location 解析為 Resource 對(duì)象的策略接口。
- MessageSource :解析 message 的策略接口,用于支撐國(guó)際化等功能。
- EnvironmentCapable :用于獲取 Environment 的接口。
下面就這些接口來(lái)一一分析
MessageSource
MessageSource 定義了獲取 message 的策略方法 getMessage(),在 ApplicationContext 體系中,該方法 AbstractApplicationContext 實(shí)現(xiàn),在 AbstractApplicationContext 中,它持有一個(gè) MessageSource 實(shí)例,將 getMessage() 的實(shí)現(xiàn)給該實(shí)例來(lái)實(shí)現(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;
}
}此外還有一個(gè) initMessageSource()方法,在 refresh()中被調(diào)用,用來(lái)初始化一些國(guó)際化的屬性。
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 + "]");
}
}
}什么是國(guó)際化?
當(dāng)我們web項(xiàng)目涉及到國(guó)外部署或者國(guó)外用戶使用時(shí),需要展示不同語(yǔ)言信息,所以就需要國(guó)際化支持,下面將講解Springboot國(guó)際化支持操作
例如拋異常,我門希望在不同的環(huán)境能夠顯示不同的報(bào)錯(cuò)
修改Springboot application.yml配置
spring:
messages:
basename: i18n/messages #配置國(guó)際化資源文件路徑
創(chuàng)建國(guó)際化資源文件

messages.properties不帶后綴為默認(rèn)語(yǔ)言資源
簡(jiǎn)體中文 messages_zh_CN.properties
unknown.exception=未知異常,請(qǐng)聯(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)容就和簡(jiǎn)體中文文件一致,如果未設(shè)置Locale參數(shù),默認(rèn)就為該文件內(nèi)容,此文件也可不用
unknown.exception=未知異常,請(qǐng)聯(lián)系管理員!
user.login.notExists={0} 用戶不存在!
獲取錯(cuò)誤信息通過(guò)messageSource獲取
public class MessageUtils {
/**
* 根據(jù)消息鍵和參數(shù) 獲取消息 委托給spring messageSource
* @param code 消息鍵
* @param args 參數(shù)
* @return 獲取國(guó)際化翻譯值
*/
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)聽(tīng)器(Listener)發(fā)送事件消息。
該接口提供了一個(gè) publishEvent() 用于通知在此應(yīng)用程序中注冊(cè)的所有的監(jiān)聽(tīng)器。該方法在 AbstractApplicationContext 中實(shí)現(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ā)對(duì)應(yīng)的監(jiān)聽(tīng)器,并且會(huì)觸發(fā)父容器的發(fā)布發(fā)布事件
ResourcePatternResolver
ResourcePatternResolver 接口繼承 ResourceLoader 接口,為將 location 解析為 Resource 對(duì)象的策略接口。它提供的 getResources() 在 AbstractApplicationContext 中實(shí)現(xiàn),在 AbstractApplicationContext 中它持有一個(gè) ResourcePatternResolver 的實(shí)例對(duì)象。 其定義如下:
public Resource[] getResources(String locationPattern) throws IOException {
return this.resourcePatternResolver.getResources(locationPattern);
}
該方法的具體實(shí)現(xiàn)在 PathMatchingResourcePatternResolve
ResourcePatternResolver 在 ResourceLoader 的基礎(chǔ)上增加了 getResources(String locationPattern),以支持根據(jù)路徑匹配模式返回多個(gè) Resource 實(shí)例,同時(shí)也新增了一種新的協(xié)議前綴 classpath*:,該協(xié)議前綴由其子類負(fù)責(zé)實(shí)現(xiàn)。
PathMatchingResourcePatternResolver 為 ResourcePatternResolver 最常用的子類,它除了支持 ResourceLoader 和 ResourcePatternResolver 新增的 classpath*: 前綴外,還支持 Ant 風(fēng)格的路徑匹配模式(類似于 **/*.xml)。
EnvironmentCapable
提供當(dāng)前系統(tǒng)環(huán)境 Environment 組件。提供了一個(gè) getEnvironment() 用于返回 Environment 實(shí)例對(duì)象,該方法在 AbstractApplicationContext 實(shí)現(xiàn)。
public ConfigurableEnvironment getEnvironment() {
if (this.environment == null) {
this.environment = this.createEnvironment();
}
return this.environment;
}
如果持有的 environment 實(shí)例對(duì)象為空,則調(diào)用 createEnvironment() 創(chuàng)建一個(gè)。
protected ConfigurableEnvironment createEnvironment() {
return new StandardEnvironment();
}
StandardEnvironment 是一個(gè)適用于非 WEB 應(yīng)用的 Environment。
Lifecycle
一個(gè)用于管理聲明周期的接口。
在 AbstractApplicationContext 中存在一個(gè) LifecycleProcessor 類型的實(shí)例對(duì)象 lifecycleProcessor,AbstractApplicationContext 中關(guān)于 Lifecycle 接口的實(shí)現(xiàn)都是委托給 lifecycleProcessor 實(shí)現(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();
}在啟動(dòng)、停止的時(shí)候會(huì)分別發(fā)布 ContextStartedEvent 和 ContextStoppedEvent 事件。
Closeable
Closeable 接口用于關(guān)閉和釋放資源,提供了 close() 以釋放對(duì)象所持有的資源。在 ApplicationContext 體系中由AbstractApplicationContext 實(shí)現(xiàn),用于關(guān)閉 ApplicationContext 銷毀所有 bean ,此外如果注冊(cè)有 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對(duì)beanFactory的實(shí)現(xiàn)實(shí)際上基本上都是通過(guò)成員變量來(lái)實(shí)現(xiàn)的DefaultListableBeanFactory
在applicationContext的實(shí)現(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集大成者
在實(shí)現(xiàn)對(duì)應(yīng)接口功能時(shí)都是使用對(duì)應(yīng)的實(shí)現(xiàn)類去做,而不是自己實(shí)現(xiàn),這點(diǎn)在ApplicationContext上十分常見(jiàn),繼承自功能接口,這樣很容易看出ApplicationContext具備的功能,但是自身并不實(shí)現(xiàn),而調(diào)用對(duì)應(yīng)的實(shí)現(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會(huì)創(chuàng)建一個(gè)ConfigurableApplicationContext
通過(guò)webApplicationType判斷創(chuàng)建什么類型的Context,如果是SERVLET那么實(shí)例化
AnnotationConfigServletWebServerApplicationContext,利用反射調(diào)用無(wú)參構(gòu)造器進(jìn)行實(shí)例化。
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下是對(duì)對(duì)應(yīng)的類,來(lái)判斷類型
根據(jù)webApplicationType加載對(duì)應(yīng)的類
調(diào)用其構(gòu)造函數(shù)進(jìn)行初始化我這里看AnnotationConfigApplicationContext的構(gòu)造過(guò)程
接下來(lái)通過(guò)構(gòu)造方法看ApplicationContext的創(chuàng)建的過(guò)程
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工廠,初始化一個(gè)空的bean工廠,后續(xù)注冊(cè)獲取bean都依靠這個(gè)bean工廠進(jìn)行實(shí)現(xiàn)
AnnotationConfigApplicationContext
public AnnotationConfigApplicationContext() {
this.reader = new AnnotatedBeanDefinitionReader(this);
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
構(gòu)造函數(shù)中創(chuàng)建了AnnotatedBeanDefinitionReader和 ClassPathBeanDefinitionScanner
AnnotatedBeanDefinitionReader是一個(gè)根據(jù)指定類注冊(cè)BeanDefinnation的功能,同時(shí)能根據(jù)Condition跳過(guò)未達(dá)到條件的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通過(guò)資源加載,和環(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)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- springboot如何獲取applicationContext?servletContext
- SpringBoot?容器刷新前回調(diào)ApplicationContextInitializer
- SpringBoot如何使用applicationContext.xml配置文件
- Springboot如何獲取上下文ApplicationContext
- springboot ApplicationContextInitializer的三種使用方法小結(jié)
- SpringBoot如何使用ApplicationContext獲取bean對(duì)象
- SpringBoot獲取ApplicationContext的3種方式
- SpringBoot ApplicationContextAware拓展接口使用詳解
相關(guān)文章
新手小白入門必學(xué)JAVA面向?qū)ο笾鄳B(tài)
說(shuō)到多態(tài),一定離不開(kāi)其它兩大特性:封裝和繼承,下面這篇文章主要給大家介紹了關(guān)于新手小白入門必學(xué)JAVA面向?qū)ο笾鄳B(tài)的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-02-02
SpringCloud_Eureka服務(wù)注冊(cè)與發(fā)現(xiàn)基礎(chǔ)及構(gòu)建步驟
Eureka服務(wù)注冊(cè)中心,主要用于提供服務(wù)注冊(cè)功能,當(dāng)微服務(wù)啟動(dòng)時(shí),會(huì)將自己的服務(wù)注冊(cè)到Eureka Server,這篇文章主要介紹了SpringCloud中Eureka的配置及詳細(xì)使用,需要的朋友可以參考下2023-01-01
Java實(shí)現(xiàn)Html轉(zhuǎn)Pdf的方法
這篇文章主要介紹了Java實(shí)現(xiàn)Html轉(zhuǎn)Pdf的方法,實(shí)例分析了java基于ITextRenderer類操作頁(yè)面及系統(tǒng)自帶字體生成pdf文件的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-07-07
SpringBoot統(tǒng)一處理功能實(shí)現(xiàn)的全過(guò)程
最近在做項(xiàng)目時(shí)需要對(duì)異常進(jìn)行全局統(tǒng)一處理,主要是一些分類入庫(kù)以及記錄日志等,下面這篇文章主要給大家介紹了關(guān)于SpringBoot統(tǒng)一功能處理實(shí)現(xiàn)的相關(guān)資料,文中通過(guò)圖文以及實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-03-03
線程池之newFixedThreadPool定長(zhǎng)線程池的實(shí)例
這篇文章主要介紹了線程池之newFixedThreadPool定長(zhǎng)線程池的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06
SpringMVC視圖轉(zhuǎn)發(fā)重定向區(qū)別及控制器詳解
這篇文章主要為大家介紹了SpringMVC視圖轉(zhuǎn)發(fā)重定向區(qū)別及控制器示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪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),本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),需要的朋友可以參考下2023-07-07

