SpringBoot ApplicationContext接口深入分析

作者:氵奄不死的鱼 时间:2021-10-21 23:00:03 

ApplicationContext简述

ApplicationContext代表IOC容器,在SpringIOC容器中读取Bean配置创建Bean实例之前,必须对它进行实例化,只有在容器实例化后才可以从IOC容器里获取Bean实例并使用。

Spring IOC容器实现方式

Spring 提供了两种类型的IOC容器实现:

  • BeanFactory:IOC容器的基本实现。

  • ApplicationContext:提供了更多的高级特性,是BeanFactory的子接口。

两种方式比较:

  • BeanFactory:BeanFactory是Spring框架的基础设施,面向Spring本身

  • ApplicationContext : 面向使用Spring框架的开发者,**几乎所有的应用场合都直接使用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);
}

根据不同的环境创建不同的ApplicationContext实现

这里看一个基础的实现

AnnotationConfigApplicationContext

类图分析

SpringBoot ApplicationContext接口深入分析

根据类图看,ApplicationContext也是一个bean工厂的实现,继承自ResourceLoader也说明其具备加载文件,扫描bean的能力。ApplicationEventPublisher事件发布的能力

  • BeanFactory:Spring 管理 Bean 的顶层接口,ApplicationContext 继承了 BeanFactory 的两个子类:HierarchicalBeanFactory 和 ListableBeanFactory。HierarchicalBeanFactory 是一个具有层级关系的 BeanFactory,拥有属性 parentBeanFactory。 ListableBeanFactory 实现了枚举方法可以列举出当前 BeanFactory 中所有的 bean 对象而不必根据 name 一个一个的获取。

  • ApplicationEventPublisher:用于封装事件发布功能的接口,向事件 * (Listener)发送事件消息。

  • ResourceLoader:Spring 加载资源的顶层接口,用于加载资源文件。ApplicationContext 继承 ResourceLoader 的子类 ResourcePatternResolver,该接口是将 location 解析为 Resource 对象的策略接口。

  • MessageSource :解析 message 的策略接口,用于支撑国际化等功能。

  • EnvironmentCapable :用于获取 Environment 的接口。

下面就这些接口来一一分析

MessageSource

MessageSource 定义了获取 message 的策略方法 getMessage(),在 ApplicationContext 体系中,该方法 AbstractApplicationContext 实现,在 AbstractApplicationContext 中,它持有一个 MessageSource 实例,将 getMessage() 的实现给该实例来实现,如下:

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国际化支持操作

例如抛异常,我门希望在不同的环境能够显示不同的报错

修改Springboot application.yml配置

spring:
  messages:
    basename: i18n/messages #配置国际化资源文件路径

创建国际化资源文件

SpringBoot ApplicationContext接口深入分析

messages.properties不带后缀为默认语言资源

简体中文 messages_zh_CN.properties

unknown.exception=未知异常,请联系管理员!
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参数,默认就为该文件内容,此文件也可不用

unknown.exception=未知异常,请联系管理员!
user.login.notExists={0} 用户不存在!

获取错误信息通过messageSource获取

public class MessageUtils {
/**
* 根据消息键和参数 获取消息 委托给spring messageSource
* @param code 消息键
* @param args 参数
* @return 获取国际化翻译值
*/
public static String message(String code, Object... args) {
MessageSource messageSource = SpringUtils.getBean(MessageSource.class);
return messageSource.getMessage(code, args, LocaleContextHolder.getLocale());
}
}

ApplicationEventPublisher

用于封装事件发布功能的接口,向事件 * (Listener)发送事件消息。

该接口提供了一个 publishEvent() 用于通知在此应用程序中注册的所有的 * 。该方法在 AbstractApplicationContext 中实现。

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);
       }
   }
}

根据不同的事件类型触发对应的 * ,并且会触发父容器的发布发布事件

ResourcePatternResolver

ResourcePatternResolver 接口继承 ResourceLoader 接口,为将 location 解析为 Resource 对象的策略接口。它提供的 getResources() 在 AbstractApplicationContext 中实现,在 AbstractApplicationContext 中它持有一个 ResourcePatternResolver 的实例对象。 其定义如下:

public Resource[] getResources(String locationPattern) throws IOException {
   return this.resourcePatternResolver.getResources(locationPattern);
}

该方法的具体实现在 PathMatchingResourcePatternResolve

ResourcePatternResolver 在 ResourceLoader 的基础上增加了 getResources(String locationPattern),以支持根据路径匹配模式返回多个 Resource 实例,同时也新增了一种新的协议前缀 classpath*:,该协议前缀由其子类负责实现。

PathMatchingResourcePatternResolver 为 ResourcePatternResolver 最常用的子类,它除了支持 ResourceLoader 和 ResourcePatternResolver 新增的 classpath*: 前缀外,还支持 Ant 风格的路径匹配模式(类似于 **/*.xml)。

EnvironmentCapable

提供当前系统环境 Environment 组件。提供了一个 getEnvironment() 用于返回 Environment 实例对象,该方法在 AbstractApplicationContext 实现。

public ConfigurableEnvironment getEnvironment() {
   if (this.environment == null) {
       this.environment = this.createEnvironment();
   }
   return this.environment;
}

如果持有的 environment 实例对象为空,则调用 createEnvironment() 创建一个。

protected ConfigurableEnvironment createEnvironment() {
 return new StandardEnvironment();
}

StandardEnvironment 是一个适用于非 WEB 应用的 Environment。

Lifecycle

一个用于管理声明周期的接口。

在 AbstractApplicationContext 中存在一个 LifecycleProcessor 类型的实例对象 lifecycleProcessor,AbstractApplicationContext 中关于 Lifecycle 接口的实现都是委托给 lifecycleProcessor 实现的。如下:

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();
}

在启动、停止的时候会分别发布 ContextStartedEvent 和 ContextStoppedEvent 事件。

Closeable

Closeable 接口用于关闭和释放资源,提供了 close() 以释放对象所持有的资源。在 ApplicationContext 体系中由AbstractApplicationContext 实现,用于关闭 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() 发布 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的实现实际上基本上都是通过成员变量来实现的DefaultListableBeanFactory

在applicationContext的实现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);
}
@Override
public void setResourceLoader(@Nullable ResourceLoader resourceLoader) {
this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
this.metadataReaderFactory = new CachingMetadataReaderFactory(resourceLoader);
this.componentsIndex = CandidateComponentsIndexLoader.loadIndex(this.resourcePatternResolver.getClassLoader());
}

来源:https://blog.csdn.net/qq_37436172/article/details/127759107

标签:SpringBoot,ApplicationContext
0
投稿

猜你喜欢

  • IntelliJ IDEA运行SpringBoot项目的详细步骤

    2022-04-29 00:59:08
  • java数据结构与算法之冒泡排序详解

    2022-08-01 11:20:08
  • VS2019打包WPF安装程序最新教程(图文详解)

    2021-11-18 15:52:08
  • Unity2021发布WebGL与网页交互问题的解决

    2023-01-27 23:44:17
  • maven中下载jar包源码和javadoc的命令介绍

    2023-07-27 04:41:01
  • Android自定义SwipeRefreshLayout高仿微信朋友圈下拉刷新

    2023-01-06 08:51:34
  • 详解idea maven nexus 常见命令配置

    2021-06-07 18:29:03
  • 使用itextpdf解决PDF合并的问题

    2023-09-21 04:47:16
  • 使用PackageManager获得应用信息实例方法

    2023-09-10 22:59:30
  • 详解在java中进行日期时间比较的4种方法

    2022-09-03 23:35:52
  • Mybatis源码解析之事务管理

    2023-01-14 10:59:34
  • java8 集合 多字段 分组 统计个数代码

    2022-12-07 21:03:34
  • Java深入浅出掌握SpringBoot之MVC自动配置原理篇

    2022-04-16 02:14:34
  • java实现动态代理示例分享

    2023-04-28 15:54:49
  • Java基础篇之反射机制示例详解

    2021-12-08 04:05:25
  • Java开发岗位面试被问到反射怎么办

    2023-01-07 00:09:12
  • 如何利用Spring把元素解析成BeanDefinition对象

    2023-11-23 05:17:16
  • java为什么会出现精度丢失这种现象你知道吗

    2023-07-04 05:24:38
  • JAVA文件扫描(递归)的实例代码

    2022-04-08 08:37:20
  • 完美解决Spring Boot前端的Access-Control-Allow-Origin跨域问题

    2023-09-19 10:16:28
  • asp之家 软件编程 m.aspxhome.com