使用事件驱动进行代码解耦-Guava篇

什么是事件驱动模型

事件驱动模型也就是我们常说的观察者,或者发布-订阅模型;理解它的几个关键点:

  1. 首先是一种对象间的一对多的依赖关系;
  2. 当一个对象的状态发生变化时,观察者(订阅者)都得到通知并做相应的处理;
  3. 观察者如何处理,目标无需干涉,松散耦合了它们之间的关系。

常见问题

比如说订单状态变化的时候,能够通知到邮件服务,短信服务,积分变化等等; 如果你是个新手,想象一下你去实现这个业务的代码怎么去实现?肯定是一个OrderService里面引入积分Service,短信Service,邮件Service,还有很多很多Service,可能还要调用第三方接口。是不是发现问题所在了,Service耦合严重,又是还会出现循环引用的问题,代码超长,以至于不方便维护。

从如上例子可以看出,应该使用一个观察者来解耦这些Service之间的依赖关系,如图:
0664b3178d2b6b5c7c925eee7bdc0847fdf.jpg

图中增加了一个Listener来解耦OrderService和其他Service,即注册成功后,只需要通知相关的监听器,不需要关心它们如何处理,处理起来非常容易。这就是一个典型的事件处理模型-观察者模式,解耦目标对象和它的依赖对象,目标只需要通知它的依赖对象,具体怎么处理,依赖对象自己决定。比如是异步还是同步,延迟还是非延迟等。
其实上边其实也使用了DIP(依赖倒置原则),依赖于抽象,而不是具体。
还是就是使用了IOC思想,即以前主动去创建它依赖的Service,现在只是被动等待别人注册进来。
主要目的是:松散耦合对象间的一对多的依赖关系。

常用事件驱动模型

  1. 设计模式里面的观察者模式

  2. JDK观察者模式

  3. JavaBean事件驱动

  4. spring事件驱动

  5. ......

JavaBean事件驱动

JavaBean规范提供了一种监听属性变化的事件驱动模型,提供操作JavaBean属性的类PropertyChangeSupport,PropertyEditorSupport以及PropertyChangeListener支持,PropertyEditorSupport就是目标,而PropertyChangeListener就是监听器。

Spring提供的事件

具体代表者是ApplicationEvent,其下有一个ApplicationContextEvent,表示Spring容器事件,且其又有如下实现:

  • ContextStartedEvent:Spring容器启动后触发的事件;
  • ContextStoppedEvent:Spring容器停止后触发的事件;
  • ContextRefreshedEvent:Spring容器初始化或刷新完成后触发的事件;
  • ContextClosedEvent:Spring容器关闭后触发的事件;

注:org.springframework.context.support.AbstractApplicationContext抽象类实现了LifeCycle的start和stop回调并发布ContextStartedEvent和ContextStoppedEvent事件。

事件发布具体代表者是:ApplicationEventPublisher及ApplicationEventMulticaster。
1、ApplicationContext接口继承了ApplicationEventPublisher,并在AbstractApplicationContext实现了具体代码,实际执行是委托给ApplicationEventMulticaster(可以认为是多播),我们常用的ApplicationContext都继承自AbstractApplicationContext,如ClassPathXmlApplicationContext、XmlWebApplicationContext等,所以自动拥有这个功能。
2、ApplicationContext自动到本地容器里找一个名字为”applicationEventMulticaster“的ApplicationEventMulticaster实现,如果没有自己new一个SimpleApplicationEventMulticaster,

监听器具体代表者是:ApplicationListener。其继承自JDK的EventListener,JDK要求所有监听器将继承它。
1. 它只提供了onApplicationEvent方法,我们需要在该方法实现内部判断事件类型来处理,或者指定某一个事件类型(泛型,实现ApplicationListener)。
2. 它没有提供按顺序触发监听器的语义,所以Spring提供了另一个接口SmartApplicationListener,该接口支持判断的事件类型、目标类型,及执行顺序。

由于在上一篇文章:使用事件驱动进行代码解耦-Spring篇,已经介绍了基于spring事件方式进行代码解耦的示例,这里主要介绍基于google guava来进行示例展示。

Guava事件机制

在guava中,事件处理器被称为事件总线EventBus,具体代表有EventBus类和AsyncEventBus类(异步);事件监听者被称为订阅者Subscriber。

1. 注册订阅者:新建一个事件总线EventBus/AsyncEventBus,就可以向其中注册订阅者,订阅者其实就是一个被标注了注解@com.google.common.eventbus.Subscribe的方法,向事件总线EventBus/AsyncEventBus注册订阅者的代码逻辑若下:

void register(Object listener) {
    Multimap<Class<?>, Subscriber> listenerMethods = findAllSubscribers(listener);

    for (Map.Entry<Class<?>, Collection<Subscriber>> entry : listenerMethods.asMap().entrySet()) {
        Class<?> eventType = entry.getKey();
        Collection<Subscriber> eventMethodsInListener = entry.getValue();

        CopyOnWriteArraySet<Subscriber> eventSubscribers = subscribers.get(eventType);

        if (eventSubscribers == null) {
            CopyOnWriteArraySet<Subscriber> newSet = new CopyOnWriteArraySet<Subscriber>();
            eventSubscribers =
                MoreObjects.firstNonNull(subscribers.putIfAbsent(eventType, newSet), newSet);
        }

        eventSubscribers.addAll(eventMethodsInListener);
    }
}

private Multimap<Class<?>, Subscriber> findAllSubscribers(Object listener) {
    Multimap<Class<?>, Subscriber> methodsInListener = HashMultimap.create();
    Class<?> clazz = listener.getClass();
    for (Method method : getAnnotatedMethods(clazz)) {
        Class<?>[] parameterTypes = method.getParameterTypes();
        Class<?> eventType = parameterTypes[0];
        methodsInListener.put(eventType, Subscriber.create(bus, listener, method));
    }
    return methodsInListener;
}

private static ImmutableList<Method> getAnnotatedMethodsNotCached(Class<?> clazz) {
    Set<? extends Class<?>> supertypes = TypeToken.of(clazz).getTypes().rawTypes();
    Map<SubscriberRegistry.MethodIdentifier, Method> identifiers = Maps.newHashMap();
    for (Class<?> supertype : supertypes) {
        for (Method method : supertype.getDeclaredMethods()) {
            if (method.isAnnotationPresent(Subscribe.class) && !method.isSynthetic()) {
                // TODO(cgdecker): Should check for a generic parameter type and error out
                Class<?>[] parameterTypes = method.getParameterTypes();
                checkArgument(
                    parameterTypes.length == 1,
                    "Method %s has @Subscribe annotation but has %s parameters."
                        + "Subscriber methods must have exactly 1 parameter.",
                    method,
                    parameterTypes.length);

                SubscriberRegistry.MethodIdentifier ident = new SubscriberRegistry.MethodIdentifier(method);
                if (!identifiers.containsKey(ident)) {
                    identifiers.put(ident, method);
                }
            }
        }
    }
    return ImmutableList.copyOf(identifiers.values());
}

从“if (method.isAnnotationPresent(Subscribe.class) && !method.isSynthetic())”这一句代码可以明显的知道,一个bean向事件总线EventBus/AsyncEventBus进行注册,注册的并不是bean本身,而是该bean中被所有标注了注解@Subscribe的方法,一个被标注了注解@Subscribe的方法就是一个订阅者。当有事件发布到事件总线中,事件总线会遍历所有的订阅者进行事件处理。
2. 向事件总线EventBus和AsyncEventBus发布事件,直接调用事件总线的post方法即可

public void post(Object event) {
    Iterator<Subscriber> eventSubscribers = subscribers.getSubscribers(event);
    if (eventSubscribers.hasNext()) {
        dispatcher.dispatch(event, eventSubscribers);
    } else if (!(event instanceof DeadEvent)) {
        // the event had no subscribers and was not itself a DeadEvent
        post(new DeadEvent(this, event));
    }
}

 

Guava事件机制示例

本示例模拟订单生成与更新时会发送短信与站内信简单业务场景demo

1. 自定义一些接口以及POJO

/**
 * 业务事件发布者
 * @author dongsilin
 * @version 1.0
 * @date 2019/1/24
 */
public interface BizEventPublisher {

     /**
      * 发布同步事件
      * @param eventData
      */
     void publishEvent(Object eventData);

     /**
      * 发布异步事件
      * @param eventData
      */
     void publishEventAsync(Object eventData);

}


/**
 * 业务事件发布者
 * @author dongsilin
 * @version 1.0
 * @date 2019/1/24
 */
public interface BizEventPublisher {

     /**
      * 发布同步事件
      * @param eventData
      */
     void publishEvent(Object eventData);

     /**
      * 发布异步事件
      * @param eventData
      */
     void publishEventAsync(Object eventData);

}

/**
 * 业务事件类型
 * @author dongsilin
 * @version 1.0
 * @date 2019/1/24
 */
public enum BizEventType {

    ORDER_CREATE("订单-创建"),
    ORDER_UPDATE("订单-修改"),
    ;

    String describe;
    BizEventType(String describe) {
        this.describe = describe;
    }

}

/**
 * @author dongsilin
 * @version 1.0
 * @date 2019/1/24
 * 测试订单类
 */
@Data
public class Order {
    private long orderId;
    private long userId;
    public Order(long orderId, long userId) {
        this.setOrderId(orderId);
        this.setUserId(userId);
    }
}

2. 自定义业务数据,包含事件通用数据属性

/**
 * @author dongsilin
 * @version 1.0
 * @date 2019/1/24
 * 业务事件数据
 */
@Data
@AllArgsConstructor
public class BizEventData<S> implements EventExecutor<S> {

    private BizEventType eventType;
    private S data;

    @Override
    public void executeEvent(Consumer<S> executor) {
        executor.accept(data);
    }

    public static<S> BizEventData of(BizEventType eventType, S data) {
        return new BizEventData(eventType, data);
    }

}

3. 业务事件发布配置管理

/**
 * @author dongsilin
 * @version 1.0
 * @date 2019/1/24
 * 业务事件发布配置管理
 */
@Slf4j
@Component
public class BizEventPublisherConfiguration implements BizEventPublisher {

    /** 同步事件总线 */
    private EventBus eventBus = new EventBus();
    /** 异步事件总线 */
    private AsyncEventBus eventBusAsync = new AsyncEventBus(
        new ThreadPoolExecutor(
            Runtime.getRuntime().availableProcessors(),
            Runtime.getRuntime().availableProcessors() * 2,
            5L,
            TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(),
            new ThreadFactoryBuilder().setNameFormat("guava-event-executor-pool-%d").build()
        ),
        (Throwable e, SubscriberExceptionContext exceptionContext) -> {
            log.error("", e);
        }
    );

    @Override
    public void publishEvent(Object eventData) {
        eventBus.post(eventData);
    }

    @Override
    public void publishEventAsync(Object eventData) {
        eventBusAsync.post(eventData);
    }

    /**
     * 构造器,注册监听者
     * @param beanFactory
     */
    public BizEventPublisherConfiguration (ListableBeanFactory beanFactory) {
        // 获取所有带有 @BizEventListener 的 bean,将他们注册为监听者
        beanFactory.getBeansWithAnnotation(BizEventListener.class)
            .forEach((beanName, listener) -> {
                eventBusAsync.register(listener);
                eventBus.register(listener);
            });
    }

}

4. 业务事件监听配置管理

/**
 * @author dongsilin
 * @version 1.0
 * @date 2019/1/24
 * 业务事件监听配置管理
 */
@Slf4j
@Component
@BizEventListener
public class BizEventListenerConfiguration {

    @Autowired
    private TestMsgService msgService;

    @Subscribe
    public void executeEvent(BizEventData bizEventData) {
        switch (bizEventData.getEventType()) {
            // 订单创建,发送短信,.......
            case ORDER_CREATE: bizEventData.executeEvent((data) -> {
                msgService.sendPhoneMsg((Order) data);
            });break;
            // 订单修改,站内信提醒,.......
            case ORDER_UPDATE: bizEventData.executeEvent((data) -> {
                msgService.sendWebMsg((Order) data);
            });break;

            default: bizEventData.executeEvent((data) -> {
                log.info("executeEvent bizEventData = {}", data);
            });
        }
    }

}

5. 定义两个测试service,TestOrderService 和 TestMsgService

/**
 * @author dongsilin
 * @version 1.0
 * @date 2019/1/24
 */
@Slf4j
@Service
public class TestOrderService {

    @Autowired
    private BizEventPublisher bizEventPublisher;

    public void create(Order order) {
        ......
        log.info("TestOrderService create order = {}", order);
        bizEventPublisher.publishEvent(BizEvent.of(BizEventType.ORDER_CREATE, order));
    }

    public void update(Order order) {
        ......
        log.info("TestOrderService update order = {}", order);
        bizEventPublisher.publishEventAsync(BizEvent.of(BizEventType.ORDER_UPDATE, order));
    }

}

/**
 * @author dongsilin
 * @version 1.0
 * @date 2019/1/24
 */
@Slf4j
@Service
public class TestMsgService {


    public void sendPhoneMsg(Order order) {
        ......
        log.info("TestMsgService sendPhoneMsg order = {}", order);
    }

    public void sendWebMsg(Order order) {
        ......
        log.info("TestMsgService sendWebMsg order = {}", order);
    }
}

此处TestOrderService 中并没有强制依赖注入TestMsgService,而是通过发布事件的方式将事件数据发布到事件管理中心,由事件管理者来统一管理事件处理方式,解决了各个业务service严重耦合的场景,实现软件开发中的“高内聚-低耦合”原则。

通过如上,大体了解了google guava的事件机制,可以使用该机制非常简单的完成事件流程。

转载于:https://my.oschina.net/dslcode/blog/3007142

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux内核UART驱动通常使用队列来原有的功能函数,以提高代码的可读性和可维护性。这种方法的基本思想是将不同的功能分割成独立的模块,每个模块都有一个输入队列和一个输出队列。 输入队列用于接收来自UART接口的数据,处理数据并将结果放入输出队列中。输出队列中的数据可以是控制命令、状态信息或者其他需要传递给上层应用程序的数据。这种方法的优点是可以使编写驱动程序更加简单,易于维护和扩展。 以下是一个使用队列原有功能函数的UART驱动程序的示例: ```c #include <linux/module.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/slab.h> #include <linux/uaccess.h> #include <linux/serial_core.h> #define BUFFER_SIZE 1024 struct uart_device { struct cdev cdev; struct uart_port port; struct mutex mutex; struct work_struct work; struct tasklet_struct tasklet; spinlock_t lock; wait_queue_head_t read_queue; wait_queue_head_t write_queue; char *buf; int head; int tail; }; static int uart_driver_open(struct inode *inode, struct file *file) { struct uart_device *dev; dev = container_of(inode->i_cdev, struct uart_device, cdev); file->private_data = dev; return 0; } static int uart_driver_release(struct inode *inode, struct file *file) { return 0; } static ssize_t uart_driver_read(struct file *file, char __user *buf, size_t count, loff_t *offset) { struct uart_device *dev = file->private_data; ssize_t ret; if (count == 0) return 0; if (wait_event_interruptible(dev->read_queue, dev->head != dev->tail)) return -ERESTARTSYS; mutex_lock(&dev->mutex); if (dev->head > dev->tail) { ret = min_t(ssize_t, count, dev->head - dev->tail); if (copy_to_user(buf, dev->buf + dev->tail, ret)) { ret = -EFAULT; goto out; } dev->tail += ret; } else { ret = min_t(ssize_t, count, BUFFER_SIZE - dev->tail); if (copy_to_user(buf, dev->buf + dev->tail, ret)) { ret = -EFAULT; goto out; } dev->tail = (dev->tail + ret) % BUFFER_SIZE; } out: mutex_unlock(&dev->mutex); return ret; } static ssize_t uart_driver_write(struct file *file, const char __user *buf, size_t count, loff_t *offset) { struct uart_device *dev = file->private_data; ssize_t ret; if (count == 0) return 0; if (wait_event_interruptible(dev->write_queue, dev->head != ((dev->tail - 1 + BUFFER_SIZE) % BUFFER_SIZE))) return -ERESTARTSYS; mutex_lock(&dev->mutex); if (dev->tail > dev->head) { ret = min_t(ssize_t, count, BUFFER_SIZE - dev->tail); if (copy_from_user(dev->buf + dev->tail, buf, ret)) { ret = -EFAULT; goto out; } dev->tail += ret; } else { ret = min_t(ssize_t, count, dev->head - dev->tail); if (copy_from_user(dev->buf + dev->tail, buf, ret)) { ret = -EFAULT; goto out; } dev->tail = (dev->tail + ret) % BUFFER_SIZE; } out: mutex_unlock(&dev->mutex); return ret; } static void uart_driver_work(struct work_struct *work) { struct uart_device *dev = container_of(work, struct uart_device, work); struct uart_port *port = &dev->port; unsigned char c; int i; mutex_lock(&dev->mutex); while (uart_chars_avail(port)) { c = uart_get_char(port); if (dev->head == ((dev->tail - 1 + BUFFER_SIZE) % BUFFER_SIZE)) { /* Buffer is full, drop the incoming character */ continue; } dev->buf[dev->head] = c; dev->head = (dev->head + 1) % BUFFER_SIZE; } mutex_unlock(&dev->mutex); wake_up_interruptible(&dev->read_queue); } static void uart_driver_tasklet(unsigned long data) { struct uart_device *dev = (struct uart_device *)data; struct uart_port *port = &dev->port; unsigned char c; int i; spin_lock(&dev->lock); while (uart_chars_avail(port)) { c = uart_get_char(port); if (dev->head == ((dev->tail - 1 + BUFFER_SIZE) % BUFFER_SIZE)) { /* Buffer is full, drop the incoming character */ continue; } dev->buf[dev->head] = c; dev->head = (dev->head + 1) % BUFFER_SIZE; } spin_unlock(&dev->lock); wake_up_interruptible(&dev->read_queue); } static void uart_driver_start(struct uart_port *port) { struct uart_device *dev = container_of(port, struct uart_device, port); INIT_WORK(&dev->work, uart_driver_work); tasklet_init(&dev->tasklet, uart_driver_tasklet, (unsigned long)dev); spin_lock_init(&dev->lock); init_waitqueue_head(&dev->read_queue); init_waitqueue_head(&dev->write_queue); mutex_init(&dev->mutex); dev->buf = kzalloc(BUFFER_SIZE, GFP_KERNEL); dev->head = 0; dev->tail = 0; uart_write_wakeup(port); } static void uart_driver_stop(struct uart_port *port) { struct uart_device *dev = container_of(port, struct uart_device, port); cancel_work_sync(&dev->work); tasklet_kill(&dev->tasklet); spin_lock_irq(&dev->lock); dev->head = dev->tail = 0; spin_unlock_irq(&dev->lock); kfree(dev->buf); } static struct uart_ops uart_driver_ops = { .tx_empty = uart_tx_empty, .set_mctrl = uart_set_mctrl, .get_mctrl = uart_get_mctrl, .stop_tx = uart_stop_tx, .start_tx = uart_start_tx, .send_xchar = uart_send_xchar, .stop_rx = uart_stop_rx, .enable_ms = uart_enable_ms, .break_ctl = uart_break_ctl, .startup = uart_driver_start, .shutdown = uart_driver_stop, }; static struct uart_driver uart_driver = { .owner = THIS_MODULE, .driver_name = "uart_driver", .dev_name = "ttyUART", .major = 0, .minor = 0, .nr = 1, .cons = NULL, .ops = &uart_driver_ops, }; static int __init uart_driver_init(void) { dev_t devno; int ret; ret = alloc_chrdev_region(&devno, 0, 1, "uart_driver"); if (ret < 0) return ret; cdev_init(&uart_driver.cdev, &uart_driver_ops); uart_driver.cdev.owner = THIS_MODULE; ret = cdev_add(&uart_driver.cdev, devno, 1); if (ret < 0) { unregister_chrdev_region(devno, 1); return ret; } uart_register_driver(&uart_driver); return 0; } static void __exit uart_driver_exit(void) { uart_unregister_driver(&uart_driver); cdev_del(&uart_driver.cdev); unregister_chrdev_region(uart_driver.cdev.dev, 1); } module_init(uart_driver_init); module_exit(uart_driver_exit); MODULE_AUTHOR("Your Name"); MODULE_LICENSE("GPL"); ``` 在这个驱动程序中,我们使用了两个输入队列和一个输出队列。`read_queue` 用于接收来自 UART 的数据,`write_queue` 用于接收要发送到 UART 的数据,`buf` 用于存储接收到的数据。 `wait_event_interruptible` 函数用于等待数据到达队列。`mutex_lock` 和 `mutex_unlock` 函数用于保护共享数据结构。`wake_up_interruptible` 函数用于唤醒等待在队列上的进程。 `uart_driver_work` 和 `uart_driver_tasklet` 函数用于从 UART 中接收数据,并将接收到的数据放入输入队列中。 `uart_driver_start` 和 `uart_driver_stop` 函数用于初始化和清除输入队列和输出队列中的数据。`uart_driver_ops` 结构体包含了驱动程序中使用的 UART 操作。`uart_driver` 结构体包含了驱动程序的基本信息。 通过使用队列原有的功能函数,我们可以将驱动程序中的不同部分分割成独立的模块,使得代码更加简洁、易于维护和扩展。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值