SpringBoot advanced--events

Introduction

Other URL

Event monitoring (based on SpringBoot example)_JustryDeng-CSDNblog

Brief description

The release and monitoring of events is subordinate to the observer mode; compared with MQ, the release and monitoring of events is biased towards processing certain logic "in the system". 

Multiple listeners can listen to the same event. For example: if event A is released, and both listener A and listener B listen to event A, both listener A and B will process it.

Synchronous and asynchronous monitoring

Monitoring method Features When to use
Synchronous monitoring The publisher thread is in the same thread as the listener thread

1. The monitoring logic is processed faster

2. It is necessary to follow the business thread according to the listener

Asynchronous monitoring The publisher thread and the listener thread are in different threads

1. Monitoring logic processing is time-consuming

2. Pursue responsiveness

priority

The order of listeners can be adjusted by implementing the Ordered interface.

Note: You must implement ApplicationListener<MyEvent>,Ordered at the same time to control the order.

The order of the following are not controllable:

  1. @Component+@EventListerner+实现Ordered
  2. Implement ApplicationListener<MyEvent>+@Order

Instance

Other URL

SpringBoot event monitoring_Chavaer-CSDN blog
SpringBoot- 4 ways to implement event monitoring_ignorewho's blog-CSDN blog_springboot event monitoring

Synchronous monitoring (unordered)

event

package com.example.event;

import org.springframework.context.ApplicationEvent;

public class MyEvent extends ApplicationEvent {

    public MyEvent(Object source) {
        super(source);
    }

}

Listener

package com.example.event;

import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

@Component
public class MyListener {
    @EventListener
    public void abc(MyEvent event) {
        System.out.println("监听器:        " + "MyListener");
        System.out.println("监听器所在线程:" + Thread.currentThread().getName());
        System.out.println("事件:          " + event);
        System.out.println("事件的数据:    " + event.getSource());
    }
}

 Listener 2

package com.example.event;

import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

@Component
public class MyListener2 {

    @EventListener
    public void onApplicationEvent(MyEvent event) {
        System.out.println("监听器:      " + "MyListener2");
        System.out.println("  所在线程:  " + Thread.currentThread().getName());
        System.out.println("  事件:      " + event);
        System.out.println("  事件的数据:" + event.getSource());
    }
}

Publisher

package com.example.event;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

@Component
public class MyPublisher {
    @Autowired
    ApplicationContext applicationContext;

    public void myPublish(String message) {
        System.out.println("发布器所在线程:" + Thread.currentThread().getName());
        applicationContext.publishEvent(new MyEvent(message));
    }
}

test

Write a Controller

package com.example.controller;

import com.example.event.MyPublisher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {
    @Autowired
    MyPublisher myPublisher;

    @GetMapping("/test1")
    public String test1() {
        myPublisher.myPublish("Hello");
        return "test1 success";
    }
}

After starting, visit: http://localhost:8080/test1

Back-end output:

发布器所在线程:http-nio-8080-exec-1
监听器:      MyListener
  所在线程:  http-nio-8080-exec-1
  事件:      com.example.event.MyEvent[source=Hello]
  事件的数据:Hello
监听器:      MyListener2
  所在线程:  http-nio-8080-exec-1
  事件:      com.example.event.MyEvent[source=Hello]
  事件的数据:Hello

It can be found that all listeners and publishers are in the same thread. 

Synchronous monitoring (ordered)

event

package com.example.event;
 
import org.springframework.context.ApplicationEvent;
 
public class MyEvent extends ApplicationEvent {
 
    public MyEvent(Object source) {
        super(source);
    }
 
}

Listener

Listener 1

package com.example.event;

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

@Component
public class MyListener implements ApplicationListener<MyEvent>,  Ordered {
    @Override
    public void onApplicationEvent(MyEvent event) {
        System.out.println("监听器:      " + "MyListener");
        System.out.println("  所在线程:  " + Thread.currentThread().getName());
        System.out.println("  事件:      " + event);
        System.out.println("  事件的数据:" + event.getSource());
    }

    @Override
    public int getOrder() {
        return 2;
    }
}

Listener 2

package com.example.event;

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

@Component
public class MyListener2 implements ApplicationListener<MyEvent>, Ordered {

    public void onApplicationEvent(MyEvent event) {
        System.out.println("监听器:      " + "MyListener2");
        System.out.println("  所在线程:  " + Thread.currentThread().getName());
        System.out.println("  事件:      " + event);
        System.out.println("  事件的数据:" + event.getSource());
    }

    @Override
    public int getOrder() {
        return 1;
    }
}

Publisher

package com.example.event;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

@Component
public class MyPublisher {
    @Autowired
    ApplicationContext applicationContext;

    public void myPublish(String message) {
        System.out.println("发布器所在线程:" + Thread.currentThread().getName());
        applicationContext.publishEvent(new MyEvent(message));
    }
}

 

test

Write a Controller

package com.example.controller;

import com.example.event.MyPublisher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {
    @Autowired
    MyPublisher myPublisher;

    @GetMapping("/test1")
    public String test1() {
        myPublisher.myPublish("Hello");
        return "test1 success";
    }
}

After starting, visit: http://localhost:8080/test1

Back-end output:

发布器所在线程:http-nio-8080-exec-1
监听器:      MyListener2
  所在线程:  http-nio-8080-exec-1
  事件:      com.example.event.MyEvent[source=Hello]
  事件的数据:Hello
监听器:      MyListener
  所在线程:  http-nio-8080-exec-1
  事件:      com.example.event.MyEvent[source=Hello]
  事件的数据:Hello

If the Ordered order of the implementation of the listener is reversed, the output result is as follows:

发布器所在线程:http-nio-8080-exec-1
监听器:      MyListener
  所在线程:  http-nio-8080-exec-1
  事件:      com.example.event.MyEvent[source=Hello]
  事件的数据:Hello
监听器:      MyListener2
  所在线程:  http-nio-8080-exec-1
  事件:      com.example.event.MyEvent[source=Hello]
  事件的数据:Hello

Asynchronous monitoring (unordered)

method:

  1. Enable asynchronous monitoring
  2. Add @Async to the listener (this listener must be registered by the @Component method)

event

package com.example.event;

import org.springframework.context.ApplicationEvent;

public class MyEvent extends ApplicationEvent {

    public MyEvent(Object source) {
        super(source);
    }

}

Sync listener

package com.example.event;
 
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
 
@Component
public class MyListener {
    @EventListener
    public void abc(MyEvent event) {
        System.out.println("监听器:      " + "MyListener");
        System.out.println("  所在线程:  " + Thread.currentThread().getName());
        System.out.println("  事件:      " + event);
        System.out.println("  事件的数据:" + event.getSource());
    }
}

Asynchronous listener

package com.example.event;

import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

@Component
@Async
public class MyListener2 {

    @EventListener
    public void onApplicationEvent(MyEvent event) {
        System.out.println("监听器:      " + "MyListener2");
        System.out.println("  所在线程:  " + Thread.currentThread().getName());
        System.out.println("  事件:      " + event);
        System.out.println("  事件的数据:" + event.getSource());
    }
}

Publisher

package com.example.event;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

@Component
public class MyPublisher {
    @Autowired
    ApplicationContext applicationContext;

    public void myPublish(String message) {
        System.out.println("发布器所在线程:" + Thread.currentThread().getName());
        applicationContext.publishEvent(new MyEvent(message));
    }
}

Start class

package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;

@SpringBootApplication
@EnableAsync
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

test

Write a Controller

package com.example.controller;

import com.example.event.MyPublisher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {
    @Autowired
    MyPublisher myPublisher;

    @GetMapping("/test1")
    public String test1() {
        myPublisher.myPublish("Hello");
        return "test1 success";
    }
}

After starting, visit: http://localhost:8080/test1

Back-end output:

发布器所在线程:http-nio-8080-exec-1
监听器:      MyListener
  所在线程:  http-nio-8080-exec-1
  事件:      com.example.event.MyEvent[source=Hello]
  事件的数据:Hello
监听器:      MyListener2
  所在线程:  task-1
  事件:      com.example.event.MyEvent[source=Hello]
  事件的数据:Hello

Process

Custom event

Customize events by inheriting ApplicationEvent.

The parameter of the constructor is the related data object of the event, and the listener can obtain the data object, and then perform related logic processing.

package com.example.event;

import org.springframework.context.ApplicationEvent;

public class MyEvent extends ApplicationEvent {

    public MyEvent(Object source) {
        super(source);
    }

}

Custom listener

ApplicationListener

法1:@EventListener

Listen to a single event 

package com.example.event;

import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

public class MyListener {
    @EventListener
    public void abc(MyEvent event) {
        System.out.println("监听器:     " + "MyListener");
        System.out.println("  线程:     " + Thread.currentThread().getName());
        System.out.println("  事件:     " + event);
        System.out.println("  事件的数据:" + event.getSource());
    }
}

or

package com.example.event;

import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

public class MyListener {
    @EventListener({MyEvent.class})
    public void abc(ApplicationEventevent) {
        System.out.println("监听器:      " + "MyListener");
        System.out.println("  线程:      " + Thread.currentThread().getName());
        System.out.println("  事件:      " + event);
        System.out.println("  事件的数据: " + event.getSource());
    }
}

 The above method is better, because there is no need for type conversion. It can be directly determined that it is of the MyEvent type.

Listen to multiple events

After the event comes in, you can use if (event instanceOf MyEvent.class) to determine what kind of event.

package com.example.event;
 
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
 
@Component
public class MyListener {
    @EventListener({MyEvent.class, MyEvent2.class})
    public void abc(ApplicationEvent event) {
        System.out.println("监听器:      " + "MyListener");
        System.out.println("  所在线程:  " + Thread.currentThread().getName());
        System.out.println("  事件:      " + event);
        System.out.println("  事件的数据:" + event.getSource());
    }
}

Listen to all ApplicationEvent

If you use this writing method, a lot of Spring's own events will be printed at startup. Any ApplicationEvent will enter here.

package com.example.event;
 
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
 
@Component
public class MyListener {
    @EventListener
    public void abc(ApplicationEvent event) {
        System.out.println("监听器:     " + "MyListener");
        System.out.println("  所在线程: " + Thread.currentThread().getName());
        System.out.println("  事件:     " + event);
        System.out.println("  事件的数据:" + event.getSource());
    }
}

Method 2: Implement ApplicationListener<T> interface

public class MyListener implements ApplicationListener<MyEvent>{

	public void onApplicationEvent(MyEvent event){
        System.out.println("监听器:     " + "MyListener");
        System.out.println("  所在线程: " + Thread.currentThread().getName());
        System.out.println("  事件:     " + event);
        System.out.println("  事件的数据:" + event.getSource());
	}
}

SmartApplicationListener

The source code is as follows

public interface SmartApplicationListener extends ApplicationListener<ApplicationEvent>, Ordered {
    boolean supportsEventType(Class<? extends ApplicationEvent> var1);

    default boolean supportsSourceType(@Nullable Class<?> sourceType) {
        return true;
    }

    default int getOrder() {
        return 2147483647;
    }
}

supportsEventType: the type of event supported

supportsSourceType: the type of data supported

getOrder: 2147483641: It is 2^31-1, which is the largest positive number of a 32-bit int.

Example

event

package com.example.event;
 
import org.springframework.context.ApplicationEvent;
 
public class MyEvent extends ApplicationEvent {
 
    public MyEvent(Object source) {
        super(source);
    }
 
}

Listener 1

package com.example.event;

import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.SmartApplicationListener;
import org.springframework.stereotype.Component;

@Component
public class MyListener implements SmartApplicationListener {

    @Override
    public boolean supportsEventType(Class<? extends ApplicationEvent> aClass) {
        return aClass == MyEvent.class;
    }

    @Override
    public boolean supportsSourceType(Class<?> sourceType) {
        return sourceType == String.class;
    }

    @Override
    public int getOrder() {
        return 2;
    }

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        System.out.println("监听器:      " + "MyListener");
        System.out.println("  所在线程:  " + Thread.currentThread().getName());
        System.out.println("  事件:      " + event);
        System.out.println("  事件的数据:" + event.getSource());
        System.out.println("  是MyEvent?:" + (event instanceof MyEvent));
    }
}

Listener 2

package com.example.event;

import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.SmartApplicationListener;
import org.springframework.stereotype.Component;

@Component
public class MyListener2 implements SmartApplicationListener {

    @Override
    public boolean supportsEventType(Class<? extends ApplicationEvent> aClass) {
        return aClass == MyEvent.class;
    }

    @Override
    public boolean supportsSourceType(Class<?> sourceType) {
        return sourceType == String.class;
    }

    @Override
    public int getOrder() {
        return 1;
    }

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        System.out.println("监听器:      " + "MyListener2");
        System.out.println("  所在线程:  " + Thread.currentThread().getName());
        System.out.println("  事件:      " + event);
        System.out.println("  事件的数据:" + event.getSource());
        System.out.println("  是MyEvent?:" + (event instanceof MyEvent));
    }
}

Publisher

package com.example.event;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

@Component
public class MyPublisher {
    @Autowired
    ApplicationContext applicationContext;

    public void myPublish(String message) {
        System.out.println("发布器所在线程:" + Thread.currentThread().getName());
        applicationContext.publishEvent(new MyEvent(message));
    }
}

test

package com.example.controller;

import com.example.event.MyPublisher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {
    @Autowired
    MyPublisher myPublisher;

    @GetMapping("/test1")
    public String test1() {
        myPublisher.myPublish("Hello");
        return "test1 success";
    }

}

After starting, visit: http://localhost:8080/test1

Back-end output:

发布器所在线程:http-nio-8080-exec-2
监听器:      MyListener2
  所在线程:  http-nio-8080-exec-2
  事件:      com.example.event.MyEvent[source=Hello]
  事件的数据:Hello
  是MyEvent?:true
监听器:      MyListener
  所在线程:  http-nio-8080-exec-2
  事件:      com.example.event.MyEvent[source=Hello]
  事件的数据:Hello
  是MyEvent?:true

If the Ordered order of the implementation of the listener is reversed, the output result is as follows:

发布器所在线程:http-nio-8080-exec-1
监听器:      MyListener
  所在线程:  http-nio-8080-exec-1
  事件:      com.example.event.MyEvent[source=Hello]
  事件的数据:Hello
  是MyEvent?:true
监听器:      MyListener2
  所在线程:  http-nio-8080-exec-1
  事件:      com.example.event.MyEvent[source=Hello]
  事件的数据:Hello
  是MyEvent?:true

Register the listener

the way Scope of application Can it be used with @Async annotations for asynchronous monitoring
@Component All listeners can
Add configuration in application.yml Listener that implements the ApplicationListener<T> interface Can't
Register in the startup class Listener that implements the ApplicationListener<T> interface Can't

Method 1: @Component (applicable to all listeners)

package com.example.event;

import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

@Component
public class MyListener {
    @EventListener
    public void abc(MyEvent event) {
        System.out.println("监听器:" + "MyListener");
        System.out.println("线程:" + Thread.currentThread().getName());
        System.out.println("事件:" + event);
        System.out.println("事件的数据:" + event.getSource());
    }
}
package com.example.event;

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

@Component
public class MyListener2 implements ApplicationListener<MyEvent> {

    @Override
    public void onApplicationEvent(MyEvent event) {
        System.out.println("监听器:" + "MyListener2");
        System.out.println("线程:" + Thread.currentThread().getName());
        System.out.println("事件:" + event);
        System.out.println("事件的数据:" + event.getSource());
    }
}

Method 2: Add configuration to application.yml (only applicable to listeners that implement the ApplicationListener<T> interface)

context:
  listener:
    classes: com.example.event.MyListener,com.example.event.MyListener2

Method 3: Register in the startup class (only applicable to listeners that implement the ApplicationListener<T> interface)

package com.example;

import com.example.event.MyListener;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        // 原来是这样的:SpringApplication.run(DemoApplication.class, args);
        ConfigurableApplicationContext context = SpringApplication.run(DemoApplication.class, args);
        context.addApplicationListener(new MyListener());
    }
}

Post an event

Method 1: Inject ApplicationContext and call its publishEvent method

package com.example.event;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

@Component
public class MyPublisher {
    @Autowired
    ApplicationContext applicationContext;

    public void myPublish(String message) {
        applicationContext.publishEvent(new MyEvent(message));
    }
}

Method 2: Release in the startup class

package com.example;

import com.example.event.MyEvent;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        //原来是:SpringApplication.run(DemoApplication.class, args);
        ConfigurableApplicationContext context =SpringApplication.run(DemoApplication.class, args);
        context.publishEvent(new MyEvent("Hello"));
    }
}

SpringBoot startup event

Other URL

Spring Boot startup events and listeners are too powerful! _Java technology stack, share the most mainstream Java technology -CSDN blog

Start events (in chronological order)

1、ApplicationStartingEvent

This event is sent when the Spring Boot application starts and before any processing (except for the registration of listeners and initializers).

2、ApplicationEnvironmentPreparedEvent

This event is sent before the Spring context is created when it is known to use the Spring Environment in the context.

3、ApplicationContextInitializedEvent

This event is sent when the Spring application context (ApplicationContext) is ready and the application initializers (ApplicationContextInitializers) have been called, before the bean definitions are loaded.

4、ApplicationPreparedEvent

This event is sent before the Spring context is refreshed and after the bean definitions are loaded.

5、ApplicationStartedEvent

This event is sent after the Spring context is refreshed and before the application/command-line runners are invoked.

6、AvailabilityChangeEvent

This event is sent immediately after the last event, the state: ReadinessState.CORRECT, which means that the application is already active.

7、ApplicationReadyEvent

This event is sent after any application/command-line runners are called.

8、AvailabilityChangeEvent

This event is sent immediately after the last event, the state: ReadinessState.ACCEPTING_TRAFFIC, indicating that the application is ready to receive requests.

9、ApplicationFailedEvent

This event is sent when the application starts abnormally.

The event list described above only includes SpringApplicationEvents events bound to SpringApplication. In addition to these events, the following events will also be sent after ApplicationPreparedEvent and before ApplicationStartedEvent:

WebServerInitializedEvent

This Web server initialization event is sent after the WebServer starts, and corresponds to ServletWebServerInitializedEvent (Servlet Web server initialization event) and ReactiveWebServerInitializedEvent (Responsive Web server initialization event).

ContextRefreshedEvent

This context refresh event is sent after the Spring application context (ApplicationContext) is refreshed. 

principle

Other URL

In- depth understanding of the Spring/SpringBoot event monitoring mechanism-Knowing the
Springboot event mechanism to integrate the EventBus application (event-driven model)_fw19940314的博客-CSDN blog

Source code analysis

myPublisher.myPublish("Hello") //HelloController
    applicationContext.publishEvent(new MyEvent(message)); //MyPublisher
        publishEvent(event, (ResolvableType)null); //AbstractApplicationContext.class
            // AbstractApplicationContext
            getApplicationEventMulticaster().multicastEvent((ApplicationEvent)applicationEvent, eventType);
                // AbstractApplicationContext
                applicationEventMulticaster.multicastEvent((ApplicationEvent)applicationEvent, eventType) 
                    
SimpleApplicationEventMulticaster#multicastEvent
以下都在SimpleApplicationEventMulticaster
multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType)
    // 此时,type为:com.example.tmp.MyEvent
    ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);
        for (ApplicationListener<?> listener : getApplicationListeners(event, type))
            invokeListener(listener, event)
                
                
AbstractApplicationEventMulticaster#getApplicationListeners
getApplicationListeners(event, type))
    retriever = new ListenerRetriever(true);
    Collection<ApplicationListener<?>> listeners = retrieveApplicationListeners(eventType, sourceType, retriever);
        listeners = new LinkedHashSet<>(this.defaultRetriever.applicationListeners);
            遍历listeners,如果监听器支持此事件,则加入集合并返回。

 

Guess you like

Origin blog.csdn.net/feiying0canglang/article/details/114081920