diff --git "a/docs/Spring/Spring\345\256\230\347\275\221\345\255\246\344\271\240\347\254\224\350\256\260/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\344\272\214\357\274\211ApplicationContext\350\257\246\350\247\243\357\274\210\344\270\255\357\274\211/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\344\272\214\357\274\211ApplicationContext\350\257\246\350\247\243\357\274\210\344\270\255\357\274\211.md" "b/docs/Spring/Spring\345\256\230\347\275\221\345\255\246\344\271\240\347\254\224\350\256\260/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\344\272\214\357\274\211ApplicationContext\350\257\246\350\247\243\357\274\210\344\270\255\357\274\211/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\344\272\214\357\274\211ApplicationContext\350\257\246\350\247\243\357\274\210\344\270\255\357\274\211.md"
new file mode 100644
index 0000000000000000000000000000000000000000..4f7fe3c46238e8f592b6ecf2390308524f0da244
--- /dev/null
+++ "b/docs/Spring/Spring\345\256\230\347\275\221\345\255\246\344\271\240\347\254\224\350\256\260/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\344\272\214\357\274\211ApplicationContext\350\257\246\350\247\243\357\274\210\344\270\255\357\274\211/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\344\272\214\357\274\211ApplicationContext\350\257\246\350\247\243\357\274\210\344\270\255\357\274\211.md"
@@ -0,0 +1,812 @@
+Spring官网阅读(十二)ApplicationContext详解(中)
+
+> 在上篇文章中我们已经对ApplicationContext的一部分内容做了介绍,ApplicationContext主要具有以下几个核心功能:
+>
+> 1. 国际化
+>
+> 2. 借助Environment接口,完成了对Spring运行环境的抽象,可以返回环境中的属性,并能出现出现的占位符
+> 3. 借助于Resource系列接口,完成对底层资源的访问及加载
+> 4. 继承了ApplicationEventPublisher接口,能够进行事件发布监听
+> 5. 负责创建、配置及管理Bean
+>
+> 在上篇文章我们已经分析学习了1,2两点,这篇文章我们继续之前的学习
+
+# 1、Spring的资源(Resource)
+
+首先需要说明的是,Spring并没有让ApplicationContext直接继承Resource接口,就像ApplicationContext接口也没有直接继承Environment接口一样。这应该也不难理解,采用这种组合的方式会让我们的类更加的轻量,也起到了解耦的作用。ApplicationContext跟Resource相关的接口的继承关系如下
+
+
+
+不管是ResourceLoader还是ResourcePatternResolver其实都是为了获取Resource对象,不过ResourcePatternResolver在ResourceLoader的基础上扩展了一个获取多个Resource的方法,我们在后文会介绍。
+
+## 接口简介
+
+Resouce接口继承了 InputStreamSource.
+
+```java
+public interface InputStreamSource {
+ // 每次调用都将返回一个当前资源对应的java.io. InputStream字节流
+ InputStream getInputStream() throws IOException;
+}
+```
+
+```java
+public interface Resource extends InputStreamSource {
+
+ // 用于判断对应的资源是否真的存在
+ boolean exists();
+
+ // 用于判断对应资源的内容是否可读。需要注意的是当其结果为true的时候,其内容未必真的可读,但如果返回false,则其内容必定不可读
+ default boolean isReadable() {
+ return exists();
+ }
+
+ // 用于判断当前资源是否代表一个已打开的输入流,如果结果为true,则表示当前资源的输入流不可多次读取,而且在读取以后需要对它进行关闭,以防止内存泄露。该方法主要针对于实现类InputStreamResource,实现类中只有它的返回结果为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进行的一次描述性的封装
+
+### InputStreamResource
+
+是针对于输入流封装的资源,它的构建需要一个输入流。 对于“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:/test.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,其接口定义如下
+
+```java
+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`方法。代码如下:
+
+```java
+ @Override
+ public Resource getResource(String location) {
+ Assert.notNull(location, "Location must not be null");
+
+ // 正常来说protocolResolvers集合是空的,除非我们调用了它的addProtocolResolver方法添加了自定义协议处理器,调用addProtocolResolver方法所添加的协议处理器会覆盖原有的处理逻辑
+ for (ProtocolResolver protocolResolver : this.protocolResolvers) {
+ 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
+
+类似下面这种含有通配符的路径
+
+```xml
+/WEB-INF/*-context.xml
+com/mycompany/**/applicationContext.xml
+file:C:/some/path/*-context.xml
+classpath:com/mycompany/**/applicationContext.xml
+```
+
+#### classpath跟classpath*
+
+classpath:用于加载类路径(包括jar包)中的一个且仅一个资源;
+
+classpath*:用于加载类路径(包括jar包)中的所有匹配的资源,可使用Ant路径模式。
+
+# 2、Spring中的事件监听机制(publish-event)
+
+我们知道,ApplicationContext接口继承了ApplicationEventPublisher接口,能够进行事件发布监听,那么什么是事件的发布跟监听呢?我们从监听者模式说起
+
+### 监听者模式
+
+#### 概念
+
+事件源经过事件的封装传给监听器,当事件源触发事件后,监听器接收到事件对象可以回调事件的方法
+
+
+
+#### Spring对监听者模式的实践
+
+我们直接通过一个例子来体会下
+
+```java
+public class Main {
+ public static void main(String[] args) {
+ // 创建一个事件发布器(事件源),为了方便,我这里直接通过传入EventListener.class来将监听器注册到容器中
+ ApplicationEventPublisher ac = new AnnotationConfigApplicationContext(EventListener.class);
+ // 发布一个事件
+ ac.publishEvent(new MyEvent("hello event"));
+ // 程序会打印如下:
+ // 接收到事件:hello event
+ // 处理事件....
+ }
+
+ static class MyEvent extends ApplicationEvent {
+ public MyEvent(Object source) {
+ super(source);
+ }
+ }
+
+ @Component
+ static class EventListener implements ApplicationListener {
+ @Override
+ public void onApplicationEvent(MyEvent event) {
+ System.out.println("接收到事件:" + event.getSource());
+ System.out.println("处理事件....");
+ }
+ }
+}
+```
+
+在上面的例子中,主要涉及到了三个角色,也就是我们之前提到的
+
+1. 事件源:ApplicationEventPublisher
+2. 事件:MyEvent,继承了ApplicationEvent
+3. 事件监听器:EventListener,实现了ApplicationListener
+
+我们通过ApplicationEventPublisher发布了一个事件(MyEvent),然后事件监听器监听到了事件,并进行了对应的处理。
+
+#### 接口简介
+
+##### ApplicationEventPublisher
+
+```java
+public interface ApplicationEventPublisher {
+
+ default void publishEvent(ApplicationEvent event) {
+ publishEvent((Object) event);
+ }
+
+ // 从版本4.2后新增的方法
+ // 调用这个方法发布的事件不需要实现ApplicationEvent接口,会被封装成一个PayloadApplicationEvent
+ // 如果event实现了ApplicationEvent接口,则会正常发布
+ void publishEvent(Object event);
+
+}
+```
+
+对于这个接口,我只需要关注有哪些子类是实现了`publishEvent(Object event)`这个方法即可。搜索发现,我们只需要关注`org.springframework.context.support.AbstractApplicationContext#publishEvent(java.lang.Object)`这个方法即可,关于这个方法在后文的源码分析中我们再详细介绍。
+
+##### ApplicationEvent
+
+继承关系如下:
+
+
+
+我们主要关注上面4个类(*PayloadApplicationEvent*在后文源码分析时再介绍),下面几个都是Spring直接在内部使用到了的事件,比如ContextClosedEvent,在容器关闭时会被创建然后发布。
+
+```java
+// 这个类在设计时是作为整个应用内所有事件的基类,之所以被设计成抽象类,是因为直接发布这个对象没有任何意义
+public abstract class ApplicationEvent extends EventObject {
+
+ // 事件创建的事件
+ 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 {
+
+ // 事件所发生的那个源,比如在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
+
+```java
+// 事件监听器,实现了java.util包下的EventListener接口
+public interface ApplicationListener extends EventListener {
+
+ // 根据接口申明的泛型类型处理对应的事件
+ // 比如在我们之前的例子中,通过《EventListener implements ApplicationListener》
+ // 在接口中申明了泛型类型为MyEvent,所以能监听到MyEvent这一类事件
+ void onApplicationEvent(E event);
+
+}
+```
+
+#### 注解方式实现事件发布机制
+
+在上面的例子中,我们通过传统的方式实现了事件的发布监听,但是上面的过程实在是有点繁琐,我们发布的事件需要实现指定的接口,在进行监听时又需要实现指定的接口。每增加一个发布的事件,代表我们需要多两个类。这样在项目的迭代过程中,会导致我们关于事件的类越来越多。所以,在Spring4.2版本后,新增一个注解,让我们可以快速的实现对发布的事件的监听。示例代码如下:
+
+```java
+@ComponentScan("com.dmz.official.event")
+public class Main02 {
+ public static void main(String[] args) {
+ ApplicationEventPublisher publisher = new AnnotationConfigApplicationContext(Main02.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类型。在这个例子中,第一,我们事件不需要去继承特定的类,第二,我们的监听器也不需要去实现特定的接口,极大的方便了我们的开发。
+
+#### 异步的方式实现事件监听
+
+对于上面的例子,我们只需要按下面这种方式添加两个注解即可实现异步:
+
+```java
+@ComponentScan("com.dmz.official.event")
+@Configuration
+@EnableAsync // 1.开启异步支持
+public class Main02 {
+ public static void main(String[] args) {
+ AnnotationConfigApplicationContext publisher = new AnnotationConfigApplicationContext(Main02.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
+ // 2.标志这个方法需要异步执行
+ public void listen(Event event) {
+ System.out.println("接收到事件:" + event);
+ System.out.println("处理事件");
+ }
+ }
+}
+```
+
+对于上面的两个注解`@EnableAsync `以及`@Async`,我会在AOP系列的文章中再做介绍,目前而言,大家知道能通过这种方式开启异步支持即可。
+
+#### 对监听器进行排序
+
+当我们发布一个事件时,可能会同时被两个监听器监听到,比如在我们上面的例子中如果同时存在两个监听器,如下:
+
+```java
+@Component
+static class Listener {
+ @EventListener
+ public void listen1(Event event) {
+ System.out.println("接收到事件1:" + event);
+ System.out.println("处理事件");
+ }
+
+ @EventListener
+ public void listen2(Event event) {
+ System.out.println("接收到事件2:" + event);
+ System.out.println("处理事件");
+ }
+}
+```
+
+在这种情况下,我们可能希望两个监听器可以按顺序执行,这个时候需要用到另外一个注解了:`@Order`
+
+还是上面的代码,我们添加注解如下:
+
+```java
+@Component
+static class Listener {
+ @EventListener
+ @Order(2)
+ public void listen1(Event event) {
+ System.out.println("接收到事件1:" + event);
+ System.out.println("处理事件");
+ }
+
+ @EventListener
+ @Order(1)
+ public void listen2(Event event) {
+ System.out.println("接收到事件2:" + event);
+ System.out.println("处理事件");
+ }
+}
+```
+
+注解中的参数越小,代表优先级越高,在上面的例子中,会执行listen2方法再执行listen1方法
+
+------
+
+那么Spring到底是如何实现的这一套事件发布机制呢?接下来我们进行源码分析
+
+#### 源码分析(publishEvent方法)
+
+我们需要分析的代码主要是`org.springframework.context.support.AbstractApplicationContext#publishEvent(java.lang.Object, org.springframework.core.ResolvableType)方法`,源码如下:
+
+```java
+protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
+ Assert.notNull(event, "Event must not be null");
+
+ // 如果发布的事件是一个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();
+ }
+ }
+ // 我们在自己的项目中调用时,这个earlyApplicationEvents必定为null
+ if (this.earlyApplicationEvents != null) {
+ this.earlyApplicationEvents.add(applicationEvent);
+ }
+ else {
+ // 获取事件发布器,发布对应的事件
+ getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
+ }
+
+ // 父容器中也需要发布事件
+ 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()方法
+
+##### getApplicationEventMulticaster()方法
+
+代码如下:
+
+```java
+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是什么?
+
+- 接口定义
+
+```java
+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. `AbstractApplicationEventMulticaster`,这个类对`ApplicationEventMulticaster`这个接口基础方法做了实现,除了其核心方法`multicastEvent`。这个类最大的作用是获取监听器,稍后我们会介绍。
+2. `SimpleApplicationEventMulticaster`,这是Spring默认提供的一个事件分发器,如果我们没有进行特别的配置的话,就会采用这个类生成的对象作为容器的事件分发器。
+
+###### 2、容器在什么时候对其进行的初始化
+
+回到我们之前的一张图
+
+
+
+可以看到,在第`3-8`步调用了一个`initApplicationEventMulticaster`方法,从名字上我们就知道,这是对`ApplicationEventMulticaster`进行初始化的,我们看看这个方法做了什么。
+
+- initApplicationEventMulticaster方法
+
+```java
+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);
+ // 删除不必要的日志
+ }
+ // 如果没有包含,new一个SimpleApplicationEventMulticaster并将其注册到容器中
+ else {
+ this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
+ beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
+ // 删除不必要的日志
+ }
+ }
+}
+```
+
+这段代码的含义就是告诉我们,可以自己配置一个applicationEventMulticaster,如果没有进行配置,那么将默认使用一个SimpleApplicationEventMulticaster。
+
+接下来,我们尝试自己配置一个简单的applicationEventMulticaster,示例代码如下:
+
+```java
+@Component("applicationEventMulticaster")
+static class MyEventMulticaster extends AbstractApplicationEventMulticaster {
+
+ @Override
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ public void multicastEvent(@NonNull ApplicationEvent event) {
+ ResolvableType resolvableType = ResolvableType.forInstance(event);
+ Collection> 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。实现的逻辑如下:
+
+```java
+public void multicastEvent(ApplicationEvent event) {
+ multicastEvent(event, resolveDefaultEventType(event));
+}
+
+@Override
+public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
+ ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
+ for (final ApplicationListener> listener : getApplicationListeners(event, type)) {
+ Executor executor = getTaskExecutor();
+ if (executor != null) {
+ executor.execute(() -> invokeListener(listener, event));
+ }
+ else {
+ invokeListener(listener, event);
+ }
+ }
+}
+```
+
+上面的代码主要的实现逻辑可以分为这么几步:
+
+1. 推断事件类型
+2. 根据事件类型获取对应的监听器
+3. 执行监听逻辑
+
+我们一步步分析
+
+- resolveDefaultEventType(event),推断事件类型
+
+```java
+
+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(int...):获取类型携带的泛型类型
+4. resolve():Type对象到Class对象的转换
+
+另外,ResolvableType的构造方法全部为私有的,我们不能直接new,只能使用其提供的静态方法进行类型获取:
+
+1. forField(Field):获取指定字段的类型
+2. forMethodParameter(Method, int):获取指定方法的指定形参的类型
+3. forMethodReturnType(Method):获取指定方法的返回值的类型
+4. forClass(Class):直接封装指定的类型
+5. ResolvableType.forInstance 获取指定的实例的泛型信息
+
+关于ResolvableType跟java中的类型中的关系请关注我的后续文章,限于篇幅原因在本文就不做过多介绍了。
+
+- getApplicationListeners(event, type),获取对应的事件监听器
+
+事件监听器主要分为两种,一种是我们通过实现接口直接注册到容器中的Bean,例如下面这种
+
+```java
+@Component
+static class EventListener implements ApplicationListener {
+ @Override
+ public void onApplicationEvent(MyEvent event) {
+ System.out.println("接收到事件:" + event.getSource());
+ System.out.println("处理事件....");
+ }
+}
+```
+
+另外一个是通过注解的方式,就是下面这种
+
+```java
+@Component
+static class Listener {
+ @EventListener
+ public void listen1(Event event) {
+ System.out.println("接收到事件1:" + event);
+ System.out.println("处理事件");
+ }
+}
+```
+
+对于实现接口的方式不用多说,因为实现了这个类本身就会被扫描然后加入到容器中。对于注解这种方式,Spring是通过一个回调方法实现的。大家关注下这个接口`org.springframework.beans.factory.SmartInitializingSingleton`,同时找到其实现类,`org.springframework.context.event.EventListenerMethodProcessor`。在这个类中,会先调用`afterSingletonsInstantiated`方法,然后调用一个`processBean`方法,在这个方法中会遍历所有容器中的所有Bean,然后遍历Bean中的每一个方法判断方法上是否加了一个`@EventListener`注解。如果添加了这个注解,会将这个Method方法包装成一个`ApplicationListenerMethodAdapter`,这个类本身也实现了`ApplicationListener`接口。之后在添加到监听器的集合中。
+
+- invokeListener,执行监听逻辑
+
+本身这个方法没有什么好说的了,就是调用了`ApplicationListener`中的`onApplicationEvent`方法,执行我们的业务逻辑。但是值得注意的是,在调用invokeListener方法前,会先进行一个判断
+
+```java
+Executor executor = getTaskExecutor();
+if (executor != null) {
+ executor.execute(() -> invokeListener(listener, event));
+}
+else {
+ invokeListener(listener, event);
+}
+```
+
+会先判断是否能获取到一个Executor,如果能获取到那么会通过这个Executor异步执行监听的逻辑。所以基于这段代码,我们可以不通过@Async注解实现对事件的异步监听,而是复写`SimpleApplicationEventMulticaster`这个类中的方法,如下:
+
+```java
+@Component("applicationEventMulticaster")
+public class MyEventMulticaster extends SimpleApplicationEventMulticaster {
+ @Override
+ public Executor getTaskExecutor() {
+ // 在这里创建自己的执行器
+ return executor();
+ }
+}
+
+```
+
+相比于通过`@Async注解实现对事件的异步监听`我更加倾向于这种通过复写方法的方式进行实现,主要原因就是如果通过注解实现,那么所有加了这个注解的方法在异步执行都都是用的同一个线程池,这些加了注解的方法有些可能并不是进行事件监听的,这样显然是不合理的。而后面这种方式,我们可以确保创建的线程池是针对于事件监听的,甚至可以根据不同的事件类型路由到不同的线程池。这样更加合理。
+
+# 3、总结
+
+在这篇文章中,我们完成了对ApplicationContext中以下两点内容的学习
+
+1. 借助于Resource系列接口,完成对底层资源的访问及加载
+2. 实现事件的发布
+
+对于整个ApplicationContext体系,目前来说还剩一个很大的功能没有涉及到。因为我们也知道ApplicationContext也继承了一系列的BeanFactory接口。所以它还会负责创建、配置及管理Bean。
+
+BeanFactory本身也有一套自己的体系,在下篇文章中,我们就会学习BeanFactory相关的内容。虽然这一系列文章是以ApplicationContext命名的,但是其中的内容覆盖面很广,这些东西对于我们看懂Spring很重要。
+
+希望大家跟我一起慢慢啃掉Spring,加油!共勉!
+
diff --git "a/docs/Spring/Spring\345\256\230\347\275\221\345\255\246\344\271\240\347\254\224\350\256\260/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\344\272\214\357\274\211ApplicationContext\350\257\246\350\247\243\357\274\210\344\270\255\357\274\211/image/2020010601.png" "b/docs/Spring/Spring\345\256\230\347\275\221\345\255\246\344\271\240\347\254\224\350\256\260/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\344\272\214\357\274\211ApplicationContext\350\257\246\350\247\243\357\274\210\344\270\255\357\274\211/image/2020010601.png"
new file mode 100644
index 0000000000000000000000000000000000000000..5ba9697a7839b16ef2357d4d2611a217d450aeb2
Binary files /dev/null and "b/docs/Spring/Spring\345\256\230\347\275\221\345\255\246\344\271\240\347\254\224\350\256\260/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\344\272\214\357\274\211ApplicationContext\350\257\246\350\247\243\357\274\210\344\270\255\357\274\211/image/2020010601.png" differ
diff --git "a/docs/Spring/Spring\345\256\230\347\275\221\345\255\246\344\271\240\347\254\224\350\256\260/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\344\272\214\357\274\211ApplicationContext\350\257\246\350\247\243\357\274\210\344\270\255\357\274\211/image/2020030901.png" "b/docs/Spring/Spring\345\256\230\347\275\221\345\255\246\344\271\240\347\254\224\350\256\260/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\344\272\214\357\274\211ApplicationContext\350\257\246\350\247\243\357\274\210\344\270\255\357\274\211/image/2020030901.png"
new file mode 100644
index 0000000000000000000000000000000000000000..8638f6f6a2331951758baad4a133bb22ab2601ea
Binary files /dev/null and "b/docs/Spring/Spring\345\256\230\347\275\221\345\255\246\344\271\240\347\254\224\350\256\260/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\344\272\214\357\274\211ApplicationContext\350\257\246\350\247\243\357\274\210\344\270\255\357\274\211/image/2020030901.png" differ
diff --git "a/docs/Spring/Spring\345\256\230\347\275\221\345\255\246\344\271\240\347\254\224\350\256\260/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\344\272\214\357\274\211ApplicationContext\350\257\246\350\247\243\357\274\210\344\270\255\357\274\211/image/2020031001.png" "b/docs/Spring/Spring\345\256\230\347\275\221\345\255\246\344\271\240\347\254\224\350\256\260/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\344\272\214\357\274\211ApplicationContext\350\257\246\350\247\243\357\274\210\344\270\255\357\274\211/image/2020031001.png"
new file mode 100644
index 0000000000000000000000000000000000000000..b986e0876f7715bd68cdc24652a4efdc9f021cde
Binary files /dev/null and "b/docs/Spring/Spring\345\256\230\347\275\221\345\255\246\344\271\240\347\254\224\350\256\260/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\344\272\214\357\274\211ApplicationContext\350\257\246\350\247\243\357\274\210\344\270\255\357\274\211/image/2020031001.png" differ
diff --git "a/docs/Spring/Spring\345\256\230\347\275\221\345\255\246\344\271\240\347\254\224\350\256\260/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\344\272\214\357\274\211ApplicationContext\350\257\246\350\247\243\357\274\210\344\270\255\357\274\211/image/2020031002.png" "b/docs/Spring/Spring\345\256\230\347\275\221\345\255\246\344\271\240\347\254\224\350\256\260/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\344\272\214\357\274\211ApplicationContext\350\257\246\350\247\243\357\274\210\344\270\255\357\274\211/image/2020031002.png"
new file mode 100644
index 0000000000000000000000000000000000000000..680467fbfc1e941a830418a4fbf440e646f2f86d
Binary files /dev/null and "b/docs/Spring/Spring\345\256\230\347\275\221\345\255\246\344\271\240\347\254\224\350\256\260/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\344\272\214\357\274\211ApplicationContext\350\257\246\350\247\243\357\274\210\344\270\255\357\274\211/image/2020031002.png" differ
diff --git "a/docs/Spring/Spring\345\256\230\347\275\221\345\255\246\344\271\240\347\254\224\350\256\260/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\344\272\214\357\274\211ApplicationContext\350\257\246\350\247\243\357\274\210\344\270\255\357\274\211/image/2020031101.png" "b/docs/Spring/Spring\345\256\230\347\275\221\345\255\246\344\271\240\347\254\224\350\256\260/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\344\272\214\357\274\211ApplicationContext\350\257\246\350\247\243\357\274\210\344\270\255\357\274\211/image/2020031101.png"
new file mode 100644
index 0000000000000000000000000000000000000000..ec7b0f30a4889a911ebb89e44297b8f31880624c
Binary files /dev/null and "b/docs/Spring/Spring\345\256\230\347\275\221\345\255\246\344\271\240\347\254\224\350\256\260/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\344\272\214\357\274\211ApplicationContext\350\257\246\350\247\243\357\274\210\344\270\255\357\274\211/image/2020031101.png" differ
diff --git "a/docs/Spring/Spring\345\256\230\347\275\221\345\255\246\344\271\240\347\254\224\350\256\260/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\344\272\214\357\274\211ApplicationContext\350\257\246\350\247\243\357\274\210\344\270\255\357\274\211/image/2020031102.png" "b/docs/Spring/Spring\345\256\230\347\275\221\345\255\246\344\271\240\347\254\224\350\256\260/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\344\272\214\357\274\211ApplicationContext\350\257\246\350\247\243\357\274\210\344\270\255\357\274\211/image/2020031102.png"
new file mode 100644
index 0000000000000000000000000000000000000000..e6fbbede7c169f12ccb1d700262c8a612d9e08dd
Binary files /dev/null and "b/docs/Spring/Spring\345\256\230\347\275\221\345\255\246\344\271\240\347\254\224\350\256\260/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\344\272\214\357\274\211ApplicationContext\350\257\246\350\247\243\357\274\210\344\270\255\357\274\211/image/2020031102.png" differ
diff --git "a/docs/Spring/Spring\345\256\230\347\275\221\345\255\246\344\271\240\347\254\224\350\256\260/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\344\272\214\357\274\211ApplicationContext\350\257\246\350\247\243\357\274\210\344\270\255\357\274\211/image/2020031201.png" "b/docs/Spring/Spring\345\256\230\347\275\221\345\255\246\344\271\240\347\254\224\350\256\260/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\344\272\214\357\274\211ApplicationContext\350\257\246\350\247\243\357\274\210\344\270\255\357\274\211/image/2020031201.png"
new file mode 100644
index 0000000000000000000000000000000000000000..e7e7b424d1b3cdfa2d1d8a4e70ce1394c8391ff7
Binary files /dev/null and "b/docs/Spring/Spring\345\256\230\347\275\221\345\255\246\344\271\240\347\254\224\350\256\260/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\344\272\214\357\274\211ApplicationContext\350\257\246\350\247\243\357\274\210\344\270\255\357\274\211/image/2020031201.png" differ