1. Spring Boot 入口——main方法
@SpringBootApplication public class Application { public static void main(String[] args) throws Exception { SpringApplication.run(Application.class, args); } } 复制代码
从上面代码可以看出,Annotation定义(@SpringBootApplication)和类定义(SpringApplication.run)最为耀眼,所以分析自动装配原理与启动过程详解,从这里开始
2. 核心注解
2.1 @SpringBootApplication
@SpringBootApplication 是最常用也几乎是必用的注解,源码如下:
/ Indicates a {@link Configuration configuration} class that declares one or more {@link Bean @Bean} methods and also triggers {@link EnableAutoConfiguration auto-configuration} and {@link ComponentScan component scanning}. This is a convenience annotation that is equivalent to declaring {@code @Configuration}, {@code @EnableAutoConfiguration} and {@code @ComponentScan}. 标示一个声明有一个或多个的@Bean方法的Configuration类并且触发自动配置(EnableAutoConfiguration) 和组建扫描(ComponentScan)。 这是一个相当于@Configuration、@EnableAutoConfiguration、@ComponentScan 三合一的组合注解。 */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) public @interface SpringBootApplication { @AliasFor(annotation = EnableAutoConfiguration.class, attribute = “exclude”) Class<?>[] exclude() default {}; @AliasFor(annotation = EnableAutoConfiguration.class, attribute = “excludeName”) String[] excludeName() default {}; @AliasFor(annotation = ComponentScan.class, attribute = “basePackages”) String[] scanBasePackages() default {}; @AliasFor(annotation = ComponentScan.class, attribute = “basePackageClasses”) Class<?>[] scanBasePackageClasses() default {}; } 复制代码
从源码声明可以看出,@SpringBootApplication相当于 @SpringBootConfiguration + @EnableAutoConfiguration + @ComponentScan** ,因此我们直接拆开来分析。
2.2 @SpringBootConfiguration
@SpringBootConfiguration 是继承自Spring的 @Configuration 注解,@SpringBootConfiguration 作用相当于 @Configuration。
微服务干货,参考资料:www.cx1314.cn/forum-64-1.…
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Configuration public @interface SpringBootConfiguration { } 复制代码
spring 3.0中增加了@Configuration,@Bean。可基于JavaConfig形式对 Spring 容器中的bean进行更直观的配置。SpringBoot推荐使用基于JavaConfig的配置形式。
基于xml配置:
<?xml version=”1.0” encoding=”UTF-8”?>
基于JavaConfig配置:
@Configuration public class MockConfiguration{ @Bean public MockService mockService(){ return new MockServiceImpl(); } } 复制代码
总结,@Configuration相当于一个spring的xml文件,配合@Bean注解,可以在里面配置需要Spring容器管理的bean。
2.3 @ComponentScan
@ComponentScan源码:
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Repeatable(ComponentScans.class) public @interface ComponentScan { / 对应的包扫描路径 可以是单个路径,也可以是扫描的路径数组 @return */ @AliasFor(“basePackages”) String[] value() default {}; / 和value一样是对应的包扫描路径 可以是单个路径,也可以是扫描的路径数组 @return / @AliasFor(“value”) String[] basePackages() default {}; /** 指定具体的扫描类 @return / Class<?>[] basePackageClasses() default {}; / 对应的bean名称的生成器 默认的是BeanNameGenerator @return */ Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class; / 处理检测到的bean的scope范围 / Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class; / 是否为检测到的组件生成代理 / ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT; / 控制符合组件检测条件的类文件 默认是包扫描下的 @return / String resourcePattern() default ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN; /** 是否对带有@Component @Repository @Service @Controller注解的类开启检测,默认是开启的 @return / boolean useDefaultFilters() default true; / 指定某些定义Filter满足条件的组件 @return */ Filter[] includeFilters() default {}; / 排除某些过来器扫描到的类 @return / Filter[] excludeFilters() default {}; /** 扫描到的类是都开启懒加载 ,默认是不开启的 @return / boolean lazyInit() default false; } 复制代码
基于xml配置:
基于JavaConfig配置:
@Configuration @ComponentScan(value = “com.youzan”, excludeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class}) }) public class ScanConfig { } 复制代码
总结:@ComponentScan通常与@Configuration一起配合使用,相当于xml里面的
2.4 @EnableAutoConfiguration 🚩
@EnableAutoConfiguration 源码如下:
/ Enable auto-configuration of the Spring Application Context, attempting to guess and configure beans that you are likely to need. Auto-configuration classes are usually applied based on your classpath and what beans you have defined. For example, If you have {@code tomcat-embedded.jar} on your classpath you are likely to want a {@link TomcatEmbeddedServletContainerFactory} (unless you have defined your own {@link EmbeddedServletContainerFactory} bean). 启用Spring应用程序上下文的自动配置,试图猜测和配置您可能需要的bean。 自动配置类通常基于您的类路径和您 定义的bean应用。 例如,如果您在类路径中有tomcat-embedded.jar,那么您可能需要一个 TomcatEmbeddedServletContainerFactory(除非您已定义了自己的 EmbeddedInvletContainerFactory bean)。
Auto-configuration classes are regular Spring {@link Configuration} beans. They are located using the {@link SpringFactoriesLoader} mechanism (keyed against this class). Generally auto-configuration beans are {@link Conditional @Conditional} beans (most often using {@link ConditionalOnClass @ConditionalOnClass} and {@link ConditionalOnMissingBean @ConditionalOnMissingBean} annotations). 自动配置类是 @Configuration 注解的bean。 它们使用 SpringFactoriesLoader 机制(针对这个类键入)。 通常,自动配置bean是 @Conditional bean(通常使用 @ConditionalOnClass 和 @ConditionalOnMissingBean 注释)。 @author Phillip Webb @author Stephane Nicoll @see ConditionalOnBean @see ConditionalOnMissingBean @see ConditionalOnClass @see AutoConfigureAfter @see SpringBootApplication */ @SuppressWarnings(“deprecation”) @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(EnableAutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration { String ENABLED_OVERRIDE_PROPERTY = “spring.boot.enableautoconfiguration”; Class<?>[] exclude() default {}; String[] excludeName() default {}; } 复制代码
这里引出了几个新的注解,@Import、@Conditional、@ConditionalOnClass、@ConditionalOnMissingBean等,@EnableAutoConfiguration 注解的核心是@Import(EnableAutoConfigurationImportSelector.class)l里面导入的EnableAutoConfigurationImportSelector.class。
/ 核心方法,加载spring.factories文件中的 org.springframework.boot.autoconfigure.EnableAutoConfiguration 配置类 / protected List
spring-boot-autoconfigure.jar 包中的 META-INF/spring.factories 里面默认配置了很多aoto-configuration,如下:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\ org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\ org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\ org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\ org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\ org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\ …… org.springframework.boot.autoconfigure.web.MultipartAutoConfiguration,\ org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration,\ org.springframework.boot.autoconfigure.web.WebClientAutoConfiguration,\ org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration,\ org.springframework.boot.autoconfigure.websocket.WebSocketAutoConfiguration,\ org.springframework.boot.autoconfigure.websocket.WebSocketMessagingAutoConfiguration,\ org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration 复制代码
例如 WebMvcAutoConfiguration.class:
@Configuration @ConditionalOnWebApplication @ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurerAdapter.class }) @ConditionalOnMissingBean(WebMvcConfigurationSupport.class) @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10) @AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, ValidationAutoConfiguration.class }) public class WebMvcAutoConfiguration { @Bean @ConditionalOnMissingBean(HiddenHttpMethodFilter.class) public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() { return new OrderedHiddenHttpMethodFilter(); } @Bean @ConditionalOnMissingBean(HttpPutFormContentFilter.class) @ConditionalOnProperty(prefix = “spring.mvc.formcontent.putfilter”, name = “enabled”, matchIfMissing = true) public OrderedHttpPutFormContentFilter httpPutFormContentFilter() { return new OrderedHttpPutFormContentFilter(); } ……etc } 复制代码
引入这个类,相当于引入了一份webmvc的基本配置,这个类跟其它很多类一样,重度依赖于Spring Boot注释。
总的来说,@EnableAutoConfiguration完成了一下功能:
*从classpath中搜寻所有的 META-INF/spring.factories 配置文件,并将其中org.springframework.boot.autoconfigure.EnableutoConfiguration 对应的配置项通过反射实例化为对应的标注了@Configuration的JavaConfig形式的IoC容器配置类,然后汇总为一个并加载到IoC容器。
2.5 @Import
相当于xml里面的
2.6 @Conditional
Spring Boot的强大之处在于使用了 Spring 4 框架的新特性:@Conditional注释,此注解使得只有在特定条件满足时才启用一些配置。这也 Spring Boot “智能” 的关键注解。Conditional大家族如下:
- @ConditionalOnBean
- @ConditionalOnClass
- @ConditionalOnExpression
- @ConditionalOnMissingBean
- @ConditionalOnMissingClass
- @ConditionalOnNotWebApplication
- @ConditionalOnResource
- @ConditionalOnWebApplication
以@ConditionalOnClass注解为例:
@Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented @Conditional(OnClassCondition.class) public @interface ConditionalOnClass { Class<?>[] value() default {}; String[] name() default {}; } 复制代码
核心实现类为OnClassCondition.class,这个注解实现类 Condition 接口:
public interface Condition { /* 决定是否满足条件的方法 */ boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata); } 复制代码
2.7 总结
上面所有的注解都在做一件事:注册bean到spring容器。他们通过不同的条件不同的方式来完成:
- @SpringBootConfiguration 通过与 @Bean 结合完成Bean的 JavaConfig配置;
- @ComponentScan 通过范围扫描的方式,扫描特定注解注释的类,将其注册到Spring容器;
- @EnableAutoConfiguration 通过 spring.factories 的配置,并结合 @Condition 条件,完成bean的注册;
- @Import 通过导入的方式,将指定的class注册解析到Spring容器;
3. Spring Boot 启动流程
3.1 SpringApplication的实例化
下面开始分析关键方法:
SpringApplication.run(Application.class, args); 复制代码
相应实现:
// 参数对应的就是Application.class以及main方法中的args public static ConfigurableApplicationContext run(Class<?> primarySource, String… args) { return run(new Class<?>[] { primarySource }, args); } // 最终运行的这个重载方法 public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args); } 复制代码
这里最终还是会构造一个 SpringApplication 的实例,然后运行它的run方法。
思考:
1、为什么要在静态方法里面实例化 ?
2、可不可以不实例化 ?
// 构造实例 public SpringApplication(Object… sources) { initialize(sources); } private void initialize(Object[] sources) { if (sources != null && sources.length > 0) { this.sources.addAll(Arrays.asList(sources)); } // 推断是否为web环境 this.webEnvironment = deduceWebEnvironment(); // 设置初始化器 setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class)); // 设置监听器 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); // 推断应用入口类 this.mainApplicationClass = deduceMainApplicationClass(); } 复制代码
在构造函数中,主要做了4件事情:3.1.1 推断应用类型是否是Web环境
// 相关常量 private static final String[] WEB_ENVIRONMENT_CLASSES = { “javax.servlet.Servlet”, “org.springframework.web.context.ConfigurableWebApplicationContext” }; private boolean deduceWebEnvironment() { for (String className : WEB_ENVIRONMENT_CLASSES) { if (!ClassUtils.isPresent(className, null)) { return false; } } return true; } 复制代码
这里通过判断是否存在 Servlet 和 ConfigurableWebApplicationContext 类来判断是否是Web环境,上文提到的 @Conditional 注解也有基于 class 来判断环境, 所以在 Spring Boot 项目中 jar包 的引用不应该随意,不需要的依赖最好去掉。3.1.2 设置初始化器(Initializer)
setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class)); 复制代码
这里出现了一个概念 - 初始化器。
先来看看代码,再来尝试解释一下它是干嘛的:
privateCollection getSpringFactoriesInstances(Class type) { return getSpringFactoriesInstances(type, new Class<?>[] {}); } // 这里的入参type就是ApplicationContextInitializer.class private Collection getSpringFactoriesInstances(Class type, Class<?>[] parameterTypes, Object… args) { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); // 使用Set保存names来去重 避免重复配置导致多次实例化 Set names = new LinkedHashSet<>( SpringFactoriesLoader.loadFactoryNames(type, classLoader)); // 根据names来进行实例化 List instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); // 对实例进行排序 可用 Ordered接口 或 @Order注解 配置顺序 AnnotationAwareOrderComparator.sort(instances); return instances; } 复制代码
该方法会加载所有配置的 ApplicationContextInitializer 并进行实例化,加载 ApplicationContextInitializer 是在SpringFactoriesLoader.loadFactoryNames 方法里面进行的:
public static ListloadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) { String factoryClassName = factoryClass.getName(); try { Enumeration urls = classLoader != null ? classLoader.getResources(“META-INF/spring.factories”):ClassLoader.getSystemResources(“META-INF/spring.factories”); ArrayList result = new ArrayList(); while(urls.hasMoreElements()) { URL 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; } catch (IOException var8) { throw new IllegalArgumentException(“Unable to load [“ + factoryClass.getName() + “] factories from location [“ + “META-INF/spring.factories” + “]”, var8); } } 复制代码
这个方法会尝试从类路径的 META-INF/spring.factories 读取相应配置文件,然后进行遍历,读取配置文件中Key为:org.springframework.context.ApplicationContextInitializer 的 value。以 spring-boot 这个包为例,它的 META-INF/spring.factories 部分定义如下所示:
# Initializers org.springframework.context.ApplicationContextInitializer=\ org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\ org.springframework.boot.context.ContextIdApplicationContextInitializer,\ org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\ org.springframework.boot.context.embedded.ServerPortInfoApplicationContextInitializer 复制代码
因此这两个类名会被读取出来,然后放入到集合中,准备开始下面的实例化操作:
// 关键参数: // type: org.springframework.context.ApplicationContextInitializer.class // names: 上一步得到的names集合 privateList createSpringFactoriesInstances(Class type, Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args, Set names) { List instances = new ArrayList (names.size()); for (String name : names) { try { Class<?> instanceClass = ClassUtils.forName(name, classLoader); Assert.isAssignable(type, instanceClass); Constructor<?> constructor = instanceClass .getDeclaredConstructor(parameterTypes); T instance = (T) BeanUtils.instantiateClass(constructor, args); instances.add(instance); } catch (Throwable ex) { throw new IllegalArgumentException( “Cannot instantiate “ + type + “ : “ + name, ex); } } return instances; } 复制代码
初始化步骤很直观,类加载,确认被加载的类确实是 org.springframework.context.ApplicationContextInitializer 的子类,然后就是得到构造器进行初始化,最后放入到实例列表中。
因此,所谓的初始化器就是 org.springframework.context.ApplicationContextInitializer 的实现类,这个接口是这样定义的:
public interface ApplicationContextInitializer{ /* Initialize the given application context. @param applicationContext the application to configure / void initialize(C applicationContext); } 复制代码
ApplicationContextInitializer是一个回调接口,它会在 ConfigurableApplicationContext 容器 refresh() 方法调用之前被调用,做一些容器的初始化工作。3.1.3. 设置监听器(Listener)
设置完了初始化器,下面开始设置监听器:
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); 复制代码
实现方式与Initializer一样:
// 这里的入参type是:org.springframework.context.ApplicationListener.class privateCollection<? extends T> getSpringFactoriesInstances(Class type) { return getSpringFactoriesInstances(type, new Class<?>[] {}); } private Collection<? extends T> getSpringFactoriesInstances(Class type, Class<?>[] parameterTypes, Object… args) { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); // Use names and ensure unique to protect against duplicates Set names = new LinkedHashSet ( SpringFactoriesLoader.loadFactoryNames(type, classLoader)); List instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; } 复制代码
同样,还是以spring-boot这个包中的 spring.factories 为例,看看相应的 Key-Value :
# Application Listeners org.springframework.context.ApplicationListener=\ org.springframework.boot.ClearCachesApplicationListener,\ 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 复制代码
至于 ApplicationListener 接口,它是 Spring 框架中一个相当基础的接口,代码如下:
@FunctionalInterface public interface ApplicationListenerextends EventListener { /* Handle an application event. @param event the event to respond to / void onApplicationEvent(E event); } 复制代码
这个接口基于JDK中的 EventListener 接口,实现了观察者模式。对于 Spring 框架的观察者模式实现,它限定感兴趣的事件类型需要是 ApplicationEvent 类型的子类,而这个类同样是继承自JDK中的 EventObject 类。3.1.4. 推断应用入口类(Main)
this.mainApplicationClass = deduceMainApplicationClass(); 复制代码
这个方法的实现有点意思:
private Class<?> deduceMainApplicationClass() { try { StackTraceElement[] stackTrace = new RuntimeException().getStackTrace(); for (StackTraceElement stackTraceElement : stackTrace) { if (“main”.equals(stackTraceElement.getMethodName())) { return Class.forName(stackTraceElement.getClassName()); } } } catch (ClassNotFoundException ex) { // Swallow and continue } return null; } 复制代码
它通过构造一个运行时异常,通过异常栈中方法名为main的栈帧来得到入口类的名字。
思考:
1、获取堆栈信息的方式?
Thread.currentThread().getStackTrace(); new RuntimeException().getStackTrace(); 复制代码
至此,对于SpringApplication实例的初始化过程就结束了。3.2 SpringApplication.run方法
完成了实例化,下面开始调用run方法:
// 运行run方法 public ConfigurableApplicationContext run(String… args) { // 此类通常用于监控开发过程中的性能,而不是生产应用程序的一部分。 StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; CollectionexceptionReporters = new ArrayList<>(); // 设置java.awt.headless系统属性,默认为true // Headless模式是系统的一种配置模式。在该模式下,系统缺少了显示设备、键盘或鼠标。 configureHeadlessProperty(); // KEY 1 - 获取SpringApplicationRunListeners SpringApplicationRunListeners listeners = getRunListeners(args); // 通知监听者,开始启动 listeners.starting(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments( args); // KEY 2 - 根据SpringApplicationRunListeners以及参数来准备环境 ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); configureIgnoreBeanInfo(environment); // 准备Banner打印器 - 就是启动Spring Boot的时候打印在console上的ASCII艺术字体 Banner printedBanner = printBanner(environment); // KEY 3 - 创建Spring上下文 context = createApplicationContext(); // 注册异常分析器 analyzers = new FailureAnalyzers(context); // KEY 4 - Spring上下文前置处理 prepareContext(context, environment, listeners, applicationArguments, printedBanner); // KEY 5 - Spring上下文刷新 refreshContext(context); // KEY 6 - Spring上下文后置处理 afterRefresh(context, applicationArguments); // 发出结束执行的事件 listeners.finished(context, null); stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass) .logStarted(getApplicationLog(), stopWatch); } return context; } catch (Throwable ex) { handleRunFailure(context, listeners, exceptionReporters, ex); throw new IllegalStateException(ex); } } 复制代码
这个run方法包含的内容有点多的,根据上面列举出的关键步骤逐个进行分析:3.2.1 获取RunListeners
private SpringApplicationRunListeners getRunListeners(String[] args) { Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class }; return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances( SpringApplicationRunListener.class, types, this, args)); } 复制代码
这里仍然利用了 getSpringFactoriesInstances 方法来获取实例:
// 这里的入参: // type: SpringApplicationRunListener.class // parameterTypes: new Class<?>[] { SpringApplication.class, String[].class }; // args: SpringApplication实例本身 + main方法传入的args privateCollection<? extends T> getSpringFactoriesInstances(Class type, Class<?>[] parameterTypes, Object… args) { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); // Use names and ensure unique to protect against duplicates Set names = new LinkedHashSet ( SpringFactoriesLoader.loadFactoryNames(type, classLoader)); List instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; } 复制代码
所以这里还是从 META-INF/spring.factories 中读取Key为 org.springframework.boot.SpringApplicationRunListener 的Values:
比如在spring-boot包中的定义的spring.factories:
# Run Listeners org.springframework.boot.SpringApplicationRunListener=\ org.springframework.boot.context.event.EventPublishingRunListener 复制代码
我们来看看这个EventPublishingRunListener是干嘛的:
/* {@link SpringApplicationRunListener} to publish {@link SpringApplicationEvent}s.Uses an internal {@link ApplicationEventMulticaster} for the events that are fired before the context is actually refreshed. @author Phillip Webb @author Stephane Nicoll */ public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered { // … } 复制代码
从类文档可以看出,它主要是负责发布SpringApplicationEvent事件的,它会利用一个内部的ApplicationEventMulticaster在上下文实际被刷新之前对事件进行处理。至于具体的应用场景,后面用到的时候再来分析。3.2.2 准备Environment环境
private ConfigurableEnvironment prepareEnvironment( SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) { // 创建环境 ConfigurableEnvironment environment = getOrCreateEnvironment(); configureEnvironment(environment, applicationArguments.getSourceArgs()); // 发布环境准备好的事件 listeners.environmentPrepared(environment); // 非Web环境处理 if (!this.webEnvironment) { environment = new EnvironmentConverter(getClassLoader()) .convertToStandardEnvironmentIfNecessary(environment); } return environment; } 复制代码
配置环境的方法:
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) { configurePropertySources(environment, args); configureProfiles(environment, args); } 复制代码
所以这里实际上包含了两个步骤:
- 配置 Property Sources
- 配置 Profiles,为应用程序环境配置哪些配置文件处于active(活动)状态。
对于Web应用而言,得到的environment变量是一个StandardServletEnvironment的实例。得到实例后,会调用前面RunListeners中的environmentPrepared方法:
@Override public void environmentPrepared(ConfigurableEnvironment environment) { this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent( this.application, this.args, environment)); } 复制代码
在这里,定义的广播器就派上用场了,它会发布一个 ApplicationEnvironmentPreparedEvent 事件。
那么有发布就有监听,在构建 SpringApplication 实例的时候不是初始化过一些 ApplicationListeners 嘛,其中的Listener就可能会监听ApplicationEnvironmentPreparedEvent事件,然后进行相应处理。
所以这里 SpringApplicationRunListeners 的用途和目的也比较明显了,它实际上是一个事件中转器,它能够感知到Spring Boot启动过程中产生的事件,然后有选择性的将事件进行中转。为何是有选择性的,看看它的实现就知道了:
@Override public void contextPrepared(ConfigurableApplicationContext context) { } 复制代码
它的 contextPrepared 方法实现为空,没有利用内部的 initialMulticaster 进行事件的派发。因此即便是外部有 ApplicationListener 对这个事件有兴趣,也是没有办法监听到的。
那么既然有事件的转发,是谁在监听这些事件呢,在这个类的构造器中交待了:
public EventPublishingRunListener(SpringApplication application, String[] args) { this.application = application; this.args = args; this.initialMulticaster = new SimpleApplicationEventMulticaster(); for (ApplicationListener<?> listener : application.getListeners()) { this.initialMulticaster.addApplicationListener(listener); } } 复制代码
前面在构建 SpringApplication 实例过程中设置的监听器在这里被逐个添加到了 initialMulticaster 对应的 ApplicationListener 列表中。所以当 initialMulticaster 调用 multicastEvent 方法时,这些 Listeners 中定义的相应方法就会被触发了。
3.2.3 创建Spring Context
protected ConfigurableApplicationContext createApplicationContext() { Class<?> contextClass = this.applicationContextClass; if (contextClass == null) { try { contextClass = Class.forName(this.webEnvironment ? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS); } catch (ClassNotFoundException ex) { throw new IllegalStateException( “Unable create a default ApplicationContext, “ + “please specify an ApplicationContextClass”, ex); } } return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass); } // WEB应用的上下文类型 public static final String DEFAULT_WEB_CONTEXT_CLASS = “org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext”; // 非WEB应用的上下文类型 public static final String DEFAULT_CONTEXT_CLASS = “org.springframework.context.” + “annotation.AnnotationConfigApplicationContext”; 复制代码
AnnotationConfigEmbeddedWebApplicationContext 是个很重要的类,以后再深入分析。
思考:ssm项目中有几个上下文环境,Spring Boot中有几个上下文环境,为什么?
3.2.4 Spring Context前置处理
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { // 将环境和上下文关联起来 context.setEnvironment(environment); // 为上下文配置Bean生成器以及资源加载器(如果它们非空) postProcessApplicationContext(context); // 调用初始化器 applyInitializers(context); // 触发Spring Boot启动过程的contextPrepared事件 listeners.contextPrepared(context); if (this.logStartupInfo) { logStartupInfo(context.getParent() == null); logStartupProfileInfo(context); } // 添加两个Spring Boot中的特殊单例Beans - springApplicationArguments以及springBootBanner context.getBeanFactory().registerSingleton(“springApplicationArguments”, applicationArguments); if (printedBanner != null) { context.getBeanFactory().registerSingleton(“springBootBanner”, printedBanner); } // 加载sources - 对于DemoApplication而言,这里的sources集合只包含了它一个class对象 Set
