spring中自定義Event事件的使用和淺析
在我目前接觸的項目中,用到了許多spring相關的技術,框架層麵的spring、spring mvc就不說了,細節上的功能也用了不少,如schedule定時任務、Filter過濾器、 interceptor攔截器等等,而這一篇我要說的是spring Event自定義事件,目前的項目中似乎沒怎麼用,但是這一項技術貌似還蠻重要,所以也不能不掌握。
對於事件驅動模型的解釋和理解,我覺得有一篇博客裏說的非常好,尤其是在解釋這個關係的時候,舉的交通信號燈的例子非常貼切,這裏就引用做一個簡單的解釋:
事件驅動模型也就是我們常說的觀察者,或者發布-訂閱模型;理解它的幾個關鍵點:
1. 首先是一種對象間的一對多的關係;最簡單的如交通信號燈,信號燈是目標(一方),行人注視著信號燈(多方);
2. 當目標發送改變(發布),觀察者(訂閱者)就可以接收到改變;
3. 觀察者如何處理(如行人如何走,是快走/慢走/不走,目標不會管的),目標無需幹涉;所以就鬆散耦合了它們之間的關係。
引用的原文地址:https://xls9577087.iteye.com/blog/2121752(這篇文章中還講解了有序監聽和無序監聽、異步事件等等,有興趣的也可以去那裏了解學習一下)
當我們對事件驅動有一個簡單的理解之後,就能大概知道它應該什麼時候用,然後再來研究它該怎麼用,單一的文字或許不太容易解釋,還是先把代碼弄上來,然後再結合起來解釋。
首先自定義一個事件,需要繼承ApplicationEvent類,相當於安裝了一個沒有通電,沒有燈光的信號燈,需要具有信號燈的基本特征。
package springTest5;
import org.springframework.context.ApplicationEvent;
public class EventTest extends ApplicationEvent {
private static final long serialVersionUID = 1L;
private String message;
public EventTest(Object source, String message) {
super(source);
this.message = message;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
然後再創建一個監聽類,相當於行人(不管是否使用交通工具),需要實現ApplicationListener接口,並且重寫onApplicationEvent方法,可以理解成這個行人需要看信號燈,並且能理解信號燈的意思才行。否則不看信號燈跟沒有信號燈沒有區別,看了不理解也沒用。
package springTest5;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
@Component
public class ListenerTest1 implements ApplicationListener<EventTest> {
public void onApplicationEvent(EventTest event) {
System.out.println("test1:" + event.getMessage());
}
}
這裏的注解就隻是簡單的聲明一個bean,應該不需要太多的解釋。
那麼第三步自然是需要一個控製信號燈變化的東西,相當於是給他接好電線,給他一個正常變換紅黃綠的程序和電路。
package springTest5;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
@Component
public class EventPbulish {
@Autowired
ApplicationContext context;
public void publish(String message) {
context.publishEvent(new EventTest(this, message));
}
}
到這裏,實際上已經寫完了,但是呢很明顯,我們合理沒有配置文件,那麼這裏的注解也是不能被spring使用的,純粹是個擺設,所以還需要一個配置文件,或者說相當於配置文件的配置類,要讓相關的類生效。
package springTest5;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan("springTest5")
public class ConfigTest {
}
對於這裏兩個注解的意思,前幾天的文章曾多次解釋,因此這裏便不說了,真有不懂得,可以翻一下前幾天的博客。
走到這裏,相當於我們創建好了一個可以正常運行的信號燈,創建好了一個正常的行人,但是呢都是靜止不動的,我們需要讓他動起來,也就是main方法的測試,相當於讓行人開始看燈,讓電路開始通電。
package springTest5;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MainTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConfigTest.class);
EventPbulish eventPbulish = context.getBean(EventPbulish.class);
eventPbulish.publish("zhangsan");
context.close();
}
}
根據上邊的代碼可以知道代表這個事件正常運行了,也就是信號燈正常發出了光,行人正常接收到了消息。
那麼還有一個細節在於,信號燈對行人,是一對多的關係,那麼這裏的事件是否確實如此呢?為了驗證,我便再寫一個監聽,再new一個行人,其他一切不變。
package springTest5;
import org.springframework.context.ApplicationListener;
@Component
public class ListenerTest2 implements ApplicationListener<EventTest> {
public void onApplicationEvent(EventTest event) {
System.out.println("tst2:" + event.getMessage());
}
}
很明顯,兩個行人都正常接收到了信號燈的信號。
那麼,根據上邊一開始的解釋,再加上之後的例子,我們應該大概知道了這裏的一個完整的事件包含些什麼內容:即要有目標,也就是一個事件;還要有接受目標信息的對象,也就是一種監聽;還要有改變或者說發出信息的一個控製體。
到這裏基本上就算是完工了,這算是最簡單的實現方式,像一些細節上的,把配置類改配置文件等等,都可以自己適當的變型。
在結尾處,結合@PropertySource注解,我把上邊的列子做了個小小的變型,模擬一個找人的廣播,喊一個人的名字,然後聽到的人進行相應的回答。
這個例子和上邊的不同在於,增加了一個properties配置,用來給接收對象初始化名稱,順便練習@PropertySource注解。
name1=zhangsan
name2=lisi
然後修改了一下監聽類,從properties文件中獲取自己的名字。
package springTest5;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.stereotype.Component;
@Component
@PropertySource("test.properties")
public class ListenerTest1 implements ApplicationListener<EventTest> {
@Value("${name1}")
String name;
public void onApplicationEvent(EventTest event) {
if (event.getMessage().equals(name)) {
System.out.println("you need to find " + event.getMessage() + ",yes, I'am " + name);
} else {
System.out
.println("you need to find " + event.getMessage() + ",but I'am not " + event.getMessage() + ",I'am " + name);
}
}
@Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigure() {
return new PropertySourcesPlaceholderConfigurer();
}
}
其中propertyConfigure()是必須的,隻有寫了這個才能正產剛從properties中獲取數據,但是經過測試,這一段代碼隻需要有一個地方出現了就可以,因此第二個監聽類就不用再寫:
package springTest5;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
@Component
@PropertySource("test.properties")
public class ListenerTest2 implements ApplicationListener<EventTest> {
@Value("${name2}")
String name;
public void onApplicationEvent(EventTest event) {
if (event.getMessage().equals(name)) {
System.out.println("you need to find " + event.getMessage() + ",yes, I'am" + name);
} else {
System.out
.println("you need to find " + event.getMessage() + ",but I'am not " + event.getMessage() + ",I'am " + name);
}
}
}
這個例子已經打包上傳,有興趣的可以看看,其中隻有springTest5這個包裏是這個例子,其他幾個包裏的內容則是對前幾天的博文中對profile等技術點的練習,有興趣的也可以看看。
https://download.csdn.net/detail/tuzongxun/9711034
最後更新:2017-09-11 18:03:28