Spring Boot Application

时间:2023-12-11 19:28:56

spring boot默认已经配置了很多环境变量,例如,tomcat的默认端口是8080,项目的contextpath是“/”等等,spring boot允许你自定义一个application.properties文件,然后放在以下的地方,来重写spring boot的环境变量

spring对配置application.properties的加载过程:

  1. 服务启动调用:SpringApplication.run
  2. 创建默认的环境参数:ConfigurableEnvironment
  3. 触发事件:ApplicationEnvironmentPreparedEvent
  4. 完成加载

整个过程主要使用spring boot 内置的ConfigFileApplicationListener监听器监听ApplicationEnvironmentPreparedEvent事件完成对application.properties加载以及设置。


下面我们来跟踪源码,看下spring boot是怎样完成对application.properties文件的加载

  • SpringApplication 入口 run:
  1. public ConfigurableApplicationContext run(String... args) {
  2. //无关的代码暂略
  3. .......
  4. ConfigurableApplicationContext context = null;
  5. FailureAnalyzers analyzers = null;
  6. configureHeadlessProperty();
  7. //获取执行监听器实例
  8. SpringApplicationRunListeners listeners = getRunListeners(args);
  9. ........
  10. //创建全局系统参数实例
  11. ApplicationArguments applicationArguments = new DefaultApplicationArguments(
  12. args);
  13. //创建 ConfigurableEnvironment 并触发ApplicationEnvironmentPreparedEvent事件
  14. //加载配置的核心地方,spring启动首要做的事情
  15. ConfigurableEnvironment environment = prepareEnvironment(listeners,
  16. applicationArguments);
  17. .........
  18. }

prepareEnvironment方法

  1. private ConfigurableEnvironment prepareEnvironment(
  2. SpringApplicationRunListeners listeners,
  3. ApplicationArguments applicationArguments) {
  4. // Create and configure the environment
  5. //创建一个配置环境信息,当是web环境时创建StandardServletEnvironment实例,非web环境时创建StandardEnvironment实例
  6. ConfigurableEnvironment environment = getOrCreateEnvironment();
  7. configureEnvironment(environment, applicationArguments.getSourceArgs());
  8. //核心事件触发方法,此方法执行后会执行所有监听ApplicationEnvironmentPreparedEvent事件的监听器,这里我们是跟踪application.properties文件的加载,就查看ConfigFileApplicationListener监听器都做了什么工作
  9. listeners.environmentPrepared(environment);
  10. if (!this.webEnvironment) {
  11. environment = new EnvironmentConverter(getClassLoader())
  12. .convertToStandardEnvironmentIfNecessary(environment);
  13. }
  14. return environment;
  15. }
  • ConfigFileApplicationListener:
  1. public void onApplicationEvent(ApplicationEvent event) {
  2. //从此处可以看到当事件为ApplicationEnvironmentPreparedEvent时,执行onApplicationEnvironmentPreparedEvent方法
  3. if (event instanceof ApplicationEnvironmentPreparedEvent) {
  4. onApplicationEnvironmentPreparedEvent(
  5. (ApplicationEnvironmentPreparedEvent) event);
  6. }
  7. if (event instanceof ApplicationPreparedEvent) {
  8. onApplicationPreparedEvent(event);
  9. }
  10. }

onApplicationEnvironmentPreparedEvent

  1. private void onApplicationEnvironmentPreparedEvent(
  2. ApplicationEnvironmentPreparedEvent event) {
  3. //此处通过SpringFactoriesLoader加载EnvironmentPostProcessor所有扩展
  4. List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
  5. //因为此监听器同样是EnvironmentPostProcessor的扩展实例,所以在此处将自己加入集合
  6. postProcessors.add(this);
  7. AnnotationAwareOrderComparator.sort(postProcessors);
  8. //遍历所有的EnvironmentPostProcessor扩展调用postProcessEnvironment
  9. //当然我们跟踪是application.properties所以主要查看当前实例的postProcessEnvironment方法
  10. for (EnvironmentPostProcessor postProcessor : postProcessors) {
  11. postProcessor.postProcessEnvironment(event.getEnvironment(),
  12. event.getSpringApplication());
  13. }
  14. }

postProcessEnvironment

  1. @Override
  2. public void postProcessEnvironment(ConfigurableEnvironment environment,
  3. SpringApplication application) {
  4. //此处添加配置信息到environment实例中,此方法完成后就将application.properties加载到环境信息中
  5. addPropertySources(environment, application.getResourceLoader());
  6. configureIgnoreBeanInfo(environment);
  7. bindToSpringApplication(environment, application);
  8. }

addPropertySources

  1. protected void addPropertySources(ConfigurableEnvironment environment,
  2. ResourceLoader resourceLoader) {
  3. //这里先添加一个Random名称的资源到环境信息中
  4. RandomValuePropertySource.addToEnvironment(environment);
  5. //通过Loader加载application.properties并将信息存入环境信息中
  6. new Loader(environment, resourceLoader).load();
  7. }

load

  1. public void load() {
  2. //创建一个资源加载器,spring boot默认支持PropertiesPropertySourceLoader,YamlPropertySourceLoader两种配置文件的加载
  3. this.propertiesLoader = new PropertySourcesLoader();
  4. this.activatedProfiles = false;
  5. //加载配置profile信息,默认为default
  6. ..........此处省略
  7. while (!this.profiles.isEmpty()) {
  8. Profile profile = this.profiles.poll();
  9. //遍历所有查询路径,默认路径有:classpath:/,classpath:/config/,file:./,file:./config/
  10. for (String location : getSearchLocations()) {
  11. //这里不仅仅是加载application.properties,当搜索路径不是以/结束,默认认为是文件名已存在的路径
  12. if (!location.endsWith("/")) {
  13. // location is a filename already, so don't search for more
  14. // filenames
  15. load(location, null, profile);
  16. }
  17. else {
  18. //遍历要加载的文件名集合,默认为application
  19. for (String name : getSearchNames()) {
  20. load(location, name, profile);
  21. }
  22. }
  23. }
  24. this.processedProfiles.add(profile);
  25. }
  26. //将加载完成的配置信息全部保存到环境信息*享
  27. addConfigurationProperties(this.propertiesLoader.getPropertySources());
  28. }

load

  1. private void load(String location, String name, Profile profile) {
  2. //此处根据profile组装加载的文件名称以及资源所放置的组信息
  3. String group = "profile=" + (profile == null ? "" : profile);
  4. if (!StringUtils.hasText(name)) {
  5. // Try to load directly from the location
  6. loadIntoGroup(group, location, profile);
  7. }
  8. else {
  9. // Also try the profile-specific section (if any) of the normal file
  10. loadIntoGroup(group, location + name + "." + ext, profile);
  11. }
  12. }
  13. }

loadIntoGroup

  1. private PropertySource<?> doLoadIntoGroup(String identifier, String location,
  2. Profile profile) throws IOException {
  3. Resource resource = this.resourceLoader.getResource(location);
  4. PropertySource<?> propertySource = null;
  5. if (resource != null && resource.exists()) {
  6. String name = "applicationConfig: [" + location + "]";
  7. String group = "applicationConfig: [" + identifier + "]";
  8. //资源加载核心方法,此处有两个实现,当后缀为,xml或者properties调用PropertiesPropertySourceLoader
  9. //当后缀为yml或者yaml时,调用YamlPropertySourceLoader
  10. propertySource = this.propertiesLoader.load(resource,
  11. }
  12. return propertySource;
  13. }
  • PropertiesPropertySourceLoader:
  1. @Override
  2. public PropertySource<?> load(String name, Resource resource, String profile)
  3. throws IOException {
  4. if (profile == null) {
  5. //此处调用PropertiesLoaderUtils工具类加载本地文件
  6. Properties properties = PropertiesLoaderUtils.loadProperties(resource);
  7. if (!properties.isEmpty()) {
  8. return new PropertiesPropertySource(name, properties);
  9. }
  10. }
  11. return null;
  12. }

到此application.properties就真正的加载并共享到环境信息中,供系统其它地方调用