SpringBoot 2.0 系列004 --启动实战之配置文件


声明:本文转载自https://my.oschina.net/lt0314/blog/1813479,转载目的在于传递更多信息,仅供学习交流之用。如有侵权行为,请联系我,我会及时删除。

插播一条广告→2021 ByteDance字节跳动内推←各城市、各方向的岗位都有,大量招人!


SpringBoot 2.0 系列004 --启动实战之配置文件

配置文件

配置文件加载流程

很多文档包括官方文档说SB的默认配置文件是application开头的文件,那么是为什么呢?

  • 我们先看下流程图
  •  

由上述流程我们发现,在执行SpringApplication的run方法中的prepareEnvironment子方法时,触发ConfigFileApplicationListener类中的 load方法,完成配置文件的加载

  • ConfigFileApplicationListener分析
public void load() {   this.profiles = Collections.asLifoQueue(new LinkedList<Profile>());   this.processedProfiles = new LinkedList<>();   this.activatedProfiles = false;   this.loaded = new LinkedHashMap<>();   // 核心初始化方法  负责初始化 profile (spring.profiles.active)    // 如果没有则使用默认的AbstractEnvironment类中的(spring.profiles.default)default(可以是application-default名称的,也可以起不加)   initializeProfiles();   // 不为空时 循环加载  初始化了  理论上不会为空   while (!this.profiles.isEmpty()) {     Profile profile = this.profiles.poll();     // 第二步 此方法加载 文件的前缀 active 以及后缀  和路径      load(profile, this::getPositiveProfileFilter,          addToLoaded(MutablePropertySources::addLast, false));     this.processedProfiles.add(profile);   }   // 默认加载的   load(null, this::getNegativeProfileFilter,        addToLoaded(MutablePropertySources::addFirst, true));   addLoadedPropertySources();
  • 接 上边标注第二步的位置
private void load(Profile profile, DocumentFilterFactory filterFactory, 				DocumentConsumer consumer) {             // getSearch 如果你配置了spring.config.location 路径  则使用此处路径             // 否则是用默认的 ConfigFileApplicationListener.DEFAULT_SEARCH_LOCATIONS             // 即 private static final String DEFAULT_SEARCH_LOCATIONS = "classpath:/,classpath:/config/,file:./,file:./config/"; 			getSearchLocations().forEach((location) -> { 				boolean isFolder = location.endsWith("/"); 				// getSearchNames 是你配置文件的名称 ,如果配置了spring.config.name 则使用配置的这个名字 				// 否则使用默认的 private static final String DEFAULT_NAMES = "application"; 				// 这就是默认是application这个名字的原因 				Set<String> names = (isFolder ? getSearchNames() : NO_SEARCH_NAMES); 				names.forEach( 				                                // 第三步  location是路径  name是文件名 profile则是-defalut部分或者其他-dev之类的 						(name) -> load(location, name, profile, filterFactory, consumer)); 			}); 		}
  • 接上边标注第三步
private void load(String location, String name, Profile profile, 				DocumentFilterFactory filterFactory, DocumentConsumer consumer) {                  // 这里需要注意的是propertySourceLoaders 此处是在new loader的时候初始化的,即我们流程中的addPropertySources一步                				        //底层使用的是META-INF/spring.factories                				        /** org.springframework.boot.env.PropertySourceLoader=\                                           org.springframework.boot.env.PropertiesPropertySourceLoader,\                                           org.springframework.boot.env.YamlPropertySourceLoader                                        */            // 这也就是 文件后缀支持 yml,yaml,properties,xml的原因            // 没有名字的情况 			if (!StringUtils.hasText(name)) { 				for (PropertySourceLoader loader : this.propertySourceLoaders) { 					if (canLoadFileExtension(loader, location)) { 						load(loader, location, profile, 								filterFactory.getDocumentFilter(profile), consumer); 					} 				} 			} 			// 带后缀名的情况 			for (PropertySourceLoader loader : this.propertySourceLoaders) { 				for (String fileExtension : loader.getFileExtensions()) { 					String prefix = location + name; 					fileExtension = "." + fileExtension; 					// 第四步 通过后缀名方式加载 					loadForFileExtension(loader, prefix, fileExtension, profile, 							filterFactory, consumer); 				} 			} 		}
  • 接第四步
private void loadForFileExtension(PropertySourceLoader loader, String prefix, 				String fileExtension, Profile profile, 				DocumentFilterFactory filterFactory, DocumentConsumer consumer) { 			DocumentFilter defaultFilter = filterFactory.getDocumentFilter(null); 			DocumentFilter profileFilter = filterFactory.getDocumentFilter(profile); 			// 前边说的 -default和-dev部分 			if (profile != null) { 				// Try profile-specific file & profile section in profile file (gh-340) 				// prefix 是路径名+文件名 				// fileExtension是.yml这种 				// profile 则是第一步initializeProfiles 是this.profiles注入的 				String profileSpecificFile = prefix + "-" + profile + fileExtension; 				// 各处执行装载 扫描不同路径下是否有对应配置文件 				load(loader, profileSpecificFile, profile, defaultFilter, consumer); 				load(loader, profileSpecificFile, profile, profileFilter, consumer); 				// Try profile specific sections in files we've already processed 				for (Profile processedProfile : this.processedProfiles) { 					if (processedProfile != null) { 						String previouslyLoaded = prefix + "-" + processedProfile 								+ fileExtension; 						load(loader, previouslyLoaded, profile, profileFilter, consumer); 					} 				} 			} 			// Also try the profile-specific section (if any) of the normal file 			//// 各处执行装载 扫描不同路径下是否有对应配置文件 			// 第五步 			load(loader, prefix + fileExtension, profile, profileFilter, consumer); 		}
  • 接 第五步

第三步和第四步都会执行下面的方法

private void load(PropertySourceLoader loader, String location, Profile profile, 				DocumentFilter filter, DocumentConsumer consumer) { 			try { 				Resource resource = this.resourceLoader.getResource(location); 				String description = getDescription(location, resource); 				if (profile != null) { 					description = description + " for profile " + profile; 				} 				if (resource == null || !resource.exists()) { 					this.logger.trace("Skipped missing config " + description); 					return; 				} 				if (!StringUtils.hasText( 						StringUtils.getFilenameExtension(resource.getFilename()))) { 					this.logger.trace("Skipped empty config extension " + description); 					return; 				} 				String name = "applicationConfig: [" + location + "]"; 				// 转换成docment对象  				List<Document> documents = loadDocuments(loader, name, resource); 				if (CollectionUtils.isEmpty(documents)) { 					this.logger.trace("Skipped unloaded config " + description); 					return; 				} 				List<Document> loaded = new ArrayList<>(); 				for (Document document : documents) { 				    // 文档Filter 					if (filter.match(document)) { 						maybeActivateProfiles(document.getActiveProfiles()); 						addProfiles(document.getIncludeProfiles()); 						loaded.add(document); 					} 				} 				Collections.reverse(loaded); 				if (!loaded.isEmpty()) { 				    // consumer对象来自 addToLoaded方法 目的是装载到PropertySources中 					loaded.forEach((document) -> consumer.accept(profile, document)); 					 					// 开启debug后 打印出这句话 表示文件被加载完毕。  					this.logger.debug("Loaded config file " + description); 				} 			} 			catch (Exception ex) { 				throw new IllegalStateException("Failed to load property " 						+ "source from location '" + location + "'", ex); 			} 		}

配置文件使用

由上述分析可知,默认的配置文件名为application(-{profile}).yml/xml/yaml/properties,且默认支持项目的resources路径

问题1 怎么使用名称不是application的文件?

根据流程分析可知,文件默认完整名称是由spring.config.name和spring.profiles.default两条属性控制。由此可知,在启动前注入此属性即可。

  • 目标 ,修改默认application为ricky01 ,defalut默认bgt01

  • 在resources新建如下几个文件及内容

默认端口是8010 其他是8011,8012,8013,

  • 注入 spring.config.name和spring.profiles.default属性

主要有如下3种方式

/**      *   测试自定义加载文件的方式 01  通过启动设置参数      *  --spring.config.name=ricky01 --spring.profiles.default=bgt01      * @param args      */     public static void main01(String[] args) throws InterruptedException {         SpringApplication application = new SpringApplication(SpringBootApplication01.class);         application.run(args);     }      /**      *   测试自定义加载文件的方式 01  通过启动设置参数      *  设置环境变量      * @param args      */     public static void main02(String[] args) throws InterruptedException {         System.setProperty("spring.config.name", "ricky01");         System.setProperty("spring.profiles.default", "bgt02");         SpringApplication application = new SpringApplication(SpringBootApplication01.class);         application.run(args);     }      /**      *   测试自定义加载文件的方式 01  通过启动设置参数      *  设置传入参数      * @param args      */     public static void main(String[] args) throws InterruptedException {         args=new String[2];         args[0]="--spring.config.name=ricky01";         args[1]="--spring.profiles.default=bgt03";         SpringApplication application = new SpringApplication(SpringBootApplication01.class);         application.run(args);     }
  • 结果

公用ricky01.yml中的contextpath,服务端口却是各自定义的。以下是bgt03时的启动日志

.   ____          _            __ _ _  /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \  \\/  ___)| |_)| | | | | || (_| |  ) ) ) )   '  |____| .__|_| |_|_| |_\__, | / / / /  =========|_|==============|___/=/_/_/_/  :: Spring Boot ::        (v2.0.1.RELEASE)  2018-05-16 13:40:25.165  INFO 24984 --- [           main] com.ricky.SpringBootApplication01        : Starting SpringBootApplication01 on jsb-bgt with PID 24984 (D:\work\ideawork\SpringBootLearn\chapter04\target\classes started by zdwljs in D:\work\ideawork\SpringBootLearn) 2018-05-16 13:40:25.169  INFO 24984 --- [           main] com.ricky.SpringBootApplication01        : No active profile set, falling back to default profiles: bgt03 2018-05-16 13:40:25.263  INFO 24984 --- [           main] ConfigServletWebServerApplicationContext : Refreshing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@3e92efc3: startup date [Wed May 16 13:40:25 CST 2018]; root of context hierarchy 2018-05-16 13:40:28.721  INFO 24984 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8013 (http) 2018-05-16 13:40:28.756  INFO 24984 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat] 2018-05-16 13:40:28.757  INFO 24984 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet Engine: Apache Tomcat/8.5.29 2018-05-16 13:40:28.770  INFO 24984 --- [ost-startStop-1] o.a.catalina.core.AprLifecycleListener   : The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: [D:\Program Files\Java\jdk1.8.0_161\bin;C:\WINDOWS\Sun\Java\bin;C:\WINDOWS\system32;C:\WINDOWS;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Program Files (x86)\NVIDIA Corporation\PhysX\Common;d:\work\Git\cmd;C:\Program Files (x86)\MySQL\MySQL Server 5.5\bin;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\Users\zdwljs\AppData\Local\Microsoft\WindowsApps;D:\Program Files\Java\jdk1.8.0_161\bin;;.] 2018-05-16 13:40:28.924  INFO 24984 --- [ost-startStop-1] o.a.c.c.C.[.[localhost].[/ricky01]       : Initializing Spring embedded WebApplicationContext 2018-05-16 13:40:28.924  INFO 24984 --- [ost-startStop-1] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 3672 ms 2018-05-16 13:40:29.149  INFO 24984 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean  : Servlet dispatcherServlet mapped to [/] 2018-05-16 13:40:29.155  INFO 24984 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'characterEncodingFilter' to: [/*] 2018-05-16 13:40:29.155  INFO 24984 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'hiddenHttpMethodFilter' to: [/*] 2018-05-16 13:40:29.155  INFO 24984 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'httpPutFormContentFilter' to: [/*] 2018-05-16 13:40:29.155  INFO 24984 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'requestContextFilter' to: [/*] 2018-05-16 13:40:29.371  INFO 24984 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler] 2018-05-16 13:40:29.783  INFO 24984 --- [           main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@3e92efc3: startup date [Wed May 16 13:40:25 CST 2018]; root of context hierarchy 2018-05-16 13:40:29.905  INFO 24984 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.error(javax.servlet.http.HttpServletRequest) 2018-05-16 13:40:29.907  INFO 24984 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse) 2018-05-16 13:40:29.947  INFO 24984 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler] 2018-05-16 13:40:29.947  INFO 24984 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler] 2018-05-16 13:40:30.183  INFO 24984 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup 2018-05-16 13:40:30.245  INFO 24984 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8013 (http) with context path '/ricky01' 2018-05-16 13:40:30.251  INFO 24984 --- [           main] com.ricky.SpringBootApplication01        : Started SpringBootApplication01 in 5.845 seconds (JVM running for 6.639)

问题2 怎么使用外部配置文件加载 方便统一管理

比如我们放在D:\work\ricky\config目录下管理

  • 目标 使用d盘config目录下的文件,如下

  • 第一步 在d盘新建上述文件 默认端口是8020 bgt01端口则是8021

  • 第二步 更改加载路径,如下

这里 只罗列一种方式 其他的和上面修改apllication方式一样

public static void main(String[] args) throws InterruptedException {         args=new String[3];         args[0]="--spring.config.location=file:d:/work/ricky/config/";         args[1]="--spring.config.name=ricky02";         args[2]="--spring.profiles.default=bgt01";         SpringApplication application = new SpringApplication(SpringBootApplication02.class);         application.run(args);     }
  • 结果
.   ____          _            __ _ _  /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \  \\/  ___)| |_)| | | | | || (_| |  ) ) ) )   '  |____| .__|_| |_|_| |_\__, | / / / /  =========|_|==============|___/=/_/_/_/  :: Spring Boot ::        (v2.0.1.RELEASE)  2018-05-16 14:09:21.111  INFO 19708 --- [           main] com.ricky.SpringBootApplication02        : Starting SpringBootApplication02 on jsb-bgt with PID 19708 (D:\work\ideawork\SpringBootLearn\chapter04\target\classes started by zdwljs in D:\work\ideawork\SpringBootLearn) 2018-05-16 14:09:21.118  INFO 19708 --- [           main] com.ricky.SpringBootApplication02        : No active profile set, falling back to default profiles: bgt01 2018-05-16 14:09:21.221  INFO 19708 --- [           main] ConfigServletWebServerApplicationContext : Refreshing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@1ab06251: startup date [Wed May 16 14:09:21 CST 2018]; root of context hierarchy 2018-05-16 14:09:24.122  INFO 19708 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8021 (http) 2018-05-16 14:09:24.187  INFO 19708 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat] 2018-05-16 14:09:24.187  INFO 19708 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet Engine: Apache Tomcat/8.5.29 2018-05-16 14:09:24.208  INFO 19708 --- [ost-startStop-1] o.a.catalina.core.AprLifecycleListener   : The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: [D:\Program Files\Java\jdk1.8.0_161\bin;C:\WINDOWS\Sun\Java\bin;C:\WINDOWS\system32;C:\WINDOWS;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Program Files (x86)\NVIDIA Corporation\PhysX\Common;d:\work\Git\cmd;C:\Program Files (x86)\MySQL\MySQL Server 5.5\bin;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\Users\zdwljs\AppData\Local\Microsoft\WindowsApps;D:\Program Files\Java\jdk1.8.0_161\bin;;.] 2018-05-16 14:09:24.427  INFO 19708 --- [ost-startStop-1] o.a.c.c.C.[.[localhost].[/ricky02]       : Initializing Spring embedded WebApplicationContext 2018-05-16 14:09:24.429  INFO 19708 --- [ost-startStop-1] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 3225 ms 2018-05-16 14:09:24.748  INFO 19708 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean  : Servlet dispatcherServlet mapped to [/] 2018-05-16 14:09:24.756  INFO 19708 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'characterEncodingFilter' to: [/*] 2018-05-16 14:09:24.757  INFO 19708 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'hiddenHttpMethodFilter' to: [/*] 2018-05-16 14:09:24.757  INFO 19708 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'httpPutFormContentFilter' to: [/*] 2018-05-16 14:09:24.758  INFO 19708 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'requestContextFilter' to: [/*] 2018-05-16 14:09:24.980  INFO 19708 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler] 2018-05-16 14:09:25.424  INFO 19708 --- [           main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@1ab06251: startup date [Wed May 16 14:09:21 CST 2018]; root of context hierarchy 2018-05-16 14:09:25.583  INFO 19708 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.error(javax.servlet.http.HttpServletRequest) 2018-05-16 14:09:25.586  INFO 19708 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse) 2018-05-16 14:09:25.672  INFO 19708 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler] 2018-05-16 14:09:25.672  INFO 19708 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler] 2018-05-16 14:09:25.989  INFO 19708 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup 2018-05-16 14:09:26.185  INFO 19708 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8021 (http) with context path '/ricky02' 2018-05-16 14:09:26.191  INFO 19708 --- [           main] com.ricky.SpringBootApplication02        : Started SpringBootApplication02 in 13.196 seconds (JVM running for 14.36)

这里实现的只是外部的,如果你还是需要在项目中则可以修改location=classpath:/XXX/

演示项目地址,欢迎fork和star

码云:SpringBootLearn

最后

本文发表于2018年05月16日 18:00
(c)注:本文转载自https://my.oschina.net/lt0314/blog/1813479,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。如有侵权行为,请联系我们,我们会及时删除.

阅读 767 讨论 0 喜欢 0

抢先体验

扫码体验
趣味小程序
文字表情生成器

闪念胶囊

你要过得好哇,这样我才能恨你啊,你要是过得不好,我都不知道该恨你还是拥抱你啊。

直抵黄龙府,与诸君痛饮尔。

那时陪伴我的人啊,你们如今在何方。

不出意外的话,我们再也不会见了,祝你前程似锦。

这世界真好,吃野东西也要留出这条命来看看

快捷链接
网站地图
提交友链
Copyright © 2016 - 2021 Cion.
All Rights Reserved.
京ICP备2021004668号-1