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()));
}
}