- Spring Boot 支持两种配置文件形式
- .properties
- .yml(推荐使用)
- Spring Boot 使用一个全局的配置文件 application.properties 或 application.yml,放置在【src/main/resources】目录下
yml文件
什么是yml文件
- yml文件格式是YAML (YAML Ain’t a Markup Language)编写的文件格式,是一个可读性高,用来表达数据序列化的格式。
- YAML是一种直观的能够被电脑识别的数据序列化格式,并且容易被人类阅读,容易和脚本语言交互的,可以被支持YAML库的不同的编程语言程序解析。
- YAML参考了其他多种语言,包括:C语言、Python、Perl,并从XML、电子邮件的数据格式(RFC 2822)中获得灵感。Clark Evans在2001年首次发表了这种语言,另外Ingy döt Net与Oren Ben-Kiki也是这语言的共同设计者
yml文件的优点
- 易于人们阅读
- yml数据在编程语言之间是可移植的
- yml易于实现和使用
- yml是以数据为核心的,比传统的xml方式更加简洁
使用场景
脚本语言
由于实现简单,解析成本很低,YAML 特别适合在脚本语言中使用
序列化
YAML是由宿主语言数据类型直转,比较适合做序列化。
配置文件
写 YAML 要比写 XML 快得多(无需关注标签或引号),并且比 ini文档功能更强。由于兼容性问题,不同语言间的数据流转建议不要用 YAML。
yml的基本语法
- YAML的配置方式,都采用阶梯化缩进的方式展现,其结构显得更为清晰易读
- 语法规则
- 大小写敏感
- 数据值前必须有空格,作为分隔符
- 使用缩进表示层级关系
- 缩进时不允许使用Tab键,只允许使用空格。
- 缩进的空格数目不重要,只要相同层级的元素左侧对齐即可
- “#”表示注释
yml支持的数据类型
字面量
- 字符串,布尔类型,数值,日期。日期格式支持yyyy/MM/dd HH:mm:ss
- 单引号:会转义特殊字符,特殊字符最终只是一个普通的字符串数据
- 双引号:不会转义字符串里面的特殊字符;特殊字符会作为本身想表示的意思
name1: zhangsan
name2: 'zhangsan \n lisi' #输出zhangsan \n lisi
name3: "zhangsan \n lisi" #输出zhangsan 换行 lisi
age: 18
flag: true
date: 2022/01/01
对象(属性和值)、Map(键值对)
- 由键值对组成,形如 key:(空格)value 的数据组成。冒号后面的空格是必须要有的,每组键值对占用一行,且缩进的程度要一致,也可以使用行内写法:{k1: v1, …kn: vn}
people:
nickName: zhangsan
age: 20
people: {name:zhangsan,age: 20} #行内写法
数组
- 由形如 -(空格)value 的数据组成。短横线后面的空格是必须要有的,每组数据占用一行,且缩进的程度要一致,也可以使用行内写法: [1,2,…n]
pets:
- dog
- pig
- cat
pets: [dog,pig,cat] #行内写法
复合结构
- 例如:数组对象、list对象、set对象
peoples:
- nickName: zhangsan
age: 22
- nickName: lisi
age: 20
- {nickName: wangwu,age: 18}
思考:Spring Boot项目中如何读取配置信息,并注入配置信息
Spring Boot配置文件的值注入
第一种读取方式@Value
- 使用场景:如果只需要配置文件中的一两个值,@Value 是最简单方便的方式
- 语法规则:@Value 注解上通过 ${key} 即可获取配置文件中和key对应的value值
user:
nickName: 周杰伦
age: 46
@RestController
public class ValueController {
@Value("${user.nickName}")
private String nickName;
@Value("${user.age}")
private Integer age;
@RequestMapping("/config")
public Object getConfig(){
return "昵称:"+nickName+",年龄:"+age;
}
}
第二种读取方式@ConfigurationProperties
- 使用场景:当配置信息比较复杂时,可以将配置信息封装为一个JavaBean,我们一般会使用@ConfigurationProperties来读取。
- 语法规则:在JavaBean中添加@ConfigurationProperties注解,并配置一个prefix (前缀) 参数, 参数是配置文件中 key的名称
配置文件
star:
starName: 周传雄
birthday: 1969/6/7
starSex: 男
配置对象类
@Data
@Component
@ConfigurationProperties(prefix = "star")
public class Star {
private String starName;
private String starSex;
private String birthday;
}
controller
@RestController
public class ConfigPropertiesController {
@Resource
private Star star;
@RequestMapping("/star")
public Star getStar(){
return star;
}
}
第三种读取方式自动装配Environment
- Spring使用Environment表示应用的上下文环境,Environment环境代表当前应用运行时所处的环境。
- Environment接口作用可以让Spring根据不同的环境配置加载不同的配置信息。例如我们常用的测试环境和生产环境需要使用不同的数据源,通过配置环境信息即可达到无缝切换
user:
nickName: 周杰伦
age: 46
@RestController
public class EnvironmentController {
@Resource
private Environment environment;
@RequestMapping("/env")
public String getEnv() {
String nickName = environment.getProperty("user.nickName");
String age = environment.getProperty("user.age");
return "昵称:"+nickName+",年龄:"+age;
}
}
思考:Spring Boot在启动的时候会自动加载 application.xxx ,但是为了区分,有时候需要自定义一个配置文件,那么如何从自定义的配置文件中取值呢?就需要配合 @PropertySource 这个注解使用了
@PropertySource注解的用法
使用场景
- 加载指定的属性文件(*.properties)到 Spring 的 Environment 中,可以配合 @Value 和@ConfigurationProperties 使用。
使用方式
- @PropertySource 和 @Value组合使用,可以将自定义属性文件中的属性变量值注入到当前类的使用@Value注解的成员变量中。
- @PropertySource 和 @ConfigurationProperties组合使用,可以将属性文件与一个JavaBean类绑定,将属性文件中的变量值注入到该Java类的成员变量中。
database.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/smbms?useUnicode=true&characterEncoding=utf8&useSSL=false
@PropertySource 和 @Value组合使用
@RestController
@PropertySource("classpath:database.properties")
public class PropertySourceValueController {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@RequestMapping("/jdbc")
public String getJdbc(){
return "驱动:" + driver + ",连接地址:" + url;
}
}
@PropertySource 和 @ConfigurationProperties组合使用
@RestController
public class PropertySourceConfigPropertiesController {
@Resource
private JdbcConfig jdbcConfig;
@RequestMapping("/jdbcConfig")
public JdbcConfig getJdbcConfig() {
return jdbcConfig;
}
}
补充
红色标记的解决方案:添加以下依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
Spring Boot中使用Java配置
Spring配置的发展
- 基于XML配置
- 基于注解配置
@RestController: 一般用在表现层,作用等同于@Controller + @ResponseBody
@Controller: 一般用在表现层
@Service:一般用在业务层
@Repository:一般用在持久层
@Component:该注解用于把当前类对象存入 Spring 容器中 - 基于Java配置
思考:是否有了注解配置,我们就可以完全摒除原来 XML 配置的方式呢?答案是否定的。有以下几点原因:
- 注解配置不一定在先天上优于 XML 配置。如果 Bean 的依赖关系是固定的,(如 Service 使用了哪几个 DAO 类),这种配置信息不会在部署时发生调整,那么注解配置优于 XML 配置;反之如果这种依赖关系会在部署时发生调整,XML 配置显然又优于注解配置,因为注解是对 Java 源代码的调整,您需要重新改写源代码并重新编译才可以实施调整。
- 如果 Bean 不是自己编写的类(如 JdbcTemplate、SessionFactoryBean 等),注解配置将无法实施,此时 XML 配置是唯一可用的方式。
- 注解配置往往是类级别的,而 XML 配置则可以表现得更加灵活。比如相比于 @Transaction 事务注解,使用 aop/tx 命名空间的事务配置更加灵活和简单。
基于Java配置(javaConfig)
什么是javaConfig
- Spring JavaConfig是Spring社区的产品,它提供了一种纯Java方法来配置Spring IoC容器。因此,它有助于避免使用XML配置。
- 使用JavaConfig 的优点是:
- 面向对象的配置。由于配置在JavaConfig中定义为类,因此用户可以充分利用Java中面向对象的功能。一个配置类可以子类化另一个,覆盖其@Bean方法等。
- 减少或消除XML配置。已经证明了基于依赖注入原理的外部化配置的好处。但是,许多开发人员不希望在XML和Java之间来回切换。JavaConfig为开发人员提供了一种纯Java方法来配置Spring容器,该容器在概念上类似于XML配置。在技术上可以仅使用JavaConfig配置类来配置容器,但是在实践中,许多人发现将JavaConfig与XML混合匹配是理想的。
- 类型安全和重构友好。JavaConfig提供了一种类型安全的方法来配置Spring容器。由于Java 5.0支持泛型,现在可以按类型而不是按名称检索bean,而不需要任何强制转换或基于字符串的查找
以创建DataSource为例
1.依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.10</version>
</dependency>
2.在src/main/resources下添加jdbc.properties
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/smbms
jdbc.user=root
jdbc.password=123456
3.添加config包,创建配置类SpringConfiguration
package com.zjl.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import javax.sql.DataSource;
/**
* @author: zjl
* @datetime: 2024/4/23
* @desc:
*/
@Configuration
@PropertySource(value={"classpath:jdbc.properties"})
public class SpringConfiguration {
@Value("${jdbc.driverClassName}")
String driverClassName;
@Value("${jdbc.url}")
String url;
@Value("${jdbc.user}")
String user;
@Value("${jdbc.password}")
String password;
@Bean(name="dataSource")
public DataSource createDataSource() {
DruidDataSource dataSource=new DruidDataSource();
dataSource.setDriverClassName(driverClassName);
dataSource.setUrl(url);
dataSource.setUsername(user);
dataSource.setPassword(password);
return dataSource;
}
}
4.测试
@RestController
public class DataSourceController {
@Resource
private DataSource dataSource;
@RequestMapping("/dataSource")
public DataSource getDataSource() {
return dataSource;
}
}
Spring Boot项目中实现多环境配置
实际的开发场景
- 在未来的工作中,开发真实的项目时,可能会遇到不同的环境,如:开发环境、测试环境、生产环境等。
- 在不同的环境下,配置有可能是不一样的,比如接口地址、数据库连接的配置信息等。如果我们每次切换环境时再去配置对应的环境配置,肯定会降低我们的开发效率。
- Spring Boot 对多环境整合已经有了很好的支持,能够在打包,运行间自由切换环境,所以,掌握多环境配置还是非常有必要的。
- Spring Boot 多环境设置机制:设置spring.profiles.active属性
多环境配置的实现
第1种:单文件实现
- 在application.yml中指定不同环境配置,通过—进行分割
- 在application.yml文件中,指定被加载的配置文件
- spring.profiles.active
- 运行时指定启动环境参数
第2种:多文件实现(推荐)
- 创建不同环境的配置文件,命名为:application-{profile}.yml
- 不同配置文件分别配置不同的端口
- 在application.yml文件中,指定被加载的配置文件
- spring.profiles.active
- 运行时指定启动环境参数
示例
- Spring Boot中多环境配置文件名需要满足application-{profile}.properties的格式。
- 通常一个公司的程序可能在开发环境(dev)、测试环境(test)、生产环境(prod)中运行
- 假设我们配置不同的配置文件
- application.yml
- application-dev.yml(开发环境)
- application-test.yml(测试环境)
- application-prod.yml(生产环境)
- 在 application.yml 中指定,确定当前使用的是哪个环境,这边环境的值与application-dev.yml中-后面的值对应,这是SpringBoot约定好的。
application-dev.yml
server:
port: 7777
application-test.yml
server:
port: 8888
application-prod.yml
server:
port: 9999
application.yml
spring:
profiles:
active: dev
切换application.yml中spring.profiles.active的值,然后启动springBoot,看下启动的端口号,就知道用的是哪个环境的配置了
日志的重要性
日志是程序员在代码运行时打印出来的一些数据和记录,是快速排查问题的好帮手
- 日志,通常不会在需求阶段作为一个功能单独提出来,也不会在产品方案中看到它的细节。但是,这丝毫不影响它在任何一个项目中的重要地位。
- 为了保证项目的正常运行,发现问题要及时,解决问题要迅速,生产环境一旦出现问题,预警系统就会通过邮件、短信甚至电话的方式实施多维轰炸模式,确保负责人不错过每一个可能的bug。预警系统判断疑似bug源于日志。当该错误日志达到一定次数出现的时候,就会触发报警。
日志的作用
- 问题追踪:辅助排查和定位线上问题,优化程序运行性能。
- 状态监控:通过日志分析,可以监控系统的运行状态。
- 安全审计:审计主要体现在安全上,可以发现非授权的操作。
- 日志在应用程序中是非常重要的,好的日志信息能有助于我们在程序出现BUG时能快速进行定位,并能找出其中的原因。
Java中如何实现日志记录
Java 拥有功能和性能都非常强大的日志库
- Log4j:Log4j全称是Log for Java,它是Apache的一个开源项目
- JUL:java.util.logging,Sun公司开发的,是Java原生的日志框架
- JCL:Jakarta Commons Logging是Apache提供的一个通用日志API(日志门面)
- SLF4J:即简单日志门面(Simple Logging Facade for Java),它主要是为了给Java日志访问提供一套标准、规范的API框架,其主要意义在于提供接口,具体的实现可以交由其它日志框架,例如log4j和logback等;
- Logback :Logback是由log4j创始人设计的另一个开源日志组件,性能比Log4j要好Logback是基于SLF4J的日志规范实现的框架。
- Log4j2 :Ceki Gülcü 觉得还是得照顾下自己的 “大儿子”——Log4j,又把 Log4j 进行了改造,就是所谓的 Log4j2,同时支持 JCL 以及 SLF4J
日志库特点
- JCL和 SLF4J 都是日志的接口,供用户使用,而没有提供实现,Log4j,JUL,logback 等等才是日志的真正实现。
- Log4j,JUL,logback 是互相不兼容的,没有共同的 Interface,通过适配器模式,抽象出来一个共同的接口,然后根据具体的日志框架来实现日志。
- 通常情况下,日志记录是由一个抽象层+实现层的组合来搭建的。
- Spring Boot的日志框架:默认情况下,Spring Boot会用Logback来记录日志,并用INFO级别输出到控制台。
Logback简介
- Logback是由log4j创始人设计开发的另一个开源日志组件,性能安全性个方面都比Log4j好,所以一般都推荐使用这个日志框架。
- Spring Boot 内部所有的日志实现都通过桥接器转换成Slf4j 日志门面进行统一管理,最终交给Logback日志框架进行日志输出。
- 官网: http://logback.qos.ch
- 主要模块:
- logback-core:其它两个模块的基础模块
- logback-classic:它是log4j的一个改良版本,同时它完整实现了slf4j API使你可以很方便地更换成其它日志系统如log4j或JDK1.4 Logging
- logback-access:模块与 Tomcat 和 Jetty 等 Servlet 容器集成,以提供 HTTP 访问日志功能。
Spring Boot中整合Logback
1.依赖
Spring Boot提供了默认的日志配置,只要将spring-boot-starter-logging作为依赖加入到当前应用中,则“开箱即用”
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
2.设置日志级别
- 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果设置为WARN,则低于WARN的信息都不会输出。
- 在 Spring 中设置日志级别,方法是使用logging.level.<logger-name>=<level>
logging:
level:
org.springframework.web: debug
3.Logback的定制化配置
- 由于日志服务一般都在ApplicationContext创建前就初始化了,它并不是通过Spring的配置文件控制。因此需要通过系统属性和传统的Spring Boot外部配置文件
- Spring Boot官方推荐优先使用带有xx-spring.xml的文件名作为你的日志配置(比如:logback-spring.xml),命名为logback-spring.xml的日志配置文件,Spring Boot可以为它添加一些特有的配置项。
- 创建logback-spring.xml文件,放在src/main/resources下面即可
<?xml version="1.0" encoding="UTF-8"?>
<configuration >
<!--contextName说明:每个logger都关联到logger上下文,默认上下文名称为“default”。但可以使用设置成其他名字,
用于区分不同应用程序的记录。一旦设置,不能修改,可以通过%contextName来打印日志上下文名称。-->
<contextName>logback-spring</contextName>
<!-- name的值是变量的名称,value的值是变量定义的值。通过定义的值会被插入到logger上下文中。定义后,可以使“${}”来使用变量。 -->
<property name="log.path" value="logs"/>
<!-- 彩色日志依赖的渲染类 -->
<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="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy年MM月dd日 HH:mm:ss}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
<!--输出日志到控制台-->
<appender name="consoleLog" class="ch.qos.logback.core.ConsoleAppender">
<!--ThresholdFilter:临界值过滤器,打印大于等于level标签设置的级别,小的舍弃-->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>debug</level>
</filter>
<!--日志输出格式-->
<layout>
<!--指定日志格式-->
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
</layout>
</appender>
<!--输出日志到文件-->
<appender name="fileLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!--LevelFilter:只打印level标签设置的日志级别-->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>info</level>
<!--匹配到就允许-->
<onMatch>ACCEPT</onMatch>
<!--没有匹配到就禁止-->
<onMismatch>DENY</onMismatch>
</filter>
<encoder>
<pattern>
%d{HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%n
</pattern>
</encoder>
<!--指定文件的输出位置-->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>d:/${log.path}/%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<!--日志文档保留天数-->
<maxHistory>30</maxHistory>
<!--配置日志文件不能超过100M,若超过100M,日志文件会以索引0开始,命名日志文件-->
<maxFileSize>100MB</maxFileSize>
<!--总大小-->
<totalSizeCap>2GB</totalSizeCap>
<!--appender启动的时候,归档文件将会被删除。默认的值为 false-->
<cleanHistoryOnStart>true</cleanHistoryOnStart>
</rollingPolicy>
</appender>
<root level="info">
<appender-ref ref="consoleLog"/>
<appender-ref ref="fileLog"/>
</root>
<!--有logger的配置,不指定级别,不指定appender-->
<!--将org.springframework.web包下的所有类的日志的打印-->
<logger name="org.springframework.web" level="debug"/>
</configuration>
4.如何记录自己代码中的日志
方式一:手动创建Logger对象
@RestController
public class LoggerController {
Logger logger = LoggerFactory.getLogger(LoggerController.class);
@RequestMapping("/log")
public Object log(){
logger.info("测试日志INFO");
logger.error("测试日志ERROR");
logger.debug("测试日志DEBUG");
return "测试日志";
}
}
方式二:使用Lombok提供的@Slf4j注解
依赖
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
使用
@RestController
@Slf4j
public class LoggerController {
@Resource
private DataSource dataSource;
@RequestMapping("/log")
public Object log(){
log.info("测试日志,日志级别是:{},数据源对象是:","INFO",dataSource);
log.error("测试日志ERROR");
log.debug("测试日志DEBUG");
return "测试日志";
}
}