spring中的ApplicationEventPublisher 是spring对发布订阅模式的一种支持,要了解它的作用和使用需要先了解下观察者模式发布订阅模式

一、观察者模式

观察者模式中涉及观察者,被观察者两种角色,其中被观察者中会持有多个观察者对象,当自己的状态发生改变时会通知所有的观察者。

下面举一个银行转账的例子来说明,银行账户发生变动时要分别通过短信和app信息两种方式通知客户,使用观察者模式来设计这个案例。

先抽象出一个发送信息的接口

//发送信息的接口
public interface Sender {
    
    void send();
}

再分别创建出发送短信和app信息的类

发送短信

public class SmsSender implements Sender{
    @Override
    public void send() {
        System.out.println("发送短信");
    }
}

发送app信息

public class AppSender implements Sender{
    @Override
    public void send() {
        System.out.println("发送app信息");
    }
}

再创建一个账户类,账户类中会持有多个sender对象,在自己状态发生变化时会通知所有的sender

public class BankAccount {
    //持有多个sender对象
    private List<Sender> senders = new ArrayList<>();
    
    public void addSender(Sender sender){
        this.senders.add(sender);
    }
    
    //通知所有的观察者
    private void send(){
        for (Sender sender : this.senders) {
            sender.send();
        }
    }
    
    public void transfer(){
        System.out.println("发起转账");
        send();
    }
}

再创建测试类进行测试

    public static void main(String[] args) {
        //创建被观察者
        BankAccount bankAccount = new BankAccount();
        //创建观察者
        SmsSender smsSender = new SmsSender();
        AppSender appSender = new AppSender();
        bankAccount.addSender(smsSender);
        bankAccount.addSender(appSender);

        //改变被观察者的状态
        bankAccount.transfer();
    }

运行后控制台会依次输出

发起转账
发送短信
发送app信息

这就是观察者模式的一个简单运用,在这种模式下被观察者不需要关注观察者的具体逻辑,实现了逻辑上的解耦。

但是被观察者内部还是需要持有观察者的对象,所以在这种模式的基础上发展出了一种完全解耦的模式即

发布订阅模式,在标准设计模式中实际是没有发布订阅模式的,它可以看做是观察者模式的一种变种。

二、发布订阅模式

发布订阅模式通过一个发布者角色实现了观察者和被观察者的完全解耦,发布者相等于一个中转站,所有观察者都在发布者中注册,被观察者状态发生改变时通过发布者来通知所有的观察者但被观察者本身并不知道有多少观察者的存在。下面用发布订阅模式来改造下第一节的例子

发布者

public class BankPublisher {
    //持有多个sender对象
    private List<Sender> senders = new ArrayList<>();

    public void addSender(Sender sender){
        this.senders.add(sender);
    }

    //通知所有的观察者
    public void send(){
        for (Sender sender : this.senders) {
            sender.send();
        }
    }
}

现在账户类中不再持有所有观察者只需要持有发布者对象就够了

public class BankAccount {
    //持有发布者对象
    private BankPublisher publisher;
    
    public void setPublisher(BankPublisher publisher){
        this.publisher = publisher;
    }

    public void transfer(){
        System.out.println("发起转账");
        publisher.send();
    }
}

测试类

public class Test {
    public static void main(String[] args) {
        //创建被观察者
        BankAccount bankAccount = new BankAccount();
        //创建观察者
        SmsSender smsSender = new SmsSender();
        AppSender appSender = new AppSender();
        //创建发布者
        BankPublisher publisher = new BankPublisher();
        publisher.addSender(smsSender);
        publisher.addSender(appSender);

        //被观察者绑定publish
        bankAccount.setPublisher(publisher);

        //改变被观察者的状态
        bankAccount.transfer();
    }
}

这种模式相比于观察者模式的优势就是完全解耦了观察者和被观察者

三、spring中的ApplicationEventPublisher

ApplicationEventPublisher是spring提供的一个事件发布器使用它可以实现发布订阅模式,

下面在spring环境中实现下上边的例子.

bankService

public class BankAccountService {
    //注入事件发布器
    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;
    
    public void transfer(){
        System.out.println("发起转账");
        
        // 参数是需要传递给订阅者的信息
        applicationEventPublisher.publishEvent(new BankEvent("转账"));
    }
}

BankEvent封装了需要传递给订阅者的消息

import org.springframework.context.ApplicationEvent;

public class BankEvent extends ApplicationEvent {

    public BankEvent(Object source) {
        super(source);
    }
}

两个观察者需要实现ApplicationListener接口

import org.springframework.context.ApplicationListener;

public class SmsListener implements ApplicationListener<BankEvent> {
    @Override
    public void onApplicationEvent(BankEvent event) {
        System.out.println("发送短信:"+event.getSource());
    }
}
import org.springframework.context.ApplicationListener;

public class AppListener implements ApplicationListener<BankEvent> {
    @Override
    public void onApplicationEvent(BankEvent event) {
        System.out.println("发送app消息:"+event.getSource());
    }
}

在测试类中配置spring

public class Test2 {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
        BankAccountService bankAccountService = applicationContext.getBean(BankAccountService.class);
        bankAccountService.transfer();
    }


    static class MyConfig {

        @Bean
        public BankAccountService bankAccountService () {
            return new BankAccountService();
        }

        @Bean
        public SmsListener smsListener() {
            return new SmsListener();
        }

        @Bean
        public AppListener appListener(){
            return new AppListener();
        }
    }
}

这样也能达到上边第二节的效果,这就是spring中对发布订阅模式的支持。