Spring 主要通过 ApplicationEvent
类和ApplicationListener
接口提供事件处理,通过继承 ApplicationEvent
类可以实现自定义事件,实现ApplicationListener
接口并注册为Bean来监听事件,详细信息可参考Spring官网文档、SpringBoot官方文档。
官方提示:
应用程序事件
1 实现应用程序事件监听器
import org.springframework.boot.context.event.ApplicationStartingEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
public class ApplicationStartingListener implements ApplicationListener<ApplicationStartingEvent> {
@Override
public void onApplicationEvent(ApplicationStartingEvent event) {
System.out.println("ApplicationStartingEvent:" + event);
}
}
2 在容器启动前注册应用程序事件监听器
import com.example.springbootdemo.config.ApplicationStartingListener;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.scheduling.annotation.EnableAsync;
@SpringBootApplication
public class SpringBootDemoApplication {
public static void main(String[] args) {
// 方式一
// SpringApplication application = new SpringApplication(SpringBootDemoApplication.class);
// application.addListeners(new ApplicationStartingListener());
// application.run(args);
// 方式二
new SpringApplicationBuilder(SpringBootDemoApplication.class)
.listeners(new ApplicationStartingListener())
.run(args);
}
}
通常的Spring Framework事件、自定义事件及事件监听器
一、自定义事件
要实现自定义事件只需要继承ApplicationEvent
类便可。发布事件需要通过 ApplicationEventPublisher 类,可以通过 @Autowired 自动注入或实现 ApplicationEventPublisherAware 接口并注册为Bean来获取该事件发布类。
1 自定义事件实现
import org.springframework.context.ApplicationEvent;
public class TestEvent extends ApplicationEvent {
public TestEvent(Object source) {
super(source);
}
}
2 自动注入获取事件发布类
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;
@Component
public class TestEventPublisher {
private ApplicationEventPublisher publisher;
@Autowired
public void setPublisher(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
public void sendTestEvent() {
publisher.publishEvent(new TestEvent(this));
}
}
3 实现 ApplicationEventPublisherAware 接口获取事件发布类
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Component;
@Component
public class TestEventPublisher implements ApplicationEventPublisherAware {
private ApplicationEventPublisher publisher;
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
public void sendTestEvent() {
publisher.publishEvent(new TestEvent(this));
}
}
二、事件监听器
方式一:通过ApplicationListener
接口实现事件监听,实现ApplicationListener
接口并注册为Bean
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class TestEventListener implements ApplicationListener<TestEvent> {
@Override
public void onApplicationEvent(TestEvent event) {
log.info("test event:" + event);
}
}
方式二:通过 @EventListener 注解实现事件监听,该注解的 condition 还支持 SpEL 表达式。
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class EventNotifier {
@EventListener
public void testEvent(TestEvent event) {
log.info("test event:" + event);
}
}
三、异步监听器
异步监听器的实现可以依靠 @Async 注解,需要在启动类上添加 @EnableAsync 注解开启异步支持。
使用异步事件时,请注意以下限制:
-
如果异步事件侦听器抛出
Exception
,则不会传播到调用者。请参阅AsyncUncaughtExceptionHandler
以获取更多详细信息。 -
异步事件侦听器方法无法通过返回值来发布后续事件。如果您需要发布另一个事件作为处理的结果,请插入一个
ApplicationEventPublisher
以手动发布事件。
1 启动类开启异步支持
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
@EnableAsync
@SpringBootApplication
public class SpringBootDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootDemoApplication.class, args);
}
}
2 监听器实现异步
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class TestEventListener implements ApplicationListener<TestEvent> {
@Async
@Override
public void onApplicationEvent(TestEvent event) {
log.info("test event:" + event);
}
}
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class EventNotifier {
@Async
@EventListener
public void testEvent(TestEvent event) {
log.info("test event:" + event);
}
}
四、监听器执行顺序
监听器顺序通过 @Order 指定,异步监听器不支持指定顺序。
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class EventNotifier {
@Order(3)
@EventListener
public void testEvent3(TestEvent event) {
log.info("testEvent3:" + event);
}
@Order(1)
@EventListener
public void testEvent1(TestEvent event) {
log.info("testEvent1:" + event);
}
}
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationListener;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Slf4j
@Order(2)
@Component
public class TestEventListener implements ApplicationListener<TestEvent> {
@Override
public void onApplicationEvent(TestEvent event) {
log.info("TestEventListener:" + event);
}
}
执行结果:
2021-02-23 23:35:35.171 INFO 29772 --- [nio-8080-exec-2] c.e.springbootdemo.config.EventNotifier : testEvent1:com.example.springbootdemo.config.TestEvent[source=com.example.springbootdemo.service.TestService@1f0dc187] 2021-02-23 23:35:35.171 INFO 29772 --- [nio-8080-exec-2] c.e.s.config.TestEventListener : TestEventListener:com.example.springbootdemo.config.TestEvent[source=com.example.springbootdemo.service.TestService@1f0dc187] 2021-02-23 23:35:35.171 INFO 29772 --- [nio-8080-exec-2] c.e.springbootdemo.config.EventNotifier : testEvent3:com.example.springbootdemo.config.TestEvent[source=com.example.springbootdemo.service.TestService@1f0dc187]
五、通用事件
自定义通用事件类
import org.springframework.context.ApplicationEvent;
public class CommonEvent<T> extends ApplicationEvent {
public CommonEvent(T source) {
super(source);
}
}
由于泛型的类型擦除,发布事件后会触发所有类型的该通用事件。所以要指定类型继承该通用事件才会精确触发监听(即 class TestServiceEvent extends CommonEvent<TestService> { … }
),或者实现 ResolvableTypeProvider
接口。
import org.springframework.context.ApplicationEvent;
import org.springframework.core.ResolvableType;
import org.springframework.core.ResolvableTypeProvider;
public class CommonEvent<T> extends ApplicationEvent implements ResolvableTypeProvider {
public CommonEvent(T source) {
super(source);
}
@Override
public ResolvableType getResolvableType() {
return ResolvableType.forClassWithGenerics(getClass(), ResolvableType.forInstance(getSource()));
}
}