Spring事件發(fā)布監(jiān)聽,順序監(jiān)聽,異步監(jiān)聽方式
最近在做公司的業(yè)務(wù)需要用到事件通知,比如啟動(dòng)成功打印日志,通知其他業(yè)務(wù)做相應(yīng)的操作,就用到了Spring的事件通知機(jī)制。
1. Spring的事件通知
Spring的事件通知本質(zhì)上就是發(fā)布-訂閱,即生產(chǎn)者-消費(fèi)者;體現(xiàn)了觀察者設(shè)計(jì)模式或者回調(diào)通知,那么Spring的事件是如何使用的?
有3要素:發(fā)布者-->事件-->監(jiān)聽者
2. Spring事件通知使用
Spring的事件必須依賴Spring容器,所以我在寫demo的時(shí)候,需要Spring-boot-starter。
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.4</version>
</dependency>
</dependencies>
2.1 Spring的事件
Spring的事件定義在ApplicationEvent類中,我們可以根據(jù)自己的需要自定義事件實(shí)體javabean。
@Data
public class TestSpringEvent extends ApplicationEvent {
/**
* Create a new ApplicationEvent.
*
* @param source the object on which the event initially occurred (never {@code null})
*/
public TestSpringEvent(Object source) {
super(source);
}
private Integer code;
private String message;
}
跟蹤源碼:
public abstract class ApplicationEvent extends EventObject {
/** use serialVersionUID from Spring 1.2 for interoperability. */
private static final long serialVersionUID = 7099057708183571937L;
/** System time when the event happened. */
private final long timestamp;
/**
* Create a new ApplicationEvent.
* @param source the object on which the event initially occurred (never {@code null})
*/
public ApplicationEvent(Object source) {
super(source);
this.timestamp = System.currentTimeMillis();
}
/**
* Return the system time in milliseconds when the event happened.
*/
public final long getTimestamp() {
return this.timestamp;
}
}
可以看出ApplicationEvent有屬性時(shí)間戳,即事件的產(chǎn)生的時(shí)間,并有Object source屬性,這個(gè)source是有特定意義的,官方的doc是The object on which the Event initially occurred.即發(fā)布事件的實(shí)體類。
進(jìn)一步跟蹤
public class EventObject implements java.io.Serializable {
private static final long serialVersionUID = 5516075349620653480L;
/**
* The object on which the Event initially occurred.
*/
protected transient Object source;
/**
* Constructs a prototypical Event.
*
* @param source The object on which the Event initially occurred.
* @exception IllegalArgumentException if source is null.
*/
public EventObject(Object source) {
if (source == null)
throw new IllegalArgumentException("null source");
this.source = source;
}
/**
* The object on which the Event initially occurred.
*
* @return The object on which the Event initially occurred.
*/
public Object getSource() {
return source;
}
/**
* Returns a String representation of this EventObject.
*
* @return A a String representation of this EventObject.
*/
public String toString() {
return getClass().getName() + "[source=" + source + "]";
}
}
定義了一個(gè)事件發(fā)生的不能序列化的對象
2.2 事件監(jiān)聽
2.2.1 接口方式實(shí)現(xiàn)
Spring事件的監(jiān)聽由ApplicationListener定義,我們寫2個(gè),來測試監(jiān)聽它執(zhí)行的順序
@Component
public class TestApplicationListener implements ApplicationListener<TestSpringEvent> {
//@Async
public void onApplicationEvent(TestSpringEvent event) {
System.out.println("code is:\t" + event.getCode() + ",\tmessage is:\t" + event.getMessage());
}
}
@Component
public class TestApplicationListenerCopy implements ApplicationListener<TestSpringEvent> {
//@Async
public void onApplicationEvent(TestSpringEvent event) {
System.out.println("2:code is:\t" + event.getCode() + ",\tmessage is:\t" + event.getMessage());
}
}
跟蹤代碼,可以看出ApplicationListener是一個(gè)函數(shù)式接口
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
/**
* Handle an application event.
* @param event the event to respond to
*/
void onApplicationEvent(E event);
}
Spring事件監(jiān)聽器的默認(rèn)是單線程同步執(zhí)行,異步執(zhí)行需要加上Spring的@Async注解,或者自定義線程池實(shí)現(xiàn).
2.2.2 注解實(shí)現(xiàn)
也可以使用@EventListener
package com.feng.spring.listener;
import com.feng.spring.event.TestSpringEvent;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
@Component
public class TestApplicationListener2{
//@Async
@EventListener
public void onApplicationEvent(TestSpringEvent event) {
System.out.println("step2:code is:\t" + event.getCode() + ",\tmessage is:\t" + event.getMessage());
}
}
@Component
public class TestApplicationListener2Copy {
//@Async
@EventListener
public void onApplicationEvent(TestSpringEvent event) {
System.out.println("step3:code is:\t" + event.getCode() + ",\tmessage is:\t" + event.getMessage());
}
}
同理寫2個(gè)實(shí)現(xiàn)
2.3 事件發(fā)布
Spring的事件的發(fā)布定義在ApplicationEventPublisher類中,而ApplicationContext繼承了這個(gè)類,因此ApplicationContext具有發(fā)布事件的能力,而且已經(jīng)注入Spring的bean容器中,可直接獲取。
public class SpringEventMain {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.scan(SpringEventMain.class.getCanonicalName());
context.refresh();
TestSpringEvent testSpringEvent = new TestSpringEvent(new SpringEventMain());
testSpringEvent.setCode(10000);
testSpringEvent.setMessage("---------My Spring task event------------");
context.publishEvent(testSpringEvent);
}
}
注意:
new TestSpringEvent(new SpringEventMain())
這里的source是傳入發(fā)布事件的實(shí)例對象。
運(yùn)行后打印如下日志:
/software/jdk1.8.0_191/bin/java -javaagent:/software/ideaIde/lib/idea_rt.jar=36631:/software/ideaIde/bin -Dfile.encoding=UTF-8 -classpath /software/jdk1.8.0_191/jre/lib/charsets.jar:/software/jdk1.8.0_191/jre/lib/deploy.jar:/software/jdk1.8.0_191/jre/lib/ext/cldrdata.jar:/software/jdk1.8.0_191/jre/lib/ext/dnsns.jar:/software/jdk1.8.0_191/jre/lib/ext/jaccess.jar:/software/jdk1.8.0_191/jre/lib/ext/jfxrt.jar:/software/jdk1.8.0_191/jre/lib/ext/localedata.jar:/software/jdk1.8.0_191/jre/lib/ext/nashorn.jar:/software/jdk1.8.0_191/jre/lib/ext/sunec.jar:/software/jdk1.8.0_191/jre/lib/ext/sunjce_provider.jar:/software/jdk1.8.0_191/jre/lib/ext/sunpkcs11.jar:/software/jdk1.8.0_191/jre/lib/ext/zipfs.jar:/software/jdk1.8.0_191/jre/lib/javaws.jar:/software/jdk1.8.0_191/jre/lib/jce.jar:/software/jdk1.8.0_191/jre/lib/jfr.jar:/software/jdk1.8.0_191/jre/lib/jfxswt.jar:/software/jdk1.8.0_191/jre/lib/jsse.jar:/software/jdk1.8.0_191/jre/lib/management-agent.jar:/software/jdk1.8.0_191/jre/lib/plugin.jar:/software/jdk1.8.0_191/jre/lib/resources.jar:/software/jdk1.8.0_191/jre/lib/rt.jar:/home/huahua/IdeaProjects/feng/spring-event/target/classes:/home/huahua/.m2/repository/org/springframework/boot/spring-boot-starter/2.1.1.RELEASE/spring-boot-starter-2.1.1.RELEASE.jar:/home/huahua/.m2/repository/org/springframework/boot/spring-boot/2.1.1.RELEASE/spring-boot-2.1.1.RELEASE.jar:/home/huahua/.m2/repository/org/springframework/spring-context/5.1.3.RELEASE/spring-context-5.1.3.RELEASE.jar:/home/huahua/.m2/repository/org/springframework/spring-aop/5.1.3.RELEASE/spring-aop-5.1.3.RELEASE.jar:/home/huahua/.m2/repository/org/springframework/spring-beans/5.1.3.RELEASE/spring-beans-5.1.3.RELEASE.jar:/home/huahua/.m2/repository/org/springframework/spring-expression/5.1.3.RELEASE/spring-expression-5.1.3.RELEASE.jar:/home/huahua/.m2/repository/org/springframework/boot/spring-boot-autoconfigure/2.1.1.RELEASE/spring-boot-autoconfigure-2.1.1.RELEASE.jar:/home/huahua/.m2/repository/org/springframework/boot/spring-boot-starter-logging/2.1.1.RELEASE/spring-boot-starter-logging-2.1.1.RELEASE.jar:/home/huahua/.m2/repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar:/home/huahua/.m2/repository/ch/qos/logback/logback-core/1.2.3/logback-core-1.2.3.jar:/home/huahua/.m2/repository/org/slf4j/slf4j-api/1.7.25/slf4j-api-1.7.25.jar:/home/huahua/.m2/repository/org/apache/logging/log4j/log4j-to-slf4j/2.11.1/log4j-to-slf4j-2.11.1.jar:/home/huahua/.m2/repository/org/apache/logging/log4j/log4j-api/2.11.1/log4j-api-2.11.1.jar:/home/huahua/.m2/repository/org/slf4j/jul-to-slf4j/1.7.25/jul-to-slf4j-1.7.25.jar:/home/huahua/.m2/repository/javax/annotation/javax.annotation-api/1.3.2/javax.annotation-api-1.3.2.jar:/home/huahua/.m2/repository/org/springframework/spring-core/5.1.3.RELEASE/spring-core-5.1.3.RELEASE.jar:/home/huahua/.m2/repository/org/springframework/spring-jcl/5.1.3.RELEASE/spring-jcl-5.1.3.RELEASE.jar:/home/huahua/.m2/repository/org/yaml/snakeyaml/1.23/snakeyaml-1.23.jar:/home/huahua/.m2/repository/org/projectlombok/lombok/1.18.4/lombok-1.18.4.jar com.feng.spring.SpringEventMain
17:54:05.347 [main] DEBUG org.springframework.context.annotation.ClassPathBeanDefinitionScanner - Identified candidate component class: file [/home/huahua/IdeaProjects/feng/spring-event/target/classes/com/feng/spring/listener/TestApplicationListener.class]
17:54:05.352 [main] DEBUG org.springframework.context.annotation.ClassPathBeanDefinitionScanner - Identified candidate component class: file [/home/huahua/IdeaProjects/feng/spring-event/target/classes/com/feng/spring/listener/TestApplicationListener2.class]
17:54:05.353 [main] DEBUG org.springframework.context.annotation.ClassPathBeanDefinitionScanner - Identified candidate component class: file [/home/huahua/IdeaProjects/feng/spring-event/target/classes/com/feng/spring/listener/TestApplicationListener3.class]
17:54:05.353 [main] DEBUG org.springframework.context.annotation.ClassPathBeanDefinitionScanner - Identified candidate component class: file [/home/huahua/IdeaProjects/feng/spring-event/target/classes/com/feng/spring/listener/TestApplicationListenerCopy.class]
17:54:05.358 [main] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@64b8f8f4
17:54:05.380 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
17:54:05.438 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'
17:54:05.440 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
17:54:05.443 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
17:54:05.444 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor'
17:54:05.451 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'testApplicationListener'
17:54:05.466 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'testApplicationListener2'
17:54:05.470 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'testApplicationListener3'
17:54:05.471 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'testApplicationListenerCopy'
17:54:05.490 [main] DEBUG org.springframework.context.event.EventListenerMethodProcessor - 1 @EventListener methods processed on bean 'testApplicationListener2': {public void com.feng.spring.listener.TestApplicationListener2.onApplicationEvent(com.feng.spring.event.TestSpringEvent)=@org.springframework.context.event.EventListener(condition=, value=[], classes=[])}
17:54:05.491 [main] DEBUG org.springframework.context.event.EventListenerMethodProcessor - 1 @EventListener methods processed on bean 'testApplicationListener3': {public void com.feng.spring.listener.TestApplicationListener3.onApplicationEvent(com.feng.spring.event.TestSpringEvent)=@org.springframework.context.event.EventListener(condition=, value=[], classes=[])}
step2:code is: 10000, message is: ---------My Spring task event------------
step3:code is: 10000, message is: ---------My Spring task event------------
code is: 10000, message is: ---------My Spring task event------------
2:code is: 10000, message is: ---------My Spring task event------------
Process finished with exit code 0
說明Spring事件發(fā)布訂閱成功。同時(shí)使用注解的監(jiān)聽比實(shí)現(xiàn)類監(jiān)聽先監(jiān)聽,如果我們需要監(jiān)聽的執(zhí)行順序怎么辦?比如某些監(jiān)聽器先執(zhí)行?
2.4 Spring順序監(jiān)聽器
上面的示例,其實(shí)都是注解或者都是實(shí)現(xiàn)類,監(jiān)聽器是沒有順序的。如果我們要實(shí)現(xiàn)順序監(jiān)聽呢?我么需要Ordered接口的實(shí)現(xiàn),Spring提供的實(shí)現(xiàn)或者暴露的實(shí)現(xiàn)接口如下

如果需要執(zhí)行順序,Spring提供SmartApplicationListener與GenericApplicationListener可供選擇。
其中GenericApplicationListener,Spring官方推薦我們提供adapter實(shí)現(xiàn)
/**
* Extended variant of the standard {@link ApplicationListener} interface,
* exposing further metadata such as the supported event and source type.
*
* <p>As of Spring Framework 4.2, this interface supersedes the Class-based
* {@link SmartApplicationListener} with full handling of generic event types.
*
* @author Stephane Nicoll
* @since 4.2
* @see SmartApplicationListener
* @see GenericApplicationListenerAdapter
*/
/**
* Extended variant of the standard {@link ApplicationListener} interface,
* exposing further metadata such as the supported event and source type.
*
* <p>As of Spring Framework 4.2, this interface supersedes the Class-based
* {@link SmartApplicationListener} with full handling of generic event types.
*
* @author Stephane Nicoll
* @since 4.2
* @see SmartApplicationListener
* @see GenericApplicationListenerAdapter
*/
public interface GenericApplicationListener extends ApplicationListener<ApplicationEvent>, Ordered {
/**
* Determine whether this listener actually supports the given event type.
* @param eventType the event type (never {@code null})
*/
boolean supportsEventType(ResolvableType eventType);
/**
* Determine whether this listener actually supports the given source type.
* <p>The default implementation always returns {@code true}.
* @param sourceType the source type, or {@code null} if no source
*/
default boolean supportsSourceType(@Nullable Class<?> sourceType) {
return true;
}
/**
* Determine this listener's order in a set of listeners for the same event.
* <p>The default implementation returns {@link #LOWEST_PRECEDENCE}.
*/
@Override
default int getOrder() {
return LOWEST_PRECEDENCE;
}
}
其實(shí)ApplicationListener在Spring中提供了很多默認(rèn)實(shí)現(xiàn),其中就有SmartApplicationListener,智能監(jiān)聽器。
下面來試試
*/
public interface SmartApplicationListener extends ApplicationListener<ApplicationEvent>, Ordered {
/**
* Determine whether this listener actually supports the given event type.
* @param eventType the event type (never {@code null})
*/
//supportsEventType與supportsSourceType同時(shí)true,監(jiān)聽器才會(huì)執(zhí)行
//監(jiān)聽器支持的事件類型
boolean supportsEventType(Class<? extends ApplicationEvent> eventType);
/**
* Determine whether this listener actually supports the given source type.
* <p>The default implementation always returns {@code true}.
* @param sourceType the source type, or {@code null} if no source
*/
//supportsEventType與supportsSourceType同時(shí)true,監(jiān)聽器才會(huì)執(zhí)行
//監(jiān)聽事件發(fā)布的類
default boolean supportsSourceType(@Nullable Class<?> sourceType) {
return true;
}
/**
* Determine this listener's order in a set of listeners for the same event.
* <p>The default implementation returns {@link #LOWEST_PRECEDENCE}.
*/
@Override
default int getOrder() {
return LOWEST_PRECEDENCE;
}
}
3個(gè)實(shí)現(xiàn)方法。支持的類型,支持的事件類,執(zhí)行順序
package com.feng.spring.listener;
import com.feng.spring.SpringEventMain;
import com.feng.spring.event.TestSpringEvent;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.SmartApplicationListener;
import org.springframework.stereotype.Component;
@Component
public class OrderedListener implements SmartApplicationListener {
public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
return eventType== TestSpringEvent.class;
}
public boolean supportsSourceType(Class<?> sourceType) {
return SpringEventMain.class==sourceType;
}
public int getOrder() {
return 5;
}
public void onApplicationEvent(ApplicationEvent event) {
System.out.println("my execute step is 555555555555555555555555555");
}
}
@Component
public class OrderedListenerCopy implements SmartApplicationListener {
public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
return eventType== TestSpringEvent.class;
}
public boolean supportsSourceType(Class<?> sourceType) {
return SpringEventMain.class==sourceType;
}
public int getOrder() {
return 6;
}
public void onApplicationEvent(ApplicationEvent event) {
System.out.println("my execute step is 6666666666666666666666666666");
}
}
同理寫2個(gè),執(zhí)行結(jié)果如下:
/software/jdk1.8.0_191/bin/java -agentlib:jdwp=transport=dt_socket,address=127.0.0.1:46603,suspend=y,server=n -javaagent:/software/ideaIde/lib/rt/debugger-agent.jar -Dfile.encoding=UTF-8 -classpath /software/jdk1.8.0_191/jre/lib/charsets.jar:/software/jdk1.8.0_191/jre/lib/deploy.jar:/software/jdk1.8.0_191/jre/lib/ext/cldrdata.jar:/software/jdk1.8.0_191/jre/lib/ext/dnsns.jar:/software/jdk1.8.0_191/jre/lib/ext/jaccess.jar:/software/jdk1.8.0_191/jre/lib/ext/jfxrt.jar:/software/jdk1.8.0_191/jre/lib/ext/localedata.jar:/software/jdk1.8.0_191/jre/lib/ext/nashorn.jar:/software/jdk1.8.0_191/jre/lib/ext/sunec.jar:/software/jdk1.8.0_191/jre/lib/ext/sunjce_provider.jar:/software/jdk1.8.0_191/jre/lib/ext/sunpkcs11.jar:/software/jdk1.8.0_191/jre/lib/ext/zipfs.jar:/software/jdk1.8.0_191/jre/lib/javaws.jar:/software/jdk1.8.0_191/jre/lib/jce.jar:/software/jdk1.8.0_191/jre/lib/jfr.jar:/software/jdk1.8.0_191/jre/lib/jfxswt.jar:/software/jdk1.8.0_191/jre/lib/jsse.jar:/software/jdk1.8.0_191/jre/lib/management-agent.jar:/software/jdk1.8.0_191/jre/lib/plugin.jar:/software/jdk1.8.0_191/jre/lib/resources.jar:/software/jdk1.8.0_191/jre/lib/rt.jar:/home/huahua/IdeaProjects/feng/spring-event/target/classes:/home/huahua/.m2/repository/org/springframework/boot/spring-boot-starter/2.1.1.RELEASE/spring-boot-starter-2.1.1.RELEASE.jar:/home/huahua/.m2/repository/org/springframework/boot/spring-boot/2.1.1.RELEASE/spring-boot-2.1.1.RELEASE.jar:/home/huahua/.m2/repository/org/springframework/spring-context/5.1.3.RELEASE/spring-context-5.1.3.RELEASE.jar:/home/huahua/.m2/repository/org/springframework/spring-aop/5.1.3.RELEASE/spring-aop-5.1.3.RELEASE.jar:/home/huahua/.m2/repository/org/springframework/spring-beans/5.1.3.RELEASE/spring-beans-5.1.3.RELEASE.jar:/home/huahua/.m2/repository/org/springframework/spring-expression/5.1.3.RELEASE/spring-expression-5.1.3.RELEASE.jar:/home/huahua/.m2/repository/org/springframework/boot/spring-boot-autoconfigure/2.1.1.RELEASE/spring-boot-autoconfigure-2.1.1.RELEASE.jar:/home/huahua/.m2/repository/org/springframework/boot/spring-boot-starter-logging/2.1.1.RELEASE/spring-boot-starter-logging-2.1.1.RELEASE.jar:/home/huahua/.m2/repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar:/home/huahua/.m2/repository/ch/qos/logback/logback-core/1.2.3/logback-core-1.2.3.jar:/home/huahua/.m2/repository/org/slf4j/slf4j-api/1.7.25/slf4j-api-1.7.25.jar:/home/huahua/.m2/repository/org/apache/logging/log4j/log4j-to-slf4j/2.11.1/log4j-to-slf4j-2.11.1.jar:/home/huahua/.m2/repository/org/apache/logging/log4j/log4j-api/2.11.1/log4j-api-2.11.1.jar:/home/huahua/.m2/repository/org/slf4j/jul-to-slf4j/1.7.25/jul-to-slf4j-1.7.25.jar:/home/huahua/.m2/repository/javax/annotation/javax.annotation-api/1.3.2/javax.annotation-api-1.3.2.jar:/home/huahua/.m2/repository/org/springframework/spring-core/5.1.3.RELEASE/spring-core-5.1.3.RELEASE.jar:/home/huahua/.m2/repository/org/springframework/spring-jcl/5.1.3.RELEASE/spring-jcl-5.1.3.RELEASE.jar:/home/huahua/.m2/repository/org/yaml/snakeyaml/1.23/snakeyaml-1.23.jar:/home/huahua/.m2/repository/org/projectlombok/lombok/1.18.4/lombok-1.18.4.jar:/software/ideaIde/lib/idea_rt.jar com.feng.spring.SpringEventMain
Connected to the target VM, address: '127.0.0.1:46603', transport: 'socket'
18:37:51.002 [main] DEBUG org.springframework.context.annotation.ClassPathBeanDefinitionScanner - Identified candidate component class: file [/home/huahua/IdeaProjects/feng/spring-event/target/classes/com/feng/spring/listener/OrderedListener.class]
18:37:51.009 [main] DEBUG org.springframework.context.annotation.ClassPathBeanDefinitionScanner - Identified candidate component class: file [/home/huahua/IdeaProjects/feng/spring-event/target/classes/com/feng/spring/listener/OrderedListenerCopy.class]
18:37:51.017 [main] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@37d31475
18:37:51.055 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
18:37:51.129 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'
18:37:51.132 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
18:37:51.135 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
18:37:51.136 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor'
18:37:51.145 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'orderedListener'
18:37:51.154 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'orderedListenerCopy'
my execute step is 555555555555555555555555555
my execute step is 6666666666666666666666666666
Disconnected from the target VM, address: '127.0.0.1:46603', transport: 'socket'
Process finished with exit code 0
實(shí)現(xiàn)了順序的監(jiān)聽。
2.5 異步監(jiān)聽
Spring使用ThreadPoolTaskExecutor來執(zhí)行異步任務(wù),其實(shí)就是SchedulingTaskExecutor,使用@EnableAsync注解@Async注解實(shí)現(xiàn)異步監(jiān)聽事件的調(diào)用
我們來試試
package com.feng.spring.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
@Configuration
public class AsyncConfig {
@Bean
public Executor initExecutor(){
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//定制線程名稱,還可以定制線程group
executor.setThreadFactory(new ThreadFactory() {
private final AtomicInteger threadNumber = new AtomicInteger(1);
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(Thread.currentThread().getThreadGroup(), r,
"async-eventListener-" + threadNumber.getAndIncrement(),
0);
return t;
}
});
executor.setCorePoolSize(10);
executor.setMaxPoolSize(20);
executor.setKeepAliveSeconds(5);
executor.setQueueCapacity(100);
// executor.setRejectedExecutionHandler(null);
return executor;
}
}
@EnableAsync
@Component
public class TestApplicationListener implements ApplicationListener<TestSpringEvent> {
@Async
public void onApplicationEvent(TestSpringEvent event) {
System.out.println("code is:\t" + event.getCode() + ",\tmessage is:\t" + event.getMessage());
//通過線程group/名稱區(qū)別
System.out.println(Thread.currentThread().getThreadGroup()+ "/" +Thread.currentThread().getName());
}
}
執(zhí)行結(jié)果:
/software/jdk1.8.0_191/bin/java -agentlib:jdwp=transport=dt_socket,address=127.0.0.1:44287,suspend=y,server=n -javaagent:/software/ideaIde/lib/rt/debugger-agent.jar -Dfile.encoding=UTF-8 -classpath /software/jdk1.8.0_191/jre/lib/charsets.jar:/software/jdk1.8.0_191/jre/lib/deploy.jar:/software/jdk1.8.0_191/jre/lib/ext/cldrdata.jar:/software/jdk1.8.0_191/jre/lib/ext/dnsns.jar:/software/jdk1.8.0_191/jre/lib/ext/jaccess.jar:/software/jdk1.8.0_191/jre/lib/ext/jfxrt.jar:/software/jdk1.8.0_191/jre/lib/ext/localedata.jar:/software/jdk1.8.0_191/jre/lib/ext/nashorn.jar:/software/jdk1.8.0_191/jre/lib/ext/sunec.jar:/software/jdk1.8.0_191/jre/lib/ext/sunjce_provider.jar:/software/jdk1.8.0_191/jre/lib/ext/sunpkcs11.jar:/software/jdk1.8.0_191/jre/lib/ext/zipfs.jar:/software/jdk1.8.0_191/jre/lib/javaws.jar:/software/jdk1.8.0_191/jre/lib/jce.jar:/software/jdk1.8.0_191/jre/lib/jfr.jar:/software/jdk1.8.0_191/jre/lib/jfxswt.jar:/software/jdk1.8.0_191/jre/lib/jsse.jar:/software/jdk1.8.0_191/jre/lib/management-agent.jar:/software/jdk1.8.0_191/jre/lib/plugin.jar:/software/jdk1.8.0_191/jre/lib/resources.jar:/software/jdk1.8.0_191/jre/lib/rt.jar:/home/huahua/IdeaProjects/feng/spring-event/target/classes:/home/huahua/.m2/repository/org/springframework/boot/spring-boot-starter/2.1.1.RELEASE/spring-boot-starter-2.1.1.RELEASE.jar:/home/huahua/.m2/repository/org/springframework/boot/spring-boot/2.1.1.RELEASE/spring-boot-2.1.1.RELEASE.jar:/home/huahua/.m2/repository/org/springframework/spring-context/5.1.3.RELEASE/spring-context-5.1.3.RELEASE.jar:/home/huahua/.m2/repository/org/springframework/spring-aop/5.1.3.RELEASE/spring-aop-5.1.3.RELEASE.jar:/home/huahua/.m2/repository/org/springframework/spring-beans/5.1.3.RELEASE/spring-beans-5.1.3.RELEASE.jar:/home/huahua/.m2/repository/org/springframework/spring-expression/5.1.3.RELEASE/spring-expression-5.1.3.RELEASE.jar:/home/huahua/.m2/repository/org/springframework/boot/spring-boot-autoconfigure/2.1.1.RELEASE/spring-boot-autoconfigure-2.1.1.RELEASE.jar:/home/huahua/.m2/repository/org/springframework/boot/spring-boot-starter-logging/2.1.1.RELEASE/spring-boot-starter-logging-2.1.1.RELEASE.jar:/home/huahua/.m2/repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar:/home/huahua/.m2/repository/ch/qos/logback/logback-core/1.2.3/logback-core-1.2.3.jar:/home/huahua/.m2/repository/org/slf4j/slf4j-api/1.7.25/slf4j-api-1.7.25.jar:/home/huahua/.m2/repository/org/apache/logging/log4j/log4j-to-slf4j/2.11.1/log4j-to-slf4j-2.11.1.jar:/home/huahua/.m2/repository/org/apache/logging/log4j/log4j-api/2.11.1/log4j-api-2.11.1.jar:/home/huahua/.m2/repository/org/slf4j/jul-to-slf4j/1.7.25/jul-to-slf4j-1.7.25.jar:/home/huahua/.m2/repository/javax/annotation/javax.annotation-api/1.3.2/javax.annotation-api-1.3.2.jar:/home/huahua/.m2/repository/org/springframework/spring-core/5.1.3.RELEASE/spring-core-5.1.3.RELEASE.jar:/home/huahua/.m2/repository/org/springframework/spring-jcl/5.1.3.RELEASE/spring-jcl-5.1.3.RELEASE.jar:/home/huahua/.m2/repository/org/yaml/snakeyaml/1.23/snakeyaml-1.23.jar:/home/huahua/.m2/repository/org/projectlombok/lombok/1.18.4/lombok-1.18.4.jar:/software/ideaIde/lib/idea_rt.jar com.feng.spring.SpringEventMain
Connected to the target VM, address: '127.0.0.1:44287', transport: 'socket'
19:08:24.692 [main] DEBUG org.springframework.context.annotation.ClassPathBeanDefinitionScanner - Identified candidate component class: file [/home/huahua/IdeaProjects/feng/spring-event/target/classes/com/feng/spring/config/AsyncConfig.class]
19:08:24.730 [main] DEBUG org.springframework.context.annotation.ClassPathBeanDefinitionScanner - Identified candidate component class: file [/home/huahua/IdeaProjects/feng/spring-event/target/classes/com/feng/spring/listener/TestApplicationListener.class]
19:08:24.741 [main] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@37d31475
19:08:24.782 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
19:08:25.091 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'
19:08:25.094 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
19:08:25.099 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
19:08:25.100 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor'
19:08:25.106 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAsyncAnnotationProcessor'
19:08:25.109 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.scheduling.annotation.ProxyAsyncConfiguration'
19:08:25.252 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'asyncConfig'
19:08:25.261 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'testApplicationListener'
19:08:25.299 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'initExecutor'
19:08:25.330 [main] INFO org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor - Initializing ExecutorService 'initExecutor'
code is: 10000, message is: ---------My Spring task event------------
java.lang.ThreadGroup[name=main,maxpri=10]/async-eventListener-1
看線程名稱,已經(jīng)異步執(zhí)行了
[name=main,maxpri=10]/async-eventListener-1
3. 總結(jié)
Spring的事件通知demo已經(jīng)完成,如果需要詳細(xì)的Spring事件從發(fā)布到訂閱的過程,需要跟蹤Spring的ApplicationContext的publishEvent的過程,其實(shí)Spring的ApplicationContext的啟動(dòng)的過程中也定義了很詳細(xì)的事件,使用intellij idea分析,如下:

Spring事件通知的本質(zhì):生產(chǎn)者-消費(fèi)者,體現(xiàn)了設(shè)計(jì)模式的觀察者模式。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
- 詳解SpringBoot實(shí)現(xiàn)ApplicationEvent事件的監(jiān)聽與發(fā)布
- spring event 事件異步處理方式(發(fā)布,監(jiān)聽,異步處理)
- SpringBoot事件發(fā)布與監(jiān)聽超詳細(xì)講解
- SpringBoot事件發(fā)布和監(jiān)聽詳解
- 詳解Spring事件發(fā)布與監(jiān)聽機(jī)制
- 解析Spring事件發(fā)布與監(jiān)聽機(jī)制
- 詳解SpringBoot 發(fā)布ApplicationEventPublisher和監(jiān)聽ApplicationEvent事件
- Spring的事件發(fā)布與監(jiān)聽方式案例講解
相關(guān)文章
Java微信公眾平臺(tái)開發(fā)(6) 微信開發(fā)中的token獲取
這篇文章主要為大家詳細(xì)介紹了Java微信公眾平臺(tái)開發(fā)第六步,微信開發(fā)中的token獲取,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-04-04
詳解Java如何使用責(zé)任鏈默認(rèn)優(yōu)雅地進(jìn)行參數(shù)校驗(yàn)
項(xiàng)目中參數(shù)校驗(yàn)十分重要,它可以保護(hù)我們應(yīng)用程序的安全性和合法性。這篇文章主要介紹了如何使用責(zé)任鏈默認(rèn)優(yōu)雅地進(jìn)行參數(shù)校驗(yàn),需要的可以參考一下2023-03-03
Java調(diào)用接口如何獲取json數(shù)據(jù)解析后保存到數(shù)據(jù)庫
這篇文章主要介紹了Java調(diào)用接口如何獲取json數(shù)據(jù)解析后保存到數(shù)據(jù)庫問題,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-03-03
RocketMQ中消費(fèi)者的消費(fèi)進(jìn)度管理
這篇文章主要介紹了RocketMQ中消費(fèi)者的消費(fèi)進(jìn)度管理,業(yè)務(wù)實(shí)現(xiàn)消費(fèi)回調(diào)的時(shí)候,當(dāng)且僅當(dāng)此回調(diào)函數(shù)返回ConsumeConcurrentlyStatus.CONSUME_SUCCESS ,RocketMQ才會(huì)認(rèn)為這批消息(默認(rèn)是1條)是消費(fèi)完成的,需要的朋友可以參考下2023-10-10
springboot封裝響應(yīng)實(shí)體的實(shí)例代碼
這篇文章主要介紹了springboot封裝響應(yīng)實(shí)體,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-07-07
SSH框架網(wǎng)上商城項(xiàng)目第27戰(zhàn)之申請域名空間和項(xiàng)目部署及發(fā)布
這篇文章主要為大家詳細(xì)介紹了SSH框架網(wǎng)上商城項(xiàng)目第26戰(zhàn)之申請域名空間和項(xiàng)目部署及發(fā)布,感興趣的小伙伴們可以參考一下2016-06-06
Java實(shí)現(xiàn)簡單客戶信息管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)簡單客戶信息管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-05-05

