Spring源码(4)Context篇之AbstractApplicationContext(下)

2023-05-16

上一篇【 Spring源码(4)Context篇之AbstractApplicationContext(上)】讲解了Spring的AbstractApplicationContext类refresh()方法, 前六步(其中五步)都是针对BeanFactory的处理…

下面接着讲其七:initMessageSource()

/**
* Initialize the MessageSource.
 * Use parent's if none defined in this context.
 */
protected void initMessageSource() {
	ConfigurableListableBeanFactory beanFactory = getBeanFactory();
	if (beanFactory.containsLocalBean(MESSAGE_SOURCE_BEAN_NAME)) {
		this.messageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource.class);
		// Make MessageSource aware of parent MessageSource.
		if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) {
			HierarchicalMessageSource hms = (HierarchicalMessageSource) this.messageSource;
			if (hms.getParentMessageSource() == null) {
				// Only set parent context as parent MessageSource if no parent MessageSource
				// registered already.
				hms.setParentMessageSource(getInternalParentMessageSource());
			}
		}
		if (logger.isDebugEnabled()) {
			logger.debug("Using MessageSource [" + this.messageSource + "]");
		}
	}
	else {	//如果工程里没有配置messageSource的bean,则走默认的处理逻辑
		// Use empty MessageSource to be able to accept getMessage calls.
		DelegatingMessageSource dms = new DelegatingMessageSource();
		dms.setParentMessageSource(getInternalParentMessageSource());
		this.messageSource = dms;
		beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource);
		if (logger.isDebugEnabled()) {
			logger.debug("Unable to locate MessageSource with name '" + MESSAGE_SOURCE_BEAN_NAME +
					"': using default [" + this.messageSource + "]");
		}
	}
}

该方法主要是关于MessageSource的处理,MessageSource是Spring处理国际化配置的资源类,一般项目会有关于MessageSource的配置:

Boot项目的配置

/**
 * 描述 : <注册消息资源处理器>. <br>
 */
@Bean
public MessageSource messageSource() {
	ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
	messageSource.setBasename("config.messages.messages");
	messageSource.setCacheSeconds(10); //reload messages every 10 seconds
	messageSource.setDefaultEncoding("UTF-8");
	messageSource.setUseCodeAsDefaultMessage(true);
	return messageSource;
}

或传统web项目的配置(xml配置):

<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
  <property name="basenames">
    <list>
      <value>resources/messages</value>
    </list>
  </property>
</bean>

如果工程里有上面的关于messageSource的配置,则工程会走if 代码块,否则走else代码块(意思是即使我们没有配置国际化相关类,spring会采用默认的国际化处理)

其八:initApplicationEventMulticaster();

/**
 * Initialize the ApplicationEventMulticaster.
 * Uses SimpleApplicationEventMulticaster if none defined in the context.
 * @see org.springframework.context.event.SimpleApplicationEventMulticaster
 */
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.isDebugEnabled()) {
			logger.debug("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
		}
	}
	else {
		this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
		beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
		if (logger.isDebugEnabled()) {
			logger.debug("Unable to locate ApplicationEventMulticaster with name '" +
					APPLICATION_EVENT_MULTICASTER_BEAN_NAME +
					"': using default [" + this.applicationEventMulticaster + "]");
		}
	}
}

这里是实例化事件传播的类,ApplicationContext容器提供了容器内部事件发布功能,ApplicationContext基于Observer模式,提供了针对Bean的事件传播功能。通过Application. publishEvent方法,我们可以将事件通知系统内所有的
ApplicationListener。

在这里插入图片描述

其九:onRefresh()
调用的是AbstractRefreshableWebApplicationContext类的onRefresh()方法

/**
 * Initialize the theme capability.
 */
@Override
protected void onRefresh() {
	this.themeSource = UiApplicationContextUtils.initThemeSource(this);
}

这里实例化的是TimeSource类,解读为主题资源类,一般工程是这样配置主题资源的(工程用不到时,可缺省配置):

<bean id="themeSource" class="org.springframework.ui.context.support.ResourceBundleThemeSource"/>

其十:registerListeners()

下面为registerListeners()方法的源码,这里暂时是该方法分了三个片断,后面针对三个片断的作用进行了解析!

/**
* Add beans that implement ApplicationListener as listeners.
 * Doesn't affect other listeners, which can be added without being beans.
 */
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);
		}
	}
}

方法注释:

  • Add beans that implement ApplicationListener as listeners.
  • Doesn’t affect other listeners, which can be added without being beans.

意思是说添加bean实例做为监听器(或称侦听器),添加哪些Bean呢? 重点就是:implement ApplicationListener (实现了ApplicationListener 接口的Bean), 这里的操作并不影响那些不是bean实例的监听器;

现在对registerListeners()方法具体分析一下:

registerListeners()片断一:

// Register statically specified listeners first.
for (ApplicationListener<?> listener : getApplicationListeners()) {
	getApplicationEventMulticaster().addApplicationListener(listener);
}
//上面的getApplicationListeners()调用的下面的方法
/** 
 * Return the list of statically specified ApplicationListeners.
 */
public Collection<ApplicationListener<?>> getApplicationListeners() {
	return this.applicationListeners;	//这里的applicationListeners是LinkedHashSet类的实例,所以是有序的
}

首先注册静态指定的监听器,这里注册加载的是xml配置文件里的监听器! 比如项目中如果使用mybtais,xml里经常会有sqlSessionFactory的配置!

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<property name="mapperLocations" value="classpath*:/**/*Mapper.xml" />
		<property name="plugins">
			<array>
				<bean class="com.zfy.core.interceptor.RequestContextInterceptor" />
				<bean class="com.zfy.core.interceptor.MultiLanguageInterceptor" />
				<bean class="com.zfy.core.interceptor.SecurityTokenInterceptor" />
				<bean class="com.github.pagehelper.PageHelper" />
			</array>
		</property>
		<property name="configLocation" value="classpath:mybatis-configuration.xml" />
	</bean>

其中SqlSessionFactoryBean就是实现了ApplicationListener接口

public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
	....
}

下图就是debug下模式下,返回的applicationListeners的具体内容!
在这里插入图片描述
返回的applicationListeners加到哪里呢? 调用的是AbstractApplicationEventMulticaster的addApplicationListener()方法

@Override
public void addApplicationListener(ApplicationListener<?> listener) {
	synchronized (this.retrievalMutex) {
		// Explicitly remove target for a proxy, if registered already,
		// in order to avoid double invocations of the same listener.
		Object singletonTarget = AopProxyUtils.getSingletonTarget(listener);
		if (singletonTarget instanceof ApplicationListener) {
			this.defaultRetriever.applicationListeners.remove(singletonTarget);
		}
		this.defaultRetriever.applicationListeners.add(listener);	//这一行是把listener添加到了Set<ApplicationListener<?>> applicationListeners; 该Set也是一个LinkedHashSet
		this.retrieverCache.clear();
	}
}

重点:这里需要注意的是listener都添加进了ListenerRetriever类的applicationListeners变量中,这是一个LinkedHashSet集合,是有序的,因为这里是有序,所以后续对applicationListeners进行遍历针对每一个listener进行处理时也是有序的!

registerListeners()片断二:

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

这里是首先获取BeanFactory中ApplicationListener.class类型的Bean,然后for循环,把每个bean添加到ListenerRetriever类的applicationListeners变量中!

那哪些Bean实例是ApplicationListener.class类型的呢?

其实项目中除了引用的第三方框架会有使用到ApplcationListener(比如上面讲到的org.mybatis.spring.SqlSessionFactoryBean),有时候根据业务需要,常常会自定义ApplicationListener,比如:

/**
 * @author zhoufy
 */
@Service
public class ApplicationStartListener implements ApplicationListener<ContextRefreshedEvent>{
	@Override
	public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
		//这里可以实现一下项目中的业务代码 
		System.out.println("初始化时我被调用了。");
	}
}

而且往往会定义多个ApplicationListener也是经常有的情况!自定义的ApplicationStartListener类,因为有@Service注解,所以会被Spring的BeanFactory所管理,所以会在registerListeners()片断二里被解析到!

注意:如果是自定义多个ApplicationListener,会根据包、类的顺序先后被BeanFactory所管理,所以自然会先后被添加到ListenerRetriever类的applicationListeners变量中,上面也提到过了applicationListeners是有序的数据结构,当后边解析applicationListeners变量,执行onApplicationEvent()方法时,也是有序的!

简单一句话就是,自定义的ApplicationListener是有序的依次执行,或者说每次项目启动执行的顺序是一样的!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Spring源码(4)Context篇之AbstractApplicationContext(下) 的相关文章

随机推荐

  • 【架构 Flutter实践 Clean架构 && TDD测试驱动开发---1.0】

    2022 11 12 补充 最近在开发中尝试用了clean架构 xff0c 感觉就是 麻烦 太多模板代码 xff0c 很容易过度开发 我认为了解这些理念是很重要的 xff0c 但应该跟随你的项目 流程 进行调整 裁剪 如果你们的流程是 服务
  • Flutter didUpdateWidget 的使用问题 ( 为什么不建议重写)

    简单总结 以我的认知 xff1a 不要重写 didUpdateWidget你需要重写 xff0c 是否是你设计有问题你确定要重写 xff0c 且设计没问题 评论下大佬 xff0c 我也学下这种case 总结 xff1a 不要依赖于 didU
  • java ClassLoader机制和如何加载外部class文件(含代码)

    Java类的生命周期 生命周期有 xff1a 加载 xff08 Loading xff09 验证 xff08 Verification xff09 准备 xff08 Preparation xff09 解析 xff08 Resolution
  • Android Studio开发Groovy

    Context xff1a 主要讲下Android Studio如何开发Groovy xff0c 搭环境这东西 xff0c 最恶心 我查了N多的外文 1 AS Android Studio 本身是支持Groovy的 xff0c Gradle
  • RXJava找不到AndroidSchedulers

    原因 xff1a 还需要依赖一个 RxAndroid xff0c RxAndroid里有AndroidSchedulers xff0c RxJava里是没有的 xff08 类似于Java的SDK不会包含 AndroidSDK里的TextVi
  • 安装APK 免输入vivo、oppo密码

    2020 06 03补充 最优化版本 GitHub demo地址 apk下载地址 新建一个APP xff0c 使用Accessibility功能 监听vivo的 安装引用 界面用户输入 vivo密码 xff0c 后续 自动填写用 安装应用弹
  • Android logct中的线程ID

    针对logcat中的线程ID 进程ID过滤 好奇的是 线程ID 并不是Thread类里的ID 64 Override protected void onCreate Bundle savedInstanceState super onCre
  • Java Socket 服务端发送数据 客户端接收数据

    服务端 xff1a package com thinkgem wlw modules api test socket 64 Author zhouhe 64 Date 2019 4 8 9 30 import java io import
  • 百度移动软件开发面试题(20131018)

    1 new与malloc的区别 xff1f 分析 xff1a 一 new和malloc都是用于申请动态内存 new使用delete释放空间 xff0c malloc使用free释放 new和delete是C 43 43 中的运算符 xff0
  • RK3568 Debian10 AP6275S Bluetooth调试

    overlay etc profile d start blutooth sh start blutooth sh chmod 777 sys class rfkill rfkill0 state echo 0 gt sys class r
  • Java与VBA通讯实践

    1 DDE 动态数据交换 方式 VBA具有DDE能力 xff0c Java没有 网上搜索了一下 xff0c 有几个公司开发了这方面的类库 xff0c 可以使Java具有DDE能力 xff08 1 xff09 JavaDDE nevaobje
  • Linux下如何设置程序的开机自启动功能

    文章目录 Linux下设置程序开机自启动1 etc rc local2 etc profile d 3 使用 crontab 方式4 使用 systemd 服务测试代码 Linux下设置程序开机自启动 在最后 xff0c 我放了我测试时候要
  • vnc连接不上,vnc连接不上是为什么?原因详解

    vnc连接不上的原因 xff0c 服务器作为网站建设的常用设备 xff0c 在服务器运行过程中起到举足轻重的作用 用户在选择服务器是常用的方式有服务器租用 虚拟主机租用以及服务器托管 xff0c 通过进行文件以及数据的下载 上传等实现网站的
  • window10安装vnc无法使用,window10安装vnc无法使用的原因和解决办法

    window10安装vnc无法使用的原因 xff0c 服务器作为网站建设的常用设备 xff0c 在服务器运行过程中起到举足轻重的作用 用户在选择服务器是常用的方式有服务器租用 虚拟主机租用以及服务器托管 xff0c 通过进行文件以及数据的下
  • 企业信息化技术架构展望

    企业用户慢慢的从前期选用一些同行业成功的项目案例来复制 xff0c 或者选用一些知名的咨询公司提出的行业内通用方案 xff0c 转型为自己主动认真思考什么是自己需求 xff0c 什么是真正适合自己的信息化之路 做为企业信息技术架构设计 xf
  • Errors were encountered while processing: google-chrome-stable

    转自 xff1a http omtlab com errors were encountered while processing google chrome stable Many people facing this issue whi
  • virtualbox 命令

    VBoxManage命令详解 xff08 一 xff09 本人对vboxmange命令按我个人的理解作了解释 xff0c 由于本人水平有限难免有错误的地方 xff0c 希望大家帮我指正 VBoxManage v version 显示virt
  • 基于Springboot的物业管理系统_代码

    下载地址 1 1 课题背景 目的及意义 1 1 1 课题背景 互联网 43 改变着我们的生活 xff0c 在传统的社区物业服务领域 xff0c 在服务业主 提升社区服务 质量方面 xff0c 如何与互联网融合 xff1f 顶级互联网技术团队
  • eclips运行generatorConfig.xml文件生成代码

    描述 xff1a 如何通过eclips工具来运行 generatorConfig xml 文件来自动生成代码并获取数据 xff08 类似于mybaits逆向生成 xff09 xff1f mybatis generator generate
  • Spring源码(4)Context篇之AbstractApplicationContext(下)

    上一篇 Spring源码 4 Context篇之AbstractApplicationContext xff08 上 xff09 讲解了Spring的AbstractApplicationContext类refresh 方法 xff0c 前