Spring context的refresh函数执行过程分析
今天看了一下Spring Boot的run函数运行过程,发现它调用了Context中的refresh函数。所以先分析一下Spring context的refresh过程,然后再分析Spring boot中run的流程。
首先我们找到spring-context组件的AbstractApplicationContext类下的refresh函数:
- @Override
- public 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()
- protected void prepareRefresh() {
- // Switch to active.
- this.startupDate = System.currentTimeMillis();
- this.closed.set(false);
- this.active.set(true);
- ......
- // Initialize any placeholder property sources in the context environment.
- initPropertySources();
- // Validate that all properties marked as required are resolvable:
- // see ConfigurablePropertyResolver#setRequiredProperties
- getEnvironment().validateRequiredProperties();
- // Store pre-refresh ApplicationListeners...
- if (this.earlyApplicationListeners == null) {
- this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
- }
- else {
- // Reset local application listeners to pre-refresh state.
- this.applicationListeners.clear();
- this.applicationListeners.addAll(this.earlyApplicationListeners);
- }
- // Allow for the collection of early ApplicationEvents,
- // to be published once the multicaster is available...
- this.earlyApplicationEvents = new LinkedHashSet<>();
- }
可以看到,这个函数做了一些准备工作,记录了refresh的开始时间,调用了留给子类重写的initPropertySources()函数。
验证了环境中必要的参数,并且将earlyListeners加入到应用程序的监听器列表中。
obtainFreshBeanFactory()
- protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
- refreshBeanFactory();
- return getBeanFactory();
- }
可以看见,refreshBeanFactory()和getBeanFactory()都是abstract函数,这两个函数设计出来就是为了让子类重写,根据子类实现具体功能,从函数名可以推断出这两个函数一个是用来初始化beanFactory,另外一个则是拿到beanFactory的实例,我们以AbstractRefreshableApplicationContext类的代码为例:
refreshBeanFactory:
- @Override
- protected final void refreshBeanFactory() throws BeansException {
- if (hasBeanFactory()) {//如果beanFactory已经被初始化过,则在此销毁
- destroyBeans();
- closeBeanFactory();
- }
- try {//这里其实就是new了一个DefaultListableBeanFactory
- DefaultListableBeanFactory beanFactory = createBeanFactory();
- beanFactory.setSerializationId(getId());
- //客制化beanFactory,例如设置能否循环引用,能否重写bean等
- customizeBeanFactory(beanFactory);
- //将xml中的bean定义加载到spring-core的注册中心中
- loadBeanDefinitions(beanFactory);
- //设置beanFactory,供getBeanFactory取得
- synchronized (this.beanFactoryMonitor) {
- this.beanFactory = beanFactory;
- }
- }
- catch (IOException ex) {
- throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
- }
- }
getBeanFactory:不难想象,这个函数从this类中拿到beanFactory实例并返回。
prepareBeanFactory(beanFactory)
篇幅原因就不贴这部分代码,实在太长,这个函数主要做了如下几件事:
1. 设置了beanFactory的classloader
2. 设置了SpEL的解析器
3. 设置了PropertyEditor,这个是用来将xml里面的占位符解析成具体值的,例如xml里面写<... value="${predefined-var}"/>,在PropertyEditor可以将${predefined-var}解析为某个具体值,并且到时候生成的beanDefinition对应的field就是这个具体的值。这一步会发生在xml被转换为beanDefinition之前
4. 增加ApplicationContextAwareProcessor的BeanPostProcessor,这个是为了让实现了ApplicationContextAware接口的bean在初始化后自身被注入当前的ApplicationContext,实现对自己所在的Context的感知
5. 忽略下述接口的依赖注入:
EnvironmentAware.class
EmbeddedValueResolverAware.class
ResourceLoaderAware.class
ApplicationEventPublisherAware.class
MessageSourceAware.class
ApplicationContextAware.class
观察上述接口的特征,发现这些都是Aware系列接口,用于使Bean感知环境中的参数(例如当前Context)。自动装配不会对这些接口进行处理,实际上实现了这些接口的类会在Spring中有专门的函数进行处理。
6. 对于一些特定的接口实现,定义默认的注入值:
BeanFactory.class
ResourceLoader.class
ApplicationEventPublisher.class
ApplicationContext.class
这些接口是用来获取关于Spring本身相关的信息的,例如Spring本身的BeanFactory等。
7. 注册一些环境相关的bean,例如systemProperties、systemEnvironment和environment
postProcessBeanFactory(beanFactory)
这个函数实际上是空的:
- /**
- * Modify the application context's internal bean factory after its standard
- * initialization. All bean definitions will have been loaded, but no beans
- * will have been instantiated yet. This allows for registering special
- * BeanPostProcessors etc in certain ApplicationContext implementations.
- * @param beanFactory the bean factory used by the application context
- */
- protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
- }
通过上面的注释,我们可以知道,当所有的xml已经被载入并且产生了对应的beanDefinition时,这个函数将会被调用,此时bean的实例都没有产生,在此处可以对beanDefinition的属性进行修改、抑或是注册特别的beanPostProcessor用于对实例化的bean做最终处理。
这里函数留空是为了让用户能够子类化,然后在里面写入自己需要的修改,典型的模板设计模式
invokeBeanFactoryPostProcessors(beanFactory)
调用所有在容器中注册的BeanFactoryPostProcessor
registerBeanPostProcessors(beanFactory)
注册BeanPostProcessors,将所有在xml中定义的beanPostProcessors加入到当前BeanFactory的列表,以便在getBean的时候调用。
initMessageSource()
初始化消息源
initApplicationEventMulticaster()
- protected void initApplicationEventMulticaster() {
- ConfigurableListableBeanFactory beanFactory = getBeanFactory();
- if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
- this.applicationEventMulticaster =
- beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
- if (logger.isTraceEnabled()) {
- logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
- }
- }
- else {
- this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
- beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
- if (logger.isTraceEnabled()) {
- logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +
- "[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
- }
- }
- }
通过Spring容器得到一个applicationEventMulticaster,如果Spring容器没有定义,则创建SimpleApplicationEventMulticaster作为applicationEventMulticaster。
通过SimpleApplicationEventMulticaster的代码我们也能推断出这个类的作用,就是向Context里面的EventListener发布消息:
- public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
- ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
- Executor executor = getTaskExecutor();
- //将当前的事件event发送给当前Context的每一个Listener
- for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
- //如果有executor则使用executor执行
- if (executor != null) {
- executor.execute(() -> invokeListener(listener, event));
- }
- else {
- //否则直接在当前线程执行
- invokeListener(listener, event);
- }
- }
- }
onRefresh()
这是模板方法,留给子类实现并执行想要的操作
registerListeners()
这一步将注册Listener,供前面initApplicationEventMulticaster注册的EventMulticaster进行广播,代码如下:
- protected void registerListeners() {
- // Register statically specified listeners first.
- for (ApplicationListener<?> listener : getApplicationListeners()) {
- getApplicationEventMulticaster().addApplicationListener(listener);
- }
- // Do not initialize FactoryBeans here: We need to leave all regular beans
- // uninitialized to let post-processors apply to them!
- String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
- for (String listenerBeanName : listenerBeanNames) {
- getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
- }
- // Publish early application events now that we finally have a multicaster...
- Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
- this.earlyApplicationEvents = null;
- if (earlyEventsToProcess != null) {
- for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
- getApplicationEventMulticaster().multicastEvent(earlyEvent);
- }
- }
- }
这段代码将静态注册的Listener和Spring中注册的Listener bean都添加到EventMulticaster中,这样从EventMulticaster中发布的消息,每个Listener都能监听到,典型的观察者模式。
值得注意的是,Context中包含earlyApplicationEvents,所有的Listener就绪后,会先接收到这个事件。
finishBeanFactoryInitialization(beanFactory)
通过上面的注释:Instantiate all remaining (non-lazy-init) singletons.
我们可以知道,这个方法初始化所有的非懒加载单例实例,代码很复杂,后面会开一个专题分析这段代码的。
finishRefresh()
从函数名可以知道这个是完成刷新,代码如下:
- protected void finishRefresh() {
- // Clear context-level resource caches (such as ASM metadata from scanning).
- clearResourceCaches();
- // Initialize lifecycle processor for this context.
- initLifecycleProcessor();
- // Propagate refresh to lifecycle processor first.
- getLifecycleProcessor().onRefresh();
- // Publish the final event.
- publishEvent(new ContextRefreshedEvent(this));
- // Participate in LiveBeansView MBean, if active.
- LiveBeansView.registerApplicationContext(this);
- }
实际上它:
1. 清空了资源缓存
2. 初始化了生命周期处理器,用于处理Bean生命周期
3. 使用生命周期处理器传播刷新事件
4. 在Context内发布刷新事件
5. 将本Context注册到ListBeansView中
至此,Context的refresh分析完毕,下一部该分析Spring Boot的run进行了什么操作了。
Spring context的refresh函数执行过程分析的更多相关文章
- 当spring 容器初始化完成后执行某个方法
在做web项目开发中,尤其是企业级应用开发的时候,往往会在工程启动的时候做许多的前置检查. 比如检查是否使用了我们组禁止使用的Mysql的group_concat函数,如果使用了项目就不能启动,并指出 ...
- Spring Context 你真的懂了吗
今天介绍一下大家常见的一个单词 context 应该怎么去理解,正确的理解它有助于我们学习 spring 以及计算机系统中的其他知识. 1. context 是什么 我们经常在编程中见到 contex ...
- 8 -- 深入使用Spring -- 5...2 使用@Cacheable执行缓存
8.5.2 使用@Cacheable执行缓存 @Cacheable可用于修饰类或修饰方法,当使用@Cacheable修饰类时,用于告诉Spring在类级别上进行缓存 ------ 程序调用该类的实例的 ...
- 【报错】spring整合activeMQ,pom.xml文件缺架包,启动报错:Caused by: java.lang.ClassNotFoundException: org.apache.xbean.spring.context.v2.XBeanNamespaceHandler
spring版本:4.3.13 ActiveMq版本:5.15 ======================================================== spring整合act ...
- MFC的执行过程分析
MFC程序的执行细节剖析 MFC程序也是Windows程序,所以它应该也有一个WinMain.可是在程序中看不到它的踪影.事实上在程序进入点之前.另一个(并且仅有一个)全局对象(theApp).这就是 ...
- Spring容器的refresh()介绍
Spring容器的refresh()[创建刷新]; 1.prepareRefresh()刷新前的预处理; 1).initPropertySources()初始化一些属性设置;子类自定义个性化的属性设置 ...
- Hi3559AV100 NNIE开发(2)-RFCN(.wk)LoadModel及NNIE Init函数运行过程分析
之后随笔将更多笔墨着重于NNIE开发系列,下文是关于Hi3559AV100 NNIE开发(2)-RFCN(.wk)LoadModel及NNIE Init函数运行过程分析,通过对LoadModel函数及 ...
- Spring @Transaction 注解是如何执行事务的?
前言 相信小伙伴一定用过 @Transaction 注解,那 @Transaction 背后的秘密又知道多少呢? Spring 是如何开启事务的?又是如何进行提交事务和关闭事务的呢? 画图猜测 在开始 ...
- 【转】Spring bean处理——回调函数
Spring bean处理——回调函数 Spring中定义了三个可以用来对Spring bean或生成bean的BeanFactory进行处理的接口,InitializingBean.BeanPost ...
随机推荐
- 攻防世界 reverse 进阶 notsequence
notsequence RCTF-2015 关键就是两个check函数 1 signed int __cdecl check1_80486CD(int a1[]) 2 { 3 signed int ...
- java位运算求一个整数的绝对值
1 import java.util.Scanner; 2 3 public class Question1 { 4 public static void main(String[] args) { ...
- 第28 章 : 理解容器运行时接口 CRI
理解容器运行时接口 CRI CRI 是 Kubernetes 体系中跟容器打交道的一个非常重要的部分.本文将主要分享以下三方面的内容: CRI 介绍 CRI 实现 相关工具 CRI 介绍 在 CRI ...
- CyclicBarrier:人齐了,老司机就可以发车了!
上一篇咱讲了 CountDownLatch 可以解决多个线程同步的问题,相比于 join 来说它的应用范围更广,不仅可以应用在线程上,还可以应用在线程池上.然而 CountDownLatch 却是一次 ...
- 问题笔记 - element表格 操作状态值
1.必须从传到表里的数据源中取值(scope.row.star)
- 【算法学习笔记】组合数与 Lucas 定理
卢卡斯定理是一个与组合数有关的数论定理,在算法竞赛中用于求组合数对某质数的模. 第一部分是博主的个人理解,第二部分为 Pecco 学长的介绍 第一部分 一般情况下,我们计算大组合数取模问题是用递推公式 ...
- [面试仓库]HTML面试题汇总
HTML这一块呢,说简单也简单,说难也不是那么容易.但我们在各个面试要求中,大部分都把HTML这一条摆在了第一位,重要性可想而知.这个位置算是有关HTML的一个汇总点了,亦会在这里及时补充. 1, ...
- oo第四单元与课程总结
Part1 本单元三次作业架构设计总结 1.组织结构 由于官方代码中已经给我们提供了许多零散的类元素,如UmlClass``UmlAssociation``UmlParameter等,因此我首先将某些 ...
- JDBC_05_ResorceBundle(资源绑定器) 绑定配置文件
ResorceBundle(资源绑定器) 绑定配置文件 jdbc.proprtise 需要在src目录下新建一个文件夹然后将jdbc.proprtise放在文件中然后右键该文件夹选择 Rebuild ...
- elasticsearch jvm优化
测试环境elasticsearch jvm 4G jdk1.8 [serveradm@test-log-server elasticsearch]$ java -version java versio ...