Spring源码解析-事件
Spring事件的组件
主要是3个组件:
1.ApplicationEvent 事件
2.ApplicationListener 监听器,对事件进行监听
3.ApplicationEventMulticaster 事件广播器,将publish的事件广播给所有的监听器。
事件

ContextRefreshedEvent :当ApplicationContext初始化或者刷新,将会发布,例如使用ConfigurableApplicationContext接口调用refresh方法,初始化意味着加载所有的bean,同时
ApplicationContext是支持热刷新。
ContextStartedEvent:当ApplicationContext启动的时候,将会调用start方法,发布此事件。
ContextStoppedEvent:当容器停止的时候,发布事件。
ContextClosedEvent:当容器关闭的时候,发布事件。
RequestHandledEvent:http请求完成后,发布事件。
监听器
ApplicationListener:ApplicationContext容器内部自定义事件监听器接口,继承自java.util.EventListener,ApplicationContext容器在启动时,会自动识别并加载EventListener类型bean的定义,一旦容器事件发布,将会通知注册到容器的监听器。

public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
/**
* Handle an application event.
* @param event the event to respond to
*/
void onApplicationEvent(E event);
}
发布事件者
ApplicationEventPublisher和ApplicationEventMulticaster
ApplicationEventPublisher:是一个封装事件发布接口,作为ApplicationContext父类接口。
ApplicationEventMulticaster:管理ApplicationListener对象,并且发布它们。

ApplicationContext继承ApplicationEventPublisher而担当者事件发布的角色,而ApplicationContext并没有进行事件的发布,而是交给了AbstractApplicationEventMulticaster来实现事件监听器的管理。
简单实例
模拟人民银行利息调整,通知到其他银行的事件。

利息事件
package org.lzyer.test; import org.springframework.context.ApplicationEvent; /**
* Created by Administrator on 2017/10/23.
*/
public class ExRateChangeEvent extends ApplicationEvent {
/**
* Create a new ApplicationEvent.
*
* @param source the component that published the event (never {@code null})
*/
public ExRateChangeEvent(Object source) {
super(source);
}
}
农业银行监听器
package org.lzyer.test; import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component; /**
* Created by Administrator on 2017/10/23.
*/
@Component
public class ABCListener implements ApplicationListener<ApplicationEvent> {
@Override
public void onApplicationEvent(ApplicationEvent event) {
if(event instanceof ExRateChangeEvent){
System.out.println("农业银行收到->"+event.getSource());
}
}
}
工商银行监听器
package org.lzyer.test; import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component; /**
* Created by Administrator on 2017/10/23.
*/
@Component
public class ICBCListener implements ApplicationListener<ApplicationEvent> { @Override
public void onApplicationEvent(ApplicationEvent event) {
if(event instanceof ExRateChangeEvent){
System.out.println("工商银行收到->"+event.getSource());
}
}
}
spring-beans.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="org.lzyer.test"></context:component-scan>
</beans>
测试代码
@Test
public void testPublishEvent(){
ApplicationContext context = new ClassPathXmlApplicationContext("spring/spring-beans.xml");
context.publishEvent(new ExRateChangeEvent("利息上调"));
}
结果:
农业银行收到->利息上调
工商银行收到:->利息上调
事件原理流程
spring容器初始化调用的refresh方法
AbstractApplicationContext@refresh
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
//省略 try {
//省略 // 初始化广播事件
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) {
//省略
}
}
}
initApplicationEventMulticaster()注册广播事件
protected void initApplicationEventMulticaster() {
//获取beanFactory
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 + "]");
}
}
}
AbstractApplicationEventMulticaster类中的属性


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 lisName : listenerBeanNames) {
getApplicationEventMulticaster().addApplicationListenerBean(lisName);
}
}
在refresh最后的一个方法,中发布刷新事件。
finishRefresh
protected void finishRefresh() {
// 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);
}
事件整体结构图

最后走一波debug





参考资料
http://www.jianshu.com/p/bc1055b065ce
http://liuxiamai.iteye.com/blog/2322197
http://jinnianshilongnian.iteye.com/blog/1902886
Spring源码解析-事件的更多相关文章
- Spring源码解析 - AbstractBeanFactory 实现接口与父类分析
我们先来看类图吧: 除了BeanFactory这一支的接口,AbstractBeanFactory主要实现了AliasRegistry和SingletonBeanRegistry接口. 这边主要提供了 ...
- spring 源码解析
1. [文件] spring源码.txt ~ 15B 下载(167) ? 1 springн┤┬вио╬Ш: 2. [文件] spring源码分析之AOP.txt ~ 15KB 下载( ...
- Spring源码解析——循环依赖的解决方案
一.前言 承接<Spring源码解析--创建bean>.<Spring源码解析--创建bean的实例>,我们今天接着聊聊,循环依赖的解决方案,即创建bean的ObjectFac ...
- Spring源码解析-ioc容器的设计
Spring源码解析-ioc容器的设计 1 IoC容器系列的设计:BeanFactory和ApplicatioContext 在Spring容器中,主要分为两个主要的容器系列,一个是实现BeanFac ...
- Spring源码解析系列汇总
相信我,你会收藏这篇文章的 本篇文章是这段时间撸出来的Spring源码解析系列文章的汇总,总共包含以下专题.喜欢的同学可以收藏起来以备不时之需 SpringIOC源码解析(上) 本篇文章搭建了IOC源 ...
- Spring源码解析之PropertyPlaceholderHelper(占位符解析器)
Spring源码解析之PropertyPlaceholderHelper(占位符解析器) https://blog.csdn.net/weixin_39471249/article/details/7 ...
- Spring源码解析之BeanFactoryPostProcessor(三)
在上一章中笔者介绍了refresh()的<1>处是如何获取beanFactory对象,下面我们要来学习refresh()方法的<2>处是如何调用invokeBeanFactor ...
- Spring源码解析之ConfigurationClassPostProcessor(二)
上一个章节,笔者向大家介绍了spring是如何来过滤配置类的,下面我们来看看在过滤出配置类后,spring是如何来解析配置类的.首先过滤出来的配置类会存放在configCandidates列表, 在代 ...
- Spring源码解析之八finishBeanFactoryInitialization方法即初始化单例bean
Spring源码解析之八finishBeanFactoryInitialization方法即初始化单例bean 七千字长文深刻解读,Spirng中是如何初始化单例bean的,和面试中最常问的Sprin ...
随机推荐
- LeetCode:9. Palindromic Number(Medium)
原题链接:https://leetcode.com/problems/palindrome-number/description/ 1. 题目要求:判断一个int类型整数是否是回文,空间复杂度O(1) ...
- Phoenix映射HBase数据表
1. 说明 安装好phoenix后对于HBase中已经存在的数据表不会自动进行映射,所以想要再phoenix中操作HBase已有数据表就需要手动进行配置. 2. 创建HBase表 > creat ...
- Coap协议学习笔记-第一篇
1. 物联网应用上一般使用单片机(或者其他SOC),单片机的RAM内存一般只有20KB~~128KB左右,然而一个TCP协议栈可能就20KB,所以只能用UDP,因为UDP相对小很多,然后在UDP上加了 ...
- 3、shader深度测试(Cull、ZWrite 、ZTest )
剔除和深度测试是渲染法线中的一个流程 Cull:默认情况下,Unity中给的所有Shader都是单面的,它都把反面的渲染给关闭掉了,如果你在开发的过程中需要使用到双面,只要把cull关闭(off)即可 ...
- coreos install megacli
基于官方的coreos ramdisk安装dell raid管理工具,其版本为debian8 jessie root@c64c7df05677:/# more /etc/apt/sources.lis ...
- windowsserver2008 重新启动计算机命令
在windowsserver2008中,若要重新启动计算机,可以输入以下命令即可立即重启计算机shutdown -r -t 0命令意义:shutdown在英文中意为关掉,在计算机中即为关机参数意义:- ...
- Java开发JDBC连接数据库
Java开发JDBC连接数据库 创建一个以JDBC连接数据库的程序,包含6个步骤: JDBC五部曲1.加载驱动2.获得链接3.获取statement对象 4.执行SQL语句5.产生resultset对 ...
- HDU 1271 整数对(思路题)
假设删除第k位,把整数A表示成如下形式: A = a * 10^(k+1) + b * 10 ^k + c; 则: B = a * 10^k + c; N = A + B = (11*a+b)*10^ ...
- HDU 4433 locker(DP)(2012 Asia Tianjin Regional Contest)
Problem Description A password locker with N digits, each digit can be rotated to 0-9 circularly.You ...
- python进制转换(二进制、十进制和十六进制)及注意事项
使用内置函数实现进制转换实现比较简单,主要用到以下函数: bin().oct().int().hex() 下面分别详解一下各个函数的使用(附实例) 第一部分:其他进制转十进制 1.二进制转十进制 使用 ...