使用 Spring 为 bean 注入属性时,通常都是在 XML 中设置属性,Java 类的中属性要为其添加 setter 方法,这样在启动的时候才可以去调用。
比如定义一个 User 类,代码如下:
package spring.domain;
public class User {
private String name;
private int age;
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
}
XML 配置如下:
<bean id="tom" class="spring.domain.User">
<property name="name" value="Tom"/>
<property name="age" value="20"/>
</bean>
在没有显式配置的情况下,为 bean 设置属性并不这么容易。
而 Spring Bean 提供了配置属性(Configuration Property)的方法。配置属性指为 Spring 上下文中的 Bean 配置属性,属性的数据源可以是 JVM 系统参数、命令行参数和环境变量。
1、细粒度的自动配置
自动配置的神奇之处在于,我们可以不显式配置 bean(包括 XML 和 Java 配置),比如现在要定义一个嵌入式的 H2 数据源,如果不使用 Spring Boot,则需要定义一个 bean:
@Bean
public DataSource dataSource() {
return new EmbeddedDataSourceBuilder()
.setType(H2)
.addScript("schema.sql")
.addScript("data.sql")
.build();
}
而使用 Spring Boot 时,只要引入 H2 的依赖,应用启动运行时就会自动在 Spring 应用上下文中创建对应的 DataSource bean,并且这个 bean 会自动运行名为 schema.sql 和 data.sql 的脚本。
(1)Spring 环境属性源
Spring 会拉取多个属性源,包括:
- JVM 系统属性;
- 操作系统环境变量;
- 命令行参数;
- 应用属性配置文件。
Spring 会将这些属性聚合到一个源中,通过这个源注入到 Spring Bean,如下图所示:
Spring Boot 自动配置的 bean 都可以通过 Spring 环境提取的属性进行配置。比如配置应用对外服务端口,可以在 application.properties 中配置:
server.port=8090
也可以在 application.yml 中配置:
server:
port: 8090
如果喜欢在外部配置属性,可以使用命令行参数指定端口:
$ java -jar springbootapp.jar --server.port=8090
还可以通过环境变量设置:
$ export SERVER_PORT=8090
Spring 会将 SERVER_PORT
的环境变量解析为 server.port
。
(2)配置数据源
Spring Boot 的自动配置很方便,但是一些属性的值如果跟默认不一致时,我们更希望能够在配置文件中自行配置。
下面是 H2 数据库的数据源配置:
# 默认的 H2 数据库名是随机生成的,这里指定为taco,默认用户名为sa,密码为空
spring:
datasource:
url: jdbc:h2:mem:taco
username: sa
password:
# 数据初始化
sql:
init:
mode: always
platform: h2
username: sa
password:
schema-locations: classpath*:schema.sql
data-locations: classpath*:data.sql
# h2-console
# path: 控制台路径
# enabled: 开启Web Console
# settings.web-allow-others: 允许远程访问Web Console
h2:
console:
path: /h2-console
enabled: true
settings:
web-allow-others: true
如果有配置跟默认的配置不一致,可以修改响应的属性
如果要配置 MySQL 的数据源,配置如下:
spring:
datasource:
url: jdbc:mysql://localhost:3306/tacocloud
username: root
password: root
# MySQL 8.0 用的驱动不一样
driver-class-name: com.mysql.jdbc.Driver
sql:
init:
mode: always
platform: mysql
username: root
password: root
schema-locations: classpath*:schema.sql
data-locations: classpath*:data.sql
使用 MySQL 要引入对应的驱动:
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
如果在类路径中存在 Tomcat 的 JDBC 连接池,DataSource 将使用该连接池。否则,Spring Boot 将会在类路径下尝试查找并使用如下的连接池实现:
- HikariCP
- Commons DBCP 2
如果自动配置不能满足需求,可以回到显式配置 DataSource Bean 的模式,这样可以使用任意喜欢的连接池实现。
(3)配置日志
默认情况下,Spring Boot 通过 Logback 配置日志,日志会以 INFO 级别写入控制台中。
可以在 src/main/resources
目录下定义一个 logback.xml:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- spring boot logback日志输出配置颜色转换器 -->
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
<conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
<conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
<property name="PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} %clr(%-5p) %magenta(${PID:-}) --- [ %t] %cyan(%-40.40logger{39}) : %m%n"/>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<charset>UTF-8</charset>
<pattern>${PATTERN}</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT"/>
</root>
<logger name="root" level="INFO"/>
</configuration>
appender 表示要将日志打印到哪里,默认是 console,也可以指定写入到对应的日志文件。
要设置日志级别,可以设置 logging.level 作为前缀的属性。随后设置想要设置日志级别的 logger。假设想要将 root logging 设置为 WARN,将 Sping Security 的日志级别设置为 DEBUG,则可在 appliaction.yml 中添加以下配置:
logging:
level:
root: INFO
org.springframework.security: DEBUG
(4)使用特定的属性值
除了设置硬编码的属性值,还可以使用 ${} 占位符,表示值来自另外属性。
greeting:
welcome: You are using ${spring.application.name}
2、创建和使用自定义配置属性
(1)定义配置属性的持有者
定义一个对象,会读取属性配置来注入到成员属性中,
package tacos;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;
import lombok.Data;
@Component
@Data
@ConfigurationProperties(prefix = "taco.orders")
@Validated
public class OrderProps {
@Min(value = 5, message = "must be between 5 and 25")
@Max(value = 25, message = "must be between 5 and 25")
private int pageSize;
}
OrderProps 使用了 @Component
注解被定义为一个 bean,使用 @ConfigurationProperties
注解表示为 bean 中那些能够根据 Spring 环境注入值的属性赋值。
比如这里的 pageSize
就会去找一个 page-size 的属性,因为注解还使用了 taco.orders
的前缀,所以要找的属性是 taco.orders.pageSize
。
(2)声明配置属性元数据
在 application.yml 中,将鼠标悬停在配置的 Spring 自带某个属性上,会介绍该属性使用的一些信息。
而如果是自定义的属性,会有警告的提示,鼠标悬停显示该属性未知,并且悬停框里还有可点击创建该属性元数据的按钮。
点击 "Create metadata for xxx" 的按钮,就会自动在 src/main/resources/META-INF
目录下创建一个名为 additional-spring-configuration-metadata.json
的元数据描述 json 文件,打开该文件添加属性描述。
再次打开 application.yml 可以看到不再有报警了,并且悬停显示 json 文件里添加的描述。
PS. 如果悬停没有显示 "Create metadata for xxx" 的按钮,则需要安装插件,详情看附录B-问题汇总12。
3、使用 profile 进行配置
在不同的环境中,同个属性的值可能是不一样的,这种情况可以使用 Spring Profile,profile 是一种条件化配置,在运行时根据哪些 profile 处于激活状态,可以使用或忽略不同的 bean、配置类和配置属性。
(1)定义特定的 profile 属性
方式1:定义不同的 profile 文件
文件的名称要遵循如下的约定:application-{profile名}.yml 或 application-{profile名}.properties ,比如现在配置两个 yml 文件,一个是 application-dev.yml 用于本地开发测试,一个是 application-prod.yml 用于生产环境。
application-dev.yml,设置 H2 嵌入内存数据库,spring security 日志级别为 DEBUG。
spring:
sql:
init:
mode: always
platform: h2
username: sa
password:
schema-locations: classpath*:schema-h2.sql
data-locations: classpath*:data-h2.sql
datasource:
url: jdbc:h2:mem:taco
username: sa
password:
logging:
level:
org.springframework.security: DEBUG
application-prod.yml,设置数据源为 mysql 数据库,spring security 日志级别为 WARN。
spring:
sql:
init:
mode: always
platform: mysql
username: root
password: root
schema-locations: classpath*:schema.sql
#data-locations: classpath*:data.sql
datasource:
url: jdbc:mysql://localhost:3306/tacocloud
username: root
password: root
logging:
level:
org.springframework.security: WARN
方式2:在一个 yml 中定义多个 profile
logging:
level:
tacos: DEBUG
---
spring:
profiles: dev
sql:
init:
mode: always
platform: h2
username: sa
password:
schema-locations: classpath*:schema-h2.sql
data-locations: classpath*:data-h2.sql
datasource:
url: jdbc:h2:mem:taco
username: sa
password:
logging:
level:
org.springframework.security: DEBUG
---
spring:
profiles: prod
sql:
init:
mode: always
platform: mysql
username: root
password: root
schema-locations: classpath*:schema.sql
#data-locations: classpath*:data.sql
datasource:
url: jdbc:mysql://localhost:3306/tacocloud
username: root
password: root
logging:
level:
org.springframework.security: WARN
在第一个 ---
上面的属性配置表示适用于所有的 profile,下面的属性表示属于不同的 profile,激活哪个 profile 就用哪些属性配置。如果 profile 的属性设置跟适用于所有 profile 的属性重复,则 profile 里的配置会覆盖优先。
(2)激活 profile
方式1:在 yml 中指定(不建议)
spring:
profiles:
active:
- prod
- taco
方式2:设置环境参数
export SPRING_PROFILES_ACTIVE=prod
方式3:命令行参数
# -Dspring.profiles.active=prod,taco
java -jar springbootapp.jar --spring.profiles.active=prod,taco
(3)条件化地创建 bean
有一些场景,我们希望某些 bean 仅在特定 profile 激活的情况下才需要创建,@Profile 注解可以将某些 bean 设置为仅适用于给定的 profile。
@Bean
@Profile("prod")
public User user() {
new User("Tom", 20);
}
上面的配置表示当 prod profile 被激活时就注册一个 User 对象的 bean,也可以设置多个 profile 中任一被激活时注册 bean,如:
@Bean
@Profile({"prod", "qa"})
public User user() {
new User("Tom", 20);
}
也可以设置某个 profile 被激活时不注册bean,使用 !
@Bean
@Profile("!prod")
public User user() {
new User("Tom", 20);
}
同样设置多个 profile 中任一被激活都不注册
@Bean
@Profile({"!prod", "!qa"})
public User user() {
new User("Tom", 20);
}
【演示项目github地址】https://github.com/huyihao/Spring-Tutorial/tree/main/2%E3%80%81SpringBoot/taco-cloud-config
网友评论