Spring Boot 启动机制源码阅读(粗略)

【写在阅读前和写作后。总体来说,对于这次阅读效果不是很满意。没有梳理清楚流程和逻辑,只是通读了代码。而且对于最关键的几个实现,类加载、Listener、Processor几个关键方法都没有解读的很清楚。私以为要搞清楚这些,需要对于Spring的实现思路有一定了解,另外需要了解一下相关的设计模式才好解读。这几天再梳理和继续阅读,后面再出一篇】

 

理清主线再深入细节

main方法解析

@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 {

 

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
//分成两部分:1.生成应用对象 2.执行run方法
  return new SpringApplication(primarySources).run(args);
}

 

new SpringApplication(primarySources)部分

/**
* Create a new {@link SpringApplication} instance. The application context will load
* beans from the specified primary sources (see {@link SpringApplication class-level}
* documentation for details. The instance can be customized before calling
* {@link #run(String...)}.
* @param resourceLoader the resource loader to use
* @param primarySources the primary bean sources
* @see #run(Class, String[])
* @see #setSources(Set)
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
  //默认为空
  this.resourceLoader = resourceLoader;
  Assert.notNull(primarySources, "PrimarySources must not be null");
  //收录主容器类
  this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
  //貌似是在识别应用类型,此处默认的结果为SERVLET
  this.webApplicationType = WebApplicationType.deduceFromClasspath();
  //这部分核心为getSpringFactoriesInstances,主要做了三件事
  //1.从META/spring.facteries文件中读取预定义的ApplicationContextInitializer的相关实现全路径类名
  //2.把读取到的类实例化
  //3.根据javax.annotation.Priority的值排序(值大的优先级高)
  setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
   //同上,这部分是读取META/spring.facteries下的ApplicationListener相关配置,实例化,排序
  setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
   //通过运行堆栈找出main方法所在的主类
   //这里有一个好玩的地方是,通过 StackTraceElement[] stackTrace = new RuntimeException().getStackTrace(); 是可以拿到当前的执行堆栈,回溯前文的
  this.mainApplicationClass = deduceMainApplicationClass();
}

 

加载细节就只解读到这个层次,后续研究类加载器文件加载时,可以以这部分为参考来学习。

这部分细节涉及的关键点主要是两个,一个是类加载器,一个是缓存。对于后续如果要解读整体的缓存方案,这部分也需要了解。

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
  ClassLoader classLoader = getClassLoader();
  // Use names and ensure unique to protect against duplicates
  //1.从META/spring.facteries文件中读取预定义的ApplicationContextInitializer的相关实现全路径类名
  Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
   //2.把读取到的类实例化
  List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
   //3.根据javax.annotation.Priority的值排序(值大的优先级高)
  AnnotationAwareOrderComparator.sort(instances);
  return instances;
}

 

 

最基础的Spring-Boot-Web的项目,

默认加载的ApplicationContextInitializer为:(排序后)

0 = "org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer" 1 = "org.springframework.boot.context.ContextIdApplicationContextInitializer" 2 = "org.springframework.boot.context.config.DelegatingApplicationContextInitializer" 3 = "org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer" 4 = "org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer" 5 = "org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer" 6 = "org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener"

默认加载的ApplicationListener为:(排序后)

0 = "org.springframework.boot.ClearCachesApplicationListener" 1 = "org.springframework.boot.builder.ParentContextCloserApplicationListener" 2 = "org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor" 3 = "org.springframework.boot.context.FileEncodingApplicationListener" 4 = "org.springframework.boot.context.config.AnsiOutputApplicationListener" 5 = "org.springframework.boot.context.config.ConfigFileApplicationListener" 6 = "org.springframework.boot.context.config.DelegatingApplicationListener" 7 = "org.springframework.boot.context.logging.ClasspathLoggingApplicationListener" 8 = "org.springframework.boot.context.logging.LoggingApplicationListener" 9 = "org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener" 10 = "org.springframework.boot.autoconfigure.BackgroundPreinitializer"

默认执行到这里的堆栈为:

0 = {StackTraceElement@1968} "org.springframework.boot.SpringApplication.deduceMainApplicationClass(SpringApplication.java:279)" 1 = {StackTraceElement@1969} "org.springframework.boot.SpringApplication.<init>(SpringApplication.java:274)" 2 = {StackTraceElement@1970} "org.springframework.boot.SpringApplication.<init>(SpringApplication.java:253)" 3 = {StackTraceElement@1971} "org.springframework.boot.SpringApplication.run(SpringApplication.java:1226)" 4 = {StackTraceElement@1972} "org.springframework.boot.SpringApplication.run(SpringApplication.java:1215)" 5 = {StackTraceElement@1973} "com.example.demo.DemoApplication.main(DemoApplication.java:10)"

 

SpringApplication.run()方法部分

主体代码为:

/**
* 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 stopWatch = new StopWatch();
stopWatch.start();
       //声明上下文容器
ConfigurableApplicationContext context = null;
       //声明异常报告容器
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
       //设置java.awt.headless为SpringApplicationheadless属性值,默认为true
configureHeadlessProperty();
       //从META/spring.facteries中读取SpringApplicationRunListener并实例化,封装成一个SpringApplicationRunListeners对象,该对象与SpringApplication共用一个logger
SpringApplicationRunListeners listeners = getRunListeners(args);
       //启动运行时监听器,此处只有org.springframework.boot.context.event.EventPublishingRunListener一个监听
       //这个监听做的事情如下:
       //1.新建一个org.springframework.boot.context.event.ApplicationStartingEvent事件
       //2.广播该事件(广播的原理是,拿到所有注册对于该事件感兴趣的监听,用taskExecutor执行)
       //那么问题来了,是在哪里注册的事件关注呢(追了一下源码,监听器本身常量定义了关注的事件,是在getApplicationListeners的过程中,扫描所有监听器,通过代理方法判断是否关注,然后缓存起来的)
listeners.starting();
try {
           //声明入参对象
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
           //准备环境,做的事情主要如下:(参见下面的解读)
           //组建一个环境对象
           //加载各种Converter、Formatter、数据读取源、profile;
           //这个过程中会广播一个ApplicationEnvironmentPreparedEvent事件
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
           //如果环境属性spring.beaninfo.ignore为空,则设置一个环境属性spring.beaninfo.ignore=true
configureIgnoreBeanInfo(environment);
           //打印Banner信息
           //SpringApplication.bannerMode=OFF时可以关闭Banner打印
           //通过属性spring.banner.image.location读取Banner的图像信息,支持gif/jpg/png三个格式,找banner.gif、banner.jpg、banner.png
           //文字版找spring.banner.location下的banner.txt
           //图片和文字都找不到时以SpringBootBanner作为默认的banner
           //找到多个Banner时会都打印
           //打印完Banner后,会通过SpringBootVersion.getVersion()打印版本信息(此处是带颜色的打印)
Banner printedBanner = printBanner(environment);
           //SERVLET应用,默认创建一个org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext容器,该容器创建时会创建AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner
context = createApplicationContext();
           //从MEAT/spring.factories中加载SpringBootExceptionReporter的相关配置并创建实例
           //默认加载的只有一个 org.springframework.boot.diagnostics.FailureAnalyzers
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
           //初始化context容器对象(塞各种属性),
           //找到之前加载的ApplicationContextInitializer,执行initialize方法 !!!
           //并发布一个ApplicationContextInitializedEvent事件
           //由logStartupInfo开关控制,打印一条开始的info日志,如果开启了debug级别还会有一条debug的运行日志;打印激活的profile
           //注册一个springApplicationArguments的单例bean
           //printedBanner如果不为空,也注册为单例bean
           //如果是默认的bean工程DefaultListableBeanFactory,设置由allowBeanDefinitionOverriding属性控制是否允许重载
           //由lazyInitialization控制加载一个LazyInitializationBeanFactoryPostProcessor处理器
           //创建BeanDefinitionLoader,执行其load方法
           //load方法里,以Competent的方式注册入口类(这部分其他内容没有看出来意图是什么)
           //load完毕后,创建并发布一个ApplicationPreparedEvent事件
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
           //这部分具体看后面关于refresh的解析 !!!
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, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}

try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}

一个Spring-Boot-Web项目

默认加载的SpringApplicationRunListener为

org.springframework.boot.context.event.EventPublishingRunListener

默认关注启动事件org.springframework.boot.context.event.ApplicationStartingEvent的监听有:

0 = {LoggingApplicationListener@2162} 1 = {BackgroundPreinitializer@2261} 2 = {DelegatingApplicationListener@2378} 3 = {LiquibaseServiceLocatorApplicationListener@2379}

 

准备环境的代码如下:

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
     ApplicationArguments applicationArguments) {
  // Create and configure the environment
   //初始化一个环境容器
   //1.读取默认的profile
   //2.读取默认的属性读取源
  ConfigurableEnvironment environment = getOrCreateEnvironment();
   //配置环境,这里就是加载各种Converter、Formatter、配置读取属性源、profile;具体可看后面的解读
  configureEnvironment(environment, applicationArguments.getSourceArgs());
   //这里是把configurationProperties属性源放到第一个,如果没有就直接添加到第一个
  ConfigurationPropertySources.attach(environment);
   //新建并广播一个ApplicationEnvironmentPreparedEvent事件,通知关注的监听器
  listeners.environmentPrepared(environment);
   //没看到这里在干什么,注释是说把环境对象绑定到SpringApplication上
  bindToSpringApplication(environment);
  if (!this.isCustomEnvironment) {
      //如果需要转化就转换环境对象(此处默认是没有转换的)
     environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
           deduceEnvironmentClass());
  }
   //和上面一样,重新把configurationProperties属性源放到第一个
  ConfigurationPropertySources.attach(environment);
  return environment;
}

默认profile

default

 

默认的属性读取源

0 = {PropertySource$StubPropertySource@2303} "StubPropertySource {name='servletConfigInitParams'}" 1 = {PropertySource$StubPropertySource@2368} "StubPropertySource {name='servletContextInitParams'}" 2 = {PropertiesPropertySource@2408} "PropertiesPropertySource {name='systemProperties'}" 3 = {SystemEnvironmentPropertySource@2409} "SystemEnvironmentPropertySource {name='systemEnvironment'}"

 

配置环境代码如下:

/**
* Template method delegating to
* {@link #configurePropertySources(ConfigurableEnvironment, String[])} and
* {@link #configureProfiles(ConfigurableEnvironment, String[])} in that order.
* Override this method for complete control over Environment customization, or one of
* the above for fine-grained control over property sources or profiles, respectively.
* @param environment this application's environment
* @param args arguments passed to the {@code run} method
* @see #configureProfiles(ConfigurableEnvironment, String[])
* @see #configurePropertySources(ConfigurableEnvironment, String[])
*/
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
  if (this.addConversionService) { //默认为true
      //获取转化服务
     ConversionService conversionService = ApplicationConversionService.getSharedInstance();
     environment.setConversionService((ConfigurableConversionService) conversionService);
  }
   //配置属性源(就是之前准备环境时读取到的),这里只是放到环境对象里
  configurePropertySources(environment, args);
   //把额外添加的profile加到环境对象里
  configureProfiles(environment, args);
}

 

默认添加的数量转化服务如下:

converterRegistry.addConverterFactory(new NumberToNumberConverterFactory());

converterRegistry.addConverterFactory(new StringToNumberConverterFactory());
converterRegistry.addConverter(Number.class, String.class, new ObjectToStringConverter());

converterRegistry.addConverter(new StringToCharacterConverter());
converterRegistry.addConverter(Character.class, String.class, new ObjectToStringConverter());

converterRegistry.addConverter(new NumberToCharacterConverter());
converterRegistry.addConverterFactory(new CharacterToNumberFactory());

converterRegistry.addConverter(new StringToBooleanConverter());
converterRegistry.addConverter(Boolean.class, String.class, new ObjectToStringConverter());

converterRegistry.addConverterFactory(new StringToEnumConverterFactory());
converterRegistry.addConverter(new EnumToStringConverter((ConversionService) converterRegistry));

converterRegistry.addConverterFactory(new IntegerToEnumConverterFactory());
converterRegistry.addConverter(new EnumToIntegerConverter((ConversionService) converterRegistry));

converterRegistry.addConverter(new StringToLocaleConverter());
converterRegistry.addConverter(Locale.class, String.class, new ObjectToStringConverter());

converterRegistry.addConverter(new StringToCharsetConverter());
converterRegistry.addConverter(Charset.class, String.class, new ObjectToStringConverter());

converterRegistry.addConverter(new StringToCurrencyConverter());
converterRegistry.addConverter(Currency.class, String.class, new ObjectToStringConverter());

converterRegistry.addConverter(new StringToPropertiesConverter());
converterRegistry.addConverter(new PropertiesToStringConverter());

converterRegistry.addConverter(new StringToUUIDConverter());
converterRegistry.addConverter(UUID.class, String.class, new ObjectToStringConverter());

默认添加的集合Converter有

converterRegistry.addConverter(new ArrayToCollectionConverter(conversionService));
converterRegistry.addConverter(new CollectionToArrayConverter(conversionService));

converterRegistry.addConverter(new ArrayToArrayConverter(conversionService));
converterRegistry.addConverter(new CollectionToCollectionConverter(conversionService));
converterRegistry.addConverter(new MapToMapConverter(conversionService));

converterRegistry.addConverter(new ArrayToStringConverter(conversionService));
converterRegistry.addConverter(new StringToArrayConverter(conversionService));

converterRegistry.addConverter(new ArrayToObjectConverter(conversionService));
converterRegistry.addConverter(new ObjectToArrayConverter(conversionService));

converterRegistry.addConverter(new CollectionToStringConverter(conversionService));
converterRegistry.addConverter(new StringToCollectionConverter(conversionService));

converterRegistry.addConverter(new CollectionToObjectConverter(conversionService));
converterRegistry.addConverter(new ObjectToCollectionConverter(conversionService));

converterRegistry.addConverter(new StreamConverter(conversionService));

其余默认加载的Converter

converterRegistry.addConverter(new ByteBufferConverter((ConversionService) converterRegistry));
converterRegistry.addConverter(new StringToTimeZoneConverter());
converterRegistry.addConverter(new ZoneIdToTimeZoneConverter());
converterRegistry.addConverter(new ZonedDateTimeToCalendarConverter());

converterRegistry.addConverter(new ObjectToObjectConverter());
converterRegistry.addConverter(new IdToEntityConverter((ConversionService) converterRegistry));
converterRegistry.addConverter(new FallbackObjectToStringConverter());
converterRegistry.addConverter(new ObjectToOptionalConverter((ConversionService) converterRegistry));

默认加载的Formatter

formatterRegistry.addFormatterForFieldAnnotation(new NumberFormatAnnotationFormatterFactory());

// Default handling of monetary values
if (jsr354Present) {
  formatterRegistry.addFormatter(new CurrencyUnitFormatter());
  formatterRegistry.addFormatter(new MonetaryAmountFormatter());
  formatterRegistry.addFormatterForFieldAnnotation(new Jsr354NumberFormatAnnotationFormatterFactory());
}

// Default handling of date-time values

// just handling JSR-310 specific date and time types
new DateTimeFormatterRegistrar().registerFormatters(formatterRegistry);

if (jodaTimePresent) {
  // handles Joda-specific types as well as Date, Calendar, Long
  new JodaTimeFormatterRegistrar().registerFormatters(formatterRegistry);
}
else {
  // regular DateFormat-based Date, Calendar, Long converters
  new DateFormatterRegistrar().registerFormatters(formatterRegistry);
}

加载的应用的Formatter

registry.addFormatter(new CharArrayFormatter());
registry.addFormatter(new InetAddressFormatter());
registry.addFormatter(new IsoOffsetFormatter());

加载应用的delimitedConverter

registry.addConverter(new ArrayToDelimitedStringConverter(service));
registry.addConverter(new CollectionToDelimitedStringConverter(service));
registry.addConverter(new DelimitedStringToArrayConverter(service));
registry.addConverter(new DelimitedStringToCollectionConverter(service));

其余应用的Converter

registry.addConverter(new StringToDurationConverter());
registry.addConverter(new DurationToStringConverter());
registry.addConverter(new NumberToDurationConverter());
registry.addConverter(new DurationToNumberConverter());
registry.addConverter(new StringToDataSizeConverter());
registry.addConverter(new NumberToDataSizeConverter());
registry.addConverter(new StringToFileConverter());
registry.addConverterFactory(new LenientStringToEnumConverterFactory());
registry.addConverterFactory(new LenientBooleanToEnumConverterFactory());

 

AbstractApplicationContext.refresh()方法

@Override
public void refresh() throws BeansException, IllegalStateException {
  synchronized (this.startupShutdownMonitor) {
     // Prepare this context for refreshing.
    //清空掉scanner的缓存,初始化当前对象的容器数据
     prepareRefresh();

     // Tell the subclass to refresh the internal bean factory.
      //获取beanFactory,这里是org.springframework.beans.factory.support.DefaultListableBeanFactory
     ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
  //初始化beanFactory
     // Prepare the bean factory for use in this context.
     prepareBeanFactory(beanFactory);

     try {
        // Allows post-processing of the bean factory in context subclasses.
         //
         //postProcessBeanFactory不知道是在干嘛,处理入口类上的扫描、其他注解
        postProcessBeanFactory(beanFactory);

        // Invoke factory processors registered as beans in the context.-》在上下文中把工厂处理器注册为bean
 //这里有一个很重要的事情就是把@Bean等bean标签做beanDefinition声明
        invokeBeanFactoryPostProcessors(beanFactory);

        // Register bean processors that intercept bean creation. 注册拦截bean创建的处理器
        registerBeanPostProcessors(beanFactory);

        // Initialize message source for this context. 为上下文初始化消息源
        initMessageSource();

        // Initialize event multicaster for this context. 为上下文初始化消息分发器
        initApplicationEventMulticaster();

        // Initialize other special beans in specific context subclasses. 初始化特定容器子类中的特殊bean
        onRefresh();

        // Check for listener beans and register them. 检查监听器并注册他们
        registerListeners();

        // Instantiate all remaining (non-lazy-init) singletons. 实例化所有尚未实例化的非懒加载单例
//除了部分过程中要用的Bean自己单独进行实例化了,大部分Bean都是在这个环节进行实例化。单例实例都是存放在工厂方法singletonObjects中,这里采用的是ConcurrentHashMap作为存储容器,默认大小256
        finishBeanFactoryInitialization(beanFactory);

        // Last step: publish corresponding event. 最后一步,发布对应的结束事件
        finishRefresh();
    }

     catch (BeansException ex) {
        if (logger.isWarnEnabled()) {
           logger.warn("Exception encountered during context initialization - " +
                 "cancelling refresh attempt: " + ex);
        }

        // Destroy already created singletons to avoid dangling resources. 销毁已经创建的单例,避免资源浪费
        destroyBeans();

        // Reset 'active' flag. 重置 active标记
        cancelRefresh(ex);

        // Propagate exception to caller. 抛出异常
        throw ex;
    }

     finally {
        // Reset common introspection caches in Spring's core, since we
        // might not ever need metadata for singleton beans anymore...
        resetCommonCaches();
    }
  }
}

 

原文地址:https://www.cnblogs.com/ybk2018af/p/12522352.html