(一)理论通识:
我们知道spring事件是基于事件/监听器编程模型,在这个模型中,有几个重要的角色,事件(ApplicationEvent),应用事件监听器(ApplicationListener),以及事件发布者(ApplicationContext)。
(二)需求描述
用户注册成功之后,发送短信告知用户。
(三)代码实现
我们先看下目录结构 image.png先看MyRegisApplicationEvent类,他是继承ApplicationEvent类:
package com.wangming.event;
import org.springframework.context.ApplicationEvent;
public class MyRegisApplicationEvent extends ApplicationEvent {
public MyRegisApplicationEvent(Object source) {
super(source);
}
}
MyRegisApplicationListener类是实现ApplicationListener接口,注意泛型为MyRegisApplicationEvent类。其中@Order注解表示这个监听器在容器中的顺序,比如我们可以注册很多监听,有时候业务需要先执行某个后执行某个,这样就需要用到排序了:
package com.wangming.listener;
import com.wangming.event.MyRegisApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.scheduling.annotation.Async;
@Order(Ordered.LOWEST_PRECEDENCE+1)
public class MyRegisApplicationListener implements ApplicationListener<MyRegisApplicationEvent> {
@Override
public void onApplicationEvent(MyRegisApplicationEvent event) {
System.out.println("MyRegisApplicationListener:name:"+event.getClass().getName());
System.out.println("MyRegisApplicationListener:getSource:"+event.getSource());
System.out.println("发送短信告知用户注册成功!");
}
}
ApplicationListenerBootstrap引导启动类:
package com.wangming.bootstrap;
import com.wangming.listener.MyRegisApplicationListener;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication(scanBasePackages = "com.wangming")
@EnableScheduling
public class ApplicationListenerBootstrap {
public static void main(String[] args) {
ConfigurableApplicationContext run =
new SpringApplicationBuilder(ApplicationListenerBootstrap.class)
// .web(WebApplicationType.NONE)
.run(args);
//此处我们可以使用之前的spring工厂加载机制,在resource目录下建立resources/META-INF/spring.factories文件,并在文件中如此配置
//# ApplicationListener 实现配置
//#org.springframework.context.ApplicationListener=\
//#com.wangming.listener.MyRegisApplicationListenertener
run.addApplicationListener(new MyRegisApplicationListener());
}
}
最后写一个注册接口RegisController,通过装配的ApplicationContext 进行事件发布:
package com.wangming.controller;
import com.wangming.event.MyRegisApplicationEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class RegisController {
@Autowired
ApplicationContext context;
@GetMapping("/hello")
public String regis() {
//....
context.publishEvent(new MyRegisApplicationEvent("注冊成功"));
return "ok";
}
}
让我们调用一下注册接口,看看控制台:
image.png
发现确实如我们预期那样。
(四)源码解读
我们以context.publishEvent(new MyRegisApplicationEvent("注冊成功"));这句代码为契机点,往下走:
AbstractApplicationContext类中关键代码:
...
/**
* Publish the given event to all listeners.
* @param event the event to publish (may be an {@link ApplicationEvent}
* or a payload object to be turned into a {@link PayloadApplicationEvent})
* @param eventType the resolved event type, if known
* @since 4.2
*/
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
Assert.notNull(event, "Event must not be null");
if (logger.isTraceEnabled()) {
logger.trace("Publishing event in " + getDisplayName() + ": " + event);
}
// 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);非常值得我们关注,ApplicationEventMulticaster这个类是我们所说的广播器,在spring事件中起到了非常关键的作用,发布者发布的事件会发送给EventMultiCaster, 而后由EventMultiCaster注册着所有的Listener,然后根据事件类型决定转发给那个Listener。
Spring 应用事广播器的默认实现类为SimpleApplicationEventMulticaster,我们继续往下走就会进入SimpleApplicationEventMulticaster类中,然我们瞅瞅关键代码:
...
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
Executor executor = getTaskExecutor();
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
}
...
/**
* Invoke the given listener with the given event.
* @param listener the ApplicationListener to invoke
* @param event the current event to propagate
* @since 4.1
*/
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);
}
}
...
@SuppressWarnings({"unchecked", "rawtypes"})
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())) {
// Possibly a lambda-defined listener which we could not resolve the generic event type for
// -> let's suppress the exception and just log a debug message.
Log logger = LogFactory.getLog(getClass());
if (logger.isDebugEnabled()) {
logger.debug("Non-matching event type for listener: " + listener, ex);
}
}
else {
throw ex;
}
}
}
...
最终我们看到了真正回调的地方 listener.onApplicationEvent(event);。
这里说一个题外话,我们在SimpleApplicationEventMulticaster广播器类中发现这样一段代码:
Executor executor = getTaskExecutor();
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
说明我们监听器中的业务代码是可以异步执行的。我们怎么实现呢?很简单,做三处修改,1在引导启动类上加上@EnableAsync,2 注释掉这行代码run.addApplicationListener(new MyRegisApplicationListener());
package com.wangming.bootstrap;
import com.wangming.listener.MyRegisApplicationListener;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication(scanBasePackages = "com.wangming")
@EnableScheduling
@EnableAsync
public class ApplicationListenerBootstrap {
public static void main(String[] args) {
ConfigurableApplicationContext run =
new SpringApplicationBuilder(ApplicationListenerBootstrap.class)
// .web(WebApplicationType.NONE)
.run(args);
// run.addApplicationListener(new MyRegisApplicationListener());
}
}
第三就是在MyRegisApplicationListener监听类上打上
@Component
@Async这两个注解
刚刚之所以屏蔽掉run.addApplicationListener(new MyRegisApplicationListener());因为@Component注解已经帮我们注入了,如果不屏蔽会有两次回调。
网友评论