• 观察者/订阅-发布模式/事件监听机制


    观察者/订阅-发布模式/事件监听机制

    观察者模式

    观察者模式定义了对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知,并自动更新。

    我们可以通过一个例子来学习观察者模式的好处。

    假设在一个系统中,用户注册成功后,我们会给用户发送邮件、赠送礼品。代码大致是下面这样的:

    public class UserController {
      private UserService userService; // 依赖注入
      private EmailService emailService; // 依赖注入
      private GiftService giftService; // 依赖注入
    
      public Long register(String telephone, String password) {
        // 注册
        long userId = userService.register(telephone, password);
        // 发送邮件
        emailService.send(userId);
        // 赠送礼品
        giftService.issueNewUserGift(userId);
        return userId;
      }
    }
    

    如果没有扩展和修改的需求,上面的代码实现是可以接受的。相反,如果需求频繁变动,比如,用户注册成功之后,不再发放礼品,而是改为发放优惠券,并且还要给用户发送一封“欢迎注册成功”的站内信。这种情况下,我们就需要频繁地修改register()函数中的代码,违反开闭原则。而且,如果注册成功之后需要执行的后续操作越来越多,那register()函数的逻辑会变得越来越复杂,也就影响到代码的可读性和可维护性。

    这个时候,观察者模式就能派上用场了。利用观察者模式,对上面的代码进行了重构。重构之后的代码如下所示:

    public interface RegObserver {
      void handleRegSuccess(long userId);
    }
    
    public class RegNotificationObserver implements RegObserver {
      private NotificationService notificationService; // 依赖注入
    
      @Override
      public void handleRegSuccess(long userId) {
        notificationService.sendInboxMessage(userId, "Welcome...");
      }
    }
    
    public class RegCouponObserver implements RegObserver {
      private CouponService couponService; // 依赖注入
    
      @Override
      public void handleRegSuccess(long userId) {
        couponService.issueNewUserCoupon(userId);
      }
    }
    
    public interface Subject {
        void registerObserver(RegObserver observer);
        void removeObserver(RegObserver observer);
        void notifyObservers(String message);
    }
    
    @Component
    public class UserRegisterSubject implements Subject {
     
        List<RegObserver> observers; // 依赖注入
     
        @Override
        public void registerObserver(RegObserver observer) {
            observers.add(observer);
        }
     
        @Override
        public void removeObserver(RegObserver observer) {
            observers.remove(observer);
        }
     
        @Override
        public void notifyObservers(String message) {
            for (RegObserver observer : observers) {
                observer.update(message);
            }
        }
    }
    
    
    public class UserController {
      private UserService userService; // 依赖注入
      private UserRegisterSubject userRegisterSubject; // 依赖注入
    
      public Long register(String telephone, String password) {
        long userId = userService.register(telephone, password);
        userRegisterSubject.notifyObservers(userId);
        return userId;
      }
    }
    

    Java 提供接口 java.util.Observablejava.util.Observer 来实现观察者模式,但感觉好难用....

    package com.example.demo;
    
    import java.util.Observable;
    import java.util.Observer;
    
    /**
     * @author Lin = ̄ω ̄=
     */
    public class ObservableDemo {
    
    	public static void main(String[] args) {
    
    		MyObservable myObservable = new MyObservable();
    		
    		myObservable.addObserver((observable, value) -> {
    			System.out.println(value);
    		});
    		myObservable.setChanged();
    		myObservable.notifyObservers("hello, world");
    	}
    
    	public static class MyObservable extends Observable {
    		public void setChanged() {
    			super.setChanged();
    		}
    	}
    }
    

    订阅-发布模式

    订阅-发布模式观察者模式 很像,但两者还是有些区别。

    可以看出,发布订阅模式相比观察者模式多了个事件通道,事件通道作为调度中心,管理事件的订阅和发布工作,彻底隔绝了订阅者和发布者的依赖关系。即订阅者在订阅事件的时候,只关注事件本身,而不关心谁会发布这个事件;发布者在发布事件的时候,只关注事件本身,而不关心谁订阅了这个事件。

    就像我们在社交平台上关注某个人,我们就可以通过社交平台的推送获得对方的动态。这里社交平台就扮演了调度中心的作用,我们和对方没有直接的消息往来。

    事件/监听模式

    Java 的事件/监听

    Java的事件监听机制主要包括:事件源(触发事件的对象),事件对象(java.util.EventObject),事件监听器(java.util.EventListener)

    Spring 的事件/监听

    事件类:ApplicationEvent 继承 java.util.EventObject

    事件监听器: ApplicationListener 继承 java.util.EventObject

    事件广播器:ApplicationEventMulticaster

    • SimpleApplicationEventMulticaster (唯一实现类)

    事件发送器:ApplicationEventPublisher

    Spring Boot 的事件/监听

    示例:

    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.context.ApplicationListener;
    import org.springframework.context.event.ContextClosedEvent;
    import org.springframework.context.support.GenericApplicationContext;
    
    /**
     * @author Lin = ̄ω ̄=
     * @date 2021/5/10
     */
    @SpringBootApplication
    public class Application {
    
    	public static void main(String[] args) {
            GenericApplicationContext context = new GenericApplicationContext();
            // 添加事件监听
    		context.addApplicationListener(event -> {
                // err 突出显示
    			System.err.println(event.getClass().getSimpleName());
    		});
    		// 启动 Spring 应用上下文
    		context.refresh();
    		context.publishEvent("hello,world");
    
    		// ContextClosedEvent
    		context.close();
    	}
    
    }
    

    输出:

    ContextRefreshedEvent

    PayloadApplicationEvent

    ContextClosedEvent

    添加自定义监听器 - ApplicationListener

    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.context.ApplicationListener;
    import org.springframework.context.event.ContextClosedEvent;
    import org.springframework.context.support.GenericApplicationContext;
    
    /**
     * @author Lin = ̄ω ̄=
     * @date 2021/5/10
     */
    @SpringBootApplication
    public class Application {
    
    	public static void main(String[] args) {
            GenericApplicationContext context = new GenericApplicationContext();
            // 添加自定义监听器
    		context.addApplicationListener(new ClosedListener());
    		// 启动 Spring 应用上下文
    		context.refresh();
    		context.publishEvent("hello,world");
    
    		// ContextClosedEvent
    		context.close();
    	}
    
        private static class ClosedListener implements ApplicationListener<ContextClosedEvent> {
    
    		@Override
    		public void onApplicationEvent(ContextClosedEvent event) {
    			System.out.println("关闭上下文: " + event);
    		}
    	}
    }
    

    输出:

    关闭上下文: org.springframework.context.event.ContextClosedEvent[source=org.springframework.context.support.GenericApplicationContext@4c75cab9

    事件的发布最终都是通过 publishEvent(xxx) 主动调用

    例如上下文关闭事件,通过查看源码可知调用链为: context.close() --> doClose() --> publishEvent(new ContextClosedEvent(this)); --> getApplicationEventMulticaster#multicastEvent(applicationEvent, eventType);,通过 multicastEvent 发送事件

    那么和我们自定义的事件发布有什么区别?通过查看源码可知由于我们传入的是字符串,所以会被自动包装为事件 PayloadApplicationEvent

    context.publishEvent("hello,world"); -->

    protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
    		Assert.notNull(event, "Event must not be null");
    
    		// Decorate event as an ApplicationEvent if necessary
    		ApplicationEvent applicationEvent;
    		if (event instanceof ApplicationEvent) {
    			applicationEvent = (ApplicationEvent) event;
    		}
    		else {
    			applicationEvent = new PayloadApplicationEvent<>(this, event);
    			if (eventType == null) {
    				eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
    			}
    		}
        // ...
    }
    

    由源码代码可知,如果我们想换成其他自定义事件,可以这么做:

    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.context.ApplicationListener;
    import org.springframework.context.event.ContextClosedEvent;
    import org.springframework.context.support.GenericApplicationContext;
    
    /**
     * @author Lin = ̄ω ̄=
     * @date 2021/5/10
     */
    @SpringBootApplication
    public class Application {
    
    	public static void main(String[] args) {
            GenericApplicationContext context = new GenericApplicationContext();
            // 添加事件监听
    		context.addApplicationListener(event -> {
    			System.err.println(event.getClass().getSimpleName());
    		});
    		// 启动 Spring 应用上下文
    		context.refresh();
    		// context.publishEvent("hello,world");
            context.publishEvent(new MyEvent("hello,world"));
    
    		// ContextClosedEvent
    		context.close();
    	}
    
    }
    
    private static class MyEvent extends ApplicationEvent {
    
        public MyEvent(Object source) {
            super(source);
        }
    }
    

    输出:

    ContextRefreshedEvent

    MyEvent

    ContextClosedEvent

    广播事件 multicaster

    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.context.ApplicationListener;
    import org.springframework.context.event.ContextClosedEvent;
    import org.springframework.context.support.GenericApplicationContext;
    
    /**
     * @author Lin = ̄ω ̄=
     * @date 2021/5/10
     */
    @SpringBootApplication
    public class Application {
    
    	public static void main(String[] args) {
            ApplicationEventMulticaster multicaster = new SimpleApplicationEventMulticaster();
    		multicaster.addApplicationListener(event -> {
    			System.out.println("加收到的事件:" + event);
    		});
    		multicaster.multicastEvent(new PayloadApplicationEvent<Object>("", "hello,world"));
    	}
    
    }
    
    
    

    输出:

    加收到的事件:org.springframework.context.PayloadApplicationEvent[source=]

    查看 Spring Boot 的事件监听

    Spring Boot "/META-INF/spring.factories" (Spring SPI)

    org.springframework.context.ApplicationListener=
    org.springframework.boot.ClearCachesApplicationListener,
    org.springframework.boot.builder.ParentContextCloserApplicationListener,
    org.springframework.boot.context.FileEncodingApplicationListener,
    org.springframework.boot.context.config.AnsiOutputApplicationListener,
    org.springframework.boot.context.config.ConfigFileApplicationListener,
    org.springframework.boot.context.config.DelegatingApplicationListener,
    org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener,
    org.springframework.boot.logging.ClasspathLoggingApplicationListener,
    org.springframework.boot.logging.LoggingApplicationListener
    

    事件的作用

    ConfigFileApplicationListener

    例如 ConfigFileApplicationListener 通过监听 ApplicationEnvironmentPreparedEvent 事件,从而加载 application.propertiesapplication.yml 配置文件

    这个称为 Spring Boot 的事件驱动模型

    另外,Spring Boot 的事件都是源于 Spring 事件,通过继承 SpringApplicationEvent 等 Spring 事件接口实现功能

    Spring Cloud 的事件/监听

    Spring Cloud "/META-INF/spring.factories"

    # Application Listeners
    org.springframework.context.ApplicationListener=
    org.springframework.cloud.bootstrap.BootstrapApplicationListener,
    org.springframework.cloud.bootstrap.LoggingSystemShutdownListener,
    org.springframework.cloud.context.restart.RestartListener
    

    BootstrapApplicationListener

    因为 Spring Cloud 的 BootstrapApplicationListener 的优先级高于 Spring Boot 的 ConfigFileApplicationListener ,所以 application.properties 文件即使定义也配置不到

    原因是 BootstrapApplicationListener 的优先级是 6,ConfigFileApplicationListener 优先级是 11:

    public class BootstrapApplicationListener
    		implements ApplicationListener<ApplicationEnvironmentPreparedEvent>, Ordered {
    
    	/**
    	 * Property source name for bootstrap.
    	 */
    	public static final String BOOTSTRAP_PROPERTY_SOURCE_NAME = "bootstrap";
    
    	/**
    	 * The default order for this listener.
    	 */
    	public static final int DEFAULT_ORDER = Ordered.HIGHEST_PRECEDENCE + 5;
        
        // ...
    }
    
    public class ConfigFileApplicationListener implements EnvironmentPostProcessor, SmartApplicationListener, Ordered {
    	private static final String DEFAULT_NAMES = "application";
        
        /**
    	 * The default order for the processor.
    	 */
    	public static final int DEFAULT_ORDER = Ordered.HIGHEST_PRECEDENCE + 10;
    }
    
  • 相关阅读:
    SSH框架总结
    SSM框架——详细整合教程(Spring+SpringMVC+MyBatis)
    iBATIS SQL Maps
    深入理解Mybatis中sqlSessionFactory机制原理
    mybatis源码分析(1)——SqlSessionFactory实例的产生过程
    MyBatis常用对象SqlSessionFactory和SqlSession介绍和运用
    Generator生成器函数
    MyBatis学习4---使用MyBatis_Generator生成Dto、Dao、Mapping
    采用图形用户界面的操作系统/应用程序
    图形界面的特点是人们不需要记忆和键入繁琐的命令
  • 原文地址:https://www.cnblogs.com/linyuhong/p/14783483.html
Copyright © 2020-2023  润新知