> 文档中心 > 一篇文章彻底搞懂spring的ApplicationContext体系

一篇文章彻底搞懂spring的ApplicationContext体系

本文主要介绍ApplicationContext在Spring中各个子类的作用和区别

一、ApplicationContext子类体系图

在这里插入图片描述

从图中我们可以得到两个重要信息:

  1. ApplicationContext的子类都是继承自AbstractApplicationContext
  2. AbstractApplicationContext的子类分为两类,一类是GenericApplicationContext的子类,一类是AbstractRefreshableApplicationContext的子类.

接下来我们沿着这两个特点进行分析

1.1.AbstractApplicationContext

1.1.1.作用介绍

我们先来看下源码中的注释对ApplicationContext作用的定位:

 /*Abstract implementation of the {@link org.springframework.context.ApplicationContext} * interface. Doesn't mandate the type of storage used for configuration; simply * implements common context functionality. Uses the Template Method design pattern, * requiring concrete subclasses to implement abstract methods. * * 

In contrast to a plain BeanFactory, an ApplicationContext is supposed * to detect special beans defined in its internal bean factory: * Therefore, this class automatically registers * {@link org.springframework.beans.factory.config.BeanFactoryPostProcessor BeanFactoryPostProcessors}, * {@link org.springframework.beans.factory.config.BeanPostProcessor BeanPostProcessors} * and {@link org.springframework.context.ApplicationListener ApplicationListeners} * which are defined as beans in the context. * *

A {@link org.springframework.context.MessageSource} may also be supplied * as a bean in the context, with the name "messageSource"; otherwise, message * resolution is delegated to the parent context. Furthermore, a multicaster * for application events can be supplied as "applicationEventMulticaster" bean * of type {@link org.springframework.context.event.ApplicationEventMulticaster} * in the context; otherwise, a default multicaster of type * {@link org.springframework.context.event.SimpleApplicationEventMulticaster} will be used. * *

Implements resource loading through extending * {@link org.springframework.core.io.DefaultResourceLoader}. * Consequently treats non-URL resource paths as class path resources * (supporting full class path resource names that include the package path, * e.g. "mypackage/myresource.dat"), unless the {@link #getResourceByPath} * method is overwritten in a subclass. */

  1. AbstractApplication是对ApplicationContext接口的一个抽象实现类,它不持有任何配置,仅仅是对公共上下文功能的实现.使用模版方法设计模式,需要子类实现其抽线方法
  2. 与BeanFactory相比,ApplicationContext的作用是检测内部Bean工厂的特殊bean的定义,因此它会有以下操作:
    1. 自动把BeanFactoryPostProcessors、BeanPostProcessors、ApplicationListeners注册成bean
    2. 在context中以bean的形式提供MessageSource,bean名称为messageSource
    3. 在context中以bean的形式提供ApplicationEventMulticaster,bean名称为applicationEventMulticaster,并提供默认实现SimpleApplicationEventMulticaster
  3. 通过继承DefaultResourceLoader来实现资源加载功能

接下来我们来看下AbstractApplicationContext具体做了哪些工作

1.1.2.AbstractApplicationContext功能

1.1.2.1.对ApplicationContext接口的实现

1.1.2.1.1.对ApplicationContext接口本身定义的实现

ApplicationContext接口定义:

public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,MessageSource, ApplicationEventPublisher, ResourcePatternResolver {@NullableString getId();String getApplicationName();String getDisplayName();long getStartupDate();@NullableApplicationContext getParent();AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;}

AbstractApplicationContext通过持有对应属性值来实现:

private String id = ObjectUtils.identityToString(this);private String displayName = ObjectUtils.identityToString(this);private ApplicationContext parent;private long startupDate;
  1. 其中ApplicationName默认为空
  2. getAutowireCapableBeanFactory通过抽象方法getBeanFactory有子类实现,
1.1.2.1.2.对ApplicationContext父接口EnvironmentCapable的实现

AbstractApplicationContext 定义了一个ConfigurableEnvironment属性:

private ConfigurableEnvironment environment;

ConfigurableEnvironment 里面持有当前应用yml文件中所用变量信息.

使用方法:

application.yml配置:

app.name: springmvc

1.通过ConfigurableEnvironment获取:

在ApplicationContext中定义的ConfigurableEnvironment,实际的子类是StandardEnvironment.

@SpringBootApplicationpublic class SpringMvcApp {    public static void main(String[] args) { ConfigurableApplicationContext context = SpringApplication.run(SpringMvcApp.class,args); StandardEnvironment standardEnvironment = (StandardEnvironment) context.getEnvironment(); String appName = standardEnvironment.getProperty("app.name"); System.out.println("appName:"+appName);    }}appName:springmvc

2.占位符获取

@Servicepublic class UserServiceImpl implements UserService , InitializingBean {    @Value("${app.name}")    private String appName;      @Override    public void afterPropertiesSet() throws Exception { System.out.println("占位符获取appName:"+appName);    }}
1.1.2.1.2.对ApplicationContext父接口ApplicationEventPublisher的实现
public interface ApplicationEventPublisher {default void publishEvent(ApplicationEvent event) {publishEvent((Object) event);}void publishEvent(Object event);}

ApplicationEventPublisher接口是发送容器事件的接口,通过观察者模式实现,会在容器不同阶段发送不同的ApplicationEvent.

AbstractApplicationContext在refresh函数中会调用initApplicationEventMulticaster初始化一个ApplicationEventMulticaster,默认子类为:SimpleApplicationEventMulticaster,用于发送ApplicationEvent和管理当前应用中注册的ApplicationListener.

你也可以定义自己的ApplicationEventMulticaster,设置bean名称为applicationEventMulticaster,就可以替换默认的实现.

public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";

我们来看下ApplicationEventMulticaster工作原理:

在这里插入图片描述

既然ApplicationEventMulticaster是通过观察者模式来实现ApplicationEvent的广播,那么ApplicationEventMulticaster的实现中一定会持有一组观察者,即一组ApplicationListener的实现.

果然在AbstractApplicationEventMulticaster,定义了一个ListenerRetriever,而这个ListenerRetriever就持有一组ApplicationListener.

public abstract class AbstractApplicationEventMulticasterimplements ApplicationEventMulticaster, BeanClassLoaderAware, BeanFactoryAware {private final ListenerRetriever defaultRetriever = new ListenerRetriever(false);  }private class ListenerRetriever {  ...public final Set<ApplicationListener<?>> applicationListeners;  ...}

AbstractApplicationEventMulticaster提供了添加、删除和获取ApplicationListener的功能.这里不在粘贴AbstractApplicationEventMulticaster的源码.

我们看下SimpleApplicationEventMulticaster是如何广播ApplicationEvent事件的.

public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {@Nullableprivate Executor taskExecutor;@Nullableprivate ErrorHandler errorHandler;@Overridepublic void multicastEvent(ApplicationEvent event) {multicastEvent(event, resolveDefaultEventType(event));}@Overridepublic void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {Executor executor = getTaskExecutor();if (executor != null) {executor.execute(() -> invokeListener(listener, event));}else {invokeListener(listener, event);}}}  protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {ErrorHandler errorHandler = getErrorHandler();if (errorHandler != null) {try {doInvokeListener(listener, event);}catch (Throwable err) {errorHandler.handleError(err);}}else {doInvokeListener(listener, event);}}@SuppressWarnings({"unchecked", "rawtypes"})private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {try {listener.onApplicationEvent(event);}catch (ClassCastException ex) {String msg = ex.getMessage();if (msg == null || matchesClassCastMessage(msg, event.getClass())) {// Possibly a lambda-defined listener which we could not resolve the generic event type for// -> let's suppress the exception and just log a debug message.Log logger = LogFactory.getLog(getClass());if (logger.isDebugEnabled()) {logger.debug("Non-matching event type for listener: " + listener, ex);}}else {throw ex;}}}}

从multicastEvent函数可以看出,SimpleApplicationEventMulticaster在广播ApplicationEvent事件时有两种策略:

  1. 设置了线程池,则将广播ApplicationEvent的任务提交到线程池异步执行
  2. 没有设置线程池,则同步执行广播ApplicationEvent的任务
1.1.2.1.3.对ApplicationContext父接口BeanFactory、ListableBeanFactory、HierarchicalBeanFactory的实现

AbstractApplicationContext对BeanFactory、ListableBeanFactory、HierachicalBeanFactory的实现,都通过抽象方法getBeanFactory获得子类持有的ConfigurableListableBeanFactory来实现这三个BeanFactory的功能,AbstractApplicationContext充当了一个代理的角色.

1.1.2.1.4.对ApplicationContext父接口MessageSource的实现

MessageSource是spring对国际化的实现,这里不详细介绍,后面会有文章详细介绍其原理.

1.1.2.1.5.对ApplicationContext父接口ResourcePatternResolver的实现

ResourcePatternResolver提供了根据路径查找对应资源文件的功能.

public interface ResourcePatternResolver extends ResourceLoader {String CLASSPATH_ALL_URL_PREFIX = "classpath*:";Resource[] getResources(String locationPattern) throws IOException;}

AbstractApplicationContext中不具体实现其功能,而是充当代理者的角色,具体实现有PathMatchingResourcePatternResolver完成.

PathMatchingResourcePatternResolver主要解析classpath路径下的文件

1.1.2.2.对ConfigurableApplicationContext接口的实现

AbstractApplicationContext最重要的实现.

public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable {  /**  添加一个BeanFactoryPostProcessor,在内部BeanFactory执行refresh时,并bean definition使用之前被执行  在读取上下文配置时使用.  **/  void addBeanFactoryPostProcessor(BeanFactoryPostProcessor postProcessor);  /**  添加一个ApplicationListener,用于监听ApplicationEvent  ***/void addApplicationListener(ApplicationListener<?> listener);  /**   添加一个特殊资源协议的解析器  ***/void addProtocolResolver(ProtocolResolver resolver);  /**   从xml、properties、数据库中加载配置,来创建bean实例.   这是一个启动方法,在调用这个方法后,要不一个bean都没有,要不所有的都已经实例化完成  **/void refresh() throws BeansException, IllegalStateException;  /**  注册jvm shutdown 钩子函数  **/void registerShutdownHook();  /**  关闭context,释放子类持有的所有资源  ***/@Overridevoid close();  /**  判断context是否启动  ***/boolean isActive();  /**   获取内部bean工厂  ***/ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;}

ConfigurableApplicationContext的作用?

ConfigurableApplicationContext 重点在于设置应用的相关组件,而ApplicationContext接口重点在于获取配置.

下面主要看下refresh函数,该函数定义了context初始化的主要流程,其中就包括初始化容器中bean的主流程.

我们看下refresh函数做了哪些工作?

@Overridepublic void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {// Prepare this context for refreshing.prepareRefresh();// Tell the subclass to refresh the internal bean factory.    ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();// Prepare the bean factory for use in this context.prepareBeanFactory(beanFactory);try {// Allows post-processing of the bean factory in context subclasses.postProcessBeanFactory(beanFactory);// Invoke factory processors registered as beans in the context.invokeBeanFactoryPostProcessors(beanFactory);// Register bean processors that intercept bean creation.registerBeanPostProcessors(beanFactory);// Initialize message source for this context.initMessageSource();// Initialize event multicaster for this context.initApplicationEventMulticaster();// Initialize other special beans in specific context subclasses.onRefresh();// Check for listener beans and register them.registerListeners();// Instantiate all remaining (non-lazy-init) singletons.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();}}}

prepareRefresh阶段

该阶段主要设置启动时间和启动标识,可以被子类覆盖,但覆盖逻辑里面一定要调用super.prepareRefresh();

obtainFreshBeanFactory阶段

获取子类持有的BeanFactory.

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {refreshBeanFactory();ConfigurableListableBeanFactory beanFactory = getBeanFactory();if (logger.isDebugEnabled()) {logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);}return beanFactory;}

refreshBeanFactory()是一个抽象函数,只有当前ApplicationContext是AbstractRefreshableApplicationContext的子类时才有具体逻辑,如下:

@Overrideprotected final void refreshBeanFactory() throws BeansException {if (hasBeanFactory()) {destroyBeans();closeBeanFactory();}try {DefaultListableBeanFactory beanFactory = createBeanFactory();beanFactory.setSerializationId(getId());customizeBeanFactory(beanFactory);loadBeanDefinitions(beanFactory);synchronized (this.beanFactoryMonitor) {this.beanFactory = beanFactory;}}catch (IOException ex) {throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);}}

如果当前ApplicationContext已经持有了BeanFactory实例,那么重新销毁所有的bean,并重新加载,相当于每次返回一个新的BeanFactory实例.

prepareBeanFactory阶段

配置BeanFactory设置内置的Bean和相关配置如classloader、el表达式解析器等

postProcessBeanFactory阶段

这时Beanfactory已经初始化完成,所有的bean definition已加载,但是还未实例bean,允许AbstractApplication的子类对BeanFactory进行修改,

如注册BeanPostProcessors.

invokeBeanFactoryPostProcessors阶段

实例化并执行所有BeanFactoryPostProcessor的bean,如有设置了顺序,则按顺序执行.

在springboot应用中,在这个过程开始ConfigurationClassPostProcessor来扫描用户系统的class,并将其转化成Bean definition注册到BeanFactory中.

registerBeanPostProcessors阶段

实例化所有的BeanPostProcessor ,并注册

initMessageSource阶段

初始化国际化相关配置

initApplicationEventMulticaster阶段

设置ApplicationEvent广播控制器

onRefresh阶段

模版方法,可以用于初始化特殊的bean

registerListeners阶段

注册ApplicationListener,用于监听ApplicationEventMulticaster广播的ApplicationEvent事件

finishBeanFactoryInitialization阶段-bean生命周期开始阶段

这个阶段才开始将剩余的Bean进行初始化,用户设置的非懒加载的bean都在这阶段完成.

如@Controller、@Service、@Compenont等修饰的.

finishRefresh阶段

ApplicationContext refresh流程完成,容器初始化完成,做以下工作

  • 发送ContextRefreshedEvent事件
  • 删除Context -level 缓存,如扫描到的类信息
  • 注册LifecycleProcessor,并执行onRefresh方法

1.1.2.5.对Lifecycle接口的实现

生命周期接口,没有看到在哪里使用到.

public interface Lifecycle {void start();void stop();boolean isRunning();}

1.2GenericApplicationContext和AbstractRefreshableApplicationContext对比

1.2.1.继承关系对比

先贴两张图,看下两者的继承关系:

GenericApplicationContext继承关系
在这里插入图片描述

AbstractRefreshableApplicationContext继承关系
在这里插入图片描述

从继承关系上看GenericeApplicationContext实现了BeanDefinitionRegistry接口,BeanDefinitionRegistry 有什么作用:

public interface BeanDefinitionRegistry extends AliasRegistry {void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)throws BeanDefinitionStoreException;void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;boolean containsBeanDefinition(String beanName);  String[] getBeanDefinitionNames();int getBeanDefinitionCount();boolean isBeanNameInUse(String beanName);}

BeanDefinitionRegistry提供了注册Bean Definition的功能,当前其子类的实现也会持有这些Bean Definitions.

1.2.2.AbstractApplicationContext抽象方法实现对比

GenericApplicationContext

public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {...@Overrideprotected 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());}...}

AbstractRefreshableApplicationContext

public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext {@Overrideprotected final void refreshBeanFactory() throws BeansException {if (hasBeanFactory()) {destroyBeans();closeBeanFactory();}try {DefaultListableBeanFactory beanFactory = createBeanFactory();beanFactory.setSerializationId(getId());customizeBeanFactory(beanFactory);loadBeanDefinitions(beanFactory);synchronized (this.beanFactoryMonitor) {this.beanFactory = beanFactory;}}catch (IOException ex) {throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);}}protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)throws BeansException, IOException;}

GenericApplicationContext和AbstractRefreshableApplicationContext在实现AbstractApplicationContext的refreshBeanFactory有区别:

  1. GenericApplicationContext 没有做任何动作,只是判断了一下当前refresh的次数,最多只能一次.
  2. AbstractRefreshableApplicationContext:每次refresh都会重新生成BeanFactory,并重新加载Bean,加载bean definitions通过loadBeanDefinitions抽象方法,尤其子类实现.

总结:

  1. GenericApplicationContext子类只会调用一次refresh过程,并且可以由其他组件向它组册Bean Definition
  2. AbstractRefreshableApplicationContext支持多次refresh,但是需要子类提供bean配置地址

二、GenericApplicationContext子类分析

我们先来看下GenericeApplicationContext有哪些子类,然后逐个介绍:

在这里插入图片描述

2.1GenericWebApplicationContext和其子类

GenericWebApplicationContext对在web环境下GenericApplicationContext实现.

有以下功能:

  1. 可以基于编程配置web相关信息,而不是传统的web.xml.
  2. web相关的配置通过WebApplicationInitializer在tomcat启动后,调用onStartup方法配置servlet/filter/listener等信息.
  3. 添加ThemeSource bean,用于web环境的主题切换

ServletWebServerApplicationContext

ServletWebServerApplicationContext是GenericWebApplicationContext的直接子类,相比于GenericWebApplicationContext,

ServletWebServerApplicationContext增加了创建、初始化一个web server的功能.

@Overrideprotected void onRefresh() {super.onRefresh();try {createWebServer();}catch (Throwable ex) {throw new ApplicationContextException("Unable to start web server", ex);}}private void createWebServer() {WebServer webServer = this.webServer;ServletContext servletContext = getServletContext();if (webServer == null && servletContext == null) {ServletWebServerFactory factory = getWebServerFactory();this.webServer = factory.getWebServer(getSelfInitializer());}else if (servletContext != null) {try {getSelfInitializer().onStartup(servletContext);}catch (ServletException ex) {throw new ApplicationContextException("Cannot initialize servlet context",ex);}}initPropertySources();}

ServletWebServerApplicationContext是可以面向开发者直接使用的一个web 类型的ApplicationContext,但是spring提供了更好的选择.

AnnotationConfigServletWebServerApplicationContext和XmlServletWebServerApplicationContext.

AnnotationConfigServletWebServerApplicationContext

该context可以根据一个配置了配置文件路径也是扫描bean的路径或包名的@Configuration类,来初始化我们的应用,完成bean的加载等.

在springboot中

@SpringBootApplicationpublic class SpringMvcApp {    public static void main(String[] args) { ConfigurableApplicationContext context = SpringApplication.run(SpringMvcApp.class,args);     }}

SpringApplication.run返回的ApplicationContext 实际就是AnnotationConfigServletWebServerApplicationContext

XmlServletWebServerApplicationContext

相比于AnnotationConfigServletWebServerApplicationContext通过注解方式配置bean的配置文件、资源文件,XmlServletWebServerApplicationContext是通过提供相应的构造函数传入文件路径或者通过load方法加载相应的资源文件.

public class XmlServletWebServerApplicationContextextends ServletWebServerApplicationContext {private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);public XmlServletWebServerApplicationContext() {this.reader.setEnvironment(this.getEnvironment());}public XmlServletWebServerApplicationContext(Resource... resources) {load(resources);refresh();}public XmlServletWebServerApplicationContext(String... resourceLocations) {load(resourceLocations);refresh();}public XmlServletWebServerApplicationContext(Class<?> relativeClass,String... resourceNames) {load(relativeClass, resourceNames);refresh();}public void setValidating(boolean validating) {this.reader.setValidating(validating);}public final void load(Resource... resources) {this.reader.loadBeanDefinitions(resources);}public final void load(String... resourceLocations) {this.reader.loadBeanDefinitions(resourceLocations);}public final void load(Class<?> relativeClass, String... resourceNames) {Resource[] resources = new Resource[resourceNames.length];for (int i = 0; i < resourceNames.length; i++) {resources[i] = new ClassPathResource(resourceNames[i], relativeClass);}this.reader.loadBeanDefinitions(resources);}}

加载资源的方式:

  • 构造函数传入
  • 调用load方法

2.2.AnnotationConfigApplicationContext和GenericXmlApplicationContext

AnnotationConfigApplicationContext/GenericXmlApplicationContext同AnnotationConfigServletWebServerApplicationContext/XmlServletWebServerApplicationContext类似

  • AnnotationConfigApplicationContext是通过@Configuration类来加载资源文件,初始化当前ApplicationContext的.
  • GenericXmlApplicationContext通过特定文件加载资源

不同的是:AnnotationConfigApplicationContextAnnotationConfigApplicationContext/GenericXmlApplicationContext是非web的ApplicationContext,AnnotationConfigServletWebServerApplicationContext/XmlServletWebServerApplicationContext是支持web服务的context,可以提供对外的web服务.

假如你要写一个tcp服务,可以用这两者中的一个作为你管理bean的容器,只不过你需要自己在AbstractApplicationContext.refresh执行的某一个步骤或其他时机,完成自己的端口监听等.

或者,当前你的服务不需要对外提供服务,你也可以选择该这两个context.

2.3.StaticApplicationContext和其子类

StaticApplicationContext和StaticWebApplicationContext不会扫描任何文件,而是通过相应的方法直接注册bean,一般用于测试.

2.4.GenericGroovyApplicationContext

GenericGroovyApplicationContext提供了spring对Groovy的支持,可以加载Groovy bean definition,并完成bean的注册.

2.5.GenericReactiveWebApplicationContext和其子类

GenericReactiveWebApplicationContext和ReactiveWebServerApplicationContext同GenericWebApplicationContext和ServletWebServerApplicationContext功能类似,只不过是对reactive环境的支持.

  • GenericReactiveWebApplicationContext不会启动一个web server,资源路径使用相对路径
  • ReactiveWebServerApplicationContext启动一个webserver

二、AbstractRefreshableApplicationContext子类分析

AbstractRefreshableApplicationContext体系基本上和GenericApplicationContext类似,它们最本质的区别就是:

在refreshBeanFactory阶段AbstractRefreshableApplicationContext体系的ApplicationContext会返回一个新的BeanFactory,并重新加载bean.

而GenericApplicationContext体系的ApplicationContext会一直使用refresh前创建的BeanFactory.

AbstractRefreshableApplicationContext的好处是可以执行多次refresh.