1 Star 1 Fork 1

浅夏 / java成神之路

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
设计模式之_观察者模式.md 10.99 KB
一键复制 编辑 原始数据 按行查看 历史
浅夏 提交于 2020-11-24 17:05 . 更新计划

设计模式之---观察者模式(Observer Pattern)

一. 定义及应用范围

​ 在对象之间定义了一对多的依赖,这样一来,一个对象的状态改变了,依赖它的对象会收到通知并自动更新。其实就是发布订阅模式,发布者发布消息,订阅者订阅消息。订阅了就能收到消息,没订阅就收不到消息。

​ 观察者模式还有其他的一些称谓,比如: 发布-订阅模式,模型-视图模式,源-监听器模式,从属模式。其实都是说的是一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个对象在状态发生变化时,会通知所有观察者对象,使它们自动的更新自己。

二. 优缺点

  • 优点:
  1. 主题和观察者之间建立了一个抽象的偶尔,而非紧密的偶尔,降低了代码的耦合度
  2. 支持广播通信,主题会向所有注册了的观察者发送通知,简化了系统一对多的设计难度;
  3. 符合开闭原则,增加新的观察者无需修改原有代码,代码的可扩展性高。
  • 缺点
  1. 如果主题有很多的直接或间接的观察者,那么全部通知到所有的观察者会很耗时。
  2. 主题和观察者之间如果存在循环依赖,可能导致系统崩溃。

三. 角色划分

  • Subject被观察者

    定义被观察者必须实行的职责,必须具有动态的增加、删除观察者。它一般是抽象类或实现类,仅仅是完成被观察者必须实现的职责: 管理观察者并通知观察者。

  • Observer观察者

    观察者接收到消息后,进行update更新操作,对接收到的信息进行处理。

  • ConcreteSubject具体的被观察者

    定义被观察者的具体业务逻辑,同时定义哪些事件进行通知

  • ConcreteObserver具体的观察者

    每个观察者接收到信息后的处理反应是不同的,各个观察者有各自的处理逻辑。

  • 具体的类图结构:

image-20201010121210311

四. 代码实现

  1. Suject被观察者接口

    public interface Subject {
    
        void add(Observer observer);
    
        void remove(Observer observer);
    
        void notifyObserver();
    }
  2. Observer观察者

    public interface Observer<T> {
        void update(T t);
    }
  3. ConcreteSubject具体的被观察者

    public class OrderSuject implements Subject {
    
        private List<Observer> observerList = Collections.synchronizedList(new ArrayList<>());
        private String message;
    
        public OrderSuject(String message) {
            this.message = message;
        }
    
        @Override
        public void add(Observer observer) {
            observerList.add(observer);
        }
    
        @Override
        public void remove(Observer observer) {
            observerList.remove(observer);
        }
    
        @Override
        public void notifyObserver() {
            observerList.forEach(observer -> observer.update(message));
        }
    }
  4. ConcreteObserver具体的观察者

    public class SmsObserver implements Observer<String>{
        @Override
        public void update(String s) {
            System.out.println("发送短信:"+s);
        }
    }
    public class EmailObserver implements Observer<String>{
        @Override
        public void update(String s) {
            System.out.println("发送邮件:"+s);
        }
    }
  5. 测试类

    public class ObserverTest {
        public static void main(String[] args) {
            OrderSuject orderSuject=new OrderSuject("订单创建成功了");
            orderSuject.add(new SmsObserver());
            orderSuject.add(new EmailObserver());
    
            orderSuject.notifyObserver();
        }
    }
    //运行结果为:
    //发送短信:订单创建成功了
    //发送邮件:订单创建成功了

五. 基于JDK实现的观察者模式

​ jdk内置了观察者接口java.util.Observer,主题被观察者 java.util.Observable。观察者实现Observer接口,被观察者继承Observable类。

  • 观察者

    public class SmsObserver implements Observer {
        @Override
        public void update(Observable o, Object arg) {
            System.out.println("jdk用户下单成功,准备发送短信了:"+arg.toString());
        }
    }
    public class EmailObserver implements Observer {
        @Override
        public void update(Observable o, Object arg) {
            System.out.println("jdk用户下单成功,准备发送邮件了:"+arg.toString());
        }
    }
  • 被观察者

    public class OrderObservable extends Observable {
    
        @Override
        public void notifyObservers(Object arg) {
            setChanged();
            super.notifyObservers(arg);
        }
    
        public static void main(String[] args) {
            OrderObservable orderObservable=new OrderObservable();
            orderObservable.addObserver(new EmailObserver());
            orderObservable.addObserver(new SmsObserver());
    
            orderObservable.notifyObservers("订单创建成功");
        }
    }
    //运行结果:
    //jdk用户下单成功,准备发送短信了:订单创建成功
    //jdk用户下单成功,准备发送邮件了:订单创建成功

六. Spring事件监听机制

Spring的事件监听机制其实就是基于观察者模式来实现的。Spring实现监听机制有多种方式,具体如下:

1. Spring实现观察模式之方式一

  • 监听事件: 实现ApplicationEvent 抽象类

  • 监听者/观察者: 实现ApplicationListener接口,重写onApplicationEvent方法

  • 发布事件: 通过 ApplicationEventPublisher接口的publishEvent方法发布事件 ,ApplicationContext继承了ApplicationEventPublisher接口。

  • 具体代码实现如下:

    //订单事件类
    @Getter
    @Setter
    public class OrderEvent extends ApplicationEvent {
    
        private Order order;
    
        public OrderEvent(Object source,Order order) {
            super(source);
            this.order=order;
        }
    }
    //短信监听类,监听事件OrderEvent
    @Component
    public class SmsListener implements ApplicationListener<OrderEvent> {
        @Override
        public void onApplicationEvent(OrderEvent event) {
            Order order = Optional.ofNullable(event.getOrder()).orElseGet(Order::new);
            System.out.println("订单创建成功了,准备发送短信了:"+order.getCode());
        }
    }
    // 邮件监听类,监听事件OrderEvent
    @Component
    public class EmailListener implements ApplicationListener<OrderEvent> {
        @Override
        public void onApplicationEvent(OrderEvent event) {
            Order order = Optional.ofNullable(event.getOrder()).orElseGet(Order::new);
            System.out.println("订单创建成功了,准备发送邮件了,cod:"+order.getCode());
        }
    }
    @RunWith(SpringRunner.class)
    @SpringBootTest(classes = ApiServerApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
    public class ApiServerApplicationTest {
        @Autowired
        private ApplicationContext applicationContext;
        @Test
        public void testEvent(){
            Order order=new Order();
            order.setId(15L);
            order.setCode("X15212541");
            OrderEvent orderEvent = new OrderEvent(this, order);
            applicationContext.publishEvent(orderEvent);
        }
    }
    //输出结果:
    //订单创建成功了,准备发送邮件了,cod:X15212541
    //订单创建成功了,准备发送短信了:X15212541

2.Spring注解@EventListener实现监听

​ 除了实现ApplicationListener接口之外,Spring可以通过注解@EventListener注解实现监听类,具体如下:

//通过注解@EventListener代替ApplicationListener实现事件监听
@Component
public class TelListener {
    @EventListener
    public void onApplicationEvent(OrderEvent event){
        System.out.println("我是注解驱动监听类,订单创建成了,code="+event.getOrder().getCode());
    }
}
// 运行结果:
//我是注解驱动监听类,订单创建成功了,code=X15212541
//订单创建成功了,准备发送邮件了,cod:X15212541
//订单创建成功了,准备发送短信了:X15212541

@EventListener注解还可以通过配置condition条件来设置满足特定条件后,才执行该监听事件,支持SpringEL表达式

3.异步执行监听事件

  • Spring支持同步和异步两种方式来执行监听事件,具体原理见 SimpleApplicationEventMulticaster类 :

    如下代码可以看到,如果Executor 不为空就会采用异步方式执行,否则的话就是同步方式

@Override
public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {
		ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
		for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
			Executor executor = getTaskExecutor();
			if (executor != null) {
				executor.execute(new Runnable() {
					@Override
					public void run() {
						invokeListener(listener, event);
					}
				});
			}
			else {
				invokeListener(listener, event);
			}
		}
	}
  • 实现异步执行的方式
    1. 在SpringBoot启动类上配置上 开启@EnableAsync
    2. 在监听方法上加上注解@Async,开启异步执行。开启新的线程去执行这个监听任务,如下可见线程ID明显发生了变化,不是同一个线程执行的结果。
    3. 采用异步的方式是为了让主线程快速的结束,给用户进行响应,事件后台异步执行。
@Component
public class SmsListener implements ApplicationListener<OrderEvent> {
    @Override
    @Async
    public void onApplicationEvent(OrderEvent event) {
        Order order = Optional.ofNullable(event.getOrder()).orElseGet(Order::new);
        System.out.println("线程ID="+Thread.currentThread().getId()+" | 订单创建成功了,准备发送短信了:"+order.getCode());
    }
}
运行结果:
线程ID=1我是注解驱动监听类,订单创建成功了,code=X15212541
线程ID=1订单创建成功了,准备发送邮件了,cod:X15212541
线程ID=57 | 订单创建成功了,准备发送短信了:X15212541

4.事务事件 @TransactionalEventListener

Spring提供了@TransactionalEventListener注解,它是@EventListener的一个扩展。允许将事件的监听器绑定到事务的一个阶段。具体的事务阶段包含以下4个:

public enum TransactionPhase {
	BEFORE_COMMIT (默认),
	AFTER_COMMIT,
	AFTER_ROLLBACK,
	AFTER_COMPLETION

}
  • BEFORE_COMMIT :在事务成功后触发该监听事件
  • AFTER_COMMIT : 事务回滚时触发该监听事件
  • AFTER_ROLLBACK : 事务完成后触发,不论是否成功
  • AFTER_COMPLETION: 事务提交之前触发

需要注意的是,如果没有正在运行的事务,就不会触发该事件,除非设置fallbackExecution =true

@TransactionalEventListener(fallbackExecution = true)

马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Java
1
https://gitee.com/dyh1183/java_god.git
git@gitee.com:dyh1183/java_god.git
dyh1183
java_god
java成神之路
master

搜索帮助

344bd9b3 5694891 D2dac590 5694891