SpringBoot之Listener

springBoot监听器的主要分为两类:

运行时监听器和上下文监听器都是定义在spring.factories文件中。

1)运行时监听器

org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener

2)上下文监听器

org.springframework.context.ApplicationListener=\
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

springBoot运行时监听器作用是用来触发springBoot上下文监听器,再根据各监听器监听的事件进行区分。


1、Listener的实现逻辑

这是一种观察者模式,即订阅-发布模式

Multicaster中注册着多个Listener,Multicaster会发布事件,其过程就会遍历其中注册的Listener,每个Listener都针对这个事件做出响应,这就是Listener订阅,Caster发布
在这里插入图片描述
我们来看看运行时监听器EventPublishingRunListener的方法
在这里插入图片描述
它主要监听这几个事件,在这几个时间点,spring会调用其方法发布事件,然后触发上下文监听器去做出响应


2、Listener的注册

跟一下springboot启动的源码,开始就是监听器的注册

public SpringApplication(Object... sources) {
   initialize(sources);
}

private void initialize(Object[] sources) {
   if (sources != null && sources.length > 0) {
      this.sources.addAll(Arrays.asList(sources));
   }
   this.webEnvironment = deduceWebEnvironment();
   setInitializers((Collection) getSpringFactoriesInstances(
         ApplicationContextInitializer.class));
 //获取监听器,并且注册到发布器上
   setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
   this.mainApplicationClass = deduceMainApplicationClass();
}

这里跟一下getSpringFactoriesInstances 方法,其中包含如何获取初始的监听器的,这个方法很重要在springboot中很常见,因为spring boot的自动配置好多都是靠读取这些已有的文件来获取类名,然后反射实例化来实现自动配置的

private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type,
      Class<?>[] parameterTypes, Object... args) {
   ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
   // 获取监听器的类的全限定名
   Set<String> names = new LinkedHashSet<String>(
         SpringFactoriesLoader.loadFactoryNames(type, classLoader));
   //实例化监听器
   List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
         classLoader, args, names);
   AnnotationAwareOrderComparator.sort(instances);
   return instances;
}

public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
   String factoryClassName = factoryClass.getName();
   //FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"
   //从文件读取listener的类名 
  Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
        ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
  List<String> result = new ArrayList<String>();
  while (urls.hasMoreElements()) {
     URL url = urls.nextElement();
     Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
     String factoryClassNames = properties.getProperty(factoryClassName);
     result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
  }
  return result;

我们从上面可以看出,监听器的类名存储在META-INF/spring.factories文件中,spring通过读取该文件来获得类名,并且实例化得到监听器,最后把它们注册得到发布器上
在这里插入图片描述
在这里插入图片描述

这样我们就可以得到一个完整的注册了listener的发布器listeners
在这里插入图片描述


3、事件发布

我们从一个listeners.starting()(发布器默认的发布事件之一,也是springboot启动时的环节之一)的源码来看发布器是如何发布事件,监听器是如果处理对应的事件的
在这里插入图片描述
到这里就可以看到,发布器在发布事件时,新建一个对应的事件,然后遍历注册在其中的监听器响应事件

public void starting() {
    //这里可以看到new了一个startedEvent,用于表名发布的是start事件
   this.initialMulticaster
         .multicastEvent(new ApplicationStartedEvent(this.application, this.args));
}

public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {
   ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
   //循环遍历发布器中的listener
   for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
      Executor executor = getTaskExecutor();
      if (executor != null) {
         executor.execute(new Runnable() {
            public void run() {
               invokeListener(listener, event);
            }
         });
      }
      else {
          //调用监听器来响应事件
         invokeListener(listener, event);
      }
   }
}

跟一些监听器响应事件的源码,从下面可以看到,监听器通过判断事件的类别,来调用相应的处理函数,这就是个多分枝选择结构

public void onApplicationEvent(ApplicationEvent event) {
   if (event instanceof ApplicationStartingEvent) {
      onApplicationStartingEvent((ApplicationStartingEvent) event);
   }
   else if (event instanceof ApplicationEnvironmentPreparedEvent) {
      onApplicationEnvironmentPreparedEvent(
            (ApplicationEnvironmentPreparedEvent) event);
   }
   else if (event instanceof ApplicationPreparedEvent) {
      onApplicationPreparedEvent((ApplicationPreparedEvent) event);
   }
   else if (event instanceof ContextClosedEvent && ((ContextClosedEvent) event)
         .getApplicationContext().getParent() == null) {
      onContextClosedEvent();
   }
   else if (event instanceof ApplicationFailedEvent) {
      onApplicationFailedEvent();
   }
}

以上就是SpringBoot中监听器的初始化和执行逻辑,这里我没有详细介绍每个Listener的功能,springboot中会有一些关键的功能会在一些重要的Listener中执行,到时我会详细介绍这些关键的Listener


4、自定义Listener

自定义Listener有多种方式
详细见:https://blog.csdn.net/ignorewho/article/details/80702827

自定义的Listener分为两类:
1、存在于context的Listener集合中,参与spring启动的各个环节
2、存在于beanfactory中,与其他实例化的bean同时被加载,不会参与spring启动阶段的事件

(1)参与spring启动的listener

在启动类启动前先将Listener加入到listeners集合中

@SpringBootApplication
public class MainApplication {
   
   public static void main(String[] args) {
      System.setProperty("version",args[0]);
      SpringApplication springApplication = new SpringApplication(MainApplication.class);
      springApplication.addListeners(new MyListener1());
      springApplication.run();
   }
   
}

自定义的Listener代码

public class MyListener1 implements ApplicationListener {
    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof ApplicationStartingEvent) {
            System.out.println("starting");
        }
        else if (event instanceof ApplicationEnvironmentPreparedEvent) {
            System.out.println("EnvironmentPrepared");
        }
        else if (event instanceof ApplicationPreparedEvent) {
            System.out.println("Prepared");
        }
        else if (event instanceof ContextClosedEvent && ((ContextClosedEvent) event)
                .getApplicationContext().getParent() == null) {
            System.out.println("ContextClosed");
        }
        else if (event instanceof ApplicationFailedEvent) {
            System.out.println("Failed");
        } else if(event instanceof MyEvent){
            System.out.println("MyEvent");
        }
    }
}

自定义的event代码

public class MyEvent extends ApplicationEvent {

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

(2)不参与spring的启动,只用了监听自定义的事件
这只需要在MyListener前面加上@Component标签,不需要在启动程序中手动addListener了

@Component
public class MyListener1 implements ApplicationListener {...}

这两种情况之所以不同,是因为listener添加的时机和位置不同,一个在spring启动前就已经添加了,一个在Spring实例化bean之后才被放到beanfactory中

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值