spring中的事件发布与监听

点赞再看,养成习惯,微信搜索「小大白日志」关注这个搬砖人。

文章不定期同步公众号,还有各种一线大厂面试原题、我的学习系列笔记。

spring事件发布与监听的应用场景

当处理完一段代码逻辑,接下来需要同时执行多个任务,有什么好方法呢?如果在微服务项目中,当属MQ莫属了;但如果是单机,spring为我们也提供了[事件发布-监听]机制去处理这种逻辑,主要有@EventListener型和@TransactionalEventListener型两种方式

@EventListener型发布与监听

一般有【实体类+事件类+事件发布类+事件监听类】,以下模拟“添加新用户”后,需要同时进行多个任务为例(异步)

//实体类
@Data
public class User {
    private String name;
    private String operate;
}
//事件类
@Setter
@Getter
public class UserEvent extends ApplicationEvent{  //事件类必须继承ApplicationEvent
    private User user;
    public UserEvent(Object source) {
        super(source);
    }
}
//事件发布类
@Service
@Slf4j
public class UserServiceImpl {
    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;

    public void addUser(){
        User user=new User();
        user.setName("afei");
        user.setOperate("add");
        UserEvent userEvent=new UserEvent(this);
        userEvent.setUser(user);
        insertUser(user);//user入库
        applicationEventPublisher.publishEvent(userEvent);//"事件发布类"发布UserEvent事件以便给"事件监听类"监听到
        log.info("{},发布[添加新用户]事件成功...",Thread.currentThread().getName());
    }

    public void insertUser(User user){}
}
//事件监听类
@Component  //必须让spring扫描到
@Slf4j
public class UserListerner {

    /*
    *执行多任务
    */
    @Async  //@Async可以开启一个异步任务(启动类要加@EnableAsync),因为监听默认是同步的:监听到UserEvent类事件后,执行本方法代码,然后返回原链路继续执行原来的代码;有了@Async就可以以线程的方式异步执行本方法
    @EventListener(value = UserEvent.class,condition = "#userEvent.user.operate=='add'")  //只监听userEvent.user.operate='add'的UserEvent事件,#userEvent与afterAddUser()方法的参数名一致
    public void afterAddUser(UserEvent userEvent){
        try {
            Thread.sleep(300); //停300ms,方便观察异步线程
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        User user=userEvent.getUser();
        log.info("{},监听到[添加新用户]事件:{}",Thread.currentThread().getName(),user.toString());
        //去数据库查询出user并执行多个任务:发邮件、送体验卡..
        bussinessOperate(getUserByUserName(user.getUserName());
    }
    void bussinessOperate(User user){}
}
//启动类
@EnableAsync  //开启对异步任务的支持,开启@EnableAsync之后@Async才能使用
@SpringBootApplication
public class DemoTestApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoTestApplication.class, args);
    }

}
//测试类
public class TestEvent extends BaseTest{
    @Autowired
    UserServiceImpl userService;

    @Test
    public void test(){
        userService.addUser();
    }
}
//测试基础类
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class BaseTest {
    @Before
    public void before(){
        log.info("单元测试before...");
    }

    @After
    public void after(){
        log.info("单元测试after...");
    }
}

运行后结果:
spring中的事件发布与监听_第1张图片

@TransactionalEventListener型发布与监听

虽然上面@EventListener型解决了单机下多任务的发布监听,但是上面"事件发布"后如果没有提交当前事务,则"事件监听"就去数据库里查询user并执行多个任务,user不存在会报空指针,解决方法是用@TransactionalEventListener保证在"事件发布"后提交了当前事务,才会去执行“事件监听”

//除了事件监听类有点不同,其他代码均与上面一致,事件监听类:
@Component
@Slf4j
public class UserListerner {

    //@EventListener  //单纯监听UserEvent类事件
    @Async  //@Async可以开启一个异步任务(启动类要@EnableAsync),因为监听默认是同步的:监听到UserEvent类事件后,执行下面的代码,然后返回原链路继续执行原来的代码
    @TransactionalEventListener(phase= TransactionPhase.AFTER_COMMIT,fallbackExecution = true)  //phase默认为TransactionPhase.AFTER_COMMIT,它指定了在事件发布的相关事务提交后才会去执行以下监听方法;fallbackExecution默认为false,指如果"事件发布"不存在事务,则不会执行监听方法,如果为true则不管“事件发布”有没有事务,都会执行以下监听方法
    public void afterAddUser(UserEvent userEvent){
        try {
            Thread.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        User user=userEvent.getUser();
        log.info("{},监听到[添加新用户]事件:{}",Thread.currentThread().getName(),user.toString());
        //业务操作:发邮件、送体验卡...
        bussinessOperate();
    }

    void bussinessOperate(){}
}

OK,如果文章哪里有错误或不足,欢迎各位留言。

创作不易,各位的「三连」是二少创作的最大动力!我们下期见!

你可能感兴趣的:(java架构师升级之路,1024程序员节)