观察者模式与spring guava事件

原创
02/06 15:20
阅读数 266

1. 观察者模式

观察者模式,也叫发布/订阅(Publish/Subscribe)模式,观察者模式比较简单,但是非常常用,当然也非常实用。

观察者模式最大的好处是,当业务逻辑变得复杂之后,通过观察者模式可以减轻耦合。

举个简单的例子,一个简单的用户充值事件,当业务复杂之后,除了处理订单之外,可能还需要送券、奖励积分金币、提示VIP等级、送抽奖等激励活动、处理邀请码、发送重要事件记录日志、处理重要事件异常操作等等。

如果这些逻辑都放在“用户充值”这个流程之中,那肯定逻辑耦合爆炸。所以,一般不这么干。

如果用户规模大,做了服务拆分,那就走消息系统,这本质上还是观察者模式。

如果没有做服务拆分,像guava的EventBus这样的观察者模式组件就派上用场了。

组件 说明
Subject 抽象主题、被观察者,一般是事件,例如用户充值、配置变化、VIP等级提升等事件
ConcreteSubject 主要提供观察者注册、取消,以及当事件发送时,回调已经注册的观察者的接口
Observer 抽象观察者
ConcrereObserver 具体观察者,在具体事件、如用户充值这个事情发送之后要执行的具体逻辑

2. Guava EventBus

首先,可以先定义一些自定义事件,例如用户充值:

import vip.mycollege.tool.guava.bean.ChargeBean;
import vip.mycollege.tool.guava.bean.UserBean;

import java.io.Serializable;

public class ChargeEvent implements Serializable{

    private static final long serialVersionUID = -8775590840527884827L;

    private UserBean user;

    private ChargeBean charge;

    public UserBean getUser() {
        return user;
    }

    public void setUser(UserBean user) {
        this.user = user;
    }

    public ChargeBean getCharge() {
        return charge;
    }

    public void setCharge(ChargeBean charge) {
        this.charge = charge;
    }
}

然后,添加观察者,也就是当事件发生时候,需要执行的逻辑:

import com.google.common.eventbus.Subscribe;
import vip.mycollege.tool.guava.event.ChargeEvent;

import java.util.concurrent.TimeUnit;

public class CommonListener {

    @Subscribe
    public void dealChargeGold(ChargeEvent event){
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("处理gold");
    }

    @Subscribe
    public void dealChargeLevel(ChargeEvent event){
        System.out.println("处理level");
    }
}

guava中的观察者非常简单,不需要实现什么接口,只需要添加@Subscribe方法注解就可以了,方法的参数就是要观察的具体事件。

接下来需要Subject,在guava中也非常简单:

import com.google.common.eventbus.AsyncEventBus;
import com.google.common.eventbus.EventBus;
import vip.mycollege.tool.guava.event.ChargeEvent;
import vip.mycollege.tool.guava.listener.CommonListener;

import java.util.concurrent.Executors;

public class EventBusStart {
    public static void main(String[] args) {
        EventBus eventBus = new EventBus();
        eventBus.register(new CommonListener());
        eventBus.post(new ChargeEvent());

        AsyncEventBus asyncEventBus = new AsyncEventBus(Executors.newCachedThreadPool());
        asyncEventBus.register(new CommonListener());
        asyncEventBus.post(new ChargeEvent());
        System.out.println("main");
    }
}

EventBus是同步事件,它使用一个main线程顺序的分发Event。

AsyncEventBus可以指定一个线程池来异步分发Event。使用AsyncEventBus需要注意业务逻辑中应该没有先后顺序约束。

**register方法:**注册时间,具体的就是去扫描参数类中的@Subscribe注解方法。

**post方法:**具体事件发生时候,触发,然后EventBus就去回调register中观察了该事件(参数和该事件类型一致)的@Subscribe方法。

3. Spring Event

除了guava之外,Spring套装也提供了观察者模式的实现,重要的类:

  1. ApplicationEvent:抽象事件,具体事件可以继承这个类
  2. ApplicationListener:观察者,需要实现onApplicationEvent方法,处理具体的事件发生逻辑
  3. ApplicationEventPublisher:发布事件,具体事件发生时,通过它触发

还是一个简单的实例:

import org.springframework.context.ApplicationEvent;

public class LoginEvent extends ApplicationEvent {

    public LoginEvent(Object source) {
        super(source);
    }
}

观察者:

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

import java.util.Map;

@Component
public class LoginEventListener implements ApplicationListener<LoginEvent> {

    @Override
    public void onApplicationEvent(LoginEvent event) {
       Map<String,String> map = (Map<String, String>) event.getSource();
        System.out.println(map.keySet());
        System.out.println(map.values());
    }
}

除了要继承ApplicationListener,还需要添加@Component之类的注解,这样通过注解扫描才能创建这个类。

事件发布:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;

import java.util.HashMap;

@Component
public class LoginLogic {

    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;

    public void login(){
        HashMap<String, String> params = new HashMap<>();
        params.put("name","tim");
        params.put("age","25");
        LoginEvent loginEvent = new LoginEvent(params);
        applicationEventPublisher.publishEvent(loginEvent);
    }

}

Spring在启动的时候,会自动创建一个ApplicationEventPublisher,所以我们可以直接注入就可以了。

当具体的事件发生时候,直接通过ApplicationEventPublisher触发响应的事件就好了,只需要简单的封装一下事件参数。

Spring的观察者模式比guava的稍微复杂一些,如果仅仅是需要观察者模式,建议使用guava,如果是已经使用了Spring,需要观察者模式,可以考虑使用Spring自带的观察者模式实现。

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部