大家好,感谢邀请,今天来为大家分享一下profiles是什麼意思的问题,以及和Spring Boot 特性之 Profiles特性的一些困惑,大家要是还不太明白的话,也没有关系,因为接下来将为大家分享,希望可以帮助到大家,解决大家的问题,下面就开始吧!
今天我们了解SpringBootProfiles特性
?配置分为编译时和运行时,而Spring采用后者,在工作中有时也会两者一起使用。
?何为“外部化配置”官方没有正面解释。通常,对于可扩展性应用,尤其是中间件,它们的功能性组件是可配置化的,如线程池配置及数据库连接信息等。
?假设设置Spring应用的Profile为dev,通过ConfigurableEnvironment#setDefaultProfiles方法实现,这种通过代码的方式配置,配置数据来源于应用内部实现的称为“内部化配置”。
?SpringBoot内置了17种外部化配置,并规定了其调用顺序。实际不止17种,也并不是必须按官方规定的顺序。
SpringBootletsyouexternalizeyourconfigurationsothatyoucanworkwiththesameapplicationcodeindifferentenvironments.Youcanusepropertiesfiles,YAMLfiles,environmentvariables,andcommand-lineargumentstoexternalizeconfiguration.Propertyvaluescanbeinjecteddirectlyintoyourbeansbyusingthe@Valueannotation,accessedthroughSpring’sEnvironmentabstraction,orbehttps://docs.spring.io/spring-boot/docs/2.2.1.RELEASE/reference/htmlsingle/#boot-features-external-config-typesafe-configuration-propertiesthrough@ConfigurationProperties.
SpringBootusesaveryparticularPropertySourceorderthatisdesignedtoallowsensibleoverridingofvalues.Propertiesareconsideredinthefollowingorder:
1.https://docs.spring.io/spring-boot/docs/2.2.1.RELEASE/reference/htmlsingle/#using-boot-devtools-globalsettingsinthe$HOME/.config/spring-bootfolderwhendevtoolsisactive.
2.https://docs.spring.io/spring/docs/5.2.1.RELEASE/javadoc-api/org/springframework/test/context/TestPropertySource.htmlannotationsonyourtests.
3.propertiesattributeonyourtests.Availableonhttps://docs.spring.io/spring-boot/docs/2.2.1.RELEASE/api//org/springframework/boot/test/context/SpringBootTest.htmlandthehttps://docs.spring.io/spring-boot/docs/2.2.1.RELEASE/reference/htmlsingle/#boot-features-testing-spring-boot-applications-testing-autoconfigured-tests.
5.PropertiesfromSPRING_APPLICATION_JSON(inlineJSONembeddedinanenvironmentvariableorsystemproperty).
6.ServletConfiginitparameters.
7.ServletContextinitparameters.
8.JNDIattributesfromjava:comp/env.
9.JavaSystemproperties(System.getProperties()).
11.ARandomValuePropertySourcethathaspropertiesonlyinrandom.*.
12.https://docs.spring.io/spring-boot/docs/2.2.1.RELEASE/reference/htmlsingle/#boot-features-external-config-profile-specific-propertiesoutsideofyourpackagedjar(application-{profile}.propertiesandYAMLvariants).
13.https://docs.spring.io/spring-boot/docs/2.2.1.RELEASE/reference/htmlsingle/#boot-features-external-config-profile-specific-propertiespackagedinsideyourjar(application-{profile}.propertiesandYAMLvariants).
14.Applicationpropertiesoutsideofyourpackagedjar(application.propertiesandYAMLvariants).
15.Applicationpropertiespackagedinsideyourjar(application.propertiesandYAMLvariants).
16.https://docs.spring.io/spring/docs/5.2.1.RELEASE/javadoc-api/org/springframework/context/annotation/PropertySource.htmlannotationsonyour@Configurationclasses.
17.Defaultproperties(specifiedbysettingSpringApplication.setDefaultProperties).
根据spring规范,元信息存放在META-INF目录下。
示例:在resources目录下创建META-INF/spring/context.xml
<?xmlversion="1.0"encoding="UTF-8"?>\n<beansxmlns="http://www.springframework.org/schema/beans"\nxmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\nxsi:schemaLocation="http://www.springframework.org/schema/beans\nhttps://www.springframework.org/schema/beans/spring-beans.xsd">\n\n<beanid="xmlPerson"class="com.example.profiledemo.property.XmlPerson">\n<propertyname="name"value="xmlname"/>\n<propertyname="age"value="10"/>\n</bean>\n</beans>
定义JavaBean
/*\n*@authyuesf\n*@data2019/11/23\n*/\npublicclassXmlPerson{\nprivateStringname;\nprivateStringage;\npublicStringgetName(){\nreturnname;\n}\n\npublicvoidsetName(Stringname){\nthis.name=name;\n}\n\npublicStringgetAge(){\nreturnage;\n}\n\npublicvoidsetAge(Stringage){\nthis.age=age;\n}\n}
使用xml配置属性
/*\n*@authyuesf\n*@data2019/11/23\n*/\n@RestController\n@ImportResource(locations={"META-INF/spring/context.xml"})\npublicclassContextController{\n\n@Autowired\nprivateXmlPersonxmlPerson;\n\n@GetMapping("/xml")\npublicXmlPersonxml(){\nreturnxmlPerson;\n}\n}
启动服务运行结果如下
{\n"name":"xmlname",\n"age":"10"\n}2)Annotation
官方提供两种方式@Value、@ConfigurationProperties
@Value是绑定application配置文件的属性变量
person.name=yuesf
使用Annotation配置属性
@Value("${person.name:defaultValue}")\nprivateStringname;
@Value在Spring中是强校验,使用时必须在配置中存在,否则会无法启动,示例中采用容错的方式,不存在使用默认值。
@Value的语义可以参考java.util.Properties#getProperty(java.lang.String,java.lang.String)方法,如果变量存在,则取变量值,若不存在取默认值
4.2.8.Type-safeConfigurationProperties
Usingthe@Value("${property}")annotationtoinjectconfigurationpropertiescansometimesbecumbersome,especiallyifyouareworkingwithmultiplepropertiesoryourdataishierarchicalinnature.SpringBootprovidesanalternativemethodofworkingwithpropertiesthatletsstronglytypedbeansgovernandvalidatetheconfigurationofyourapplication.
使用@Value来表达多个属性时特别麻烦,官方说明使用与JavaBean绑定的方式联合使用,使用方式如下:
使用@ConfigurationProperties需要两步完成使用
/*\n*@authyuesf\n*@data2019/11/22\n*/\n@ConfigurationProperties("person")\npublicclassPerson{\nprivateStringname;\nprivateStringage;\npublicStringgetName(){\nreturnname;\n}\n\npublicvoidsetName(Stringname){\nthis.name=name;\n}\n\npublicStringgetAge(){\nreturnage;\n}\n\npublicvoidsetAge(Stringage){\nthis.age=age;\n}\n}使用@EnableConfigurationProperties激活Person配置
示例说明:
@SpringBootApplication\n@EnableConfigurationProperties(Person.class)\npublicclassProfileDemoApplication{\n\npublicstaticvoidmain(String[]args){\nSpringApplication.run(ProfileDemoApplication.class,args);\n}\n}3)JavaCode(硬编码)(1)实现EnvironmentAware
示例通过实现EnvironmentAware接口来自定义server.port端口号为7070:
/*\n*@authyuesf\n*@data2019/11/26\n*/\n@Component\npublicclassCustomizedEnvironmentimplementsEnvironmentAware{\n@Override\npublicvoidsetEnvironment(Environmentenvironment){\nSystem.out.println("当前激活profile文件是:"+Arrays.asList(environment.getActiveProfiles()));\nif(environmentinstanceofConfigurableEnvironment){\nConfigurableEnvironmentenv=ConfigurableEnvironment.class.cast(environment);\nMutablePropertySourcespropertySources=env.getPropertySources();\nMap<String,Object>source=newHashMap<>();\nsource.put("server.port","7070");\nPropertySourcepropertySource=newMapPropertySource("javacode",source);\npropertySources.addFirst(propertySource);\n}\n}\n}\n
启动后验证端口号未发生变更,不是我们想要的效果
...\nThefollowingprofilesareactive:dev\n2019-11-2618:04:26.850INFO54924---[main]o.s.b.w.embedded.tomcat.TomcatWebServer:Tomcatinitializedwithport(s):8080(http)\n...\n当前激活profile文件是:[dev]\n...\n
通过actuator查看端口号已经变更
http://127.0.0.1:8080/actuator/env/server.port
"propertySources":\n[\n{\n"name":"server.ports"\n},\n{\n"name":"javacode",\n"property":{\n"value":"7070"\n}\n},\n{\n"name":"commandLineArgs"\n},\n...\n]\n
问题:
这里会遇到一个问题,请问为什么这里的7070端口号没有使用呢?
文中javacode是我们代码中指定的名称。propertySources的取值逻辑是顺序读取,一但有值就会返回。而返回后又对propertySources做了addFirst操作,所以会造成相互覆盖。
源码地址:org.springframework.core.env.PropertySourcesPropertyResolver#getProperty(java.lang.String,java.lang.Class,boolean)
要想使用改后的属性,我们可以仿照源码使用下面这种自定义事件ApplicationListener的方式。
/*\n*@authyuesf\n*@data2019/11/23\n*/\npublicclassCustomizedSpringBootApplicationListener\nimplementsApplicationListener<ApplicationEnvironmentPreparedEvent>{\n@Override\npublicvoidonApplicationEvent(ApplicationEnvironmentPreparedEventevent){\nConfigurableEnvironmentenv=event.getEnvironment();\nMutablePropertySourcespropertySources=env.getPropertySources();\nMap<String,Object>source=newHashMap<>();\nsource.put("server.port","6060");\nPropertySourcepropertySource=newMapPropertySource("customizedListener",source);\npropertySources.addFirst(propertySource);\n}\n}\n
添加SpringSPI配置META-INF/spring.factories
#ApplicationListeners\norg.springframework.context.ApplicationListener=\\\ncom.example.profiledemo.listener.CustomizedSpringBootApplicationListener\n
启动后验证结果,启动端口已经生效
...\nThefollowingprofilesareactive:dev\nTomcatinitializedwithport(s):6060(http)\n...\n当前激活profile文件是:[dev]\n...\n
通过actuator查看端口号,发现7070为第一个,6060为第二个。
"propertySources":\n[\n{\n"name":"server.ports"\n},\n{\n"name":"javacode",\n"property":{\n"value":"7070"\n}\n},\n{\n"name":"customizedListener",\n"property":{\n"value":"6060"\n}\n},\n{\n"name":"commandLineArgs"\n},\n...\n]\n
根据结果猜测,这样结果虽然已经修改过来了,但由于后使用addFirst方法对顺序做了改动。把javacode放在了第一位。
<?xmlversion="1.0"encoding="UTF-8"?>\n<beansxmlns="http://www.springframework.org/schema/beans"\nxmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\nxsi:schemaLocation="http://www.springframework.org/schema/beans\nhttps://www.springframework.org/schema/beans/spring-beans.xsd"\nprofile="test">\n...\n</beans>\n
spring中对xml做了属性封装,使用profile方式来加载,示例中使用的是profile="test"
properties文件名按application-{profile}.properties规约来命名
通过@Profile方式指定一个或多个profile
通过--spring.profiles.active命令行指定使用的profile,还可以使用--spring.profiles.include引用多个profile
通过上面说明并没有讲清楚他的装配原理是什么,那么我们通过源码了解下装配原理。
1.首先第一步是查看spring-boot源码#META-INF/spring.factories
#PropertySourceLoaders\norg.springframework.boot.env.PropertySourceLoader=\\\norg.springframework.boot.env.PropertiesPropertySourceLoader,\\\norg.springframework.boot.env.YamlPropertySourceLoader\n
PropertySourceLoader接口有两个实现
publicclassPropertiesPropertySourceLoaderimplementsPropertySourceLoader{\n\nprivatestaticfinalStringXML_FILE_EXTENSION=".xml";\n\n@Override\npublicString[]getFileExtensions(){\nreturnnewString[]{"properties","xml"};\n}\n...\n}\nYamlPropertySourceLoader解析yml和yaml
publicclassYamlPropertySourceLoaderimplementsPropertySourceLoader{\n\n@Override\npublicString[]getFileExtensions(){\nreturnnewString[]{"yml","yaml"};\n}\n...\n}\n
2.不管哪种解析,查下load方法是由谁来调用
本文使用idea查看代码,查看代码需要下载源码才可以查看。
本文中提到查看源码的方法调用统一使用idea自带的快捷键Alt+F7,或鼠标右键FindUsages
发现load方法是由ConfigFileApplicationListener.Loader#loadDocuments方法调用。
再次查看ConfigFileApplicationListener这个类是被谁调用,同样使用鼠标右键FindUsages,发现会出来很多,那么我们会有选择性的查看。看哪一个呢?使劲找就能找到我们刚才看到的那个文件/META-INF/spring.factories文件中有个ApplicationListener
#ApplicationListeners\norg.springframework.context.ApplicationListener=\\\norg.springframework.boot.ClearCachesApplicationListener,\\\norg.springframework.boot.builder.ParentContextCloserApplicationListener,\\\norg.springframework.boot.context.FileEncodingApplicationListener,\\\norg.springframework.boot.context.config.AnsiOutputApplicationListener,\\\norg.springframework.boot.context.config.ConfigFileApplicationListener,\\\norg.springframework.boot.context.config.DelegatingApplicationListener,\\\norg.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\\\norg.springframework.boot.context.logging.LoggingApplicationListener,\\\norg.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener\n
查到这里就涉及到spring的事件,如果你不清楚Spring事件可以看下相关文档。
@FunctionalInterface\npublicinterfaceApplicationListener<EextendsApplicationEvent>extendsEventListener{\n\n/**\n*Handleanapplicationevent.\n*@parameventtheeventtorespondto\n*/\nvoidonApplicationEvent(Eevent);\n\n}\n
ApplicationListener只有一个方法onApplicationEvent同样查看刚才我们定位到spring.factories文件查看ConfigFileApplicationListener#onApplicationEvent方法,
@Override\npublicvoidonApplicationEvent(ApplicationEventevent){\nif(eventinstanceofApplicationEnvironmentPreparedEvent){\nonApplicationEnvironmentPreparedEvent(\n(ApplicationEnvironmentPreparedEvent)event);\n}\nif(eventinstanceofApplicationPreparedEvent){\nonApplicationPreparedEvent(event);\n}\n}\n
这个方法非常简单,通过判断如果是ApplicationEnvironmentPreparedEvent类型时怎么做,意思是当应用环境准备时怎么做。第二个是如果是ApplicationPreparedEvent类型怎么做,意思是应用准备时怎么做。
3.反过来在看下我们JavaCode的方式使用自定义事件时会生效的原因。
本文由博客一文多发平台https://openwrite.cn?from=article_bottom发布!
再次感谢!!!您已看完全文,欢迎关注微信公众号猿码,你的支持是我持续更新文章的动力!
文章分享结束,profiles是什麼意思和Spring Boot 特性之 Profiles特性的答案你都知道了吗?欢迎再次光临本站哦!