Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

qiaohhgz/spring-boot-event

Open more actions menu

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

通过使用spring 事件来解决业务代码的耦合

下面通过一个下单的业务代码,拆解为使用事件驱动的方式开发

原始的业务代码

package com.itunion.example.service;

import com.itunion.example.domain.Order;
import com.itunion.example.mapper.OrderMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class OrderServiceImpl {

    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private EmailServiceImpl emailService;

    // 下单
    @Transactional
    public void placeOrder(Order order) {
        // 保存订单
        orderMapper.save(order);

        // 发送邮件通知
        emailService.sendEmail(order);
    }
}

这里有个下单接口,首先保存订单到数据库,然后发送邮件通知给客户

思考:如果某一段时间邮件服务器挂了,那是不是就下不了单了? 如果后续业务变化需要在下单之后增加其他逻辑,是不是需要修改代码

为了不影响下单我们需要把发送邮件解耦出来

引入事件发布对象

package com.itunion.example.service;

import com.itunion.example.domain.Order;
import com.itunion.example.mapper.OrderMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class OrderServiceImpl {

    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private ApplicationEventPublisher publisher;

    // 下单
    @Transactional
    public void placeOrder(Order order) {
        // 保存订单
        orderMapper.save(order);

        // 发布下单事件
        publisher.publishEvent(order);
    }
}

删除了邮件的依赖和发送邮件的方法 这里我们引入了 ApplicationEventPublisher 对象,用来发布下单事件

发布总要有接收处理事件的地方

接收并处理事件

package com.itunion.example.service;

import com.itunion.example.domain.Order;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;

@Service
public class EmailServiceImpl {

    public void sendEmail(Order order) {
        System.out.println("发送邮件到: " + order.getUserName().toLowerCase());
    }

    @EventListener
    public void placeOrderNotice(Order order) {
        sendEmail(order);
    }

}

sendEmail 是原本的发送邮件方法,增加一个 placeOrderNotice 方法,并加上@EventListener 注解,这样只要是Order 类型的消息都会到这个方法里来,然后调用原本的发送邮件方法

运行

package com.itunion.example;

import com.itunion.example.domain.Order;
import com.itunion.example.service.OrderServiceImpl;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringBootEventApplicationTests {

    @Autowired
    private OrderServiceImpl orderService;

    @Test
    public void placeOrder() {
        Order order = new Order();
        order.setUserName("张三");
        order.setGoodsName("iphone X");
        orderService.placeOrder(order);
    }

}

编写一个单元测试运行一下

正常业务都执行了

模拟异常

    @Test
    public void placeOrder() {
        Order order = new Order();
        order.setUserName(null);
        order.setGoodsName("iphone X");
        orderService.placeOrder(order);
    }

单元测试的用户名设置为空,让邮件输出调用toLowerCase方法是报错

邮件报错,订单事务回滚了!这不是我们期望的结果呀

那能不能让我们的方法异步执行呢?答案肯定是可以的

开启异步执行

@EnableAsync
@SpringBootApplication
public class SpringBootEventApplication {

在我们的启动类上增加一个 @EnableAsync 注解

    @EventListener
    @Async
    public void placeOrderNotice(Order order) {
        sendEmail(order);
    }

在下单事件处理的方法上增加 @Async 异步调用注解

当我们再次执行的时候单元测试执行通过了,但是控制台打印了邮件发送失败的消息,订单也入库了,说明符合我们的逾期结果

仔细看日志打印了一个

[cTaskExecutor-1] .a.i.SimpleAsyncUncaughtExceptionHandler

说明spring 是通过一个默认的线程池执行了这个发送邮件的方法,@Async 其实也支持指定你自己配置的线程池的

自定义线程池

 @Bean
    public ThreadPoolTaskExecutor myExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);
        executor.setMaxPoolSize(50);
        return executor;
    }

增加自定义线程池配置 myExecutor ,然后运行查看日志发现输出如下内容

2018-09-04 13:55:34.597 ERROR 7072 --- [   myExecutor-1]

说明已经在使用我们配置的线程池了

也可以增加多个 @EventListener 方法对下单做一连串的后续操作

当有多个下单处理的时候可以使用 @org.springframework.core.annotation.Order 注解来设置执行顺序

完整的项目结构

更多精彩内容

关注我们

About

架构实战篇(十五):Spring Boot 解耦之事件驱动

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

Morty Proxy This is a proxified and sanitized view of the page, visit original site.