spring-boot-2.2.6.REALEASE的jar包的META-INF/spring.factories文件,有如下配置:
# Run Listeners org.springframework.boot.SpringApplicationRunListener=\ org.springframework.boot.context.event.EventPublishingRunListenerSpringApplicationRunListener是SpringApplicationRunListener接口的实现类,同时也实现了Ordered接口来排序;
我们从下面这个代码可以知道Springboot它会读取每个jar包下的META-INF/spring.factories文件,并且读取到org.springframework.boot.SpringApplicationRunListener的值,而spring-boot-2.2.6.REALEASE的jar包正好配置了这个,所以EventPublishingRunListener将会通过spi机制被实例化,给到下面代码中的listeners变量。当然我们也可以模拟它这样做,然后也会加入到listeners变量中。我们现在知道了,listeners是怎样加载过来的了,然后接下来,我们看看EventPublishingRunListener它会做什么呢?
EventPublishingRunListener是Springboot为我们提供的一套事件发布机制,spring的事件发布机制基于spring容器中实现ApplicationListener接口的bean的,而Springboot的事件发布机制基于spi机制加载的ApplicationListener接口全类名对应的一系列使用逗号隔开的字符串通过反射实例化的对象。下面将分析springboot的这个事件发布机制
// 我们还是回到SpringApplication的 run方法, 我们这里只看getRunListeners的过程 public class SpringApplication { /** * Run the Spring application, creating and refreshing a new * {@link ApplicationContext}. * @param args the application arguments (usually passed from a Java main method) * @return a running {@link ApplicationContext} */ public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); configureHeadlessProperty(); // 使用spi机制加载SpringApplicationRunListener.class, 获取这个类的实例 SpringApplicationRunListeners listeners = getRunListeners(args); // <------SPI机制加载, 进去看看 listeners.starting(); try { // 封装请求参数 ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); configureIgnoreBeanInfo(environment); // 打印banner Banner printedBanner = printBanner(environment); // 创建ApplicationContext对象, 此过程中完成类型推断 context = createApplicationContext(); // 使用spi机制加载SpringBootExceptionReporter.class, 获取这个类的实例 exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); prepareContext(context, environment, listeners, applicationArguments, printedBanner); // 刷新ApplicationContext, 即刷新spring容器 refreshContext(context); // 空实现, 受保护的方法 afterRefresh(context, applicationArguments); stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch); } // 回调所有listener的started方法, // 这个listener就是前面提到的由spi机制加载的SpringApplicationRunListeners的实例 listeners.started(context); // 在此处, 回调spring容器中所有的runner, 即实现了ApplicationRunner和CommandLineRunner接口的bean callRunners(context, applicationArguments); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, listeners); throw new IllegalStateException(ex); } try { // 回调所有listener的running方法, // 这个listener就是前面提到的由spi机制加载的SpringApplicationRunListeners的实例 listeners.running(context); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, null); throw new IllegalStateException(ex); } return context; } } public class SpringApplication { private static final Log logger = LogFactory.getLog(SpringApplication.class); private SpringApplicationRunListeners getRunListeners(String[] args) { // 这里指定了构造器, 在第二部分会有体现 Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class }; // 用SpringApplicationRunListeners这个对象 // 封装由SPI机制加载的所有SpringApplicationRunListener监听器对象 return new SpringApplicationRunListeners( logger, getSpringFactoriesInstances( //<--SPI加载过程, 不再赘述 SpringApplicationRunListener.class, // 第一个重要的参数 types, // 有用, 向spi机制指定所需要加载类的指定参数的构造器 this, // 当前SpringApplication对象 args // 传过来的参数 ) ); } }好的,我们来到EventPublishingRunListener源码,看看他是做了哪些事。
从下面的代码来看,它似乎都在做一些事件发布相关的事情,具体来说EventPublishingRunListener这个类通过spi机制实例化之后,重写SpringApplicationRunListener接口的这些个方法,都将被Springboot主动在代码中回调。所以下面的方法将会被执行
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered { private final SpringApplication application; private final String[] args; // Springboot的事件发布机制都是依靠这个在构造方法中实例化的多播器完成的 private final SimpleApplicationEventMulticaster initialMulticaster; // 实现了SpringApplicationRunListener接口,并且在META-INF/spring.factories中配置了, // 那么它就一定要有这两个参数的构造器, 这是在springboot源码中写死的 或者说规定好的, 我们自己模拟写的话,也要这样 public EventPublishingRunListener(SpringApplication application, String[] args) { this.application = application; this.args = args; // 这里初始化了一个多播器, 注意, 相对之前看的spring的事件发布机制源码, 这里并没有传beanFactory进去 // 因为spi机制加载SpringApplicationRunListener这个类的时候, 还没开始创建ApplicationContext this.initialMulticaster = new SimpleApplicationEventMulticaster(); // 下面这个getListeners(), 依旧是从spi机制加载的ApplicationListener, // 这个是在new SpringApplication()过程中通过spi加载ApplicationListener,并且设置给了SpringApplication // 这部分的代码, 我们在后面再看 // 在这里跟spring的事件发布机制差不多, 把ApplicationListener接口实现类对象放到多播器里 // 这样一旦使用多播器发布事件, 就能通知这些监听器要回调相应的方法了 // 与spring的事件发布机制区别在于, 这些ApplicationListener接口的实现类对象是通过spi机制加载的, 因此需要配置 // 在META-INF/spring.factories文件中, 这样才能在new SpringApplication()过程中, // 读取并实例化, 交给SpringApplication对象, 这样下面这行代码才能获取到监听器了 // 具体的代码, 我们可以在下面再具体看一看 for (ApplicationListener<?> listener : application.getListeners()) { this.initialMulticaster.addApplicationListener(listener); } } @Override public int getOrder() { // 好的, 这里指定当前EventPublingRunListener的在spi中的实例化顺序 return 0; } // 通过查看SpringApplicationRunListener接口, 下面这几个重写的方法, 都将在Springboot源码中被回调, // 继而, 下面这几个方法将会在合适的时机被调用, // 具体的调用时机, 可以参看SpringApplication的run(String... args)方法, // 可以看到listeners的各种回调方法, 不断的穿插在容器创建过程中 // 分析到这里为止, 还有两部分问题没有说到, // 1.这里的多播器, 在上面的构造方法里面获取ApplicationListener的过程是怎么样的, 这个下面再说 // 2.多播器, 它的发布事件具体是如何做的, 这个就只挑下面这个方法说一下 @Override public void starting() { this.initialMulticaster .multicastEvent(new ApplicationStartingEvent(this.application,this.args)); } @Override public void environmentPrepared(ConfigurableEnvironment environment) { this.initialMulticaster .multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment) ); } @Override public void contextPrepared(ConfigurableApplicationContext context) { this.initialMulticaster .multicastEvent(new ApplicationContextInitializedEvent(this.application, this.args, context)); } @Override public void contextLoaded(ConfigurableApplicationContext context) { for (ApplicationListener<?> listener : this.application.getListeners()) { if (listener instanceof ApplicationContextAware) { ((ApplicationContextAware) listener).setApplicationContext(context); } context.addApplicationListener(listener); } this.initialMulticaster.multicastEvent(new ApplicationPreparedEvent(this.application, this.args, context)); } @Override public void started(ConfigurableApplicationContext context) { context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context)); } @Override public void running(ConfigurableApplicationContext context) { context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context)); } @Override public void failed(ConfigurableApplicationContext context, Throwable exception) { ApplicationFailedEvent event = new ApplicationFailedEvent(this.application, this.args, context, exception); if (context != null && context.isActive()) { // Listeners have been registered to the application context so we should // use it at this point if we can context.publishEvent(event); } else { // An inactive context may not have a multicaster so we use our multicaster to // call all of the context's listeners instead if (context instanceof AbstractApplicationContext) { for (ApplicationListener<?> listener : ((AbstractApplicationContext) context) .getApplicationListeners()) { this.initialMulticaster.addApplicationListener(listener); } } this.initialMulticaster.setErrorHandler(new LoggingErrorHandler()); this.initialMulticaster.multicastEvent(event); } } private static class LoggingErrorHandler implements ErrorHandler { private static final Log logger = LogFactory.getLog(EventPublishingRunListener.class); @Override public void handleError(Throwable throwable) { logger.warn("Error calling ApplicationEventListener", throwable); } } } // 好的, 先看下没说到的问题的第二个问题, EventPublishingRunListener是怎样做到发布事件的 // 我觉得, 这里值得提醒一下, EventPublishingRunListener是Springboot通过spi加载过来的, // 作为SpringApplicationRunListener接口的实现类被加载到springboot中, // 并且在Springboot启动过程中被穿插回调, 而在回调的过程中, 又是使用多播器来做事情的 // 因此, 我们可以仿造这个EventPublishingRunListener来实现自己想要的逻辑, 并且可以指定它们的顺序 // 由于初始化的initialMulticaster是SimpleApplicationEventMulticaster, 我们到这个简单多播器里看看发布过程 // 其实整个的事件发布过程, springboot还是沿用了spring的事件发布。 // 其中的不同点, 在于AbstractApplicationEventMulticaster类中的对私有内部类ListenerRetriever的使 // 用不同, springboot在这里使用的多播器中并没有注入beanFactory, 所有的监听器都被加载到 // 私有内部类ListenerRetriever对象的Set<ApplicationListener<?>> applicationListeners中去了, // 而没有用到私有内部类ListenerRetriever对象的Set<String> applicationListenerBeans的这个属性 // 而Spring的是两个都有用到, 但是默认不改变的情况下, spring用的是传过来的beanFactory拿到实现了 // ApplicationListener接口的bean的名字(特别注意,这个bean还没有实例化出来,因为这个时候 // spring容器还没开始预实例化), 所以用的是ListenerRetriever的applicationListenerBeans这个属性。 // 这也就是为什么springboot这里的这个多播器没有传beanFactory的原因,而spring里面传了BeanFactory // 从这一点, 就可以看出多播器它的整体的设计。 public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster { @Override public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) { ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event)); Executor executor = getTaskExecutor(); // 最重要的还是看getApplicationListeners这个方法, 对这个方法的调用和spring的完全一样, 区别在上面说过了 for (ApplicationListener<?> listener : getApplicationListeners(event, type)) { if (executor != null) { executor.execute(() -> invokeListener(listener, event)); } else { invokeListener(listener, event); } } } } // 我们再看第一个问题, Springboot是怎样通过spi拿到监听器的呢, 这个很简单 // 从我们自己写的启动类出发, 来到SpringApplication类的静态方法run(Class<?>[] primarySources, String[] args) public class SpringApplication { public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { // 这里分成两部分: // 先, 使用new创建了SpringApplication对象 // 再, 调用SpringApplication对象的run方法 // primarySources是封装了我们的启动类的Class数组 // 其实, 我们也可以自己手动创建SpringApplication对象,但后调用这个对象的run方法 // 这样去看的话, Springboot使用静态方法,帮我们把这件事都给做了(手动捂脸) return new SpringApplication(primarySources).run(args); } // 我们只看第一部分,new SpringApplication(..)的部分 // resourceLoader现在是null, 也就是说允许我们自己指定资源加载器 public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { this.resourceLoader = resourceLoader; // 启动类不能为null Assert.notNull(primarySources, "PrimarySources must not be null"); this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); // 推断当前应用类型,有3种, // WebApplicationType.REACTIVE // WebApplicationType.NONE // WebApplicationType.SERVLET // 具体的判断过程是根据已经写好的全类名直接通过反射去加载,能加载到,就是true,加载不到就是false // 推断的过程在WebApplicationType类的静态方法deduceFromClasspath()完成,返回当前应用类型 this.webApplicationType = WebApplicationType.deduceFromClasspath(); // 以下两个都是通过SPI机制来分别加载下面两个,并设置给当前的SpringApplication对象,只看ApplicationListener // ApplicationListener: org.springframework.context.ApplicationListener // ApplicationContextInitializer: org.springframework.context.ApplicationContextInitializer setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); // 推断主类, 还可以这样来玩异常?? // new一个异常, 但是不抛出来, 直接遍历这个异常的栈, 根据main名字, 拿到main方法的所属的栈 // 再从这个栈中拿到当前所在类的全类名, 再通过反射这个类的Class的对象 this.mainApplicationClass = deduceMainApplicationClass(); } // 好的,ApplicationListener.class传过来了 private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) { // 注意到第二个参数是个空的Class数组, // 这个第二个参数决定了SPI机制通过反射创建对象时,获取的构造器, 这里也就是说无参构造器,也就是默认的构造方法 return getSpringFactoriesInstances(type, new Class<?>[] {}); } // Springboot通过spi机制创建对象的过程, 都要使用这个方法 private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, // ApplicationListener.class Class<?>[] parameterTypes, // 空的Class数组 Object... args) { // null ClassLoader classLoader = getClassLoader(); // Use names and ensure unique to protect against duplicates // 依旧是这个类的静态方法去加载, 之前加载过一次, 就不用再去扫描了, 直接使用已经缓存好了的 Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader .loadFactoryNames(type,classLoader)); // 创建实例 List<T> instances = createSpringFactoriesInstances(type, // ApplicationListener.class parameterTypes, // 空的Class数组,指定构造器用的 classLoader, args, // 参数 names); // 全类名 // 排序 AnnotationAwareOrderComparator.sort(instances); // 返回 return instances; // 好的, 至此.我们看到了ApplicationListener的SPI加载的时机, 以及加载的过程 } }