您的位置 首页 > 德语词汇

profiles是什麼意思?Spring Boot 特性之 Profiles特性

大家好,感谢邀请,今天来为大家分享一下profiles是什麼意思的问题,以及和Spring Boot 特性之 Profiles特性的一些困惑,大家要是还不太明白的话,也没有关系,因为接下来将为大家分享,希望可以帮助到大家,解决大家的问题,下面就开始吧!

今天我们了解SpringBootProfiles特性

?配置分为编译时和运行时,而Spring采用后者,在工作中有时也会两者一起使用。

profiles是什麼意思?Spring Boot 特性之 Profiles特性

?何为“外部化配置”官方没有正面解释。通常,对于可扩展性应用,尤其是中间件,它们的功能性组件是可配置化的,如线程池配置及数据库连接信息等。

?假设设置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特性的答案你都知道了吗?欢迎再次光临本站哦!

本站涵盖的内容、图片、视频等数据,部分未能与原作者取得联系。若涉及版权问题,请及时通知我们并提供相关证明材料,我们将及时予以删除!谢谢大家的理解与支持!

Copyright © 2023