在Spring源码剖析的前三篇文章,我们介绍了ApplicationContext、Bean相关内容、BeanPostProcessor的内容;但从普遍反馈和自己事后阅读的体验来看,文章过长,没有重点,条理并不是特别清楚。想必是写作方式出了问题,最突出的莫过于流水账式写法,虽然写作的目的并不一定是写出好的文章,而是主要服务自己,但时间一长,自己也是个普通的读者,同样会看不大懂。
因此,写作方法是需要变更了:要突出条理和重点,如需大段源码讲解,可在文章最后增加源码解析一节,读者可选读。也就是说,长度还是那么长,但可读性增强了很多。
本文我们关注SpringBoot启动时做了什么,这是理解自动注解的基础,下一篇文章我们将探索自动注解的实现方式。
一个最简单的SpringBoot应用,可以是这样
1 2 3 4 5 6 @SpringBootApplication class MyApplication fun main (args: Array <String >) { SpringApplication(MyApplication::class .java ).run (*args) }
那么重点落在SpringApplication
类和其run方法上。
SpringApplication SpringApplication是一个大类,包含了所有应用启动流程。包含的主要步骤如下
创建ApplicationContext
注册CommandLinePropertySource
到Environment
,用于将命令行参数暴露成容器中的属性值
刷新容器,即调用容器的refresh()
方法
创建CommandLineRunner
并调用其run()
方法
关键点
SpringApplication能够启动三种类型的应用
普通的Servlet应用
响应式的Reactive应用
普通的非Web应用
不同的应用,会创建对应的ApplciationContext实现类,在ApplicationContext源码分析那一节有所描述
SpringApplication引入了一个新的临时容器,Bootstrapper
、BootstrapRegistry
,用于在创建出ApplicationContext
实例之前管理启动阶段的Bean
SpringApplication提供ApplicationContextInitializer
接口,用于在ApplicationContext
刷新前修改它,这为我们提供了一个自定义ApplicationContext
的扩展点。
向SpringApplication注册它的方式有两个
调用SpringApplication.addInitializers()
方法手动注册,手写代码时候可用
放在META-INF/spring.factories
中,自定义starter时可用
SpringApplication的核心 —— META-INF/spring.factories
,启动时,会从所有jar包的该文件中搜寻指定factory类型的实现,SpringBoot的诸多特性,比如自动配置,都是依赖于该机制实现。加载该文件有专门的类——SpringFactoriesLoader
。在SpringApplication中,加载的类有四种
Bootstrapper
:SpringBoot启动阶段用
ApplicationContextInitializer
:Spring上下文初始化器
SpringApplicationRunListener
:SpringApplication启动时候的监听器
ApplicationListener
:Spring事件监听器
这其中最重要的当属ApplicationContextInitializer
,考虑到其能力,SpringBoot的诸多功能应该都是由其子类实现。
如果我们要查看哪些初始化器生效了,可以去对应jar包的spring.factories下查看。比如要看自动配置,去spring-boot-autoconfigure包下的spring.factories文件下查看(我们将在下一篇文章研究这个)
SpringApplication提供的自定义点 SpringApplication启动时也支持高级写法,可自定义更多内容,再run
,比如
1 2 3 4 5 6 7 fun main (args: Array <String >) { springApplicationContext = SpringApplication(MylogApplication::class .java ).run { addInitializers(MylogApplication()) run(*args) } }
这是除了spring.factories外,Spring为我们提供了的额外定义方法,主要可自定义如下内容:
主类
WebApplicationType
是否允许Bean覆盖
Bean是否延迟加载
Banner的模式:不显示、显示在控制台、显示在日志中
命令行参数否写入Spring环境
是否将ApplicationConversionService
添加到ApplicationContext
的转换服务中
添加Bootstrapper
添加BootstrapRegistryInitializer
设置默认属性,会被添加到环境变量中
设置额外的profile
设置BeanNameGenerator
设置ConfigurableEnvironment
添加其它需要被添加到容器的资源,主要是指Configuration的资源
设置ResourceLoader
设置环境变量前缀,当从系统中获取环境变量时,将会应用该前缀
设置ConfigurableApplicationContext
,相当于手动指定容器的类型了
设置ApplicationContextFactory
设置ApplicationContextInitializer
,即添加自定义的初始化器
添加ApplicationListener
,即事件监听器
设置ApplicationStartup
,该类并没有什么用,仅日志输出用
还缺啥 如上,加上后面的源码分析,我们只看到了SpringApplication
的启动流程,貌似并没有看到关键之所在
application.properties
配置文件的加载原理
自动配置加载的原理
这些其实在spring-boot.jar
和spring-boot-autoconfigure.jar
中的META-INF/spring.factories
有指定,出于篇幅,我们下篇文章再讨论。
源码分析 这次源码分析我们单独放在一边
构造方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public SpringApplication (Class<?>... primarySources) { this (null , primarySources); } public SpringApplication (ResourceLoader resourceLoader, Class<?>... primarySources) { this .resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null" ); this .primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); this .webApplicationType = WebApplicationType.deduceFromClasspath(); this .bootstrappers = new ArrayList<>(getSpringFactoriesInstances(Bootstrapper.class)); setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); this .mainApplicationClass = deduceMainApplicationClass(); }
新出现的类
WebApplicationType
:一个枚举,包含三个值:SERVLET
、REACTIVE
、NONE
。用于决定应用是否需要启动特定类型的web服务器
Bootstrapper
:用于初始化BootstrapRegistry
1 2 3 4 5 6 7 8 9 public interface Bootstrapper { void intitialize (BootstrapRegistry registry) ; }
BootstrapRegistry
:启动注册器,在服务启动阶段到ApplicationContext
准备好这段时间有效;作用有两个:创建创建过程比较复杂的Bean、创建需要在ApplicationContext
准备好之前共享的Bean。可以理解为预加载容器,即SpringBoot
启动阶段使用的容器。
ApplicationContextInitializer
:用于初始化ApplicationContext
,调用时机在AbstractApplicationContext.refresh()
之前。
也就是说,在SpringBoot中,可以添加自己的ApplicationContextInitializer
在容器初始化时修改内容。
1 2 3 4 5 6 7 8 9 public interface ApplicationContextInitializer <C extends ConfigurableApplicationContext > { void initialize (C applicationContext) ; }
获取工厂对象 - getSpringFactoriesInstances 听这个方法,像是抽象工厂模式,但总体看下来不大像,如果我们只是把它们当成一种插件机制,会更加方便理解。
getSpringFactoriesInstances()
是贯穿SpringBoot启动的静态方法,用于加载指定类型的实例,来源是所有包下的META-INF/spring.factories
文件中指定的类型。其作用就是获取指定类型的所有实现类的实例。
我们首先温习一下spring.factories
文件长啥样
1 2 3 4 5 org.springframework.boot.env.EnvironmentPostProcessor =\ com.baomidou.mybatisplus.autoconfigure.SafetyEncryptProcessor org.springframework.boot.autoconfigure.EnableAutoConfiguration =\ com.baomidou.mybatisplus.autoconfigure.MybatisPlusLanguageDriverAutoConfiguration,\ com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration
然后再来看这个方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 private <T> Collection<T> getSpringFactoriesInstances (Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = getClassLoader(); Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; } private <T> List<T> createSpringFactoriesInstances (Class<T> type, Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args, Set<String> names) { List<T> 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; }
理解上面那段代码的重点,又落在了SpringFactoriesLoader.loadFactoryNames(type, classLoader)
上。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories" ;public static <T> List<T> loadFactories (Class<T> factoryType, @Nullable ClassLoader classLoader) { Assert.notNull(factoryType, "'factoryType' must not be null" ); ClassLoader classLoaderToUse = classLoader; if (classLoaderToUse == null ) { classLoaderToUse = SpringFactoriesLoader.class.getClassLoader(); } List<String> factoryImplementationNames = loadFactoryNames(factoryType, classLoaderToUse); if (logger.isTraceEnabled()) { logger.trace("Loaded [" + factoryType.getName() + "] names: " + factoryImplementationNames); } List<T> result = new ArrayList<>(factoryImplementationNames.size()); for (String factoryImplementationName : factoryImplementationNames) { result.add(instantiateFactory(factoryImplementationName, factoryType, classLoaderToUse)); } AnnotationAwareOrderComparator.sort(result); return result; } public static List<String> loadFactoryNames (Class<?> factoryType, @Nullable ClassLoader classLoader) { ClassLoader classLoaderToUse = classLoader; if (classLoaderToUse == null ) { classLoaderToUse = SpringFactoriesLoader.class.getClassLoader(); } String factoryTypeName = factoryType.getName(); return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList()); } private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) { Map<String, List<String>> result = cache.get(classLoader); if (result != null ) { return result; } result = new HashMap<>(); try { Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION); while (urls.hasMoreElements()) { URL url = urls.nextElement(); UrlResource resource = new UrlResource(url); Properties properties = PropertiesLoaderUtils.loadProperties(resource); for (Map.Entry<?, ?> entry : properties.entrySet()) { String factoryTypeName = ((String) entry.getKey()).trim(); String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String) entry.getValue()); for (String factoryImplementationName : factoryImplementationNames) { result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>()) .add(factoryImplementationName.trim()); } } } result.replaceAll((factoryType, implementations) -> implementations.stream().distinct() .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList))); cache.put(classLoader, result); } catch (IOException ex) { ... ... } return result; } private static <T> T instantiateFactory (String factoryImplementationName, Class<T> factoryType, ClassLoader classLoader) { try { Class<?> factoryImplementationClass = ClassUtils.forName(factoryImplementationName, classLoader); return (T) ReflectionUtils.accessibleConstructor(factoryImplementationClass).newInstance(); } catch (Throwable ex) { ... ... } }
检测WebApplicationType 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet" , "org.springframework.web.context.ConfigurableWebApplicationContext" };private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet" ;private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler" ;private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer" ;static WebApplicationType deduceFromClasspath () { if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null ) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null ) && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null )) { return WebApplicationType.REACTIVE; } for (String className : SERVLET_INDICATOR_CLASSES) { if (!ClassUtils.isPresent(className, null )) { return WebApplicationType.NONE; } } return WebApplicationType.SERVLET; }
检测逻辑
类路径中只有org.springframework.web.reactive.DispatcherHandler
时,是响应式应用
类路径中同时存在javax.servlet.Servlet
、org.springframework.web.context.ConfigurableWebApplicationContext
时,是Servlet应用
否则,是普通应用
WebApplicationType有什么用
创建environment时决定创建什么类型的Environment:private ConfigurableEnvironment getOrCreateEnvironment()
创建ApplicationContext时指定具体类型
检测主类 1 2 3 4 5 6 7 8 9 10 11 12 13 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) { } return null ; }
检测逻辑:从当前调用栈中,寻找main方法那一层,其所属类就是主类
run方法 这是应用启动的主要逻辑之所在
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 public ConfigurableApplicationContext run (String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); DefaultBootstrapContext bootstrapContext = createBootstrapContext(); ConfigurableApplicationContext context = null ; SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(bootstrapContext, this .mainApplicationClass); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments); Banner printedBanner = printBanner(environment); context = createApplicationContext(); context.setApplicationStartup(this .applicationStartup); prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner); refreshContext(context); afterRefresh(context, applicationArguments); stopWatch.stop(); if (this .logStartupInfo) { new StartupInfoLogger(this .mainApplicationClass).logStarted(getApplicationLog(), stopWatch); } listeners.started(context); callRunners(context, applicationArguments); } catch (Throwable ex) { handleRunFailure(context, ex, listeners); throw new IllegalStateException(ex); } try { listeners.running(context); } catch (Throwable ex) { handleRunFailure(context, ex, null ); throw new IllegalStateException(ex); } return context; }
获取SpringApplicationRunListener 1 2 3 4 5 private SpringApplicationRunListeners getRunListeners (String[] args) { Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class }; return new SpringApplicationRunListeners(logger,getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this , args), this .applicationStartup); }
准备环境 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 private ConfigurableEnvironment prepareEnvironment (SpringApplicationRunListeners listeners, DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) { ConfigurableEnvironment environment = getOrCreateEnvironment(); configureEnvironment(environment, applicationArguments.getSourceArgs()); ConfigurationPropertySources.attach(environment); listeners.environmentPrepared(bootstrapContext, environment); DefaultPropertiesPropertySource.moveToEnd(environment); configureAdditionalProfiles(environment); ... ... return environment; } private ConfigurableEnvironment getOrCreateEnvironment () { if (this .environment != null ) { return this .environment; } switch (this .webApplicationType) { case SERVLET: return new StandardServletEnvironment(); case REACTIVE: return new StandardReactiveWebEnvironment(); default : return new StandardEnvironment(); } } protected void configureEnvironment (ConfigurableEnvironment environment, String[] args) { if (this .addConversionService) { ConversionService conversionService = ApplicationConversionService.getSharedInstance(); environment.setConversionService((ConfigurableConversionService) conversionService); } configurePropertySources(environment, args); configureProfiles(environment, args); }
创建ApplicationContext 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 protected ConfigurableApplicationContext createApplicationContext () { return this .applicationContextFactory.create(this .webApplicationType); } ApplicationContextFactory DEFAULT = (webApplicationType) -> { try { switch (webApplicationType) { case SERVLET: return new AnnotationConfigServletWebServerApplicationContext(); case REACTIVE: return new AnnotationConfigReactiveWebServerApplicationContext(); default : return new AnnotationConfigApplicationContext(); } } catch (Exception ex) { ... ... } };
准备容器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 private void prepareContext (DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { context.setEnvironment(environment); postProcessApplicationContext(context); applyInitializers(context); listeners.contextPrepared(context); bootstrapContext.close(context); ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); beanFactory.registerSingleton("springApplicationArguments" , applicationArguments); if (printedBanner != null ) { beanFactory.registerSingleton("springBootBanner" , printedBanner); } if (beanFactory instanceof DefaultListableBeanFactory) { ((DefaultListableBeanFactory) beanFactory).setAllowBeanDefinitionOverriding(this .allowBeanDefinitionOverriding); } if (this .lazyInitialization) { context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor()); } Set<Object> sources = getAllSources(); Assert.notEmpty(sources, "Sources must not be empty" ); load(context, sources.toArray(new Object[0 ])); listeners.contextLoaded(context); }
其它 - 注解排序 为组件排序的方法,我们发现哪儿都有它,这里关照一下:AnnotationAwareOrderComparator.sort()
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 public class AnnotationAwareOrderComparator extends OrderComparator { public static final AnnotationAwareOrderComparator INSTANCE = new AnnotationAwareOrderComparator(); public static void sort (List<?> list) { if (list.size() > 1 ) { list.sort(INSTANCE); } } protected Integer findOrder (Object obj) { Integer order = super .findOrder(obj); if (order != null ) { return order; } return findOrderFromAnnotation(obj); } @Nullable private Integer findOrderFromAnnotation (Object obj) { AnnotatedElement element = (obj instanceof AnnotatedElement ? (AnnotatedElement) obj : obj.getClass()); MergedAnnotations annotations = MergedAnnotations.from(element, SearchStrategy.TYPE_HIERARCHY); Integer order = OrderUtils.getOrderFromAnnotations(element, annotations); if (order == null && obj instanceof DecoratingProxy) { return findOrderFromAnnotation(((DecoratingProxy) obj).getDecoratedClass()); } return order; } }
所以,重点肯定在OrderComparator
的compare()
方法上。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 public int compare (@Nullable Object o1, @Nullable Object o2) { return doCompare(o1, o2); } private int doCompare (@Nullable Object o1, @Nullable Object o2) { boolean p1 = (o1 instanceof PriorityOrdered); boolean p2 = (o2 instanceof PriorityOrdered); if (p1 && !p2) { return -1 ; } else if (p2 && !p1) { return 1 ; } int i1 = getOrder(o1); int i2 = getOrder(o2); return Integer.compare(i1, i2); } protected int getOrder (@Nullable Object obj) { if (obj != null ) { Integer order = findOrder(obj); if (order != null ) { return order; } } return Ordered.LOWEST_PRECEDENCE; } protected Integer findOrder (Object obj) { return (obj instanceof Ordered ? ((Ordered) obj).getOrder() : null ); }
可以总结出优先级顺序,两个组件
如果一个实现了PriorityOrdered
接口,一个没有实现PriorityOrdered
接口,则实现了这个优先级更高
PriorityOrdered
是Order
的子接口,没有任何附加实现
也就是说,PriorityOrdered
就是想实现一点:它比普通Ordered
接口具有更高的优先级
如果都实现了PriorityOrdered
,则根据其order值排序
如果实现了Ordered
或被@Order
注解,则根据其order值排序
没有任何排序接口或注解的组件之间相互对比,永远是相等的
没有任何排序接口或注解的组件,和,有任意一个排序接口或注解的组件,后者优先级更高,除非后者主动指定order为Ordered.LOWEST_PRECEDENCE