一篇文章彻底搞懂spring的ApplicationContext体系
本文主要介绍ApplicationContext在Spring中各个子类的作用和区别
一、ApplicationContext子类体系图
从图中我们可以得到两个重要信息:
- ApplicationContext的子类都是继承自AbstractApplicationContext
- 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. */
- AbstractApplication是对ApplicationContext接口的一个抽象实现类,它不持有任何配置,仅仅是对公共上下文功能的实现.使用模版方法设计模式,需要子类实现其抽线方法
- 与BeanFactory相比,ApplicationContext的作用是检测内部Bean工厂的特殊bean的定义,因此它会有以下操作:
- 自动把BeanFactoryPostProcessors、BeanPostProcessors、ApplicationListeners注册成bean
- 在context中以bean的形式提供MessageSource,bean名称为messageSource
- 在context中以bean的形式提供ApplicationEventMulticaster,bean名称为applicationEventMulticaster,并提供默认实现SimpleApplicationEventMulticaster
- 通过继承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;
- 其中ApplicationName默认为空
- 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事件时有两种策略:
- 设置了线程池,则将广播ApplicationEvent的任务提交到线程池异步执行
- 没有设置线程池,则同步执行广播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有区别:
- GenericApplicationContext 没有做任何动作,只是判断了一下当前refresh的次数,最多只能一次.
- AbstractRefreshableApplicationContext:每次refresh都会重新生成BeanFactory,并重新加载Bean,加载bean definitions通过loadBeanDefinitions抽象方法,尤其子类实现.
总结:
- GenericApplicationContext子类只会调用一次refresh过程,并且可以由其他组件向它组册Bean Definition
- AbstractRefreshableApplicationContext支持多次refresh,但是需要子类提供bean配置地址
二、GenericApplicationContext子类分析
我们先来看下GenericeApplicationContext有哪些子类,然后逐个介绍:
2.1GenericWebApplicationContext和其子类
GenericWebApplicationContext对在web环境下GenericApplicationContext实现.
有以下功能:
- 可以基于编程配置web相关信息,而不是传统的web.xml.
- web相关的配置通过WebApplicationInitializer在tomcat启动后,调用onStartup方法配置servlet/filter/listener等信息.
- 添加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.