springboot监听器模式源码精讲

1.前言

很多时候我们看源码的时候看不下去,其中一个原因是系统往往使用了许多设计模式,如果你不清楚这些设计模式,这无疑增加了你阅读源码的难度。

springboot中就大量使用了设计模式,本文主要介绍其中的一种监听器模式,这是观察者模式中的一种。

作者利用空闲时间去阅读了一下相关的源码,然后决定分享出来,大家相互学习。

这篇文章主要分为3个部分:

第一个部分主要讲下监听模式的几个步骤以及一个简单的例子,这样我们在阅读源码的时候就知道是怎么回事了。

第二个部分是这篇文章的核心,也就是springboot中是如何使用监听模式的。

第三个部分主要是在spingboot中自定义实现监听功能

2.监听模式

实现监听模式的四个步骤:

  • 创建事件
  • 创建事件的监听器
  • 创建广播器
  • 发布事件

我们就按照上面的步骤实现一个监听功能

2.1新建工程

创建一个springboot工程,主要的依赖是这个

xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

我们的springboot版本是2.7.17

2.2创建事件

先创建一个基础的模板事件

csharp 复制代码
package com.example.springbootcode.order.event;

public abstract class OrderEvent {
    abstract void desc();
}

这是一个抽象类,里面定义了抽象方法,接下里就创建具体的事件

订单开始创建事件

scala 复制代码
package com.example.springbootcode.order.event;

public class OrderCreateStartingEvent extends OrderEvent{
    @Override
    public void desc() {
        System.out.print("第一步:开始创建订单,......");
    }
}

订单创建完成事件

scala 复制代码
package com.example.springbootcode.order.event;

public class OrderCreateFinishEvent extends OrderEvent{
    @Override
    public void desc() {
        System.out.print("最后一步:订单创建完成,......");
    }
}

这里定义了两个事件,都继承了OrderEvent,实现了里面的desc方法

2.3创建监听器

创建一个监听器接口

csharp 复制代码
package com.example.springbootcode.order.event;

public interface OrderListener {
    void onOrderEvent(OrderEvent event);
}

定义了一个监听事件的方法,其参数就是事件,接下来我们实现具体的监听器

订单创建过程监听器

csharp 复制代码
public class OrderLogListener implements OrderListener{
    @Override
    public void onOrderEvent(OrderEvent event) {
        if (event instanceof OrderCreateStartingEvent) {
            event.desc();
        } else if (event instanceof OrderCreateFinishEvent){
            event.desc();
        }
    }
}

监听事件,并调用事件里面的desc()方法

2.4创建广播器

定义事件广播器接口

csharp 复制代码
package com.example.springbootcode.order.event;

public interface EventMulticaster {
    void multicasterEvent(OrderEvent event);

    void addListener(OrderListener listener);

    void removeListener(OrderListener listener);
}

广播器我们可以这样理解:主要复杂监听器的管理,并将事件广播给所有监听器,如果该监听器对广播的事件感兴趣,那么就会处理事件

接下来实现一下具体的广播器

java 复制代码
package com.example.springbootcode.order.event;


import java.util.ArrayList;
import java.util.List;

public class OrderEventMulticaster implements EventMulticaster{
    List<OrderListener> listenerList = new ArrayList<>();

    @Override
    public void multicasterEvent(OrderEvent event) {
        listenerList.forEach(listener -> listener.onOrderEvent(event));
    }

    @Override
    public void addListener(OrderListener listener) {
        listenerList.add(listener);
    }

    @Override
    public void removeListener(OrderListener listener) {
        listenerList.remove(listener);
    }
}

这里主要实现了3个方法,事件广播、新增监听器、删除监听器

2.5发布事件

发布事件,其实说白了就是在项目中调用OrderEventMulticaster

typescript 复制代码
@SpringBootApplication
public class SpringbootCodeApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootCodeApplication.class, args);

        OrderEventMulticaster orderEventMulticaster = new OrderEventMulticaster();
        orderEventMulticaster.addListener(new OrderLogListener());
        orderEventMulticaster.multicasterEvent(new OrderCreateStartingEvent());
    }
}

加了两个监听器,然后我们发布了一个订单开始创建事件,控制台会打印"第一步:开始创建订单,......"

到这里为止,一个简单的监听模式就实现了,接下来我们根据这个例子去阅读一下springboot源码中关于监听器的部分。

3.监听模式源码精讲

由于springboot有太多地方使用了监听模式,我们不可能去阅读所有代码,这也不是我写这篇文章的目的。我们只需了解其中的一个事件、一个监听器就知道springboot大概是怎么实现事件的监听和发布。

下面我们以应用程序启动为例,看看其是怎么实现事件的监听和发布。

3.1启动事件

在讲启动事件之前,我们先来看看springboot跟生命周期相关的事件脉络

由图可以发现,启动程序开始到完成会发布ApplicationStartingEventApplicationPreparedEventApplicationReadyEventApplicationStartedEvent......这些事件。

其实由名字大概就知道它们的意思,这里我们以 ApplicationStartingEvent 这个开始启动事件为例,去看了解其实现过程。

EventObject

是所有事件状态对象的根类, 这是 java.util 包中的 EventObject 类的源代码,里面有个方法 getSource() 用于返回事件最初发生的对象。

ApplicationEvent

提供了一个基本的事件模型,为应用程序中的事件提供了一种通用的表示方式 。

SpringApplicationEvent

提供了一个 与 Spring Boot 应用程序的启动过程相关的事件通用的基类,这里就是类似于我们前面例子中的OrderEvent事件

scala 复制代码
public abstract class SpringApplicationEvent extends ApplicationEvent {

	private final String[] args;

	public SpringApplicationEvent(SpringApplication application, String[] args) {
		super(application);
		this.args = args;
	}

	public SpringApplication getSpringApplication() {
		return (SpringApplication) getSource();
	}

	public final String[] getArgs() {
		return this.args;
	}
}

总得来说就是获取事件的源和事件相关的参数

ApplicationStartingEvent

这个就是启动事件了,类似于我们例子中的OrderCreateStartingEvent,我们看看这个启动事件的代码

scala 复制代码
public class ApplicationStartingEvent extends SpringApplicationEvent {

	private final ConfigurableBootstrapContext bootstrapContext;

	public ApplicationStartingEvent(ConfigurableBootstrapContext bootstrapContext, SpringApplication application,
			String[] args) {
		super(application, args);   // Ⅰ
		this.bootstrapContext = bootstrapContext; // Ⅱ
	}

	public ConfigurableBootstrapContext getBootstrapContext() {
		return this.bootstrapContext;
	}

}

它干了下面这些事:

Ⅰ: 调用父类的构造函数,将 application 设置为事件的源,将参数数组 args 设置为事件的相关参数

**Ⅱ:**初始化一个上下文接口,并通过getBootstrapContext()来获取该对象

总得来说这个事件是比较简单的。

3.2监听器

还是一样,我们先上图,先了解一下关于启动程序事件监听器的实现脉络

EventListener

这是一个标记接口 ,在 Java 编程中常用于为一组类提供一个共同的标记,而不包含任何方法。标记接口通常用于指示类具有某种特定的性质、行为或能力。

ApplicationListener

这是 Spring Framework 中的 ApplicationListener 接口,用于定义应用程序事件监听器,这里就类似于上面例子中的OrderListener

csharp 复制代码
@FunctionalInterface //Ⅰ
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
	
	void onApplicationEvent(E event); // Ⅱ

	static <T> ApplicationListener<PayloadApplicationEvent<T>> forPayload(Consumer<T> consumer) {
		return event -> consumer.accept(event.getPayload());
	}

}

Ⅰ: @FunctionalInterface 是 Java 注解,用于标记一个接口,指示它是一个函数式接口。函数式接口是只包含一个抽象方法的接口,通常用于支持 Lambda 表达式和函数引用。

Ⅱ: onApplicationEvent(E event)处理事件,其参数是一个泛型,但必须是继承ApplicationEvent

SmartApplicationListenerGenericApplicationListener 主要是为了扩展ApplicationListener ,这里就不深入讲解了。springboot这样设计的目的其实就是后续的扩展,而不是把所有东西都写在ApplicationListener,基本上所有的系统都是这么个原则。

LoggingApplicationListener

最后我们的主角登场了,从名字大概可以猜到这是一个跟启动日志记录监听器,它会在启动过程的某个阶段记录日志,下面是核心代码

scss 复制代码
public class LoggingApplicationListener implements GenericApplicationListener {
    // 省略......
    
	@Override
    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof ApplicationStartingEvent) {
            onApplicationStartingEvent((ApplicationStartingEvent) event);
        }
        else if (event instanceof ApplicationEnvironmentPreparedEvent) {
            onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
        }
        else if (event instanceof ApplicationPreparedEvent) {
            onApplicationPreparedEvent((ApplicationPreparedEvent) event);
        }
        else if (event instanceof ContextClosedEvent) {
            onContextClosedEvent((ContextClosedEvent) event);
        }
        else if (event instanceof ApplicationFailedEvent) {
            onApplicationFailedEvent();
        }
    }
    // 省略......
}

从代码中可以看到它是监听启动过程的所有事件,其中第一个就是我们上面讲到的ApplicationStartingEvent开始启动事件,这里类似于我们上面例子中的OrderLogListener

3.3广播器

跟上面一样,我们还是先上图

ApplicationEventMulticaster

java 复制代码
package org.springframework.context.event;

import java.util.function.Predicate;

import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.core.ResolvableType;
import org.springframework.lang.Nullable;

public interface ApplicationEventMulticaster {

	void addApplicationListener(ApplicationListener<?> listener);

	void addApplicationListenerBean(String listenerBeanName);

	void removeApplicationListener(ApplicationListener<?> listener);

	void removeApplicationListenerBean(String listenerBeanName);

	void removeApplicationListeners(Predicate<ApplicationListener<?>> predicate);

	void removeApplicationListenerBeans(Predicate<String> predicate);

	void removeAllListeners();

	void multicastEvent(ApplicationEvent event);

	void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType);

}

定义事件广播器接口,类似于前面例子中的EventMulticaster

AbstractApplicationEventMulticaster

java 复制代码
public abstract class AbstractApplicationEventMulticaster
		implements ApplicationEventMulticaster, BeanClassLoaderAware, BeanFactoryAware {
    
    //省略......
    
    @Override
	public void addApplicationListener(ApplicationListener<?> listener) {
		synchronized (this.defaultRetriever) {
			// 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);
			this.retrieverCache.clear();
		}
	}
    
    //省略......
    
    @Override
	public void removeApplicationListener(ApplicationListener<?> listener) {
		synchronized (this.defaultRetriever) {
			this.defaultRetriever.applicationListeners.remove(listener);
			this.retrieverCache.clear();
		}
	}
    
}

这是一个抽象类,实现了监听器的新增和删除的具体逻辑,这样做的目的就是封装一些通用的功能,至于说要不要加一个这样的抽象类完全就是根据你的实际业务情况而定。

SimpleApplicationEventMulticaster

java 复制代码
public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
    
    //省略......
    
    @Override
	public void multicastEvent(ApplicationEvent event) {
		multicastEvent(event, resolveDefaultEventType(event));
	}

	@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) {
				executor.execute(() -> invokeListener(listener, event));
			}
			else {
				invokeListener(listener, event);
			}
		}
	}
    
    //省略......
    
    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()) ||
					(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;
			}
		}
	}
}

这个类继承AbstractApplicationEventMulticaster,并且实现了multicastEvent()广播事件,这类似于例子中的OrderEventMulticaster

3.4发布事件

前面所有东西都准备好了,看看程序启动时是如何监听事件的。我们从启动类开始跟跟踪

typescript 复制代码
@SpringBootApplication
public class SpringbootCodeApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootCodeApplication.class, args);
    }
}

进入run方法

typescript 复制代码
public class SpringApplication {
    // 省略代码
    
    
    public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
		return run(new Class<?>[] { primarySource }, args);
	}

	public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
		// Ⅰ:SpringApplication  Ⅱ:run
        return new SpringApplication(primarySources).run(args); 
	}
    
    
    public SpringApplication(Class<?>... primarySources) {
		this(null, primarySources);
	}
	
    // Ⅲ
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
		this.bootstrapRegistryInitializers = new ArrayList<>(
				getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
        // Ⅳ
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		this.mainApplicationClass = deduceMainApplicationClass();
	}
    
    // Ⅴ
    public void setListeners(Collection<? extends ApplicationListener<?>> listeners) {
		this.listeners = new ArrayList<>(listeners);
	}
    
}

:我们先进入SpringApplication构造函数,最后我们发现会来到Ⅰ->Ⅲ->Ⅳ->Ⅴthis.listeners存储了启动时需要加载的监听器。

我们看看this.listeners存储了哪些监听器

我们可以发现LoggingApplicationListener监听器也保存在里面,接下来就是怎么把这些监听器加载到广播器中

构造函数实际上时初始化一些变量

还记得上面**Ⅱ:**run这个注释吗,构造函数执行完后,开始执行run方法,我们进入run方法

ini 复制代码
public ConfigurableApplicationContext run(String... args) {
    long startTime = System.nanoTime();
    DefaultBootstrapContext bootstrapContext = createBootstrapContext();
    ConfigurableApplicationContext context = null;
    configureHeadlessProperty();
    SpringApplicationRunListeners listeners = getRunListeners(args);
    // Ⅵ
    listeners.starting(bootstrapContext, this.mainApplicationClass);
 	// 省略代码
    
    return context;
}

**Ⅵ:**这里就是发布一个启动开始事件了,进入starting方法

scss 复制代码
package org.springframework.boot;
class SpringApplicationRunListeners {

	// Ⅰ
	void starting(ConfigurableBootstrapContext bootstrapContext, Class<?> mainApplicationClass) {
		doWithListeners("spring.boot.application.starting", (listener) -> listener.starting(bootstrapContext),
				(step) -> {
					if (mainApplicationClass != null) {
						step.tag("mainApplicationClass", mainApplicationClass.getName());
					}
				});
	}
	// 省略代码

}

这里都是发布启动相关的事件,starting里面调用了doWithListeners方法,其中最主要的是第二个参数,是一个表达式,我们进去看看,最后我们来到这里

kotlin 复制代码
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
	//省略代码
    
    // 前面已经被初始化过
	public EventPublishingRunListener(SpringApplication application, String[] args) {
		this.application = application;
		this.args = args;
        // Ⅰ new广播器
		this.initialMulticaster = new SimpleApplicationEventMulticaster();
		for (ApplicationListener<?> listener : application.getListeners()) {
            // Ⅱ 添加监听器,application.getListeners()获取的就是前面的this.listeners
			this.initialMulticaster.addApplicationListener(listener);
		}
	}
	//省略代码
    
	@Override
	public void starting(ConfigurableBootstrapContext bootstrapContext) {

		this.initialMulticaster
            // Ⅲ 广播事件
			.multicastEvent(new ApplicationStartingEvent(bootstrapContext, this.application, this.args));
	}

    //省略代码
}

这里的步骤就像我们上面例子中的一样

typescript 复制代码
@SpringBootApplication
public class SpringbootCodeApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootCodeApplication.class, args);

        OrderEventMulticaster orderEventMulticaster = new OrderEventMulticaster();
        orderEventMulticaster.addListener(new OrderLogListener());
        orderEventMulticaster.multicasterEvent(new OrderCreateStartingEvent());
    }
}

new一个广播器,然后添加监听器,最后是广播事件。

4.自定义监听器

了解了spingboot的监听模式后,接下来我们看看如何在springboot项目中使用它。

4.1创建事件

scala 复制代码
package com.example.springbootcode.my;

import org.springframework.context.ApplicationEvent;

public class UserMsgEvent extends ApplicationEvent {

    public UserMsgEvent(Object source) {
        super(source);
    }
	
    // 自定义自己的逻辑方法
    public void sendMsg(){
        System.out.print("向用户发送消息");
    }
}

这里需要注意的是我们继承的是ApplicationEvent,不继承SpringApplicationEvent主要是因此其跟启动相关的,我们定义的事件明显跟应用启动没啥关系。

4.2创建监听器

java 复制代码
package com.example.springbootcode.my;

import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

@Component
public class UserListener implements ApplicationListener<UserMsgEvent> {

    @Override
    public void onApplicationEvent(UserMsgEvent event) {
        event.sendMsg();
    }
}

这里继承ApplicationListener

4.3自定义触发事件

java 复制代码
package com.example.springbootcode.my;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;

@Service
public class UserService {
    private final ApplicationEventPublisher eventPublisher;

    public UserService(ApplicationEventPublisher eventPublisher) {
        this.eventPublisher = eventPublisher;
    }

    public void send(){
        eventPublisher.publishEvent(new UserMsgEvent(this));
    }
}

通过构造函数注入 ApplicationEventPublisher , 这是一个Spring框架提供的接口,用于发布事件

最后在需要调用的地方调用UserService里面的send方法即可

相关推荐
m0_748256144 小时前
SpringBoot
java·spring boot·后端
Mr.朱鹏4 小时前
针对Feign客户端请求体参数处理问题
java·jvm·spring boot·spring·spring cloud·maven·intellij-idea
多想和从前一样5 小时前
Django 创建表时 “__str__ ”方法的使用
后端·python·django
涛粒子6 小时前
Spring Bean 生命周期的执行流程
java·后端·spring
钝挫力PROGRAMER6 小时前
SpringBoot中Mybatis记录执行sql日志
spring boot·sql·mybatis
赵琳琅7 小时前
Java语言的云计算
开发语言·后端·golang
赵琳琅7 小时前
MDX语言的安全开发
开发语言·后端·golang
林林总肿7 小时前
Mybatis后端数据库查询多对多查询解决方案
数据库·spring boot·mybatis
夏梓蕙8 小时前
Elixir语言的软件开发工具
开发语言·后端·golang
夏梓蕙8 小时前
R语言的Web开发
开发语言·后端·golang