觀察者模式的本質是“定義對象之間的一對多依賴關系,以便當一個對象改變狀態時,其所有依賴關系都會得到通知并自動更新。” GoF。 觀察者模式是發布/訂閱模式的子集,它允許許多觀察者對象查看事件。
可以在不同的情況下使用此模式,但總而言之,可以說觀察者模式可以在對象應該能夠將消息通知其他對象并且您不希望這些對象緊密耦合時應用。 就我而言,當異步事件應通知一個或多個圖形組件時,我使用了這種模式。
可以使用臨時解決方案或使用java.util.Observer/Observable類來實現此模式。 但是我的項目總是用Spring開發的,無論是Web還是桌面應用 。 因此,在當前文章中,我將解釋如何使用Spring實現Observer模式。
手扶
Spring ApplicationContext中的事件處理是通過ApplicationEvent類和ApplicationListener接口提供的。 如果將實現ApplicationListener接口的bean部署到上下文中,則每次將ApplicationEvent發布到容器時, ApplicationListener都會接收到它。
Spring帶有內置事件,例如ContextStartedEvent , ContextStoppedEvent ,但是您也可以創建自己的自定義事件。
為了開發自己的事件,需要三個類, 觀察者角色, 可觀察角色和事件 。 觀察者是那些接收事件并且必須實現ApplicationListener類的人。 可觀察類負責發布事件,并且必須實現ApplicationEventPublisherAware 。 最后, 事件類必須擴展ApplicationEvent 。
編碼
我要實現的是Observer模式的Wikipedia示例( http://en.wikipedia.org/wiki/Observer_pattern#Example ),但是使用Spring Events而不是Observer / Observable Java類。 該示例是一個基本的發布/訂閱示例,其中一個String消息從一個模塊發送到另一個模塊。
讓我們創建MessageEvent 。 此事件包含一個String,它表示我們要發送的消息。 這是一個從ApplicationEvent擴展的簡單類。
public class MessageEvent extends ApplicationEvent {*** *private static final long serialVersionUID = 5743058377815147529L;private String message;public MessageEvent(Object source, String message) {super(source);this.message = message;}@Overridepublic String toString() {StringBuilder builder = new StringBuilder();builder.append('MessageEvent [message=').append(message).append(']');return builder.toString();}}
下一個類是Observable類。 此類必須實現ApplicationEventPublisherAware 。 此接口使用ApplicationEventPublisher作為參數定義了一個setter方法。 此參數用于發布事件。
在當前的實現中,該代碼還實現了Runnable接口,因此用戶可以從控制臺輸入中進行創建,
public class EventSource implements Runnable, ApplicationEventPublisherAware {private ApplicationEventPublisher applicationEventPublisher = null;public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {this.applicationEventPublisher = applicationEventPublisher;}public void run() {final InputStreamReader isr = new InputStreamReader(System.in);final BufferedReader br = new BufferedReader(isr);while (true) {try {String response = br.readLine();System.out.println(Thread.currentThread().getName());this.applicationEventPublisher.publishEvent(new MessageEvent(this, response));} catch (IOException e) {e.printStackTrace();}}}}
Observer類甚至更簡單。 實現ApplicationListener接口。 發布事件時將調用onApplicationEvent方法。 看到它是一個通用接口,因此不需要強制轉換。 這不同于java.util.Observer類。
public class ResponseHandler implements ApplicationListener<MessageEvent> {public void onApplicationEvent(MessageEvent messageEvent) {System.out.println(Thread.currentThread().getName());System.out.println(messageEvent);}}
在應用程序上下文文件中,您同時注冊了ApplicationListener和ApplicationEventPublisherAware Bean。
最后是一個主類來測試系統。 創建一個線程以執行多個異步事件。
public class MyApp {public static void main(String args[]) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext('classpath:META-INFspringapp-context.xml');EventSource eventSource = applicationContext.getBean('eventSource', EventSource.class);Thread thread = new Thread(eventSource);thread.start();}}
因此,啟動程序并編寫一些內容以進行控制臺。 您將看到類似以下內容:
你好 Thread-0 Thread-0 MessageEvent [message = hello]
我輸入了“ hello ”消息,并打印了事件發布者的線程名 。 然后發送事件并打印處理程序線程名稱 。 最后顯示接收到的事件。 有一件事情應該引起您的注意。 發送者( Observable )和接收者( Observer )都在同一線程中執行; 默認情況下,事件偵聽器同步接收事件。 這意味著publishEvent()方法將阻塞,直到所有偵聽器都已完成對事件的處理為止。 這種方法有很多優點(例如,重用事務上下文等),但是在某些情況下,您希望每個事件都在新線程中執行, Spring也支持此策略。
在Spring中 ,負責事件管理的類是SimpleApplicationEventMulticaster 。 此類將所有事件多播到所有注冊的偵聽器,讓偵聽器忽略它們不感興趣的事件。默認行為是在調用線程中調用所有偵聽器。
現在,我將解釋如何初始化Spring Event Architecture以及如何進行修改。 默認情況下,當ApplicationContext為 啟動后,它將調用initApplicationEventMulticaster方法。 此方法驗證是否存在與類型ApplicationEventMulticaster的ID applicationEventMulticaster的bean。 如果是這樣,則使用已定義的ApplicationEventMulticaster ,否則,將創建具有默認配置的新SimpleApplicationEventMulticaster 。
SimpleApplicationEventMulticaster具有可用于指定哪些java.util.concurrent.Executor將執行事件setTaskExecutor。 因此,如果您希望每個事件在不同的線程中執行,那么一個好的方法是使用ThreadPoolExecutor 。 如上一段所述,現在我們必須顯式定義SimpleApplicationEventMulticaster而不是 使用默認的。 讓我們實現:
<beans xmlns='http:www.springframework.orgschemabeans' xmlns:xsi='http:www.w3.org2001XMLSchema-instance' xmlns:context='http:www.springframework.orgschemacontext' xmlns:task='http:www.springframework.orgschematask' xsi:schemaLocation='http:www.springframework.orgschematask http:www.springframework.orgschemataskspring-task-3.0.xsd http:www.springframework.orgschemabeans http:www.springframework.orgschemabeansspring-beans-3.0.xsd http:www.springframework.orgschemacontext http:www.springframework.orgschemacontextspring-context-3.0.xsd'><bean id='eventSource' class='org.asotobu.oo.EventSource' > <bean id='responseHandler' class='org.asotobu.oo.ResponseHandler' > <task:executor id='pool' pool-size='10' > <bean id='applicationEventMulticaster' class='org.springframework.context.event.SimpleApplicationEventMulticaster'><property name='taskExecutor' ref='pool' > <bean><beans>
首先,必須將SimpleApplicationEventMulticaster定義為ID為applicationEventMulticaster的bean。 然后設置任務池,然后我們重新運行主類。 輸出將是:
你好 線程1 池1 MessageEvent [message = hello]
請注意,現在發送方和接收方線程有所不同。
當然,您可以為更復雜的操作創建自己的ApplicationEventMulticaster 。 您只需要實現ApplicationEventMulticaster并使用applicationEventMulticaster bean名稱定義它,事件將根據您自己的策略執行。
希望現在您的Spring桌面應用程序可以充分利用Spring事件來分隔模塊。
下載代碼。
參考:來自JCG合作伙伴 Alex Soto的Spring Events觀察者模式,來自One Jar To Rule All All博客。
翻譯自: https://www.javacodegeeks.com/2012/08/observer-pattern-with-spring-events.html