Spring的事件发布机制

港控/mmm° 2022-06-05 05:53 361阅读 0赞

一:Spring的事件发布

ApplicationContext提供了针对Bean的事件传播功能,其中的主角是publishEvent()方法,通过这个方法可以将事件通知给系统内的监听器(需实现ApplicationListener接口)。

ApplicationContext这个接口,是Spring的上下文,通常获取Bean就需要这个接口,这个接口并不是直接继承于BeanFactory,其中最著名的是直接继承了ApplicationPublisher接口,这个接口查看源码可以发现:只有一个方法,那就是主角 void publishEvent(ApplicationEvent event);

Spring提供的基于Aware相关的接口有ApplicationContextAware,ResourceloaderAware,ServletContextAware(注意:Struts2也有这个接口,注意区分),最常用的就这三个,而Spring的事件发布机制需要用到ApplicationContextAware接口。

实现了ApplicationContextAware的Bean,在Bean初始化时将会被注入ApplicationContext实例(因为这个接口里有set(ApplictationContext ctx)方法)

二:有了以上基础,看示例代码:

1.首先创建事件类 TradeEvent










1


2


3


4


5


6


7


8


9


10


11


12


13


14


15


16


17



package  
net.wang.test;


 


import  
org.springframework.context.ApplicationEvent;


 


/*


 
事件Event


 
@author LiuRuoWang


 
/


public  
class  
TradeEvent
extends  
ApplicationEvent{


     


    
public  
TradeEvent(Object source) {


        
super
(source);


        
System.out.println(
“事件:TradeEvent event !!”
);


    
}


     


}






事件必须继承Spring提供的ApplicationEvent抽象类













2
.然后编写 事件的发布者HelloWorld










1


2


3


4


5


6


7


8


9


10


11


12


13


14


15


16


17


18


19


20


21


22


23


24


25


26


27


28


29


30



package  
net.wang.test;


 


import  
org.springframework.context.ApplicationEventPublisher;


import  
org.springframework.context.ApplicationEventPublisherAware;


 


/*


 
事件的发布者


 
@author LiuRuoWang


 
/


public  
class  
HelloWorld
implements  
ApplicationEventPublisherAware{


     


    
private  
String word;


     


    
public  
void  
setWord(String word) {


        
this
.word = word;


    
}


     


    
private  
ApplicationEventPublisher tradeEventPublisher;


     


    
public  
void  
setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {


        
this
.tradeEventPublisher=applicationEventPublisher;


    
}


     


    
public  
void  
say(){


        
System.out.println(
“say:”
+
this
.word);


        
TradeEvent tradeEvent =
new  
TradeEvent(
new  
String(
“HelloWorld!”
));


        
this
.tradeEventPublisher.publishEvent(tradeEvent);


    
}


     


}










1



其中在say()方法里发布了事件










1



3
.最后编写 事件的接收者EventReceiver:










1


2


3


4


5


6


7


8


9


10


11


12


13


14



package  
net.wang.test;


 


import  
org.springframework.context.ApplicationListener;


 


/*


 
事件的接收者


 
@author LiuRuoWang


 
/


public  
class  
EventReceiver
implements  
ApplicationListener<TradeEvent>{


 


    
public  
void  
onApplicationEvent(TradeEvent event) {


        
System.out.println(
“监听到的事件:”
+event.getSource());


    
}


}










1



事件的接收者其实是一个监听器,必须实现ApplicationListener,注意把事件TradeEvent直接写到泛型中










1



4
.applicationContext.xml:










1


2


3


4


5


6


7


8


9


10


11


12


13


14


15


16


17


18


19


20


21



<?xml version=
“1.0”  
encoding=
“GBK”
?>


<beans



    
xmlns:xsi=
http://www.w3.org/2001/XMLSchema-instance


    
xmlns:p=
http://www.springframework.org/schema/p


    
xmlns:aop=
http://www.springframework.org/schema/aop


    
xmlns:tx=
http://www.springframework.org/schema/tx


    
xsi:schemaLocation=”http:
//www.springframework.org/schema/beans


        
http:
//www.springframework.org/schema/beans/spring-beans-3.1.xsd


        
http:
//www.springframework.org/schema/aop


        
http:
//www.springframework.org/schema/aop/spring-aop-3.1.xsd


        
http:
//www.springframework.org/schema/tx


        
http:
//www.springframework.org/schema/tx/spring-tx-3.1.xsd”>


     


    
<bean name=
“helloWrold”  
class
=
“net.wang.test.HelloWorld”
>


        
<property name=
“word”  
value=
“Word!”
/>


    
</bean>


     


    
<bean name=
“eventReceiver”  
class
=
“net.wang.test.EventReceiver”
/>


     


</beans>

注意把事件的接收者写入配置文件中

5.测试Test:










1


2


3


4


5


6


7


8


9


10


11


12



package  
net.wang.test;


 


import  
org.springframework.context.ApplicationContext;


import  
org.springframework.context.support.ClassPathXmlApplicationContext;


 


public  
class  
Test {


    
public  
static  
void  
main(String[] args) {


        
ApplicationContext ctx=
new  
ClassPathXmlApplicationContext(
“applicationContext.xml”
);


        
HelloWorld h = (HelloWorld) ctx.getBean(
“helloWrold”
);


        
h.say();


    
}


}

6.结果显示:

image

结果中已经显示监听到的事件,说明成功。

Spring 中的事件监听的实现

这里我们不讨论事件监听的机制的原理,我们只讨论如何在项目中实现时间监听。
Spring的事件监听是基于观察者模式。设计开发中。如下类与接口是我们必须要使用的。

ApplicationContext

首先我们了解一下ApplicationContext,还记得

  1. ApplicationContext ac=new ClassPathXmlApplicationContext("beans.xml");
  2. 1

ApplicationContext相当于Spring的一个与IOC容器连接的桥梁,通过getBean();方法,我们可以轻松的从IOC容器中获取Bean对象。
因为ApplicationContext是实现ApplicationEventPublisher的。查看ApplicationEventPublisher的源码,我们发现有一方法publishEvent。此方法便是发布事件的方法,即触发事件的方法,通过调用publishEvent方法,注入事件ApplicationEvent的子类,实现事件的触发。

  1. //这个是ApplicationContext类的声明
  2. public interface ApplicationContext extends EnvironmentCapable,ListableBeanFactory,HierarchicalBeanFactory,MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
  3. //...}
  4. 1
  5. 2
  6. //ApplicationEventPublisher源码
  7. public interface ApplicationEventPublisher {
  8. //该类只有这一个方法,用于发布通知,需要事件作为参数
  9. void publishEvent(ApplicationEvent event);
  10. }
  11. 1
  12. 2
  13. 3
  14. 4
  15. 5
  16. 6

说了一大堆,就是想说ApplicationContext的

publicEvent(ApplicationEvent event);

方法是可以用来发布通知,相当于触发事件的事件源。

ApplicationContextAware

ApplicationContextAware类似于ServeletRequestAware,通过让Action实现Aware,使得Action初始化之后便可以获得一些资源,这里我们让Action实现ApplicationContext,使得Action拥有ApplicationContext,Action中拥有ApplicationContext之后就可以调用publicEvent方法进行通知

  1. public interface ApplicationContextAware extends Aware {
  2. void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
  3. }
  4. 1
  5. 2
  6. 3
  7. 4

ApplicationEvent

ApplicationEvent相当于一个事件,所有自定义事件都需要继承这个抽象类。在Eclipse中Ctrl+Shift+H调用类的层次结构列表,可以看到如下
这里写图片描述
Application下抽象子类ApplicationContextEvent的下面有4个已经实现好的事件
ContextClosedEvent(容器关闭时)
ContextRefreshedEvent(容器刷新是)
ContextStartedEvent(容器启动时候)
ContextStoppedEvent(容器停止的时候)
同样,这四个事件都继承了ApplicationEvent,如果我们想自定义事件,也可以通过继承ApplicationEvent来实现
嗯,同样是一句话总结ApplicationEvent就是一个抽象类,创建时间的时候只需要继承它就可以。

ApplicationListener

从名字可以看出来,这是一个监听器。为什么需要监听器呢?监听器是用于接收事件,并触发事件的操作,这样说起来可能有点费解,简单的说就是,Listener是监听ApplicationContext.publishEvent,方法的调用,一旦调用publishEvent,就会执行ApplicaitonListener中的方法,下面这个是ApplicationContext的源码。

  1. public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
  2. /**
  3. * publishEvent触发该方方法
  4. * 可以在该方法中写各种业务逻辑
  5. */
  6. void onApplicationEvent(E event);
  7. }
  8. 1
  9. 2
  10. 3
  11. 4
  12. 5
  13. 6
  14. 7
  15. 8
  16. 9

这里是实际代码实现的过程

  1. 新建一个MyEvent的类,继承ApplicationEvent抽象类

    package cn.blueboz.elec.event;

    import org.springframework.context.ApplicationEvent;

    public class MyEvent extends ApplicationEvent {

    1. //存放构造器送入的值
    2. private String msg;
    3. //构造器参数可以随意设置,这里为了方便调试,设置为字符串
    4. public MyEvent(String msg) {
    5. super(msg);
    6. this.msg=msg;
    7. }
    8. //自定义一个方法,这个方法也可以随意写,这里也是测试用
    9. public void myevent(){
    10. System.out.println("********My event**************");
    11. System.out.println(msg);
    12. System.out.println("*******************************");
    13. }

    }

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 17
  18. 18
  19. 19

2.新建一个监听器MyListener

  1. package cn.blueboz.elec.listener;
  2. import org.springframework.context.ApplicationEvent;
  3. import org.springframework.context.ApplicationListener;
  4. import org.springframework.context.event.ContextClosedEvent;
  5. import org.springframework.context.event.ContextRefreshedEvent;
  6. import org.springframework.context.event.ContextStartedEvent;
  7. import org.springframework.context.event.ContextStoppedEvent;
  8. import org.springframework.stereotype.Service;
  9. import cn.blueboz.elec.event.HisEvent;
  10. import cn.blueboz.elec.event.MyEvent;
  11. //注入IOC容器中
  12. @Service("myListener")
  13. public class MyListener implements ApplicationListener<ApplicationEvent> {
  14. //调用ApplicationContext.publishEvent方法时会触发执行该方法
  15. @Override
  16. public void onApplicationEvent(ApplicationEvent event) {
  17. //判断事件为MyEvent时候执行
  18. if(event instanceof MyEvent){
  19. //强制转换
  20. MyEvent evt=(MyEvent) event;
  21. //执行自定义事件中的自定义方法
  22. evt.myevent();
  23. }
  24. //如果容器关闭时,触发
  25. if(event instanceof ContextClosedEvent){
  26. ContextClosedEvent cce=(ContextClosedEvent) event;
  27. System.out.println("#####################");
  28. System.out.println("容器关闭");
  29. System.out.println(cce);
  30. System.out.println("#####################");
  31. }
  32. //容器刷新时候触发
  33. if(event instanceof ContextRefreshedEvent){
  34. ContextRefreshedEvent cre=(ContextRefreshedEvent) event;
  35. System.out.println("#####################");
  36. System.out.println("容器刷新");
  37. System.out.println(cre);
  38. System.out.println("#####################");
  39. }
  40. //容器启动的时候触发
  41. if(event instanceof ContextStartedEvent){
  42. ContextStartedEvent cse=(ContextStartedEvent) event;
  43. System.out.println("#####################");
  44. System.out.println("容器启动");
  45. System.out.println(cse);
  46. System.out.println("#####################");
  47. }
  48. //容器停止时候触发
  49. if(event instanceof ContextStoppedEvent){
  50. ContextStoppedEvent cse=(ContextStoppedEvent) event;
  51. System.out.println("#####################");
  52. System.out.println("容器停止");
  53. System.out.println(cse);
  54. System.out.println("#####################");
  55. }
  56. }
  57. }
  58. 1
  59. 2
  60. 3
  61. 4
  62. 5
  63. 6
  64. 7
  65. 8
  66. 9
  67. 10
  68. 11
  69. 12
  70. 13
  71. 14
  72. 15
  73. 16
  74. 17
  75. 18
  76. 19
  77. 20
  78. 21
  79. 22
  80. 23
  81. 24
  82. 25
  83. 26
  84. 27
  85. 28
  86. 29
  87. 30
  88. 31
  89. 32
  90. 33
  91. 34
  92. 35
  93. 36
  94. 37
  95. 38
  96. 39
  97. 40
  98. 41
  99. 42
  100. 43
  101. 44
  102. 45
  103. 46
  104. 47
  105. 48
  106. 49
  107. 50
  108. 51
  109. 52
  110. 53
  111. 54
  112. 55
  113. 56
  114. 57
  115. 58
  116. 59
  117. 60
  118. 61

3.最后,我们要再Action中发布通知publishEvent;

  1. package cn.blueboz.elec.web.action;
  2. import javax.annotation.Resource;
  3. import org.apache.struts2.interceptor.ServletRequestAware;
  4. import org.springframework.beans.BeansException;
  5. import org.springframework.context.ApplicationContext;
  6. import org.springframework.context.ApplicationContextAware;
  7. import org.springframework.context.annotation.Scope;
  8. import org.springframework.context.support.ClassPathXmlApplicationContext;
  9. import org.springframework.stereotype.Controller;
  10. import cn.blueboz.elec.domain.ElecText;
  11. import cn.blueboz.elec.event.MyEvent;
  12. import cn.blueboz.elec.service.IElecTextService;
  13. //指定为prototype原型,对应每一个请求都会产生一个实例对象
  14. @Controller("elecTextAction")
  15. @Scope(value="prototype")
  16. public class ElecTextAction extends BaseAction<ElecText> implements ApplicationContextAware,ServletRequestAware {
  17. //首先获得模型驱动对象
  18. ElecText elecText=getModel();
  19. protected ApplicationContext applicationContext;
  20. //注入Service指定从Spring的IOC容器中注入的对象的名称
  21. @Resource(name=IElecTextService.SERVICE_NAME)
  22. private IElecTextService elecTextService;
  23. public String save(){
  24. //从表单中传送过来的实例对象
  25. elecTextService.saveElecText(elecText);
  26. /**
  27. * 请关注这一行代码,在页面中访问时候调用save方法
  28. * save方法中执行了publishEvent方法发布通知。
  29. * 传入参数是自定义事件MyEvent
  30. */
  31. applicationContext.publishEvent(new MyEvent("在Action中的save方法Public了Event"));
  32. return "save";
  33. }
  34. @Override
  35. public void setApplicationContext(ApplicationContext applicationContext)
  36. throws BeansException {
  37. this.applicationContext=applicationContext;
  38. }
  39. }
  40. 1
  41. 2
  42. 3
  43. 4
  44. 5
  45. 6
  46. 7
  47. 8
  48. 9
  49. 10
  50. 11
  51. 12
  52. 13
  53. 14
  54. 15
  55. 16
  56. 17
  57. 18
  58. 19
  59. 20
  60. 21
  61. 22
  62. 23
  63. 24
  64. 25
  65. 26
  66. 27
  67. 28
  68. 29
  69. 30
  70. 31
  71. 32
  72. 33
  73. 34
  74. 35
  75. 36
  76. 37
  77. 38
  78. 39
  79. 40
  80. 41
  81. 42
  82. 43
  83. 44
  84. 45
  85. 46

4.启动Tomcat时候命令行输出

  1. #####################
  2. 容器刷新
  3. org.springframework.context.event.ContextRefreshedEvent[source=Root WebApplicationContext: startup date [Fri Nov 20 17:12:47 CST 2015]; root of context hierarchy]
  4. #####################
  5. 1
  6. 2
  7. 3
  8. 4

访问页面的时候,命令行输出,可以看出,触发了MyEvent方法输出。

  1. ********My event**************
  2. Action中的save方法PublicEvent
  3. *******************************

发表评论

表情:
评论列表 (有 0 条评论,361人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Spring 事件发布

    前言 事件发布是 Spring 框架中最容易被忽视的功能之一,但实际上它是一个很有用的功能。使用事件机制可以将同一个应用系统内互相耦合的代码进行解耦,并且可以将事件与 S

    相关 Spring事件机制

    1.背景 事件驱动的一个常见形式便是发布-订阅模式。在跨进程的通信间,我们通常采用引入 MQ (消息队列) 来实现消息的发布和订阅。目前主流应用的架构中,均采用消息的发布

    相关 spring事件机制

    事件驱动模型简介 事件驱动模型也就是我们常说的观察者,或者发布-订阅模型;理解它的几个关键点: 1. 首先是一种对象间的一对多的关系;最简单的如交通信号灯,信号灯是目

    相关 spring事件机制

    事件驱动模型简介 事件驱动模型也就是我们常说的观察者,或者发布-订阅模型;理解它的几个关键点: 首先是一种对象间的一对多的关系;最简单的如交通信号灯,信号灯是目标