1. 程式人生 > >觀察者模式實際應用場景-----Spring事件機制

觀察者模式實際應用場景-----Spring事件機制

以下虛擬碼是一個儲存訂單的功能,並會發送簡訊訊息:

/**
* Author heling on 2019/1/9
*/
@Service
public class OrderServiceImpl implements OrderService {

    @Override
    public void saveOrder() {
        //1.建立訂單
        System.out.println("訂單建立成功");
        //2.傳送簡訊
        System.out.println("恭喜您訂單建立成功!----by sms");
    }
}

現有新需求:需要加一個微信通知的功能,程式碼如下:

/**
* Author heling on 2019/1/9
*/
@Service
public class OrderServiceImpl implements OrderService {

    @Override
    public void saveOrder() {
        //1.建立訂單
        System.out.println("訂單建立成功");
        //2.傳送簡訊
        System.out.println("恭喜您訂單建立成功!----by sms");
        //新需求:微信通知
        // 3.傳送微信
        System.out.println("恭喜您訂單建立成功!----by wechat");
    }
}

存在問題:每次建立訂單需要加新功能(如新的通知方式),則要修改原有的類,難以維護。

違背設計模式的原則

1.單一職責:訂單儲存功能,雜糅了訊息通知這些功能

2.開閉原則:對拓展開放,對修改關閉

 

優化方案:使用觀察者模式,使建立訂單和訊息通知進行分離,低耦合。可以選擇訊息佇列,spring事件機制等,本文選擇Spring事件機制。

 

改造開始:

1.建立事件

package com.pengshu.magicwallet.admin.test;

import org.springframework.context.ApplicationEvent;

import java.util.List;

/**
* Author heling on 2019/1/9
* 訂單建立活動事件
*/
public class OrderCreateEvent extends ApplicationEvent {

    private String name;

    //訊息引數
    private List<String> contentList;

    public OrderCreateEvent(Object source, String name, List<String> contentList) {
        super(source);
        this.name = name;
        this.contentList = contentList;
    }

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public List<String> getContentList() {
        return contentList;
    }
    public void setContentList(List<String> contentList) {
        this.contentList = contentList;
    }
}

2.監聽器

package com.pengshu.magicwallet.admin.test;

import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

/**
* Author heling on 2019/1/9
* 簡訊監聽器
* ApplicationListener是無序的
*/
@Component
public class SmsListener implements ApplicationListener<OrderCreateEvent> {

    @Override
    public void onApplicationEvent(OrderCreateEvent event) {
        //傳送簡訊
        System.out.println(event.getContentList().get(0) + ",您的訂單:" + event.getContentList().get(1) + "建立成功! ----by sms");

    }
}
package com.pengshu.magicwallet.admin.test;

import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

/**
* Author heling on 2019/1/9
* 微信監聽器
*/
@Component
public class WechatListener implements ApplicationListener<OrderCreateEvent> {

    @Override
    public void onApplicationEvent(OrderCreateEvent event) {
        //傳送微信
        System.out.println(event.getContentList().get(0) + ",您的訂單:" + event.getContentList().get(1) + "建立成功! ----by wechat");

    }
}

3.事件釋出

package com.pengshu.magicwallet.admin.test;

import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;

/**
* Author heling on 2019/1/9
*/
@Service
public class OrderServiceImpl implements OrderService {

    @Resource
    private ApplicationContext applicationContext;

    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;

    @Override
    public void saveOrder() {
        //1.建立訂單
        System.out.println("訂單建立成功");
        //2.釋出事件
        ArrayList<String> contentList = new ArrayList<>();
        contentList.add("heling");
        contentList.add("123456789");
        OrderCreateEvent orderCreateEvent = new OrderCreateEvent(this, "訂單建立", contentList);
        applicationContext.publishEvent(orderCreateEvent);//ApplicationContext是我們的事件容器上層,我們釋出事件,也可以通過此容器完成釋出
        //applicationEventPublisher.publishEvent(orderCreateEvent);//也可以
        System.out.println("finished!");
    }
}

列印結果:

訂單建立成功

heling,您的訂單:123456789建立成功! ----by sms

heling,您的訂單:123456789建立成功! ----by wechat

finished!

如何非同步執行監聽器?

1.springboot開啟事件非同步設定

package com.pengshu.magicwallet;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.PropertySource;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@SpringBootApplication
@MapperScan("com.pengshu.magicwallet.mapper")
@PropertySource("classpath:authority.properties")
@EnableTransactionManagement
@EnableAsync //開啟spring事件非同步設定,加@Async註解
public class MagicWalletAdminApplication {
    public static void main(String[] args) {
        SpringApplication.run(MagicWalletAdminApplication.class, args);
    }
}

 

2.監聽器類或方法新增@Async註解

列印結果:

訂單建立成功

finished!

heling,您的訂單:123456789建立成功! ----by sms

heling,您的訂單:123456789建立成功! ----by wechat

如何制定監聽器執行順序?

package com.pengshu.magicwallet.admin.test;

import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.SmartApplicationListener;
import org.springframework.stereotype.Component;

/**
* Author heling on 2019/1/9
* 微信監聽器
* SmartApplicationListener可以設定順序等
*/
@Component
public class WechatListener implements SmartApplicationListener {

    //設定監聽優先順序
    @Override
    public int getOrder() {
        return 1;
    }

    //監聽器智慧所在之一,能夠根據事件型別動態監聽
    @Override
    public boolean supportsEventType(Class<? extends ApplicationEvent> aClass) {
        return aClass == OrderCreateEvent.class;
    }

    //監聽器智慧所在之二,能夠根據事件釋出者型別動態監聽
    @Override
    public boolean supportsSourceType(Class<?> aClass) {
        return aClass == OrderServiceImpl.class;
    }

    @Override
    public void onApplicationEvent(ApplicationEvent applicationEvent) {
        OrderCreateEvent event = (OrderCreateEvent) applicationEvent;
        //傳送微信
        System.out.println(event.getContentList().get(0) + ",您的訂單:" + event.getContentList().get(1) + "建立成功! ----by wechat");

    }

//    @Override
//    @Async
//    public void onApplicationEvent(OrderCreateEvent event) {
//
//        //傳送微信
//        System.out.println(event.getContentList().get(0) + ",您的訂單:" + event.getContentList().get(1) + "建立成功! ----by wechat");
//
//    }
}
package com.pengshu.magicwallet.admin.test;

import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.SmartApplicationListener;
import org.springframework.stereotype.Component;

/**
* Author heling on 2019/1/9
* 簡訊監聽器
*/
@Component
public class SmsListener implements SmartApplicationListener {

    @Override
    public int getOrder() {
        return 2;
    }

    @Override
    public boolean supportsEventType(Class<? extends ApplicationEvent> aClass) {
        return aClass == OrderCreateEvent.class;
    }

    @Override
    public boolean supportsSourceType(Class<?> aClass) {
        return aClass == OrderServiceImpl.class;
    }

    @Override
    public void onApplicationEvent(ApplicationEvent applicationEvent) {
        OrderCreateEvent event = (OrderCreateEvent) applicationEvent;
        //傳送簡訊
        System.out.println(event.getContentList().get(0) + ",您的訂單:" + event.getContentList().get(1) + "建立成功! ----by sms");

    }

//    @Override
//    @Async
//    public void onApplicationEvent(OrderCreateEvent event) {
//
//        //傳送簡訊
//        System.out.println(event.getContentList().get(0) + ",您的訂單:" + event.getContentList().get(1) + "建立成功! ----by sms");
//
//    }
}

 

列印結果:

訂單建立成功

heling,您的訂單:123456789建立成功! ----by wechat

heling,您的訂單:123456789建立成功! ----by sms

finished!

 

在實現了SmartApplicationListener的監聽器中,我們通過重寫GetOrder方法來修改不同監聽器的順序,優先順序越小,則越先被呼叫。通過配置不同的優先順序,且讓監聽器之間阻塞呼叫。我們就能實現流水線式的有序事件呼叫,這在實際應用場景中還是蠻有意義的