README.md 10.7 KB
Newer Older
duanledexianxianxian's avatar
duanledexianxianxian committed
1

duanledexianxianxian's avatar
duanledexianxianxian committed
2

duanledexianxianxian's avatar
duanledexianxianxian committed
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315
​	使用springboot进行应用业务开发,为了程序更具灵活性、通用性,能够在不同的环境下使用相同的代码。我们通过读取配置文件,让应用可配置化。比如编写一个Oss上传下载文件相关的工具类,对应的不同环境oss服务器地址是不同的。如果将服务器地址硬编码到代码中,那么每次部署不同的环境,都需要开发人员修改Oss服务器地址。通过读取配置文件获取Oss服务器地址,能够极大的减少开发人员的工作量,使应用更加通用。



## Springboot加载配置属性顺序

Spring Boot用过PropertySource加载配置属性,以允许对属性值进行合理的覆盖,属性会以如下的顺序进行设值:

1. home目录下的devtools全局设置属性(~/.spring-boot-devtools.properties,如果devtools激活)。
2. 测试用例上的@TestPropertySource注解。
3. 测试用例上的@SpringBootTest#properties注解。
4. `命令行参数。`
5. 来自SPRING_APPLICATION_JSON的属性(环境变量或系统属性中内嵌的内联JSON)。
6. ServletConfig初始化参数。
7. ServletContext初始化参数。
8. 来自于java:comp/env的JNDI属性。
9. `Java系统属性(System.getProperties())。`
10. `操作系统环境变量。`
11. RandomValuePropertySource,只包含random.*中的属性。
12. 没有打进jar包的Profile-specific应用属性(application-{profile}.properties和YAML变量)。
13. `打进jar包中的Profile-specific应用属性(application-{profile}.properties和YAML变量)。`
14. 没有打进jar包的应用配置(application.properties和YAML变量)。
15. `打进jar包中的应用配置(application.properties和YAML变量)。`
16. @Configuration类上的@PropertySource注解。
17. 默认属性(使用SpringApplication.setDefaultProperties指定)。

4、9、10,一般用于应用部署发布的时候设置属性默认值。比如JVM参数设置-Xms、-Xmx、-Xss等。对于开发者我们应该更加关注通过application.properties/YAML加载配置属性。



## application配置文件读取顺序

Spring Boot启动会扫描以下位置的application.properties或者application.yml文件作为Spring Boot的默认配置文件。

1. 当前工作目录下的/config/。
2. 当前工作目录。
3. classpath下的/config目录。
4. classpath根路径(root)。

```
工作目录
│  application.yml  // 2
│  pom.xml

├─config
│      application.yml  //1

└─src
   ├─main
   │  ├─java
   │  │  └─com
   │  │      └─duanledexianxianxian
   │  │          └─core
   │  │              │  SpringWebApplication.java
   │  │              │
   │  │              └─config
   │  │                      SystemConfiguration.java
   │  │                      SystemEnvironmentProperties.java
   │  │                      SystemProperties.java
   │  │                      SystemPropertySourceValueProperties.java
   │  │                      SystemValueProperties.java
   │  │
   │  └─resources
   │      │  application.yml  //4
   │      │
   │      └─config
   │              application.yml  //3

   └─test
      └─java

```



该列表是按优先级排序的(列表中位置高的路径下定义的属性将覆盖位置低的),所有位置的文件都会被加载,配置文件中的内容是互补配置。

> **互补配置**
>
> 1. 存在相同的配置内容,高优先级的内容会覆盖低优先级的内容
> 2. 存在不同的内容的时候,高优先级和低优先级的配置内容取**并集**

> **工作目录**
>
> 1. 当前工作目录通过"user.dir"设置,可以通过`System.getProperty("user.dir")`打印当前程序的工作目录。
>
> 2. 对于单项目模块工程,当前工作目录的默认为模块根目录。
>
>    ![image-20200803144748418](https://i.loli.net/2020/08/03/apONU5VRYJFWQEP.png)
>
> 3. 对于多项目模块工程,当前工作目录的默认为根模块的根目录。
>
>    ![image-20200803144848536](https://i.loli.net/2020/08/03/iXWsZAqCOpQvHV5.png)
>
> 4. 对于jar命令方式启动的springboot程序,当前工作目录的默认为jar包所在的目录。
>
>    ```shell
>    java -jar core-1.0-SNAPSHOT.jar// 工作目录为core-1.0-SNAPSHOT.jar所在目录
>    ```
>
> 5. Idea中修改工作目录方式
>
>    - 顶部工具栏 "Edit Configrations"
>
>      ![image-20200803150134463](https://i.loli.net/2020/08/03/GhIK3D4HBieEnwF.png)
>
>    - 输入你需要配置的工作目录
>
>      ![image-20200803150218907](C:%5CUsers%5C44902%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5Cimage-20200803150218907.png)
>
>    - "OK" or "Apply"

对于单模块项目,配置文件加载顺序:

![image-20200803152410092](https://i.loli.net/2020/08/03/ugHr1xbwDQFn7lN.png)

对于多模块项目,配置文件加载顺序:

![image-20200803152635511](C:%5CUsers%5C44902%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5Cimage-20200803152635511.png)



## Springboot读取配置文件:

Springboot提供了多种方式,方便开发者读取配置文件。下面介绍常用的读取配置文件方式。

示例配置文件都从classpath目录下读取application.yml,需要读取的内容为系统的名称以及编码,如下所示。

```yaml
#系统相关配置
system:
#  系统名称
  name: "classpath root config"
#  系统编码
  code: "classpath root code"
```

#### **1. @Value注解**

```java
/**
 * 通过@value加载配置参数
 * @author duanledexianxianxian
 */
@Component
@Data
public class SystemValueProperties {
    /**
     * 名称
     */
    @Value("${system.name}")
    private String name;
    /**
     * 编码
     */
    @Value("${system.code}")
    private String code;
}
```

平常开发中我们看到大部分开发人员使用@value读取配置文件属性值。这种方式使用简单,对于需要获取配置属性的地方,加上@value注解即可。

但是,不太推荐使用@value读取配置属性。原因如下:

1. @value导致配置碎片化。可能多个地方使用同一个配置属性值,开发者可能会在每个读取配置值的地方都使用@value来读取相同的配置属性。

2. 在一些情况下,@value属性key,会有很长的重复前缀,代码不美观。

3. @Value无法注入static属性。

   > 使用@Value直接放在static的属性上是无法注入内容的。此方式会一直是null.
   >
   > spring 不允许/不支持把值注入到静态变量中;spring支持set方法注入,我们可以利用非静态setter 方法注入静态变量,并且使用@Value的类必须交个spring进行管理.就如同@Autowired有时无法注入一样.

4. 配置文件中没有配置属性值,@value会导致应用启动失败。

   > **Note**:
   >
   > 对于一个项目,我们通常会定义多套环境配置。比如dev、test、pre、prod。
   >
   > 很多开发人员在本地开发环境的配置文件配置了属性值,但是test、pre、prod忘记配置属性值。导致应用程序在其他环境启动失败。
   >
   > 解决方式:
   >
   > 1. 给@value设置默认值
   >
   >    ```java
   >       @Value("${system.name:null}")
   >       private String name;
   >    ```
   >
   >    在配置项的表达式后面加一个冒号和默认值就可以为@Value的变量指定默认值,当配置项没有该项的时候,就会采用默认值而不是抛出错误。
   >
   > 2. 使用@ConfigurationProperties,后面详细介绍。

#### 2. @PropertySource+@Value+@ConfigurationProperties注解

不推荐,比@value还坑。

```
/**
 * 通过@value加载配置参数
 * @author duanledexianxianxian
 */
@Component
@ConfigurationProperties(prefix = "system")
@PropertySource(value = { "/config/system.properties" })
@Data
public class SystemPropertySourceValueProperties {
    /**
     * 名称
     */
    @Value("${name}")//最好设置默认值,否则配置文件即使有相应key,也会应用启动失败
    private String name;
    /**
     * 编码
     */
    @Value("${code}")
    private String code;
}
```

如果需要按照模块读取配置文件,比如system.properties进行系统相关配置;mail.properties进行邮件相关配置。我们可以使用@PropertySource注解读取模块单独配置文件。

为@ConfigurationProperties注解设置prefix属性,可以减少输入重复@value前缀。

> @PropertySource不支持yml文件,只支持读取properties文件。
>
> 题外话:我们可以使用@PropertySource读取任意配置properties文件。

#### 3. Environment

在需要使用配置项的地方,注入环境变量,读取配置属性。

```java
@Autowired
private Environment env;
 
// 获取参数
String getProperty(String key);
```

代码可读性差。

#### 4. @ConfigurationProperties注解(`推荐`)

我们可以根据业务逻辑将定义一个模块相关的属性配置类。比如系统配置类:

```
/**
 * 系统配置类
 * @author duanledexianxianxian
 */
@Component
@ConfigurationProperties(prefix = "system")
@Data
public class SystemProperties {
    /**
     * 名称
     */
    private String name;
    /**
     * 编码
     */
    private String code;
}
```

在需要读取系统配置类的时候,直接通过@Autowired注入即可。

```
@Autowired
private SystemProperties systemProperties;
```

如果配置文件已经按照模块进行了多个文件的切分,可以配合@PropertySource注解读取配置文件。

@ConfigurationProperties的prefix用于指定属性key的公共前缀。

**@ConfigurationProperties  VS  @value**

1. @ConfigurationProperties配置集中化。所有关于system相关的属性,我们都可以在SystemProperties中获取,一目了然,代码可读性高。如果需要修改属性key,不需要找到所有引用key的地方。
2. @ConfigurationProperties即使配置文件中没有相关key,也不会导致应用启动失败。

**代码地址:**

## 总结

平常在项目开发中,一般读取的是classpath下面的application.properties/yml文件。

1. `优先使用@ConfigurationProperties来读取配置。`

   根据模块定义一个属性配置类,用于设置以及获取模块相关属性,放在config包下,类命名规则:模块名称+Properties。比如SystemProperties。

2. `如果使用@value注解,切记设置默认值,否则在某些情况下导致应用启动失败。`

对于按模块切分多个配置文件的配置读取。优先使用@ConfigurationProperties+@PropertySource方式,切记@PropertySource只能读取properties文件。





## 参考链接

1. [SpringBoot读取配置文件的6种方式]: https://my.oschina.net/imlim/blog/1859091

2. [Spring Boot官方参考文档]: https://docs.spring.io/spring-boot/docs/2.1.6.RELEASE/reference/html/