spring官网笔记12

Spring官网阅读(十二) ApplicationContext详解(中)

在上篇文章中我们已经对ApplicationContext中的一部分内容做了介绍,ApplicationContext主要有以下几个核心功能:

1、国际化

2、借助Environment接口,完成了对Spring运行环境的抽象,可以返回环境中的属性,并能出现占位符

3、借助Resource系列的接口,完成对底层资源的访问和加载

4、继承了ApplicationEventPublisher接口,能够进行事件发布监听

5、负责创建、配置及管理Bean

1、Spring的资源(Resource)

首先要说明的是,Spring并没有让ApplicationContext接口,就像ApplicationContext接口也没有直接继承Environment接口一样。这应该也不难理解,采用组合的方式让我们的类更加清亮,也起到了解耦的作用。ApplicationContext跟Resource相关的接口的继承关系如下

在这里插入图片描述

不管是ResourceLoader还是ResourcePatternResolver其实都是为了获取Resource对象,不过ResourcePatternResolver在ResourceLoader的基础上扩展了一个获取多个Resource的方法,我们在后文介绍。

接口简介

public interface InputStreamSource {
   //每次调用都返回一个当前资源对应的java.io.InputStream字节流
   InputStream getInputStream() throws IOException;
}
public interface Resource extends InputStreamSource {
   //用于判断对应的资源是否存在
   boolean exists();
   //用于判断对应资源的内容是否可读,需要注意的是其结果为true的时候,其内容未必真的可读,但结果返回false未必真的不可读
   default boolean isReadable() {
      return exists();
   }
   //用于判断当前资源是否代表一个已经打开的输入流,如果结果为true,则表示当前资源的输入流不可多次读取,而且在读取以后需要对它进行关闭,以防止内存泄漏。
   //该方法主要针对于实现类InputStreamSource,实现类中只有它的返回结果为true,其它都为false
   default boolean isOpen() {
      return false;
   }
   //当前资源是否是一个文件
   default boolean isFile() {
      return false;
   }
   //当前资源对应的URL。如果当前资源不能解析为一个URL则会抛出异常
   URL getURL() throws IOException;
   //当前资源对应的URI。如果当前资源不能解析为一个URI则会抛出异常
   URI getURI() throws IOException;
   //返回当前资源对应的File。如果当前资源不能以绝对路径解析为一个File则会抛出异常
   File getFile() throws IOException;
   //返回一个ReadableByteChannel
   default ReadableByteChannel readableChannel() throws IOException {
      return Channels.newChannel(getInputStream());
   }
   //返回资源的长度
   long contentLength() throws IOException;
   //最后修改时间
   long lastModified() throws IOException;
   //根据当前资源以及相对当前资源的路径创建一个新的资源,比如当前Resource代表文件资源d:/abc/a.java 则createRelative(“xyz。txt”)将返回文件资源d:/abc/xyz.txt"
   Resource createRelative(String relativePath) throws IOException;
   //返回文件一个文件名称,通常来说会返回该资源路径的最后一段
   @Nullable
   String getFilename();
   //返回描述信息
   String getDescription();

}

UML类图

在这里插入图片描述

因为实现了Resource接口的类很多,并且一些类我们也不常用到或者很简单,所以上图中省略了一些不重要的分支,接下来我们就一个一个分析。

抽象基类AbstractResource

实现了Resource接口,是大多数Resource的实现类的基类,提供了很多通用的方法。

比如exists方法会检查是否一个文件或者输入流能够被打开。isOpen永远返回false。“getUrl()”和“getFile()”方法会抛出异常。toString将会返回描述信息。

FileSystemResource

基于java的文件系统封装而成的一个资源对象

AbstractFileResolvingResource

将URL解析成文件引用,既会处理协议为“file”的URL,也会处理JBoss的“vfs”协议。然后相应的解析成对应的文件系统引用。

ByteArrayResource

根据一个给定的字节数组构建的一个资源。同时给出一个对应的输入流

BeanDefinitionResource

只是对BeanDefinition进行的一次性描述性的封装

InputStreamSource

是针对输入流封装的资源,他的构建需要一个输入流。对于“getInputStream”操作将直接返回字节流,因此只能读取一次该字节流,即“isOpen”永远返回true。

UrlResource

UrlResource代表URL资源,用于简化URL资源访问。

UrlResource一般支持如下资源访问:

-http:通过标准的http协议访问web资源,如new UrlResource(“http://地址”);

-ftp:通过ftp协议访问资源,如new UrlResource(“ftp://地址”)

-file:通过file协议访问本地文件系统资源,如new UrlResource(“file:d:/text.txt”)

ClassPathResource

JDK获取资源方式有两种

1、使用Class对象的getResource(String path)获取资源URL,getResourceAsStream(String path)获取资源流。参数既可以是当前class文件的相对路径(以文件夹或文件开头),也可以是当前class文件的绝对路径(以"/"开头,相当于当前classpath的根目录)

2、使用ClassLoader对象的getResource(String path)获取资源URL,getResourceAsStream(String path获取资源流。参数只能是绝对路径,但不以“/”开头

ClassPathResource代表classpath路径的资源,将使用给定的Class或ClassLoader进行加载classpath资源。isOpen永远是false,表示可以获取多次资源。

ServletContextResource

是针对于ServletContext封装的资源,用于访问ServletContext环境下的资源。ServletContextResource持有一个ServletContext的引用,其底层是通过ServletContext的getResource()方法和getResourceAsStream()方法来获取资源的。

ResourceLoader

接口简介

ResourceLoader接口被设计用来从指定的位置加载一个Resource,其接口定义如下

public interface ResourceLoader {
   //classpath:
   String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;
   //核心方法,从指定的位置加载一个Resource
   //1、支持权限的URL格式,如 file:C:test.dat
   //2、支持classpath的格式,如:classpath:test.dat
   //3、支持文件相对的路径,如//WEB-INF/test.dat
   Resource getResource(String location);
   //返回用于加载资源的classLoader
   @Nullable
   ClassLoader getClassLoader();

}

UML类图

在这里插入图片描述

对于一些不是很重要的类我都省略了,其实核心的类我们只需要关注DefaultResourceLoader就可以了,因为其余子类(除了GenericApplicationContext)都是直接继承了DefaultResourceLoader的getResource方法。代码如下:

@Override
public Resource getResource(String location) {
   Assert.notNull(location, "Location must not be null");
   //正常来说protocolResolvers集合是空的除非我们调用了它的addProtocolResolvers方法添加了协议处理器
   //调用addProtocolResolvers方法添加的协议处理器会覆盖原有的处理器
   for (ProtocolResolver protocolResolver : getProtocolResolvers()) {
      Resource resource = protocolResolver.resolve(location, this);
      if (resource != null) {
         return resource;
      }
   }
   //如果是以"/"开头,直接返回一个classpathResource
   if (location.startsWith("/")) {
      return getResourceByPath(location);
   }
   //如果是形如:classpath:test.dat也直接返回一个classpathResource
   else if (location.startsWith(CLASSPATH_URL_PREFIX)) {
      return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
   }
   else {
      try {
         //否则将其解析为一个URL
         URL url = new URL(location);
         //如果是一个文件,直接返回一个FileUrlResource 否则返回一个普通的UrlResource
         return (ResourceUtils.isFileURL(url) ? new FileUrlResource(url) : new UrlResource(url));
      }
      catch (MalformedURLException ex) {
         //如果URL转换失败,还是作为一个普通的classPathResource
         return getResourceByPath(location);
      }
   }
}

资源路径

ant-style

类似下面这种含有通配符的路径

/WEB-INF/*-context.xml
com/myhome/**/applicationContext.xml
file:D:/any/path/*-spring.xml
classpath:com/myhome/**/applicationContext.xml

classpath跟classpath*

classpath:用于加载类路径(包括jar包)中的一个且仅有一个资源

classpath*:用于加载类路径(包括jar包)中所有匹配的资源,可使用Ant路径格式

2、Spring中的时间监听机制(publish-event)

我们知道ApplicationContext继承了ApplicationEventPublisher接口,能够进行事件发布监听,那么什么是事件发布监听呢?我们从监听者模式说起

监听者模式

概念

事件源经过事件的封装传给监听器,当事件源触发事件后,监听器接收到的事件可以回调事件的方法

在这里插入图片描述

Spring对监听者模式的实践

我们通过一个例子来体会下

public class Main12 {
   public static void main(String[] args) {
      //创建一个事件发布器(事件源),为了方便,我这里直接通过传入EventListener.class来将监听器注册到容器中
      AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
      //发布一个事件
      ac.publishEvent(new MyEvent("hello event"));
      //接收到事件:hello event
      //处理事件。。。。
   }
}
public class MyEvent extends ApplicationEvent {
	public MyEvent(Object source) {
		super(source);
	}
}
@Component
public class MyListener implements ApplicationListener<MyEvent> {
	@Override
	public void onApplicationEvent(MyEvent event) {
		System.out.println("接收到事件:" + event.getSource());
		System.out.println("处理事件。。。。");
	}
}

在上面的例子中,主要涉及到三个角色,也就是我们之前提到的

1、事件源 AnnotationConfigApplicationContext

2、事件:MyEvent,继承了ApplicationEvent

3、事件监听器:MyListener,实现了ApplicationListener

我们通过AnnotationConfigApplicationContext发布了一个事件(MyEvent),然后事件监听器监听到了事件,并进行了对应的处理

UML类图

在这里插入图片描述

接口简介

ApplicationEventPublisher

@FunctionalInterface
public interface ApplicationEventPublisher {

   default void publishEvent(ApplicationEvent event) {
      publishEvent((Object) event);
   }
   //从版本4.2后新增的方法
   //调用这个方法发布事件不需要实现ApplicationEvent接口,会被封装成PayloadApplicationEvent
   //如果实现了ApplicationEvent接口,则会正常发布
   void publishEvent(Object event);

}

对于这个接口,我们需要关注有哪些子类实现了publishEvent(Object event)这个方法即可,搜索发现,我们只需要关注

org.springframework.context.support.AbstractApplicationContext#publishEvent(org.springframework.context.ApplicationEvent)这个方法即可,关于这个方法在后文的源码分析中我们再详细介绍

ApplicationEvent

继承关系如下:

在这里插入图片描述

我们主要关注上面4个类(PayloadApplicationEvent在后面源码分析中再介绍),下面几个都是spring直接在内部使用到了的事件,比如ContextClosedEvent,在容器关闭时会被创建然后发布。

//这个类在设计时是作为整个应用类所有事件的基类,之所以设计成抽象类,是因为直接发布这个对象没有任何意义
public abstract class ApplicationEvent extends EventObject {
   private static final long serialVersionUID = 7099057708183571937L;
   //事件创建的事件
   private final long timestamp;
   public ApplicationEvent(Object source) {
      super(source);
      this.timestamp = System.currentTimeMillis();
   }
   public final long getTimestamp() {
      return this.timestamp;
   }
}
//这个类是java的util包下的一个类,java本身也具有一套事件机制
public class EventObject implements java.io.Serializable {

    private static final long serialVersionUID = 5516075349620653480L;
    //事件所发生的那个源,比如在java中我们发起了一个鼠标点击事件,那么这个source就是鼠标
    protected transient Object  source;
    public EventObject(Object source) {
        if (source == null)
            throw new IllegalArgumentException("null source");

        this.source = source;
    }
    public Object getSource() {
        return source;
    }

    public String toString() {
        return getClass().getName() + "[source=" + source + "]";
    }
}
//这个类是2.5版本时增加的一个类,相对于它直接的父类ApplicationEvent而言,最大的区别就是
//将source规定为了当前的容器。就目前而言的话这个类作用不大,一般情况下我们定义的事件也不一定需要ApplicationContextEvent
//后面我会介绍注解的方式进行事件的发布监听
public abstract class ApplicationContextEvent extends ApplicationEvent {
	public ApplicationContextEvent(ApplicationContext source) {
		super(source);
	}
	public final ApplicationContext getApplicationContext() {
		return (ApplicationContext) getSource();
	}
}

ApplicationListener

@FunctionalInterface
//事件监听器,实现了java.util包下的eventListener接口
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
    //根据接口声明的泛型类型处理对应的事件
    //比如在我们之前的例子中,通过 MyListener implements ApplicationListener<MyEvent>
    //在接口中声明了泛型类型为MyEvent这一类事件
   void onApplicationEvent(E event);
}

注解方式实现事件发布机制

在上面的例子中,我们通过传统的方式事件的发布监听,但是上面的过程有点繁琐,我们发布的事件需要实现指定的接口,在进行监听时又需要实现指定的接口,每增加一个发布的事件,代表我们需要多两个类。这样项目的迭代中,会导致我们关于事件的类越来越多。所以,在Spring4.2版本后,新增一个注解,让我们可以快速的实现对发布的事件的监听。示例代码如下:

@ComponentScan("com.phr.event1")
public class Main {
   public static void main(String[] args) {
      ApplicationEventPublisher publisher = new AnnotationConfigApplicationContext(Main.class);
      publisher.publishEvent(new Event("注解事件"));
       //程序打印
       //接收到事件:注解事件
	  //处理事件
   }
   static class Event{
      String name;
      Event(String name){
         this.name = name;
      }
      @Override
      public String toString(){
         return name;
      }
   }
   @Component
   static class Listener{
      @EventListener
      public void listen(Event event){
         System.out.println("接收到事件:" + event);
         System.out.println("处理事件");
      }
   }
}

在上面的例子中,我们使用一个@EventListener注解,直接标注了Listener类中的一个方法是一个监听器,并通过方法的参数类型Event指定了这个监听器监听的事件类型为Event类型。在这个例子中,第一,我们事件不需要去继承指定的类。第二,我们的监听器也不需要去实现特定的接口,极大的方便了我们的开发。

异步的方式实现事件监听

对于上面的例子,我们只需要按下面这种方式添加两个注解即可实现异步。

@ComponentScan("com.phr.event1")
@Configuration
@EnableAsync//开启异步的支持
public class Main {
	public static void main(String[] args) {
		ApplicationEventPublisher publisher = new AnnotationConfigApplicationContext(Main.class);
		publisher.publishEvent(new Event("注解事件"));
	}
	static class Event{
		String name;
		Event(String name){
			this.name = name;
		}
		@Override
		public String toString(){
			return name;
		}
	}
	@Component
	static class Listener{
		@EventListener
		//标注这个方法要异步执行
		@Async
		public void listen(Event event){
			System.out.println("接收到事件:" + event);
			System.out.println("处理事件");
		}
	}
}

对于上面的两个注解@EnableAsync和@Async,我们会在Aop系列文章中再做介绍,目前而言,大家知道这种方式能开启异步支持即可。

对监听器进行排序

我们发布一个事件时,可能会同时被两个监听器监听到,比如我们上面的例子中如果同时存在两个监听器,如下:

    @Component
   static class Listener{
      @EventListener
      //标注这个方法要异步执行
//    @Async
      public void listen(Event event){
         System.out.println("接收到事件:" + event);
         System.out.println("处理事件");
      }
      @EventListener
      public void listen2(Event event){
         System.out.println("接收到事件2:" + event);
         System.out.println("处理事件2");
      }
   }

在这种情况下,我们可能希望监听器可以按照我们想要的顺序执行,这个时候就需要用到另外一个注解@Order

还是上面的代码,我们添加注解如下:

    @Component
   static class Listener{
      @EventListener
      //标注这个方法要异步执行
//    @Async
      @Order(2)
      public void listen(Event event){
         System.out.println("接收到事件:" + event);
         System.out.println("处理事件");
      }
      @EventListener
      @Order(1)
      public void listen2(Event event){
         System.out.println("接收到事件2:" + event);
         System.out.println("处理事件2");
      }
   }

输出:

接收到事件2:注解事件
处理事件2
接收到事件:注解事件
处理事件

注解中参数越小,代表的优先级越高,在上面的例子中,从打印的结果就可以看出。

那么spring到底是如何实现的这一套事件发布机制呢?接下来我们来进行源码分析。

源码分析(publishEvent方法)

我们需要分析的代码主要是

org.springframework.context.support.AbstractApplicationContext#publishEvent(java.lang.Object, org.springframework.core.ResolvableType)方法,源码如下:

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 applicationEvent;
   if (event instanceof ApplicationEvent) {
      applicationEvent = (ApplicationEvent) event;
   }
   else {
      //如果发布的事件不是一个ApplicationEvent,包装成一个PayloadApplicationEvent
      applicationEvent = new PayloadApplicationEvent<>(this, event);
      //我们在应用程序中发布事件时,这个eventType必定为null
      if (eventType == null) {
         eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
      }
   }

   // Multicast right now if possible - or lazily once the multicaster is initialized
   //我们在自己的项目中调用时,这个earlyApplicationEvents必定为null
   if (this.earlyApplicationEvents != null) {
      this.earlyApplicationEvents.add(applicationEvent);
   }
   else {
      //获取事件发布器,发布对应的事件
      getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
   }

   // Publish event via parent context as well...
   //父容器中也需要发布事件
   if (this.parent != null) {
      if (this.parent instanceof AbstractApplicationContext) {
         ((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
      }
      else {
         this.parent.publishEvent(event);
      }
   }
}

上面的代码核心就是getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);方法,我们按以下几个部分分析

getApplicationEventMulticaster()方法

multicastEvent(applicationEvent, eventType)方法

getApplicationEventMulticaster()方法代码如下

ApplicationEventMulticaster getApplicationEventMulticaster() throws IllegalStateException {
   if (this.applicationEventMulticaster == null) {
      throw new IllegalStateException("ApplicationEventMulticaster not initialized - " +
            "call 'refresh' before multicasting events via the context: " + this);
   }
   return this.applicationEventMulticaster;
}

可以看到很简单,只是简单的把容器已经初始化的applicationEventMulticaster返回,那么现在问题来了。

1、applicationEventMulticaster是什么

public interface ApplicationEventMulticaster {
   //添加事件监听器
   void addApplicationListener(ApplicationListener<?> listener);
   //通过名称添加事件监听器
   void addApplicationListenerBean(String listenerBeanName);
   //移除事件监听器
   void removeApplicationListener(ApplicationListener<?> listener);
   //根据名称移除事件监听器
   void removeApplicationListenerBean(String listenerBeanName);
   //移除注册在这个事件分发器的所有监听器
   void removeAllListeners();
   //分发事件
   void multicastEvent(ApplicationEvent event);
   //分发事件eventType代表事件类型,如果eventType这个为空,会从事件中推断出事件类型
   void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType);
}

UML类图

在这里插入图片描述

主要涉及到两个类:

1.1、AbstractApplicationEventMulticaster这个类对ApplicationEventMulticaster这个接口基础方法做了实现,除了核心方法multicastEvent。这个类最大的作用就是获取监听器,稍后我们会介绍。

1.2、SimpleApplicationEventMulticaster这个是spring默认提供的一个事件分发器,如果我们没有进行特别配置的话,就会采用这个类生成的对象为容器的事件分发器

2,容器在什么时候对其进行的初始化

在这个org.springframework.context.support.AbstractApplicationContext#refresh这个方法里面的

public void refresh() throws BeansException, IllegalStateException {
   synchronized (this.startupShutdownMonitor) {
      prepareRefresh();
      // 刷新BeanFactory,得到一个空的BeanFactory-Default
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();  // co
      // 准备BeanFactory
      // 1. 设置BeanFactory的类加载器、表达式解析器、类型转化注册器
      // 2. 添加三个BeanPostProcessor,注意是具体的BeanPostProcessor实例对象
      // 3. 记录ignoreDependencyInterface
      // 4. 记录ResolvableDependency
      // 5. 添加三个单例Bean
      prepareBeanFactory(beanFactory);
      try {
         // 子类可以对BeanFactory进行进一步初始化
         postProcessBeanFactory(beanFactory);
         // BeanFactory准备好了之后,执行BeanFactoryPostProcessor,开始对BeanFactory进行处理
         // 默认情况下:
         // 此时beanFactory的beanDefinitionMap中有6个BeanDefinition,5个基础BeanDefinition+AppConfig的BeanDefinition
         // 而这6个中只有一个BeanFactoryPostProcessor:ConfigurationClassPostProcessor
         // 这里会执行ConfigurationClassPostProcessor进行@Component的扫描,扫描得到BeanDefinition,并注册到beanFactory中
         // 注意:扫描的过程中可能又会扫描出其他的BeanFactoryPostProcessor,那么这些BeanFactoryPostProcessor也得在这一步执行
         invokeBeanFactoryPostProcessors(beanFactory);  //BeanDefinitionRegistryPostProcessor ,BeanFactoryPostProcessors
         // 从BeanFactory找出扫描得到得BeanPostProcessor,实例化并注册到BeanFactory中
         registerBeanPostProcessors(beanFactory);
         // 初始化MessageSource,如果配置了一个名字叫做“messageSource”的BeanDefinition
         // 就会把这个Bean创建出来,并赋值给ApplicationContext的messageSource属性
         // 这样ApplicationContext就可以使用国际化的功能了
         initMessageSource();
         // 设置ApplicationContext的applicationEventMulticaster
         initApplicationEventMulticaster();
         // 执行子类的onRefresh方法
         onRefresh();
         // 注册Listener
         registerListeners();
         // 完成beanFactory的初始化(实例化非懒加载的单例bean)
         finishBeanFactoryInitialization(beanFactory);
         // 发布事件
         finishRefresh();
      }

initApplicationEventMulticaster()方法,从名字上我们就知道,这是对ApplicationEventMulticaster进行初始化的,我们看看这个方法做了什么?

protected void initApplicationEventMulticaster() {
   ConfigurableListableBeanFactory beanFactory = getBeanFactory();
   //判断容器中是否包含了一个名为applicationEventMulticaster的applicationEventMulticaster类对象,如果包含直接获取
   if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
      this.applicationEventMulticaster =
            beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
      if (logger.isTraceEnabled()) {
         logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
      }
   }
   //没有包含,new SimpleApplicationEventMulticaster并将其注册到容器中
   else {
      this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
      beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
      if (logger.isTraceEnabled()) {
         logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +
               "[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
      }
   }
}

这段代码的含义告诉我们,可以自己配置ApplicationEventMulticaster,如果没有进行配置,那么将默认使用一个SimpleApplicationEventMulticaster。

接下来,我们配置一个简单的ApplicationEventMulticaster,示例代码如下:

@Component("applicationEventMulticaster")
static class MyEventMulticaster extends AbstractApplicationEventMulticaster{

   @Override
   public void multicastEvent(ApplicationEvent event) {
      ResolvableType resolvableType = ResolvableType.forInstance(event);
      Collection<ApplicationListener<?>> applicationListeners = getApplicationListeners(event, resolvableType);
      for(ApplicationListener applicationListener : applicationListeners){
         applicationListener.onApplicationEvent(event);
      }
   }

   @Override
   public void multicastEvent(ApplicationEvent event, ResolvableType eventType) {
      System.out.println("进入MyEventMulticaster");
   }
}

运行程序会发现“进入MyEventMulticaster”这句话打印了两次,这是一次是容器启动时会发布一个ContextStartedEvent事件,也会调用我们配置的事件分发器进行发布。

multicastEvent方法

在spring容器中,只内置了一个方法的实现类,就是SimpleApplicationEventMulticaster。实现的逻辑如下:

@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
   ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
   Executor executor = getTaskExecutor();
   for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
      if (executor != null) {
         executor.execute(() -> invokeListener(listener, event));
      }
      else {
         invokeListener(listener, event);
      }
   }
}

上面的代码主要的实现逻辑可以分为几步:

1、推断事件类型

2、根据事件类型获取对应的监听器

3、执行监听逻辑

我们一步步分析

private ResolvableType resolveDefaultEventType(ApplicationEvent event) {
   return ResolvableType.forInstance(event);
}
	public static ResolvableType forInstance(Object instance) {
		Assert.notNull(instance, "Instance must not be null");
		if (instance instanceof ResolvableTypeProvider) {
			ResolvableType type = ((ResolvableTypeProvider) instance).getResolvableType();
			if (type != null) {
				return type;
			}
		}
		//返回通过事件的class类型封装的一个ResolvableType
		return ResolvableType.forClass(instance.getClass());
	}

上面的代码涉及到一个概念就是ResolvableType,对于ResolvableType我们需要了解的是,ResolvableType为所有的java类型提供了统一的数据结构以及API,换句话说,一个ResolvableType对象对应着一种java的类型。我们可以通过ResolvableType获取类型携带的信息:

1、getSuperType():获取直接父类型

2、getInterfaces() :获取接口类型

3、getGeneric():获取类型携带的泛型类型

4、resolve():Type对象到Class对象的转换

另外,ResolvableType的构造方法全部为私有的,我们不能直接new,只能使用其提供的静态方法进行类型获取:

1、forField(Field field)获取指定字段的类型

2、forMethodParameter(Method method, int parameterIndex)获取指定方法指定形参的类型

3、forMethodReturnType(Method method)获取指定方法的返回值类型

4、forClass(@Nullable Class<?> clazz)直接封装的指定的类型

5、forInstance(Object instance)获取指定实例的泛型信息

关于ResolvableType跟java中的类型中的关系请关注我的后续文章,限于篇幅本文不做过多介绍

getApplicationListeners(event, type),获取对应的事件监听器

事件监听器主要分为两种,一种是我们通过实现接口直接注册到容器中的Bean,例如下面这种

@Component
public class MyListener implements ApplicationListener<MyEvent> {
   @Override
   public void onApplicationEvent(MyEvent event) {
      System.out.println("接收到事件:" + event.getSource());
      System.out.println("处理事件。。。。");
   }
}

另外一种通过注解的方式,就是下面这种

    @Component
   static class Listener{
      @EventListener
      //标注这个方法要异步执行
//    @Async
      @Order(2)
      public void listen(Event event){
         System.out.println("接收到事件:" + event);
         System.out.println("处理事件");
      }
      @EventListener
      @Order(1)
      public void listen2(Event event){
         System.out.println("接收到事件2:" + event);
         System.out.println("处理事件2");
      }
   }

对于实现接口的方式不用多说,因为实现了这个类本身就会被扫描然后加入到容器中。对于注解这种方式,Spring是通过一个回调的方式实现的。大家关注下这个接口org.springframework.beans.factory.SmartInitializingSingleton,同时找到实现类org.springframework.context.event.EventListenerMethodProcessor。在这个类中,会先调用afterSingletonsInstantiated方法,然后调用processBean方法,在这个方法里会遍历容器中的所有的bean,然后遍历Bean中的每个方法是否加了@EventListener注解。如果添加了这个注解,会将这个Method方法包装成一个ApplicationListenerMethodAdapter,这个类本身也实现了applicationListener,之后在添加到监听器的集合中。

invokeListener,执行监听的逻辑

本身这个方法没有什么好说的了,就是调用了ApplicationListener的onApplicationEvent方法,执行我们的业务逻辑。但是值得注意的饿是,在调用invokeListener方法前进行了一个判断

@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
   ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
   Executor executor = getTaskExecutor();
   for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
      if (executor != null) {
         executor.execute(() -> invokeListener(listener, event));
      }
      else {
         invokeListener(listener, event);
      }
   }
}

会先判断能否取到一个executor,如果能获取到会通过这个executor异步执行监听的逻辑,所以基于这段代码,我们可以不通过@Async注解实现对事件的异步监听,而是复写SimpleApplicationEventMulticaster这个类中的方法,如下:

Listener,之后在添加到监听器的集合中。

invokeListener,执行监听的逻辑

本身这个方法没有什么好说的了,就是调用了ApplicationListener的onApplicationEvent方法,执行我们的业务逻辑。但是值得注意的饿是,在调用invokeListener方法前进行了一个判断

@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
   ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
   Executor executor = getTaskExecutor();
   for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
      if (executor != null) {
         executor.execute(() -> invokeListener(listener, event));
      }
      else {
         invokeListener(listener, event);
      }
   }
}

会先判断能否取到一个executor,如果能获取到会通过这个executor异步执行监听的逻辑,所以基于这段代码,我们可以不通过@Async注解实现对事件的异步监听,而是复写SimpleApplicationEventMulticaster这个类中的方法,如下:

相比于通过@Async注解实现对事件的异步监听,我更加倾向于这种通过复写方法的方式进行实现,主要原因是我们通过注解实现所有加了这个注解的方法都是用的同一个线程池,这些加了注解的方法有些可能并不是进行事件监听的,这样显然是不合理的。而后面这种方式,我们可以确保创建的线程池是针对事件监听的,甚至可以根据不同的事件类型路由到不同的线程池。这样更加合理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值