聊一聊springboot项目配置参数的三个来源
大家好,我是G探险者,今天我们聊一聊,springboot的参数的三个来源。 平时我们部署项目后,难免都会进行一下配置修改,有的是在修改环境变量,有的是直接通过命令行参数进行修改,有的是直接改,配置文件,那么这三种不同来源的参数修改,都是如何生效的,以及他们之间的一些区别是啥样的,下面我就写问题聊一聊。
1. 配置参数的三个来源
当配置一个应用程序时,可以从多个来源获取配置参数。下面是三个常见的配置参数来源:
-
命令行参数: 命令行参数是在启动应用程序时通过命令行传递的参数。这些参数可以用来修改应用程序的行为或配置。在Java中,可以使用
main()
方法中的args
参数来接收命令行参数。例如,以下命令行启动应用程序并传递参数:java MyApp --param1=value1 --param2=value2
在这个例子中,
--param1=value1
和--param2=value2
就是命令行参数。应用程序可以通过解析命令行参数来获取这些值,并根据需要进行配置。 -
环境变量: 环境变量是在操作系统级别设置的全局变量。它们包含有关操作系统和正在运行的应用程序的信息。应用程序可以读取环境变量以获取配置参数。在大多数操作系统中,可以使用特定的命令来设置和获取环境变量。例如,在Linux和Mac上,可以使用
export
命令设置环境变量,如下所示:export MY_VAR=value
应用程序可以通过读取
MY_VAR
环境变量来获取值。 -
Spring Boot自带的配置文件(
application.properties
或application.yml
): Spring Boot是一个用于构建Java应用程序的框架,提供了许多便捷的配置方式。其中一种方式是使用application.properties
或application.yml
配置文件来定义应用程序的属性。这些文件通常位于应用程序的类路径下,可以包含键值对或层级结构的配置。应用程序在启动时会读取这些文件,并将其中定义的配置参数应用到应用程序中。例如,以下是一个application.properties
文件的示例:server.port=8080 database.url=jdbc:mysql://localhost:3306/mydb
在这个例子中,
server.port
和database.url
是配置参数的键,8080
和jdbc:mysql://localhost:3306/mydb
是对应的值。Spring Boot应用程序将读取这些值,并相应地配置嵌入式服务器的端口和数据库连接。
这些来源可以单独或组合使用,以提供应用程序的配置参数。在实际开发中,可以根据需要选择最适合的配置方式。例如,命令行参数适合临时修改配置,环境变量适合在不同环境中设置不同的配置,而配置文件则适合将配置集中管理并与代码一起打包部署。
2. 不通来源的参数配置区别
我们先通过以下这个表格直观的看一下,三种不同来源(命令行参数、环境变量和Spring Boot自带的配置文件)有以下区别:
命令行参数 | 临时调整配置,特定操作 | 最高优先级,覆盖其他来源 | 易于理解和管理,但参数较多时可能冗长 |
环境变量 | 在不同环境中设置不同配置 | 高于配置文件,低于命令行参数 | 需要操作系统级别的设置和管理 |
Spring Boot自带的配置文件 | 长期配置,集中管理 | 低于命令行参数和环境变量 | 可读性高,易于维护和理解 |
请注意,以上表格提供了一般性的对比,并且在特定情况下可能会有例外或变化。对于每个项目和场景,最佳选择可能会有所不同,取决于特定的要求和偏好。
-
用途和灵活性:
- 命令行参数:命令行参数通常用于在每次启动应用程序时提供临时的、特定于该次运行的配置。这些参数可以根据需要进行更改,因此在不同的运行实例中可以使用不同的参数。命令行参数对于临时调整配置或执行特定操作非常有用。但是,它们不适合长期或全局配置。
- 环境变量:环境变量是在操作系统级别设置的全局变量。它们可以在不同的应用程序和进程之间共享,并且可以用于配置各种应用程序。环境变量通常用于在不同的环境(例如开发、测试、生产)之间设置不同的配置。它们提供了一种灵活且易于配置的方式,可以根据不同的部署环境进行调整。
- Spring Boot自带的配置文件:Spring Boot的配置文件提供了一种在应用程序内部集中管理配置的方法。配置文件可以包含多个属性,并且支持层级结构。这种方式适合于将配置与代码分离,以便更好地组织和维护配置。配置文件通常与应用程序一起打包,并在应用程序启动时被读取和应用。它们适用于长期配置,不容易在每次运行时进行更改。
-
优先级和覆盖:
- 命令行参数:命令行参数通常具有最高优先级。如果在命令行参数中指定了某个配置,它将覆盖其他来源中的相同配置。这使得可以通过命令行参数临时覆盖应用程序的默认配置。
- 环境变量:环境变量的优先级通常高于Spring Boot的配置文件。如果在环境变量中设置了某个配置,它将覆盖配置文件中的相同配置。这允许在不修改配置文件的情况下,通过设置环境变量来更改应用程序的配置。
- Spring Boot自带的配置文件:配置文件中的配置参数提供了默认值,但可以通过命令行参数或环境变量进行覆盖。如果在配置文件中定义了某个配置,但在命令行参数或环境变量中也有相同的配置,后者将覆盖前者。
-
可读性和管理:
- 命令行参数:命令行参数是直接在启动命令中指定的,因此它们在启动命令中是可见的。这使得命令行参数易于理解和管理,因为可以清楚地看到应用程序在启动时使用了哪些参数。然而,当需要配置大量参数时,命令行参数可能会变得冗长和难以管理。
- 环境变量:环境变量是在操作系统级别设置的,可以在操作系统中进行管理。这使得环境变量的设置和管理相对容易,可以在不同的部署环境中轻松配置和更改变量。然而,环境变量的设置和值不直接与应用程序相关联,因此在应用程序代码中可能需要额外的逻辑来读取和处理这些变量。
- Spring Boot自带的配置文件:Spring Boot的配置文件可以集中管理应用程序的配置,并具有良好的可读性和可维护性。配置文件使用键值对或层级结构的方式组织配置参数,使得它们更易于理解和管理。此外,配置文件可以通过注释和文档来提供参数的说明,进一步提高可读性。配置文件与应用程序代码直接相关,因此在代码中可以直接使用配置参数,无需额外的处理。
总结起来,命令行参数适合临时修改和调试配置,环境变量适合在不同环境中设置不同的配置,而Spring Boot自带的配置文件适合集中管理和维护应用程序的配置。 它们各自具有不同的优点和适用场景,可以根据具体需求选择最合适的配置方式。
3. 不同来源的参数是如何被springboot识别并解析的?
在Spring Boot中,配置参数的识别和解析是通过Spring Boot的配置处理机制完成的。这个机制可以自动识别并解析来自命令行参数、环境变量和配置文件的配置参数。以下是对每种来源的配置参数在Spring Boot中的处理方式:
-
命令行参数:
-
Spring Boot使用
org.springframework.boot.ApplicationArguments
接口来处理命令行参数。可以通过在Spring Bean中注入ApplicationArguments
来访问命令行参数。 -
可以使用
getOptionValues(String name)
方法获取指定名称的选项值,其中name
是命令行参数的名称。 -
例如,假设有一个命令行参数
--param1=value1
,可以使用以下方式在Spring Bean中获取该参数的值:@Autowired private ApplicationArguments applicationArguments; public void someMethod() { List<String> param1Values = applicationArguments.getOptionValues("param1"); // 使用param1Values进行后续处理 }
-
-
环境变量:
-
Spring Boot使用
org.springframework.core.env.Environment
接口来处理环境变量。可以通过在Spring Bean中注入Environment
来访问环境变量。 -
可以使用
getProperty(String key)
方法获取指定键的环境变量值,其中key
是环境变量的键。 -
例如,假设有一个环境变量
MY_VAR=value
,可以使用以下方式在Spring Bean中获取该环境变量的值:@Autowired private Environment environment; public void someMethod() { String myVarValue = environment.getProperty("MY_VAR"); // 使用myVarValue进行后续处理 }
-
-
Spring Boot自带的配置文件(
application.properties
或application.yml
):-
Spring Boot使用
org.springframework.boot.context.properties.ConfigurationProperties
注解来绑定配置文件中的属性到Java对象上。可以通过在Spring Bean上使用@ConfigurationProperties
注解来实现属性绑定。 -
配置文件中的属性会自动与带有相同名称的Java对象的属性进行绑定。
-
例如,假设有一个配置文件中的属性
server.port=8080
,可以使用以下方式在Spring Bean中绑定该属性:@Component @ConfigurationProperties(prefix = "server") public class ServerProperties { private int port; public int getPort() { return port; } public void setPort(int port) { this.port = port; } }
在上述示例中,
server.port
属性会自动绑定到ServerProperties
对象的port
属性上。可以通过注入ServerProperties
Bean 来访问该属性的值。
-
通过上述方式,Spring Boot能够自动识别并解析来自命令行参数、环境变量和配置文件的配置参数,使开发者可以方便地使用这些配置参数来配置应用程序的行为。
了解了三种不同来源的参数是如何被解析的,感兴趣的可以读一下源码,观察一下他们是如何解析的,这里我就不分析源码了。
4. springboot里面集成的第三方组件,他们的配置参数如何被管理的?
现在,很多开源组件库都基本集成到springboot里了,也就是按照springboot的starter规范做了一些自动化配置,并抽离出自身的一些配合参数到application.properties里面。
有一些组件有他们特有的配置,可能是考虑到配置在application.properties配置文件里面显的太过于冗长,不容易维护,所以需要独立出一个单独的配置文件来管理。
比如mybatis,log4j2等等这些组件。
mybatis的独立配置文件:
mybatis有一个设置文件,一般都是叫做,xxx-config.xml,内容基本如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 设置全局属性 -->
<settings>
<!-- 开启驼峰命名自动映射 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!-- 其他配置项 -->
</settings>
<!-- 配置插件 -->
<plugins>
<!-- 自定义插件1 -->
<plugin interceptor="com.example.MyPlugin1">
<!-- 自定义插件1的配置项 -->
</plugin>
<!-- 自定义插件2 -->
<plugin interceptor="com.example.MyPlugin2">
<!-- 自定义插件2的配置项 -->
</plugin>
<!-- 其他插件 -->
</plugins>
<!-- 加载映射文件 -->
<mappers>
<!-- 单个映射文件的配置 -->
<mapper resource="com/example/mapper/SomeMapper.xml"/>
<!-- 批量加载映射文件的配置 -->
<package name="com.example.mapper"/>
</mappers>
</configuration>
这是一个简单的示例,展示了 MyBatis 的配置文件结构。你可以根据自己的需求进行配置。其中包含了以下几个重要的元素和属性:
<settings>
:用于配置全局属性和设置,例如开启驼峰命名自动映射。<plugins>
: 中配置自定义插件。每个插件都使用 标签表示,其中的 interceptor 属性指定了插件的实现类的完全限定名。<mappers>
:用于加载映射文件的配置,可以单个配置或者批量配置。<mapper resource="com/example/mapper/SomeMapper.xml"/>
:指定单个映射文件的路径。<package name="com.example.mapper"/>
:指定一个包名,MyBatis 会自动扫描该包下的映射文件。
log4j2的独立配置文件
以下是一个Log4j2 配置文件示例:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN" monitorInterval="30">
<Properties>
<Property name="logDir">logs</Property>
</Properties>
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
</Console>
<RollingFile name="File" fileName="${logDir}/application.log"
filePattern="${logDir}/application-%d{yyyy-MM-dd}.log">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
<Policies>
<TimeBasedTriggeringPolicy interval="1" modulate="true" />
</Policies>
<DefaultRolloverStrategy max="10" />
</RollingFile>
</Appenders>
<Loggers>
<Root level="info">
<AppenderRef ref="Console" />
<AppenderRef ref="File" />
</Root>
<Logger name="com.example" level="debug" additivity="false">
<AppenderRef ref="File" />
</Logger>
</Loggers>
</Configuration>
在这个示例中,添加了一些新的配置选项,你可以根据实际需求,调整日志级别、输出格式、文件路径等配置项:
<Properties>
:在此部分可以定义属性,供后面引用。示例中定义了logDir
属性,用于指定日志文件目录。monitorInterval
:配置文件的监视间隔,单位为秒。如果监视间隔内的配置文件更改,Log4j2 将重新配置自己。在示例中设置为 30 秒。RollingFile
Appender:使用滚动文件策略,可以按时间滚动日志文件。示例中的文件名为application.log
,并使用%d{yyyy-MM-dd}
进行日期格式化。<Policies>
:用于配置日志文件滚动策略,示例中使用TimeBasedTriggeringPolicy
,每天滚动一次日志文件。<DefaultRolloverStrategy>
:配置日志文件滚动的策略,默认为最多保留 10 个日志文件。
以上尽管这些组件都有独立配置文件,但是,这些独立配置文件依然和springboot的application.properties有关联。
比如mybatis,在application.properties指定独立配置的加载路径:
mybatis-plus.config-location=classpath:xxx-config.xml
比如log4j2:
logging.config=classpath:log.xml
所以,如果我们在开发一个组件集成到springboot里面的时候,尽可能的遵循它的规范,用application.properties来管理你的配置,如果你的配置比较多且比较杂,比如像mybatis,log4j2这样的组件,有自己独立的配置,尽量将这些独立配置文件也和application.properties建立着关联。
以上,我一直在强调要和application.properties建立关联是因为,后期我们的项目比如进行容器化部署,这些配置那面要修改,那就方便多了。
具体可以参照我的这篇文章 : Docker和Kubernetes部署Spring Boot项目:如何灵活修改配置文件?