Spring event mechanism

Yin Jihuan 2020-11-06 01:17:08
spring event mechanism


Spring Event mechanism

Reference resources :

spring Event mechanism in :https://www.cnblogs.com/rickiyang/p/12001524.html

Observer mode : http://c.biancheng.net/view/1390.html

Observer mode

Definition of observer pattern : Refers to the existence of one to many dependencies among multiple objects , When the state of an object changes , All objects that depend on it are notified and automatically updated . This pattern is sometimes called Publishing - A subscription model 、 Model - View mode , It's an object behavior pattern .

The main advantages of the observer model are as follows .

  1. It reduces the coupling between the target and the observer , There is an abstract coupling between the two .
  2. There is a trigger mechanism between the target and the observer .

Its main disadvantages are as follows .

  1. The dependence between the target and the observer is not completely relieved , And it's possible to have circular references .
  2. When there are many observers , The announcement conference took a lot of time , Affect the efficiency of the program .

When implementing the observer pattern, we should pay attention to that the specific target object and the specific observer object cannot be called directly , Otherwise, the two will be closely coupled , This violates the object-oriented design principles .

  1. The main roles of the observer pattern are as follows .
    1. Abstract themes (Subject) role : Also called abstract target class , It provides an aggregation class to hold observer objects and add 、 How to delete the observer object , And an abstract way to inform all observers .
    2. Specific themes (Concrete Subject) role : It's also called specific target class , It implements the notification method in the abstract target , When the internal state of a specific topic changes , Notify all registered observer objects .
    3. Abstract observer (Observer) role : It's an abstract class or interface , It contains an abstract method to update itself , Called when notified of a change to a specific topic .
    4. Concrete observer (Concrete Observer) role : Implement the abstract methods defined in the abstract observer , So as to update its status when getting the change notification of the target .

To illustrate with a small chestnut , It's paid , Wife and children have their own expenses

  1. public class MyObserverDesign {
  2. public static void main(String[] args) {
  3. // The salary in September has been paid
  4. MyMon myMon = new OctberMon();
  5. myMon.addMyRelative(new MyWife());
  6. myMon.addMyRelative(new MySon());
  7. myMon.addMyRelative(new MyDaughter());
  8. myMon.payOff();
  9. }
  10. // Abstract goals : My salary
  11. public static abstract class MyMon {
  12. protected List<MyRelative> myRelatives = new ArrayList<>();
  13. public void addMyRelative(MyRelative myRelative) {
  14. myRelatives.add(myRelative);
  15. }
  16. public void removeMyRelative(MyRelative myRelative) {
  17. myRelatives.remove(myRelative);
  18. }
  19. // It's paid
  20. public abstract void payOff();
  21. }
  22. // Specific goals :11 Monthly salary
  23. public static class OctberMon extends MyMon{
  24. @Override
  25. public void payOff() {
  26. System.out.println(" Babies , It's paid , What do you want ");
  27. for (MyRelative obs : myRelatives) {
  28. obs.spendMoney();
  29. }
  30. }
  31. }
  32. // Abstract observer My family
  33. public interface MyRelative {
  34. // Waiting to spend money
  35. public void spendMoney();
  36. }
  37. // Concrete observer My wife
  38. public static class MyWife implements MyRelative {
  39. @Override
  40. public void spendMoney() {
  41. System.out.println(" Husband, let's go traveling ~");
  42. }
  43. }
  44. public static class MySon implements MyRelative {
  45. @Override
  46. public void spendMoney() {
  47. System.out.println(" Dad , I want spider man ~");
  48. }
  49. }
  50. public static class MyDaughter implements MyRelative {
  51. @Override
  52. public void spendMoney() {
  53. System.out.println(" Dad , I want beautiful dolls ~");
  54. }
  55. }
  56. }

Execution results

  1. Babies , It's paid , What do you want
  2. Husband, let's go traveling ~
  3. Dad , I want spider man ~
  4. Dad , I want beautiful dolls ~

Java Its own support for events

Java Basic event handling base classes are provided in :

  1. EventObject: All event state objects will derive from their root class ;
  2. EventListener: All event listener interfaces must extend the tag interface ;

Also rewrite the observer model scene above

The event of creating salary is inherited from EventObject

  1. /**
  2. * Pay Events
  3. */
  4. public class PayOffEvent extends EventObject {
  5. /**
  6. * Constructs a prototypical Event.
  7. *
  8. * @param source The object on which the Event initially occurred.
  9. * @throws IllegalArgumentException if source is null.
  10. */
  11. public PayOffEvent(Object source) {
  12. super(source);
  13. }
  14. }

And then create a listener , Inherit EventListener, Wife and children are waiting for money -_-

  1. public interface MyRelative extends EventListener { void spendMoney();}

Specific monitoring implementation class 1

  1. public class MyWife implements MyRelative {
  2. @Override
  3. public void spendMoney() {
  4. System.out.println(" Husband, let's go traveling ~");
  5. }
  6. }

Specific monitoring implementation class 2

  1. public class MySon implements MyRelative {
  2. @Override
  3. public void spendMoney() {
  4. System.out.println(" Dad , I want spider man ~");
  5. }
  6. }

source

  1. public class Source {
  2. Set<MyRelative> listeners = new HashSet<>();
  3. public void addStateChangeListener(MyRelative listener) {
  4. listeners.add(listener);
  5. }
  6. public void removeStateChangeListener(MyRelative listener) {
  7. listeners.remove(listener);
  8. }
  9. public void notifyListener(){
  10. for (MyRelative listener : listeners) {
  11. listener.spendMoney();
  12. }
  13. }
  14. }

test

  1. public static void main(String[] args) {
  2. Source source = new Source();
  3. source.addStateChangeListener(new MyWife());
  4. source.addStateChangeListener(new MySon());
  5. source.notifyListener();
  6. }

Search a lot about java event They're all the same as the model , But personal feelings I don't see the specific role of the event interface , A direct call to a slightly typed method , It doesn't reflect the propagation characteristics of events . Get rid of Two tag interfaces EventListener It doesn't affect

Spring Event mechanism in

So much is said above to supplement the pre knowledge .

stay Spring Through the container ApplicationEvent Classes and ApplicationListener Interface to handle events , If a bean Realization ApplicationListener Interface and deployed into the container , So every time it corresponds to ApplicationEvent The bean , This is a typical observer pattern .

Spring By default, the event is synchronous , That is to call publishEvent Method post event , It's going to be blocked , until onApplicationEvent After receiving the event and processing the return, the execution will continue , The advantage of this single thread synchronization is that transaction management can be done .

Define an order success event

  1. /**
  2. * Define an order success event
  3. */
  4. public class OrderSuccessEvent extends ApplicationEvent {
  5. /**
  6. * Create a new {@code ApplicationEvent}.
  7. *
  8. * @param source the object on which the event initially occurred or with
  9. * which the event is associated (never {@code null})
  10. */
  11. public OrderSuccessEvent(Object source) {
  12. super(source);
  13. }
  14. }

Define listener one When the order is placed successfully Send a text message

  1. @Service
  2. public class SmsListener implements ApplicationListener<OrderSuccessEvent> {
  3. @Override
  4. public void onApplicationEvent(OrderSuccessEvent event) {
  5. sendSms();
  6. }
  7. private void sendSms() {
  8. System.out.println(" Send order success email ");
  9. }
  10. }

Define listener two When the order is placed successfully Start

  1. @Service
  2. public class CarService implements ApplicationListener<OrderSuccessEvent> {
  3. @Override
  4. public void onApplicationEvent(OrderSuccessEvent event) {
  5. sendCarInfo();
  6. }
  7. private void sendCarInfo() {
  8. System.out.println(" It's time for Lao Wang to start ");
  9. }
  10. }

adopt Spring Provided applicationContext Release the event

  1. @Service
  2. public class OrderService {
  3. @Autowired
  4. private ApplicationContext applicationContext;
  5. public void order(){
  6. System.out.println(" Order complete ...");
  7. applicationContext.publishEvent(new OrderSuccessEvent("126625874"));
  8. System.out.println(" End of the process ");
  9. }
  10. }

adopt web Way to trigger test

  1. @RestController
  2. public class OrderController {
  3. @Autowired
  4. private OrderService orderService;
  5. @GetMapping(value = "/test/event")
  6. public void testEvent(){
  7. orderService.order();
  8. }
  9. }

Post visit results

  1. Order complete ...
  2. It's time for Lao Wang to start
  3. Send order success email
  4. End of the process

You can see It's waiting for the monitor It's only after all the success End of the process , It's synchronous , And when there is an exception, it will not continue to propagate

Change the departure to

  1. @Service
  2. public class CarService implements ApplicationListener<OrderSuccessEvent> {
  3. @Override
  4. public void onApplicationEvent(OrderSuccessEvent event) {
  5. sendCarInfo();
  6. }
  7. private void sendCarInfo() {
  8. System.out.println(" It's time for Lao Wang to start ");
  9. if (1/0 == 1){
  10. }
  11. }
  12. }

After exception, it will not be executed The task of sending text messages

You can modify the task to execute asynchronously , In source SimpleApplicationEventMulticaster Class will judge when executing the propagate event task Whether to Executor Thread executor If you give it to me It is executed by thread pool , Otherwise, synchronous execution

  1. @Override
  2. public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
  3. ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
  4. Executor executor = getTaskExecutor();
  5. for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
  6. if (executor != null) {
  7. executor.execute(() -> invokeListener(listener, event));
  8. }
  9. else {
  10. invokeListener(listener, event);
  11. }
  12. }
  13. }

So the easiest way is through Build yourself SimpleApplicationEventMulticaster bean Then, we pass in an actuator to implement the listener's asynchronous execution

  1. @Configuration
  2. public class AsyncEventConfig {
  3. @Bean(name = "applicationEventMulticaster")
  4. public ApplicationEventMulticaster getSimpleApplicationEventMultCaster() {
  5. SimpleApplicationEventMulticaster simpleApplicationEventMulticaster = new SimpleApplicationEventMulticaster();
  6. simpleApplicationEventMulticaster.setTaskExecutor(new SimpleAsyncTaskExecutor());
  7. return simpleApplicationEventMulticaster;
  8. }
  9. }

After configuration Running results

  1. Order complete ...
  2. End of the process
  3. Send order success email
  4. It's time for Lao Wang to start
  5. Exception in thread "SimpleAsyncTaskExecutor-19" java.lang.ArithmeticException: / by zero
  6. at com.sun.event.demo.CarService.sendCarInfo(CarService.java:15)
  7. at com.sun.event.demo.CarService.onApplicationEvent(CarService.java:11)
  8. at com.sun.event.demo.CarService.onApplicationEvent(CarService.java:6)
  9. at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172)
  10. at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165)
  11. at org.springframework.context.event.SimpleApplicationEventMulticaster.lambda$multicastEvent$0(SimpleApplicationEventMulticaster.java:136)
  12. at java.lang.Thread.run(Thread.java:745)

You can see that even Lao Wang I'm sick. Yes Texting Monitoring has no effect and The main stream doesn't have to wait Email and departure completed

SmartApplicationListener Achieve orderly monitoring

Between multiple listeners, if the sequence of execution needs to be maintained between multiple listeners, it can be implemented by ApplicationListener Subclasses of SmartApplicationListener To achieve

Put the top two The listener is modified to

Departure monitor

  1. @Service
  2. public class CarServiceWithOrder implements SmartApplicationListener {
  3. @Override
  4. public void onApplicationEvent(ApplicationEvent event) {
  5. System.out.println(" It's off The order number " + event.getSource());
  6. }
  7. @Override
  8. public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
  9. return eventType == OrderSuccessEvent.class;
  10. }
  11. @Override
  12. public boolean supportsSourceType(final Class<?> sourceType) {
  13. return sourceType == String.class;
  14. }
  15. @Override
  16. public int getOrder() {
  17. return 2;
  18. }
  19. }

Send SMS monitor

  1. @Service
  2. public class SmsListenerWithOrder implements SmartApplicationListener {
  3. @Override
  4. public void onApplicationEvent(ApplicationEvent event) {
  5. System.out.println(" Send a text message The order number " + event.getSource());
  6. }
  7. @Override
  8. public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
  9. return eventType == OrderSuccessEvent.class;
  10. }
  11. @Override
  12. public boolean supportsSourceType(final Class<?> sourceType) {
  13. return sourceType == String.class;
  14. }
  15. @Override
  16. public int getOrder() {
  17. return 1;
  18. }
  19. }

Final execution result ,order The larger the number, the earlier the execution

  1. Order complete ...
  2. End of the process
  3. It's off The order number 126625874
  4. Send a text message The order number 126625874

Spring Principle of event mechanism

Starting with the method of event Publishing , It's here to launch the event , See how the listener gets the event

  1. // Release Order success event
  2. applicationContext.publishEvent(new OrderSuccessEvent("126625874"));

Can be in AbstractApplicationContext Class sees Concrete perform publishEvent The core logic of the method is mainly in

  1. getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);

in , among getApplicationEventMulticaster() Method returns SimpleApplicationEventMulticaster object You can view the implementation of multicast directly

  1. @Override
  2. public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
  3. // here type Namely com.sun.event.demo.OrderSuccessEvent Type of event
  4. ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
  5. // Determine if there is an injection Thread pool
  6. Executor executor = getTaskExecutor();
  7. // Get all the monitors
  8. for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
  9. // If it's injected into the cable pool It is put into the thread pool and executed asynchronously
  10. if (executor != null) {
  11. executor.execute(() -> invokeListener(listener, event));
  12. }
  13. else {
  14. invokeListener(listener, event);
  15. }
  16. }
  17. }

perform listener To determine whether there is injection Exception handling ErrorHandler When monitoring exception handling

  1. protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
  2. ErrorHandler errorHandler = getErrorHandler();
  3. if (errorHandler != null) {
  4. try {
  5. doInvokeListener(listener, event);
  6. }
  7. catch (Throwable err) {
  8. errorHandler.handleError(err);
  9. }
  10. }
  11. else {
  12. doInvokeListener(listener, event);
  13. }
  14. }

The final Monitor the execution method

  1. private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
  2. try {
  3. listener.onApplicationEvent(event);
  4. }
  5. catch (ClassCastException ex) {
  6. String msg = ex.getMessage();
  7. if (msg == null || matchesClassCastMessage(msg, event.getClass())) {
  8. Log logger = LogFactory.getLog(getClass());
  9. if (logger.isTraceEnabled()) {
  10. logger.trace("Non-matching event type for listener: " + listener, ex);
  11. }
  12. }
  13. else {
  14. throw ex;
  15. }
  16. }
  17. }

Because all custom listeners will implement ApplicationListener Interface so that it can be executed through the method The specific logic in each listener .

There's a very important way to miss it getApplicationListeners(event, type) This method is used to find all the listener class

  1. protected Collection<ApplicationListener<?>> getApplicationListeners(
  2. ApplicationEvent event, ResolvableType eventType) {
  3. // Get the parameters passed in when the event is published
  4. Object source = event.getSource();
  5. Class<?> sourceType = (source != null ? source.getClass() : null);
  6. ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);
  7. // Quick check for existing entry on ConcurrentHashMap...
  8. ListenerRetriever retriever = this.retrieverCache.get(cacheKey);
  9. if (retriever != null) {
  10. return retriever.getApplicationListeners();
  11. }
  12. if (this.beanClassLoader == null ||
  13. (ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) &&
  14. (sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
  15. // this.retrievalMutex For all the spring bean You need to add Heavyweight lock
  16. synchronized (this.retrievalMutex) {
  17. retriever = this.retrieverCache.get(cacheKey);
  18. if (retriever != null) {
  19. return retriever.getApplicationListeners();
  20. }
  21. retriever = new ListenerRetriever(true);
  22. Collection<ApplicationListener<?>> listeners =
  23. retrieveApplicationListeners(eventType, sourceType, retriever);
  24. this.retrieverCache.put(cacheKey, retriever);
  25. return listeners;
  26. }
  27. }
  28. else {
  29. // No ListenerRetriever caching -> no synchronization necessary
  30. return retrieveApplicationListeners(eventType, sourceType, null);
  31. }
  32. }

It's going on debug It was found in the process that ,spring The sequence of events is published during startup , You can also get a general idea of spring Start up process of

  1. org.springframework.boot.context.event.ApplicationStartingEvent--->
  2. org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent -->
  3. org.springframework.boot.context.event.ApplicationContextInitializedEvent -->
  4. org.springframework.boot.context.event.ApplicationPreparedEvent -->
  5. org.springframework.boot.web.servlet.context.ServletWebServerInitializedEvent-->
  6. org.springframework.context.event.ContextRefreshedEvent -->
  7. org.springframework.boot.context.event.ApplicationStartedEvent -->
  8. org.springframework.boot.context.event.ApplicationReadyEvent -->
  9. When asked to come over
  10. com.sun.event.demo.OrderSuccessEvent[source=126625874] -->
  11. org.springframework.web.context.support.ServletRequestHandledEvent -->
版权声明
本文为[Yin Jihuan]所创,转载请带上原文链接,感谢

  1. 【计算机网络 12(1),尚学堂马士兵Java视频教程
  2. 【程序猿历程,史上最全的Java面试题集锦在这里
  3. 【程序猿历程(1),Javaweb视频教程百度云
  4. Notes on MySQL 45 lectures (1-7)
  5. [computer network 12 (1), Shang Xuetang Ma soldier java video tutorial
  6. The most complete collection of Java interview questions in history is here
  7. [process of program ape (1), JavaWeb video tutorial, baidu cloud
  8. Notes on MySQL 45 lectures (1-7)
  9. 精进 Spring Boot 03:Spring Boot 的配置文件和配置管理,以及用三种方式读取配置文件
  10. Refined spring boot 03: spring boot configuration files and configuration management, and reading configuration files in three ways
  11. 精进 Spring Boot 03:Spring Boot 的配置文件和配置管理,以及用三种方式读取配置文件
  12. Refined spring boot 03: spring boot configuration files and configuration management, and reading configuration files in three ways
  13. 【递归,Java传智播客笔记
  14. [recursion, Java intelligence podcast notes
  15. [adhere to painting for 386 days] the beginning of spring of 24 solar terms
  16. K8S系列第八篇(Service、EndPoints以及高可用kubeadm部署)
  17. K8s Series Part 8 (service, endpoints and high availability kubeadm deployment)
  18. 【重识 HTML (3),350道Java面试真题分享
  19. 【重识 HTML (2),Java并发编程必会的多线程你竟然还不会
  20. 【重识 HTML (1),二本Java小菜鸟4面字节跳动被秒成渣渣
  21. [re recognize HTML (3) and share 350 real Java interview questions
  22. [re recognize HTML (2). Multithreading is a must for Java Concurrent Programming. How dare you not
  23. [re recognize HTML (1), two Java rookies' 4-sided bytes beat and become slag in seconds
  24. 造轮子系列之RPC 1:如何从零开始开发RPC框架
  25. RPC 1: how to develop RPC framework from scratch
  26. 造轮子系列之RPC 1:如何从零开始开发RPC框架
  27. RPC 1: how to develop RPC framework from scratch
  28. 一次性捋清楚吧,对乱糟糟的,Spring事务扩展机制
  29. 一文彻底弄懂如何选择抽象类还是接口,连续四年百度Java岗必问面试题
  30. Redis常用命令
  31. 一双拖鞋引发的血案,狂神说Java系列笔记
  32. 一、mysql基础安装
  33. 一位程序员的独白:尽管我一生坎坷,Java框架面试基础
  34. Clear it all at once. For the messy, spring transaction extension mechanism
  35. A thorough understanding of how to choose abstract classes or interfaces, baidu Java post must ask interview questions for four consecutive years
  36. Redis common commands
  37. A pair of slippers triggered the murder, crazy God said java series notes
  38. 1、 MySQL basic installation
  39. Monologue of a programmer: despite my ups and downs in my life, Java framework is the foundation of interview
  40. 【大厂面试】三面三问Spring循环依赖,请一定要把这篇看完(建议收藏)
  41. 一线互联网企业中,springboot入门项目
  42. 一篇文带你入门SSM框架Spring开发,帮你快速拿Offer
  43. 【面试资料】Java全集、微服务、大数据、数据结构与算法、机器学习知识最全总结,283页pdf
  44. 【leetcode刷题】24.数组中重复的数字——Java版
  45. 【leetcode刷题】23.对称二叉树——Java版
  46. 【leetcode刷题】22.二叉树的中序遍历——Java版
  47. 【leetcode刷题】21.三数之和——Java版
  48. 【leetcode刷题】20.最长回文子串——Java版
  49. 【leetcode刷题】19.回文链表——Java版
  50. 【leetcode刷题】18.反转链表——Java版
  51. 【leetcode刷题】17.相交链表——Java&python版
  52. 【leetcode刷题】16.环形链表——Java版
  53. 【leetcode刷题】15.汉明距离——Java版
  54. 【leetcode刷题】14.找到所有数组中消失的数字——Java版
  55. 【leetcode刷题】13.比特位计数——Java版
  56. oracle控制用户权限命令
  57. 三年Java开发,继阿里,鲁班二期Java架构师
  58. Oracle必须要启动的服务
  59. 万字长文!深入剖析HashMap,Java基础笔试题大全带答案
  60. 一问Kafka就心慌?我却凭着这份,图灵学院vip课程百度云