之前做个数据统计的需求,就是用的Spring事件发布与监听机制(用于采集基础数据),今天做个小小的总结。
下面我们先回顾下基础的Spring知识(BeanFactory&ApplicationContext的相关知识),因为Spring事件发布与监听机制就包含在其中初始化流程的某个步骤中。
1、BeanFactory 使用demo:
/**
* <p>spring源码小例子</p>
* @date: 2021/1/3 08:59
*/
@SuppressWarnings("deprecation")
public class BeanFactoryTest {
@Test
public void testSimpleLoad(){
//BeanFactory容器的使用
BeanFactory bf = new XmlBeanFactory(new ClassPathResource("beanFactoryTest.xml"));
MyTestBean bean = (MyTestBean) bf.getBean("myTestBean");
assertEquals("testBean", bean.getTestStr());
}
}
1.1 BeanFactory 源码(节选getBean()方法)
//---------------------------------------------------------------------
// Implementation of BeanFactory interface
//---------------------------------------------------------------------
@Override
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
@SuppressWarnings("unchecked")
protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
throws BeansException {
String beanName = transformedBeanName(name);
Object bean;
// Eagerly check singleton cache for manually registered singletons.
//Return the (raw) singleton object registered under the given name.
// 从单例池获取Bean对象
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
if (logger.isTraceEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
}
else {
logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
}
}
//获取Bean对象的实例
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
1.2 代码分析:
2、ApplicationContext 使用demo:
/**
* <p>自定义标签解析</p>
* @date: 2021/1/6 18:31
*/
public class CustomXSDTagTest {
@Test
public void testSimpleLoad() {
//读取配置文件,ApplicationContext 容器的使用
ApplicationContext bf = new ClassPathXmlApplicationContext("test.xml");
User user = (User) bf.getBean("testbean");
System.out.println(user.getEmail() + " " + user.getUserName() + "" + user.getAge());
}
}
2.1 ApplicationContext 源码
ApplicationContext 接口的关系架构图:(ApplicationContext 本质是对 BeanFactory 进行了功能拓展)
ApplicationContext跟BeanFactory 相反,它是在容器启动时,一次性创建了所有的Bean。同时,注册Spring监听器的工作也发生在这里:registerListeners();
public ClassPathXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
super(parent);
setConfigLocations(configLocations);
if (refresh) {
//此处的refresh() 方法,会一次性将所有的bean全部装载到Spring容器
refresh();
}
}
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// 解析xml配置文件
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// 注册未来bean实例化的前后置处理的PostProcessor接口实现
postProcessBeanFactory(beanFactory);
//执行所有实现BeanFactoryPostProcessor接口实现,对beanFactory进行处理
invokeBeanFactoryPostProcessors(beanFactory);
// 注册未来bean实例化的前后置处理的PostProcessor接口实现
registerBeanPostProcessors(beanFactory);
// 注册未来bean实例化的前后置处理的PostProcessor接口实现
initMessageSource();
// 实例化spring事件发布监听机制的核心类,SimpleApplicationEventMulticaster
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// 注册事件监听器
registerListeners();
// 实例化非懒加载的bean,完成ioc容器中bean的实例化和反转依赖,并在内部实现动态代理相关的操作
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
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;
}
}
}
2.2 代码分析:
梳理下源码的流程,见下图:(我们这章节的重点就是从10步进行切入:注册事件监听器)
3、BeanFacotry与ApplicationContext的区别
原始的BeanFactory无法支持spring的许多插件,如AOP功能、Web应用等。
ApplicationContext以一种更向面向框架的方式工作以及对上下文进行分层和实现继承,ApplicationContext包还提供了以下的功能: 1)MessageSource, 提供国际化的消息访问 2)资源访问,如URL和文件 3)事件传播 (我们这章节的重点) 4)载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层。
1、事件源&事件pojo
// 事件源
public class TestEvent extends ApplicationEvent {
/**
* Create a new {@code ApplicationEvent}.
*
* @param source the object on which the event initially occurred or with
* which the event is associated (never {@code null})
*/
public TestEvent(TestInfo source) {
super(source);
}
}
//事件pojo
public class TestInfo {
private String info;
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
}
2、事件监听器
ApplicationListener 需要设置泛型限定类,也就是上面提到的事件源。
@Component
public class TestEventListener implements ApplicationListener<TestEvent> {
@Override
public void onApplicationEvent(TestEvent event) {
TestInfo testInfo = (TestInfo) event.getSource();
System.out.println("testInfo = " + testInfo);
}
}
3、发布自定义事件
在业务的需要地方进行事件发布:
@Autowired
private ApplicationEventPublisher applicationEventPublisher;
private void publishEvent() {
TestInfo testInfo = new TestInfo();
testInfo.setInfo("zk-init");
TestEvent testEvent = new TestEvent(testInfo);
applicationEventPublisher.publishEvent(testEvent);
}
4、代码分析
ApplicationContext事件机制是观察者设计模式的实现。
两个重要成员
源码剖析思路,以3个组件作为线索:
组件一:事件 ApplicationEvent 的5种实现
ApplicationEvent 是所有事件的基础抽象类,包括我们的自定义事件也是继承了它。
组件二:监听器 ApplicationListener
ApplicationListener:ApplicationContext容器内部自定义事件监听器接口,继承自java.util.EventListener,ApplicationContext容器在启动时,会自动识别并加载EventListener类型bean的定义,一旦容器事件发布,将会通知注册到容器的监听器。
组件三:广播器ApplicationEventMulticaster
1、概述:发布器 ApplicationEventPublisher 和 广播器ApplicationEventMulticaster 的关系
ApplicationEventPublisher:是一个封装事件发布接口,作为ApplicationContext父类接口。
ApplicationEventMulticaster:管理ApplicationListener对象,并且发布它们。
ApplicationContext 委托给了 AbstractApplicationEventMulticaster 来实现事件监听器(ApplicationListener)的管理。
2、发布事件 - ApplicationEventPublisher
源码 :使用了 applicationEventPublisher.publishEvent() 的代码段,可以将事件发布出去。
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
Assert.notNull(event, "Event must not be null");
// Decorate event as an ApplicationEvent if necessary
ApplicationEvent applicationEvent;
if (event instanceof ApplicationEvent) {
applicationEvent = (ApplicationEvent) event;
}
else {
applicationEvent = new PayloadApplicationEvent<>(this, event);
if (eventType == null) {
eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
}
}
// Multicast right now if possible - or lazily once the multicaster is initialized
if (this.earlyApplicationEvents != null) {
this.earlyApplicationEvents.add(applicationEvent);
}
else {
//获取到广播器,并且将自定义事件告诉广播器。
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
}
// Publish event via parent context as well...
if (this.parent != null) {
if (this.parent instanceof AbstractApplicationContext) {
((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
}
else {
this.parent.publishEvent(event);
}
}
}
源码分析:
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType); 获取到广播器(SimpleApplicationEventMulticaster),并且将自定义事件告诉广播器。
3、广播器 - SimpleApplicationEventMulticaster
3.1、SimpleApplicationEventMulticaster 广播器的类架构图:
ApplicationEventMulticaster 接口实现类是 SimpleApplicationEventMulticaster,它的 multicastEvent() 方法功能是:实现了遍历监听器列表,逐个发布事件到监听器中(观察者模式的应用场景)。
代码分析:
上文提及的代码段:getApplicationEventMulticaster() 方法便是获取到注入的实例 SimpleApplicationEventMulticaster,它即是ApplicationEventMulticaster 的实现类了。
3.2、SimpleApplicationEventMulticaster 内部维护了一个监听器列表,即是一个 ConcurrentHashMap 进行管理的。
final Map<ListenerCacheKey, CachedListenerRetriever> retrieverCache = new ConcurrentHashMap<>(64);
3.3、SimpleApplicationEventMulticaster 广播事件源码,通过multicastEvent() 方法实现
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
Executor executor = getTaskExecutor();
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
if (executor != null) {
//调用监听器的 onApplicationEvent 方法,处理事件
executor.execute(() -> invokeListener(listener, event));
}
else {
//调用监听器的 onApplicationEvent 方法,处理事件
invokeListener(listener, event);
}
}
}
代码解析:
最终调用 SimpleApplicationEventMulticaster 的 invokeListener() 方法进行实质事件处理。
SimpleApplicationEventMulticaster的 invokeListener() 方法源码,最终调用了 doInvokeListener() 方法。
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);
}
}
doInvokeListener() 最终会调用监听器的 onApplicationEvent 方法,实现监听效果。
这里注意,方法会抛出 ClassCastException 异常,因为事件源被业务处理时可能发生类型转换失败的情况,这样也能够捕获到这类运行时异常。
@SuppressWarnings({"rawtypes", "unchecked"})
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
//若是匹配上监听器,则会调用该监听器类的 onApplicationEvent 方法
listener.onApplicationEvent(event);
}
catch (ClassCastException ex) {
String msg = ex.getMessage();
if (msg == null || matchesClassCastMessage(msg, event.getClass()) ||
(event instanceof PayloadApplicationEvent &&
matchesClassCastMessage(msg, ((PayloadApplicationEvent) event).getPayload().getClass()))) {
// Possibly a lambda-defined listener which we could not resolve the generic event type for
// -> let's suppress the exception.
Log loggerToUse = this.lazyLogger;
if (loggerToUse == null) {
loggerToUse = LogFactory.getLog(getClass());
this.lazyLogger = loggerToUse;
}
if (loggerToUse.isTraceEnabled()) {
loggerToUse.trace("Non-matching event type for listener: " + listener, ex);
}
}
else {
throw ex;
}
}
}
代码分析:
再走读一下源码,我们可以发现 SimpleApplicationEventMulticaster 其实是支持异步事件通知 和同步事件通知。
而 SimpleApplicationEventMulticaster 作为默认的事件广播器,用的是同步通知的方式;但是Spring给我们提供了一个解决方案来实现我们需要的异步广播器(参考下面的小点)。
1、自定义广播器
@Component("applicationEventMulticaster") 注解则声明了Bean的name为固定的“applicationEventMulticaster”。
/**
* <p>
* 继承 SimpleApplicationEventMulticaster ,实现异步监听器
* 如下我们看到在以上的判断是否自定义了多播器的代码中,判断在ioc容器中是否包含如下名字的bean作为判断条件的,所以只要我们自定义一个bean命名为applicationEventMulticaster,并把异步支持的executor植入就行了
* </p>
*/
@Component("applicationEventMulticaster")
public class AsnyTestEventListener extends SimpleApplicationEventMulticaster {
public AsnyTestEventListener () {
setTaskExecutor(Executors.newSingleThreadExecutor());
}
}
2、源码分析(自定义广播器是如何被注册到Spring容器的)
2.1、我们走读一下 AbstractApplicationContext 的源码,注意到一个静态字符串变量的值为“applicationEventMulticaster”;
/**
* Name of the ApplicationEventMulticaster bean in the factory.
* If none is supplied, a default SimpleApplicationEventMulticaster is used.
* @see org.springframework.context.event.ApplicationEventMulticaster
* @see org.springframework.context.event.SimpleApplicationEventMulticaster
*/
public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";
2.2、同时定位到 initApplicationEventMulticaster() 方法的作用就是 Initialize the ApplicationEventMulticaster.(初始化事件广播器);如果可以获取到则使用这个“applicationEventMulticaster” Bean,则可以进行注册了(其实就是获取对象引用然后赋值)。
/**
* Initialize the ApplicationEventMulticaster.
* Uses SimpleApplicationEventMulticaster if none defined in the context.
* @see org.springframework.context.event.SimpleApplicationEventMulticaster
*/
protected void initApplicationEventMulticaster() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
//Bean工厂是否可以获取到 applicationEventMulticaster 的Bean
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 {
//获取不到自定义的广播器,那么就使用默认的 SimpleApplicationEventMulticaster
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() + "]");
}
}
}
}
代码分析:
当在我们自定义的多播器中设置了executor时,SimpleApplicationEventMulticaster 广播器的exeutor就不为空了 ,就会走到第一个异步多播的路径。
通过上文,我们复习了Spring的两大容器组件(BeanFacotry与ApplicationContext);进而切入到“Spring事件发布&监听机制”:包括了它们的应用案例、底层源码分析;最后针对Spring事件广播器的特性,拓展了如何自定义异步广播器以及它背后的原理。希望对大家有所帮助。