单机环境下优雅地使用事件驱动进行代码解耦

栏目: Android · 发布时间: 6年前

内容简介:虽然现在的各种应用都是集群部署,单机部署的应用越来越少了,但是不可否认的是,市场上还是存在许多单机应用的。本文要介绍的是 Guava 中的 EventBus 的使用。EventBus 处理的事情类似观察者模式,基于事件驱动,观察者们监听自己感兴趣的特定事件,进行相应的处理。本文想要介绍的内容是,在 Spring 环境中优雅地使用 Guava 包中的 EventBus,对我们的代码进行一定程度的解耦。当然,本文不介绍 EventBus 的原理,我所说的优雅也只是我觉得优雅,也许读者有更漂亮的代码,欢迎在评论

虽然现在的各种应用都是集群部署,单机部署的应用越来越少了,但是不可否认的是,市场上还是存在许多单机应用的。本文要介绍的是 Guava 中的 EventBus 的使用。

EventBus 处理的事情类似观察者模式,基于事件驱动,观察者们监听自己感兴趣的特定事件,进行相应的处理。

本文想要介绍的内容是,在 Spring 环境中优雅地使用 Guava 包中的 EventBus,对我们的代码进行一定程度的解耦。当然,本文不介绍 EventBus 的原理,我所说的优雅也只是我觉得优雅,也许读者有更漂亮的代码,欢迎在评论区留言。

Step 0:添加 Guava 依赖

<dependency>
   <groupId>com.google.guava</groupId>
   <artifactId>guava</artifactId>
   <version>22.0</version>
</dependency>
复制代码

作为 java 程序员,如果你还没有使用过 Google Guava,请从现在开始将它加到你的每一个项目中。

Step 1:定义一个注解用于标记 listener

/**
 * 用于标记 listener
 */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface EventBusListener {
}
复制代码

Step 2:定义注册中心

package com.javadoop.eventbus;

import java.util.List;
import java.util.concurrent.Executors;
import javax.annotation.PostConstruct;
import org.springframework.stereotype.Component;
import com.google.common.eventbus.AsyncEventBus;
import com.google.common.eventbus.EventBus;
import com.hongjiev.javadoop.util.SpringContextUtils;

@Component
public class EventBusCenter {

    // 管理同步事件
    private EventBus syncEventBus = new EventBus();

    // 管理异步事件
    private AsyncEventBus asyncEventBus = new AsyncEventBus(Executors.newCachedThreadPool());

    public void postSync(Object event) {
        syncEventBus.post(event);
    }

    public void postAsync(Object event) {
        asyncEventBus.post(event);
    }

    @PostConstruct
    public void init() {

        // 获取所有带有 @EventBusListener 的 bean,将他们注册为监听者
        List<Object> listeners = SpringContextUtils.getBeansWithAnnotation(EventBusListener.class);
        for (Object listener : listeners) {
            asyncEventBus.register(listener);
            syncEventBus.register(listener);
        }
    }
}
复制代码

Step 3:定义各种事件

举个例子,我们定义一个订单创建事件:

package com.javadoop.eventbus.event;

public class OrderCreatedEvent {
    private long orderId;
    private long userId;
    public OrderCreatedEvent(long orderId, long userId) {
        this.setOrderId(orderId);
        this.setUserId(userId);
    }
    // getter、setter
}
复制代码

Step 4:定义事件监听器

首先,类上面需要加我们之前定义的注解:@EventBusListener,然后监听方法需要加注解 @Subscribe,方法参数为具体事件。

package com.javadoop.eventbus.listener;

import org.springframework.stereotype.Component;
import com.google.common.eventbus.Subscribe;
import com.javadoop.eventbus.EventBusListener;
import com.javadoop.eventbus.event.OrderCreatedEvent;

@Component
@EventBusListener
public class OrderChangeListener {

    @Subscribe
    public void created(OrderCreatedEvent event) {
        long orderId = event.getOrderId();
        long userId = event.getUserId();
        // 订单创建成功后的各种操作,如发短信、发邮件等等。
        // 注意,事件可以被订阅多次,也就是说可以有很多方法监听 OrderCreatedEvent 事件,
        // 所以没必要在一个方法中处理发短信、发邮件、更新库存等
    }

    @Subscribe
    public void change(OrderChangeEvent event) {
        // 处理订单变化后的修改
        // 如发送提醒、更新物流等
    }
}
复制代码

Step 5:发送事件

@Service
public class OrderService {

    @Autowired
    private EventBusCenter eventBusCenter;

    public void createOrder() {
        // 处理创建订单
        // ...
        // 发送异步事件
        eventBusCenter.postAsync(new OrderCreatedEvent(1L, 1L));
    }
}
复制代码

总结

EventBus 的好处在于,它将发生事件的代码和事件处理的代码进行了解耦。

比如系统中很多地方都会修改订单,用户可以自己修改、客服也可以修改、甚至可能是团购没成团系统进行的订单修改,所有这些触发订单修改的地方都要发短信、发邮件,假设以后还要增加其他操作,那么需要修改的地方就比较多。

而如果采用事件驱动的话,只要这些地方抛出事件就可以了,后续的维护是比较简单的。

而且,EventBus 支持同步事件和异步事件,可以满足我们不同场景下的需求。比如发短信,系统完全没必要等在那边,完全是可以异步做的。

附录:SpringContextUtils

上面的代码使用到了 SpringContextUtils,我想大部分的 Spring 应用都会写这么一个 工具 类来从 Spring 容器中获取 Bean,用于一些不方便采用注入的地方。

@Component
public class SpringContextUtils implements BeanFactoryPostProcessor {

    private static ConfigurableListableBeanFactory beanFactory;

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        SpringContextUtils.beanFactory = configurableListableBeanFactory;
    }

    public static <T> T getBean(String name) throws BeansException {
        return (T) beanFactory.getBean(name);
    }

    public static <T> T getBean(Class<T> clz) throws BeansException {
        T result = beanFactory.getBean(clz);
        return result;
    }

    public static <T> List<T> getBeansOfType(Class<T> type) {
        return beanFactory.getBeansOfType(type).entrySet().stream().map(entry->entry.getValue()).collect(Collectors.toList());
    }

    // 上面的例子用到了这个
    public static List<Object> getBeansWithAnnotation(Class<? extends Annotation> annotationType) {
        Map<String, Object> beansWithAnnotation = beanFactory.getBeansWithAnnotation(annotationType);

        // java 8 的写法,将 map 的 value 收集起来到一个 list 中
        return beansWithAnnotation.entrySet().stream().map(entry->entry.getValue()).collect(Collectors.toList());

        // java 7
        List<Object> result = new ArrayList<>();
        for (Map.Entry<String, Object> entry : beansWithAnnotation.entrySet()) {
            result.add(entry.getValue());
        }
        return result;
    }
}
复制代码

(全文完)


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

财富博客

财富博客

Robert Scoble、Shel Israel / 李宛蓉 / 重庆出版社 / 2008 / 38.00元

《财富博客》作者斯考伯(美国最多人阅读的企业博客作者)和谢尔•以色列(资深技术顾问)相信,博客已经开始改变企业的面貌。他们阐释说明了员工博客如何改变外界对微软的观感,敢说敢做的NBA球队老板如何运用博客和球迷连接,小企业和大公司又如何能从博客获益……另外,还有万一使用不当,博客又会招致怎样的灾难。斯考伯和以色列直言不讳,这是卓越博客的基本要件,因此他们在说完博客的好处之后,也以同样诚实的心态讨论博......一起来看看 《财富博客》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

URL 编码/解码
URL 编码/解码

URL 编码/解码

MD5 加密
MD5 加密

MD5 加密工具