spring事件发布器ApplicationEventPublisher的使用

38 篇文章 0 订阅
26 篇文章 0 订阅

1、前言

  spring中有一个事件发布器,使用了观察者模式,当有事件发布的时候,事件监听者会立刻收到发布的事件。今天我们来介绍下这个事件发布器怎么使用。

2、简单使用

2.1、创建事件实体类

  事件实体类需要继承ApplicationEvent。我们模拟老师发布事件的诉求。

public class TeacherCommand extends ApplicationEvent {

    private Integer score;

    private String name;

    public TeacherCommand(Object source, Integer score, String name) {
        super(source);
        this.name = name;
        this.score = score;
    }

    public Integer getScore() {
        return score;
    }

    public void setScore(Integer score) {
        this.score = score;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

2.2、事件发布者

  事件发布者需要实现ApplicationEventPublisherAware接口,实现接口不是必须的,实现接口的目的是为了给applicationEventPublisher赋值。所以只要能给applicationEventPublisher赋值即可。

@Component
public class TeacherCommandPublish implements ApplicationEventPublisherAware {

    private ApplicationEventPublisher applicationEventPublisher;


    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }

    /**
     * 发布事件的方法
     */
    public void publishEvent() {
        TeacherCommand teacherCommand = new TeacherCommand(this, 1, "数学作业");
        applicationEventPublisher.publishEvent(teacherCommand);
        System.out.println("事件发布成功:" + Thread.currentThread().getName());
    }
}

2.3、事件监听者

  事件监听者需要实现ApplicationListener接口。

@Component
public class TeacherCommandListener implements ApplicationListener<TeacherCommand> {

    @Override
    public void onApplicationEvent(TeacherCommand teacherCommand) {
        System.out.println("收到事件:" + teacherCommand.getScore() + ";" + teacherCommand.getName() + ";"
                + Thread.currentThread().getName());
    }
}

2.4、调用事件发布者

    @Autowired
    private TeacherCommandPublish teacherCommandPublish;

    @GetMapping("/teacherCommand")
    public String teacherCommand() {
        teacherCommandPublish.publishEvent();
        System.out.println("线程名字:"+Thread.currentThread().getName());
        return "";
    }

  输出结果:

收到事件:1;数学作业;http-nio-8083-exec-2
事件发布成功:http-nio-8083-exec-2
线程名字:http-nio-8083-exec-2

  根据输出结果我们可以看到,事件被成功发出和收到了。同时,我们也打印了线程名字,可以得知,事件的发布和监听是同步执行的。因为他们是同一个线程,事件监听者的代码执行完了才会接着执行事件发布者后面的代码。

3、进阶使用

  到这里,我们已经会简单使用spring的事件发布器了,但它是同步执行的,这样其实会影响效率,如果我想改成异步执行的,该怎么做呢?这里我们主要来借助@Async注解来实现。

3.1、配置类上启用EnableAsync

@MapperScan("com.myf.zouding.database.mapper")
@SpringBootApplication(scanBasePackages = {"com.myf"})
@EnableAsync
public class NingJinGameStarterApplication {
    private static final int NETTY_PORT = 8084;

    public static void main(String[] args) {
        SpringApplication.run(NingJinGameStarterApplication.class, args);
        NettyServer nettyServer =new NettyServer(NETTY_PORT);
        nettyServer.start();
    }

}

3.2、在事件发布者或监听者方法上使用Async注解

  这里我是在发布者方法上使用了Async注解,也就是实现的发布和接收会由另外一个线程来处理。

@Component
public class TeacherCommandPublish implements ApplicationEventPublisherAware {

    private ApplicationEventPublisher applicationEventPublisher;


    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }

    /**
     * 发布事件的方法
     */
    @Async
    public void publishEvent() {
        TeacherCommand teacherCommand = new TeacherCommand(this, 1, "数学作业");
        applicationEventPublisher.publishEvent(teacherCommand);
        System.out.println("事件发布成功:" + Thread.currentThread().getName());
    }
}

3.3、输出结果

线程名字:http-nio-8083-exec-5
收到事件:1;数学作业;task-1
事件发布成功:task-1

  输出结果符合预期。task-1线程负责处理了事件的发布和接收。

3.4、使用线程池配合Async来实现异步,在配置类里实现线程池bean

@MapperScan("com.myf.zouding.database.mapper")
@SpringBootApplication(scanBasePackages = {"com.myf"})
@EnableAsync
public class NingJinGameStarterApplication {
    private static final int NETTY_PORT = 8084;

    public static void main(String[] args) {
        SpringApplication.run(NingJinGameStarterApplication.class, args);
        NettyServer nettyServer =new NettyServer(NETTY_PORT);
        nettyServer.start();
    }

    @Bean(name = "eventTaskExecutor")
    public Executor eventTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);
        executor.setMaxPoolSize(10);
        executor.setQueueCapacity(25);
        executor.setThreadNamePrefix("event-task-");
        executor.initialize();
        return executor;
    }

}

3.5、仅让监听者来异步处理

  上面我们是让事件发布者和监听者都异步来处理,这次我们只让监听者来异步处理,发布者还是同步。

@Component
public class TeacherCommandListener implements ApplicationListener<TeacherCommand> {

    @Override
    @Async("eventTaskExecutor")
    public void onApplicationEvent(TeacherCommand teacherCommand) {
        System.out.println("收到事件:" + teacherCommand.getScore() + ";" + teacherCommand.getName() + ";"
                + Thread.currentThread().getName());
    }
}

3.6、输出结果

  执行两次:

事件发布成功:http-nio-8083-exec-1
线程名字:http-nio-8083-exec-1
收到事件:1;数学作业;event-task-1
事件发布成功:http-nio-8083-exec-8
线程名字:http-nio-8083-exec-8
收到事件:1;数学作业;event-task-2

  通过结果我们可以看到,事件的发布是由主线程来执行的,事件监听者是由线程池来处理的。符合预期。

4、高阶使用

  思考,如果我们不借助Async注解同时还想实现事件的异步该怎么实现呢?答案是自己实现事件的执行器。关键代码在这个方法里:org.springframework.context.event.SimpleApplicationEventMulticaster#multicastEvent(org.springframework.context.ApplicationEvent, org.springframework.core.ResolvableType),applicationEventPublisher.publishEvent最终也会调用这个方法。
在这里插入图片描述
  也就是要给org.springframework.context.event.SimpleApplicationEventMulticaster#taskExecutor这个属性赋值。

4.1、在配置类里实现给taskExecutor赋值

@MapperScan("com.myf.zouding.database.mapper")
@SpringBootApplication(scanBasePackages = {"com.myf"})
public class NingJinGameStarterApplication {
    private static final int NETTY_PORT = 8084;

    public static void main(String[] args) {
        SpringApplication.run(NingJinGameStarterApplication.class, args);
        NettyServer nettyServer =new NettyServer(NETTY_PORT);
        nettyServer.start();
    }

    @Bean(name = "applicationEventMulticaster")
    public SimpleApplicationEventMulticaster simpleApplicationEventMulticaster(
            @Qualifier("eventTaskExecutor") Executor executor) {
        SimpleApplicationEventMulticaster eventMulticaster = new SimpleApplicationEventMulticaster();
        eventMulticaster.setTaskExecutor(executor);
        return eventMulticaster;
    }

    @Bean(name = "eventTaskExecutor")
    public Executor eventTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);
        executor.setMaxPoolSize(10);
        executor.setQueueCapacity(25);
        executor.setThreadNamePrefix("event-task-");
        executor.initialize();
        return executor;
    }

}

  把其它地方的异步注解Async去掉,执行输出结果

事件发布成功:http-nio-8083-exec-1
收到事件:1;数学作业;event-task-2
线程名字:http-nio-8083-exec-1
事件发布成功:http-nio-8083-exec-7
线程名字:http-nio-8083-exec-7
收到事件:1;数学作业;event-task-1

  可以看到,事件的监听者是由线程池来处理的,符合预期。

  • 12
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值