Spring Boot源码分析七:prepareEnvironment

前言

本文是作者写关于Spring源码的第一篇文章,作者水平有限,所有的源码文章仅限用作个人学习记录。文中如有错误欢迎各位留言指正。

run

java 复制代码
public ConfigurableApplicationContext run(String... args) {
// 为了计时用的,老版本和新版本不一样
   long startTime = System.nanoTime();
   // 初始化一个引导器的上下文,这是属于Spring Boot的上下文。后边还有一个Spring的上下文。apach好喜欢context这个东西,证明写框架这个context是真的好用。
   DefaultBootstrapContext bootstrapContext = createBootstrapContext();
   // 这是Spring的上下文,在这里定义,在下面进行的初始化
   ConfigurableApplicationContext context = null;
   // 配置一个系统属性
   configureHeadlessProperty();
   // 获取配置文件的监听器 重点 也是扩展点,凡是读取配置文件的地方都是扩展点,因为配置在配置文件中的initializer、listener都会在某个阶段被调用
   SpringApplicationRunListeners listeners = getRunListeners(args);
   // 调用监听器发送启动事件,这里可以通过自定义监听器消费这个事件,处理自己的逻辑
   listeners.starting(bootstrapContext, this.mainApplicationClass);
   try {
   // 解析命令行参数 将其封装成一个ApplicationArguments,这个类的变量name被设置成commandLineArgs字符串,变量source是解析args封装的CommandLineArgs对象。
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
      // 环境预处理
      ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
      configureIgnoreBeanInfo(environment);
      Banner printedBanner = printBanner(environment);
      context = createApplicationContext();
      context.setApplicationStartup(this.applicationStartup);
      prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
      refreshContext(context);
      afterRefresh(context, applicationArguments);
      Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
      if (this.logStartupInfo) {
         new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
      }
      listeners.started(context, timeTakenToStartup);
      callRunners(context, applicationArguments);
   }
   catch (Throwable ex) {
      handleRunFailure(context, ex, listeners);
      throw new IllegalStateException(ex);
   }
   try {
      Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
      listeners.ready(context, timeTakenToReady);
   }
   catch (Throwable ex) {
      handleRunFailure(context, ex, null);
      throw new IllegalStateException(ex);
   }
   return context;
}

prepareEnvironment

java 复制代码
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
      DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
   // Create and configure the environment
   // 创建一个配置的环境变量
   ConfigurableEnvironment environment = getOrCreateEnvironment();
   // 给环境变量中添加args参数资源
   configureEnvironment(environment, applicationArguments.getSourceArgs());
   // 给环境变量中sources取出来封装成PropertySourcer然后又保存到sources中。
   ConfigurationPropertySources.attach(environment);
   // 发送环境预处理事件
   listeners.environmentPrepared(bootstrapContext, environment);
   // 将defaultProperties属性移动到sources的最后去
   DefaultPropertiesPropertySource.moveToEnd(environment);
   Assert.state(!environment.containsProperty("spring.main.environment-prefix"),
         "Environment prefix cannot be set via properties.");
   bindToSpringApplication(environment);
   if (!this.isCustomEnvironment) {
      environment = convertEnvironment(environment);
   }
   ConfigurationPropertySources.attach(environment);
   return environment;
}

getOrCreateEnvironment

创建配置的环境变量。

java 复制代码
private ConfigurableEnvironment getOrCreateEnvironment() {
   if (this.environment != null) {
      return this.environment;
   }
   switch (this.webApplicationType) {
   case SERVLET:
      return new ApplicationServletEnvironment();
   case REACTIVE:
      return new ApplicationReactiveWebEnvironment();
   default:
      return new ApplicationEnvironment();
   }
}

默认是SERVLET。创建的是ApplicationServletEnvironment类型实例。

configureEnvironment

java 复制代码
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
// 默认为true
   if (this.addConversionService) {
   // 给配置环境对象中添加类型转换器Conversion。构建这个ApplicationConversionService的构造函数中给自己添加了类型转换器和格式化器
      environment.setConversionService(new ApplicationConversionService());
   }
   // 将环境中添加命args参数到source中
   configurePropertySources(environment, args);
   // 这个方法中没有具体的实现。子类可以继承SpringApplication重写这个方法
   configureProfiles(environment, args);
}

ApplicationConversionService

在它的重载的构造方法中调用了configure(this)方法,这个方法的this就是ApplicationConversionService自己。它也是FormatterRegistry的子类。 总的来说就是给这个类添加了类型转换器和格式化器。

java 复制代码
public static void configure(FormatterRegistry registry) {
// 给ApplicationConversionService实例中添加默认的类型转换器
   DefaultConversionService.addDefaultConverters(registry);
   // 添加默认的格式化器
   DefaultFormattingConversionService.addDefaultFormatters(registry);
   addApplicationFormatters(registry);
   addApplicationConverters(registry);
}

addDefaultConverters

DefaultConversionService.addDefaultConverters方法。

ApplicationConversionService也是converterRegistry的子类。

java 复制代码
public static void addDefaultConverters(ConverterRegistry converterRegistry) {
// 和scalar相关
   addScalarConverters(converterRegistry);
   // 添加默认的转换器 集合、数组、对象之间的转换
   addCollectionConverters(converterRegistry);

// 添加一些转换器
   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));
}

addCollectionConverters

java 复制代码
public static void addCollectionConverters(ConverterRegistry converterRegistry) {
   ConversionService conversionService = (ConversionService) converterRegistry;
// 数组转集合转换器
   converterRegistry.addConverter(new ArrayToCollectionConverter(conversionService));
   // 集合转数组转换器
   converterRegistry.addConverter(new CollectionToArrayConverter(conversionService));

// 数组转数组转换器
   converterRegistry.addConverter(new ArrayToArrayConverter(conversionService));
   // 集合转集合转换器
   converterRegistry.addConverter(new CollectionToCollectionConverter(conversionService));
   // Map转Map转换器
   converterRegistry.addConverter(new MapToMapConverter(conversionService));
// 数组转String类型转换器
   converterRegistry.addConverter(new ArrayToStringConverter(conversionService));
   // 字符串转数组类型转换器
   converterRegistry.addConverter(new StringToArrayConverter(conversionService));
// 数组转对象类型转换器
   converterRegistry.addConverter(new ArrayToObjectConverter(conversionService));
   // 对象转数组类型转换器
   converterRegistry.addConverter(new ObjectToArrayConverter(conversionService));
// 集合转String类型转换器
   converterRegistry.addConverter(new CollectionToStringConverter(conversionService));
   // String转集合类型转换器
   converterRegistry.addConverter(new StringToCollectionConverter(conversionService));
// 集合转对象类型转换器
   converterRegistry.addConverter(new CollectionToObjectConverter(conversionService));
   // 对象转集合类型转换器
   converterRegistry.addConverter(new ObjectToCollectionConverter(conversionService));
// 流转换器
   converterRegistry.addConverter(new StreamConverter(conversionService));
}

addDefaultFormatters

DefaultFormattingConversionService.addDefaultFormatters(registry)。

java 复制代码
public static void addDefaultFormatters(FormatterRegistry formatterRegistry) {
   // Default handling of number values
   // @NumberFormat注解标注一个字段时,JDBC在读取或写入该字段的值时,会使用NumberFormatAnnotationFormatterFactory创建的格式化器来对值进行格式化处理
   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
   // 添加java8出来的日期类型的格式化,跟进去发现是添加了一些类型转换器convertor
   new DateTimeFormatterRegistrar().registerFormatters(formatterRegistry);

   if (jodaTimePresent) {
      // handles Joda-specific types as well as Date, Calendar, Long
      new org.springframework.format.datetime.joda.JodaTimeFormatterRegistrar().registerFormatters(formatterRegistry);
   }
   else {
      // regular DateFormat-based Date, Calendar, Long converters
      // 之前的日期格式化器
      new DateFormatterRegistrar().registerFormatters(formatterRegistry);
   }
}

configurePropertySources

给environment中的sources赋值。

java 复制代码
protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {
// 获取environment中的sources
   MutablePropertySources sources = environment.getPropertySources();
   // 如果我们给SpringApplication的defaultProperties赋值了这里会进行解析然后保存找到sources中
   if (!CollectionUtils.isEmpty(this.defaultProperties)) {
      DefaultPropertiesPropertySource.addOrMerge(this.defaultProperties, sources);
   }
   // 将命令行参数添加到sources中
   if (this.addCommandLineProperties && args.length > 0) {
      String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
      if (sources.contains(name)) {
         PropertySource<?> source = sources.get(name);
         CompositePropertySource composite = new CompositePropertySource(name);
         composite.addPropertySource(
               new SimpleCommandLinePropertySource("springApplicationCommandLineArgs", args));
         composite.addPropertySource(source);
         sources.replace(name, composite);
      }
      else {
         sources.addFirst(new SimpleCommandLinePropertySource(args));
      }
   }
}

OK 今天先到这里吧。

See you next time :)

相关推荐
XiaoLeisj27 分钟前
【JavaEE初阶 — 多线程】单例模式 & 指令重排序问题
java·开发语言·java-ee
paopaokaka_luck29 分钟前
【360】基于springboot的志愿服务管理系统
java·spring boot·后端·spring·毕业设计
dayouziei29 分钟前
java的类加载机制的学习
java·学习
Yaml42 小时前
Spring Boot 与 Vue 共筑二手书籍交易卓越平台
java·spring boot·后端·mysql·spring·vue·二手书籍
小小小妮子~2 小时前
Spring Boot详解:从入门到精通
java·spring boot·后端
hong1616882 小时前
Spring Boot中实现多数据源连接和切换的方案
java·spring boot·后端
aloha_7893 小时前
从零记录搭建一个干净的mybatis环境
java·笔记·spring·spring cloud·maven·mybatis·springboot
记录成长java3 小时前
ServletContext,Cookie,HttpSession的使用
java·开发语言·servlet
睡觉谁叫~~~4 小时前
一文解秘Rust如何与Java互操作
java·开发语言·后端·rust
程序媛小果4 小时前
基于java+SpringBoot+Vue的旅游管理系统设计与实现
java·vue.js·spring boot