Spring event handling mechanism-ApplicationEvent

Event handling in ApplicationContext is provided through ApplicationEvent class and ApplicationListener interface. If a bean that implements the ApplicationListener interface is registered in the ApplicationContext, the bean will be notified every time an ApplicationEvent is posted to the ApplicationContext. Essentially, this is the standard observer design pattern. Starting from Spring 4.2, the event structure has been significantly improved, providing an annotation-based model and the ability to publish arbitrary events (that is, objects that do not necessarily extend from ApplicationEvent). When such an object is published, we wrap it in an event. Understanding Spring's event mechanism is of great help to the integration of Spring and other third-party frameworks. Dubbo publishes services through Spring's event mechanism. Spring and Spring also provide many built-in events, as shown in the following table:

event

Description

ContextRefreshedEvent

Published when the ApplicationContext is initialized or refreshed (for example, by using the refresh() method on the ConfigurableApplicationContext interface). Here, "initialize" means to load all beans, detect and activate the post-processor bean, pre-instantiate the singleton, and the ApplicationContext object is ready for use. As long as the context is not closed, as long as the selected ApplicationContext actually supports this type of "hot" refresh, the refresh can be triggered multiple times. For example, XMLWebApplicationContext supports hot refresh, but GenericApplicationContext does not.

ContextStartedEvent

Published when the ApplicationContext is started using the start() method on the configurable ConfigurableApplicationContext interface. Here, "start" means that all life cycle beans will receive a clear start signal. This signal is used to restart be an after an explicit stop, but it can also be used to start a component that has not been configured to start automatically (for example, a component that has not been started during initialization).

ContextStoppedEvent

Released when the ApplicationContext is stopped using the stop() method on the ConfigurableApplicationContext interface. Here, "stop" means that all life cycle beans will receive a clear stop signal. The stopped context can be restarted by calling start().

ContextClosedEvent

Released when the ApplicationContext is closed using the close() method on the ConfigurableApplicationContext interface. Here, "closed" means that all singleton beans are destroyed. The closed environment has reached the end of life. Unable to refresh or restart.

RequestHandledEvent

A specific web event tells all beans that the HTTP request has been processed. This event is posted after the request is completed. This event only applies to web applications that apply Spring DispatcherServlet

We can also create our own events through the ApplicationEvent class and ApplicationListener interface. The following example shows a simple class that extends Spring's ApplicationEvent base class:

public class BlackListEvent extends ApplicationEvent {
    private final String address;
    private final String content;

    public BlackListEvent(Object source, String address, String content) {
        super(source);
        this.address = address;
        this.content = content;
    }
    // 其他方法
}

To publish a custom ApplicationEvent, you need to call the PublishEvent() method on ApplicationEventPublisher. Usually, this is done by creating a class that implements ApplicationEventPublisheraware and registering it as a Spring Bean. As shown in the following example:

public class EmailService implements ApplicationEventPublisherAware {

    private List<String> blackList;
    private ApplicationEventPublisher publisher;

    public void setBlackList(List<String> blackList) {
        this.blackList = blackList;
    }

    public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
        this.publisher = publisher;
    }

    public void sendEmail(String address, String content) {
        if (blackList.contains(address)) {
            publisher.publishEvent(new BlackListEvent(this, address, content));
            return;
        }
        // send email...
    }
}

During configuration, the Spring container detects that EmailService implements ApplicationEventPublisheraware, and automatically calls setApplicationEventPublisher() to inject the ApplicationEventPublisher instance. In fact, the parameter passed in is the Spring container itself. To receive a custom ApplicationEvent, you can create a class that implements ApplicationListener and register it as a Spring Bean. The code is as follows:

public class BlackListNotifier implements ApplicationListener<BlackListEvent> {
    private String notificationAddress;
    public void setNotificationAddress(String notificationAddress) {
        this.notificationAddress = notificationAddress;
    }
    public void onApplicationEvent(BlackListEvent event) {
        // notify appropriate parties via notificationAddress...
    }
}

Note that ApplicationListener is usually parameterized with the type of custom event (BackListEvent in the previous example). This means that the onApplicationEvent() method can guarantee type safety and avoid any down-casting needs. You can register as many event listeners as you want, but please note that by default, event listeners receive events synchronously. This means that the publishEvent() method will block until all listeners have finished processing the event. One advantage of this synchronous and single-threaded approach is that when the listener receives an event, if the transaction context is available, it will operate in the transaction context of the publisher. If you need another strategy for event publishing, please refer to the ApplicationEventMulticaster interface. The following is the Spring configuration in the above example:

<bean id="emailService" class="example.EmailService">
    <property name="blackList">
        <list>
            <value>[email protected]</value>
            <value>[email protected]</value>
            <value>[email protected]</value>
        </list>
    </property></bean>
<bean id="blackListNotifier" class="example.BlackListNotifier">
    <property name="notificationAddress" value="[email protected]"/>
</bean>

When the sendemail() method of emailServicebean is called, if any email should be blacklisted, a custom event of type BlackListEvent will be posted. The blackListNotifier bean is registered as an ApplicationListener and receives BlackListEvent, at which time it can notify the corresponding party.

In addition to publishing events using ApplicationEventPublisher, we can publish events through ApplicationContext instances. ApplicationContext provides the publishEvent() method for publishing events. The code is as follows:

public class MessageEvent extends ApplicationEvent {
    private static final long serialVersionUID = 1L;
    private String eventType;
    public String getEventType() {
	return eventType;
    }
    public void setEventType(String eventType) {
	this.eventType = eventType;
    }
    public MessageEvent(Object source) {
	super(source);
    }
}
@Component
public class MessageEventListener implements ApplicationListener<MessageEvent> {
    public void onApplicationEvent(MessageEvent event) {
        String eventType = event.getEventType();
	//发布事件的对象
	Object object = event.getSource();
    }
}
public class SpringEventTest {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext("cn.org.microservice.spring.ioc.event");
	MessageEvent event = new MessageEvent(applicationContext);
	event.setEventType("发布");
	applicationContext.publishEvent(event);
    }
}

Spring's event mechanism is designed for simple communication between SpringBeans in the same application context. However, for more complex enterprise integration requirements, the separately maintained Spring Integration project provides complete support for building a lightweight, pattern-oriented, event-driven architecture based on the Spring programming model. Starting from Spring 4.2, you can use the EventListener annotation to register an event listener on any public method of the managed bean. The BlackListNotifier program can be rewritten as follows:

public class BlackListNotifier {
    private String notificationAddress;
    public void setNotificationAddress(String notificationAddress) {
        this.notificationAddress = notificationAddress;
    }
    @EventListener
    public void processBlackListEvent(BlackListEvent event) {
        // notify appropriate parties via notificationAddress...
    }
}

The method signature again declares the type of event it wants to listen to, but this time with a flexible name instead of implementing a specific listener interface. As long as the actual event type resolves the generic parameters in its implementation hierarchy, the event type can be narrowed by generics. If your method should listen to multiple events, or you want to define it without using any parameters, you can also specify the event type on the annotation itself. The following example shows how to do this:

@EventListener({ContextStartedEvent.class, ContextRefreshedEvent.class})
public void handleContextStart() {
    ...
}

If you want a specific listener to process events asynchronously, you can reuse the regular @Async support. The following example shows how to do this:

@EventListener
@Async
public void processBlackListEvent(BlackListEvent event) {
    // BlackListEvent is processed in a separate thread
}

If you need to call a listener first and then call another listener, you can add the @Order annotation to the method declaration, as shown in the following example:

@EventListener
@Order(42)
public void processBlackListEvent(BlackListEvent event) {
    // notify appropriate parties via notificationAddress...
}

You can also use generics to further define the structure of the event. Consider using EntityCreatedEvent, where t is the type of actual entity created. For example, you can create the following listener definition to receive only Person's EntityCreatedEvent:

@EventListener
public void onPersonCreated(EntityCreatedEvent<Person> event) {
    ...
}

Due to type elimination, this operation is only valid when the triggered event resolves the generic parameters filtered by the event listener (that is, the PersonCreatedEvent class extends EntityCreatedEvent<Person> {…}). In some cases, if all events follow the same structure (like the events in the previous example), this can become quite tedious. In this case, you can implement ResolvableTypeProvider to guide the framework to provide content on top of the runtime environment. The following events show how to do this:

public class EntityCreatedEvent<T> extends ApplicationEvent implements ResolvableTypeProvider {

    public EntityCreatedEvent(T entity) {
        super(entity);
    }

    @Override
    public ResolvableType getResolvableType() {
        return ResolvableType.forClassWithGenerics(getClass(), ResolvableType.forInstance(getSource()));
    }
}

 

Guess you like

Origin blog.csdn.net/wk19920726/article/details/108717994