关于后端:Spring源码之七registerListeners及发布订阅模式

28次阅读

共计 6065 个字符,预计需要花费 16 分钟才能阅读完成。

Spring 源码之七 registerListeners() 及公布订阅模式

大家好,我是程序员田同学。

明天带大家解读 refresh() 办法中的 registerListeners() 办法,也就是咱们常常说的 Spring 的公布 - 订阅模式。文章首先举一个公布 - 订阅模式的样例,而后解说了公布 - 订阅四个模式的原理,及对公布 - 订阅模式所依赖的观察者模式进行了举例,最初引出该模式在 Springboot 中的大量利用。

照例放一份 refresh() 办法的源码,registerListeners() 办法位于该办法的第七个地位。

@Override
public void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {
      // Prepare this context for refreshing.
      //1、刷新前的筹备
      prepareRefresh();

      // Tell the subclass to refresh the internal bean factory.
      //2、将会初始化 BeanFactory、加载 Bean、注册 Bean
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

      // Prepare the bean factory for use in this context.
      //3、设置 BeanFactory 的类加载器,增加几个 BeanPostProcessor,手动注册几个非凡的 bean
      prepareBeanFactory(beanFactory);

      try {
         //4、模板办法
         // Allows post-processing of the bean factory in context subclasses.
         postProcessBeanFactory(beanFactory);

         // Invoke factory processors registered as beans in the context.
         // 执行 BeanFactory 后置处理器
         invokeBeanFactoryPostProcessors(beanFactory);

         // 5、Register bean processors that intercept bean creation.
         // 注册 bean 后置处理器
         registerBeanPostProcessors(beanFactory);

         // Initialize message source for this context.
         // 国际化
         initMessageSource();

         // Initialize event multicaster for this context.
         initApplicationEventMulticaster();

         // Initialize other special beans in specific context subclasses.
         //6、模板办法 --springboot 实现了这个办法
         onRefresh();

         // Check for listener beans and register them.
         //7、注册监听器
         registerListeners();

         // Instantiate all remaining (non-lazy-init) singletons.
         //8、实现 bean 工厂的初始化 ** 办法重要 **********************************************
         finishBeanFactoryInitialization(beanFactory);

         //9、Last step: publish corresponding event.
         finishRefresh();}

      catch (BeansException ex) {if (logger.isWarnEnabled()) {
            logger.warn("Exception encountered during context initialization -" +
                  "cancelling refresh attempt:" + ex);
         }

         // Destroy already created singletons to avoid dangling resources.
         destroyBeans();

         // Reset 'active' flag.
         cancelRefresh(ex);

         // Propagate exception to caller.
         throw ex;
      }

先大抵看一下 registerListeners() 办法的源码。

protected void registerListeners() {
        // Register statically specified listeners first.
        // 首先注册动态的指定的监听器, 注册的是非凡的事件监听器, 而不是配置中的 bean
        for (ApplicationListener<?> listener : getApplicationListeners()) {getApplicationEventMulticaster().addApplicationListener(listener);
        }

        // Do not initialize FactoryBeans here: We need to leave all regular beans
        // uninitialized to let post-processors apply to them!
        // 这里不会初始化 FactoryBean, 咱们须要保留所有的一般 bean
        // 不会实例化这些 bean, 让后置处理器能够感知到它们
        String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
        for (String listenerBeanName : listenerBeanNames) {getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
        }

        // Publish early application events now that we finally have a multicaster...
        // 当初有了事件播送组, 公布之前的利用事件
        Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
        this.earlyApplicationEvents = null;
        if (!CollectionUtils.isEmpty(earlyEventsToProcess)) {for (ApplicationEvent earlyEvent : earlyEventsToProcess) {getApplicationEventMulticaster().multicastEvent(earlyEvent);
            }
        }
    }

这个办法要做的很简略,就是两个循环遍历,把 Spring 通过硬编码定义的监听器注册到容器中,而后把咱们自定义的监听器注册到容器中,通过这些间接叙述有一些苍白无力,咱们写一个简略的公布 - 订阅的例子不便了解。

在公布订阅模式用须要四个角色:

ApplicationEvent:事件,每个实现类示意一类事件,可携带数据。抽象类。
ApplicationListener:事件监听器,用于接管事件处理工夫。接口。
ApplicationEventMulticaster:事件管理者,能够注册(增加)/ 移除 / 公布事件。用于事件监听器的注册和事件的播送。接口。
ApplicationEventPublisher:事件发布者,委托事件管理者 ApplicationEventMulticaster 实现事件公布。

事件:

@Component
public class MyEvent extends ApplicationEvent {

    private static final long serialVersionUID = 1L;
    
    /**
     * Create a new ApplicationEvent.
     *
     * @param source the object on which the event initially occurred (never {@code null})
     */
    public MyEvent(Object source) {super(source);
    }
}

事件监听器:

@Component
public class MyEventListener implements ApplicationListener<MyEvent> {

    @EventListener //@EventListener 注解实现事件监听
    @Override
    public void onApplicationEvent(MyEvent event) {Object msg = event.getSource();
        System.out.println("自定义事件监听器(MyEventListener1)收到公布的音讯:" + msg);

    }
}

事件发布者:

public static void main(String[] args) {System.out.println(1);
        ApplicationContext ac =new AnnotationConfigApplicationContext(MyEventListener.class);
        MyEvent myEvent=new MyEvent(new Object());
        ac.publishEvent(myEvent);

    }

那么事件的管理器跑哪去了呢?

咱们在 registerListeners() 办法下面打一个断点,看看咱们自定义的事件是如何在 Spring 中起作用的。

第一个循环是 Spring 默认的监听器默认状况下是空。

咱们自定义的事件监听器在第二个循环外面被加载到了 ApplicationEventMulticaster 中,曾经很显著了,ApplicationEventMulticaster 就是咱们的事件管理者。

咱们在把他们之间的关系具体串一下。

注:播送器和下面的管理者是一个意思

到这个阶段,事件管理者和监听器都在 Spring 容器里初始化和注册了,之后就能够实现监听者模式了,对事件的公布进行监听而后解决。

在就是 ac.publishEvent(myEvent); 办法公布事件后进行的业务解决。

ApplicationContext ac =new AnnotationConfigApplicationContext(MyEventListener.class);
        MyEvent myEvent=new MyEvent(new Object());
        ac.publishEvent(myEvent);

事件监听机制理论就是主题 - 订阅模式(观察者模式)的实现,可能升高代码耦合。

本文顺便把观察者模式简要的叙述一下,不便读者能够更好的了解。

观察者(Observer)模式的定义:指多个对象间存在一对多的依赖关系,当一个对象的状态产生扭转时,所有依赖于它的对象都失去告诉并被自动更新。

public class RMBrateTest {public static void main(String[] args) {Rate rate = new RMBrate();
        Company watcher1 = new ImportCompany();
        Company watcher2 = new ExportCompany();
        rate.add(watcher1);
        rate.add(watcher2);
        rate.change(10);
        rate.change(-9);
    }
}
// 形象指标:汇率
abstract class Rate {protected List<Company> companys = new ArrayList<Company>();
    // 减少观察者办法
    public void add(Company company) {companys.add(company);
    }
    // 删除观察者办法
    public void remove(Company company) {companys.remove(company);
    }
    public abstract void change(int number);
}
// 具体指标:人民币汇率
class RMBrate extends Rate {public void change(int number) {for (Company obs : companys) {((Company) obs).response(number);
        }
    }
}
// 形象观察者:公司
interface Company {void response(int number);
}
// 具体观察者 1:进口公司
class ImportCompany implements Company {public void response(int number) {if (number > 0) {System.out.println("人民币汇率贬值" + number + "个基点,升高了进口产品老本,晋升了进口公司利润率。");
        } else if (number < 0) {System.out.println("人民币汇率升值" + (-number) + "个基点,晋升了进口产品老本,升高了进口公司利润率。");
        }
    }
}
// 具体观察者 2:进口公司
class ExportCompany implements Company {public void response(int number) {if (number > 0) {System.out.println("人民币汇率贬值" + number + "个基点,升高了进口产品支出,升高了进口公司的销售利润率。");
        } else if (number < 0) {System.out.println("人民币汇率升值" + (-number) + "个基点,晋升了进口产品支出,晋升了进口公司的销售利润率。");
        }
    }
}

汇率变动就是一个事件,当该事件产生时,它的观察者进口公司和进口公司就要做相应的变动。

书归正传从新回到 registerListeners() 办法中,这时所有的监听器都注册到了容器中,只等 publishEvent() 公布事件当前,就会执行咱们监听器中的业务逻辑。

据说在 Springboot 中利用了大量的公布订阅模式,抱着求知若渴的态度咱们去 Springboot 中搂一眼,在 registerListeners() 下面打一个断点,看看和 Spring 下面的断点有什么区别。

在 Spirng 中监听器默认是空,过后在 Springboot 中有 15 个之多,不愧是加强版的 Spring。具体这些监听器是干嘛的咱们就不深究了,在 Springboot 中会对其逐渐拆解的。

好啦,明天对 registerListeners() 的办法的分析也就完结啦。

正文完
 0