Spring boot应用回顾
1. SpringBoot应用回顾
1.1 约定优于配置
概念:约定优于配置(Convention over Configuration),又称按约定编程,是一种软件设计规范。本质上是对系统、类库或框架中一些东西假定一个大众化合理的默认值(缺省值)。例如在模型中存在一个名为User的类,那么对应到数据库会存在一个名为user的表,此时无需做额外的配置,只有在偏离这个约定时才需要做相关的配置(例如你想将表名命名为t_user等非user时才需要写关于这个名字的配置)。
如果所用工具的约定与你的期待相符,便可省去配置;反之,你可以配置来达到你所期待的方式。简单来说就是假如你所期待的配置与约定的配置一致,那么就可以不做任何配置,约定不符合期待时才需要对约定进行替换配置。
好处:大大减少了配置项
1.2 SpringBoot概念
1.2.1 什么是SpringBoot
spring官方的网站:https://spring.io/
通过Spring Boot,可以轻松地创建独立的,基于生产级别的基于Spring的应用程序,并且可以“运行”它们
以下内容来自百度百科
SpringBoot是由Pivotal团队在2013年开始研发、2014年4月发布第一个版本的全新开源的轻量级框架。它基于Spring4.0设计,不仅继承了Spring框架原有的优秀特性,而且还通过简化配置来进一步简化了Spring应用的整个搭建和开发过程。另外SpringBoot通过集成大量的框架使得依赖包的版本冲突,以及引用的不稳定性等问题得到了很好的解决
1.2.2 SpringBoot主要特性
1、 SpringBoot Starter:他将常用的依赖分组进行了整合,将其合并到一个依赖中,这样就可以一次性添加到项目的Maven或Gradle构建中;
2、 使编码变得简单,SpringBoot采用 JavaConfig的方式对Spring进行配置,并且提供了大量的注解,极大的提高了工作效率。
3、 自动配置:SpringBoot的自动配置特性利用了Spring对条件化配置的支持,合理地推测应用所需的bean并自动化配置他们;
4、 使部署变得简单,SpringBoot内置了三种Servlet容器,Tomcat,Jetty,undertow.我们只需要一个Java的运行环境就可以跑SpringBoot的项目了,SpringBoot的项目可以打成一个jar包。
1.3 SpringBoot 案例实现
1.3.1 案例实现
案例需求:使用Spring Initializr方式构建Spring Boot项目,并请求Controller中的目标方法,将返回值响应到页面
- (1)使用Spring Initializr方式构建Spring Boot项目
本质上说,Spring Initializr是一个Web应用,它提供了一个基本的项目结构,能够帮助我们快速构建一个基础的Spring Boot项目
Project SDK”用于设置创建项目使用的JDK版本,这里,使用之前初始化设置好的JDK版本即可;在“Choose Initializr Service URL(选择初始化服务地址)”下使用默认的初始化服务地址“https://start.spring.io”进行Spring Boot项目创建(注意使用快速方式创建Spring Boot项目时,所在主机须在联网状态下)
Spring Boot项目就创建好了。创建好的Spring Boot项目结构如图:
使用Spring Initializr方式构建的Spring Boot项目会默认生成项目启动类、存放前端静态资源和页面的文件夹、编写项目配置的配置文件以及进行项目单元测试的测试类 - (2)创建Controller
com.lagou包下创建名称为controller的包,在该包下创建一个请求处理控制类HelloController,并编写一个请求处理方法 (注意:将项目启动类SpringBootDemoApplication移动到com.lagou包下)
@RestController // 该注解为组合注解,等同于Spring中@Controller+@ResponseBody注解
public class DemoController {
@RequestMapping("/demo")
public String demo(){
return "hello springBoot";
}
}
-
(3) 运行项目
运行主程序启动类SpringbootDemoApplication,项目启动成功后,在控制台上会发现Spring Boot项目默认启动的端口号为8080,此时,可以在浏览器上访问“http://localhost:8080/hello”
页面输出的内容是“hello Spring Boot”,至此,构建Spring Boot项目就完成了附:解决中文乱码:
解决方法一:@RequestMapping(produces = "application/json; charset=utf-8")
解决方法二:
#设置响应为utf-8 spring.http.encoding.force-response=true
1.3.2 疑问
- starter是什么?我们如何去使用这些starter?
- 为什么包扫描只会扫描核心启动类所在的包及其子包
- 在springBoot启动的过程中,是如何完成自动装配的?
- 内嵌Tomcat是如何被创建及启动的?
- 使用了web场景对应的starter,springmvc是如何自动装配?
(保留疑问:在源码剖析部分,主要将以上疑问进行解答)
1.4 热部署
在开发项目过程中,当修改了某些代码后需要本地验证时,需要重启本地服务进行验证,启动这个项目,如果项目庞大的话还是需要较长时间的,spring开发团队为我们带来了一个插件:spring-bootdevtools,很好的解决了本地验证缓慢的问题。
1.4.1 热部署实现演示
-
1.添加spring-boot-devtools热部署依赖启动器
在Spring Boot项目进行热部署测试之前,需要先在项目的pom.xml文件中添加spring-boot-devtools热部署依赖启动器:<!-- 引入热部署依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency>
由于使用的是IDEA开发工具,添加热部署依赖后可能没有任何效果,接下来还需要针对IDEA开发工具进行热部署相关的功能设置
-
2.IDEA工具热部署设置
选择IDEA工具界面的【File】->【Settings】选项,打开Compiler面板设置页面
选择Build下的Compiler选项,在右侧勾选“Build project automatically”选项将项目设置为自动编译,单击【Apply】→【OK】按钮保存设置在项目任意页面中使用组合快捷键“Ctrl+Shift+Alt+/”打开Maintenance选项框,选中并打开Registry页面,具体如图所示
列表中找到“compiler.automake.allow.when.app.running”,将该选项后的Value值勾选,用于指定IDEA工具在程序运行过程中自动编译,最后单击【Close】按钮完成设置 -
3.热部署效果测试
启动chapter01http://localhost:8080/hello
页面原始输出的内容是“hello Spring Boot”。为了测试配置的热部署是否有效,接下来,在不关闭当前项目的情况下,将DemoController 类中的请求处理方法hello()的返回值修改为“你好,Spring Boot”并保存,查看控制台信息会发现项目能够自动构建和编译,说明项目热部署生效
可以看出,浏览器输出了“你好,Spring Boot”,说明项目热部署配置成功
1.4.2 热部署原理分析
该原理其实很好说明,就是我们在编辑器上启动项目,然后改动相关的代码,然后编辑器自动触发编译替换掉历史的.class文件后,项目检测到有文件变更后会重启srpring-boot项目。可以看看官网的触发描述:
可以看到,我们引入了插件后,插件会监控我们classpath的资源变化,当classpath有变化后,会触发重启
这里提到了,该插件重启快速的原因:这里对类加载采用了两种类加载器,对于第三方jar包采用baseclassloader来加载,对于开发人员自己开发的代码则使用restartClassLoader来进行加载,这使得比停掉服务重启要快的多,因为使用插件只是重启开发人员编写的代码部分。这边做个简单的验证:
@Component
public class Devtools implements InitializingBean {
private static final Logger log = LoggerFactory.getLogger(Devtools.class);
@Override
public void afterPropertiesSet() throws Exception {
log.info("guava-jar classLoader: " +
DispatcherServlet.class.getClassLoader().toString());
log.info("Devtools ClassLoader: " +
this.getClass().getClassLoader().toString());
}
}
这边先去除spring-boot-devtools插件,跑下工程:
可以看到,DispatcherServlet(第三方jar包)和Devtools(自己编写的类)使用的都是AppClassLoader加载的。
我们现在加上插件,然后执行下代码:
发现第三方的jar包的类加载器确实是使用的系统的类加载器,而我们自己写的代码的类加载器为
RestartClassLoader,并且每次重启,类加载器的实例都会改变。
上图为代码修改前后类文件的变更。
1.4.3 排除资源
某些资源在更改后不一定需要触发重新启动。例如,Thymeleaf模板可以就地编辑。默认情况下,改变资源/META-INF/maven , /META-INF/resources , /resources , /static , /public ,或/templates 不触发重新启动,但确会触发现场重装。如果要自定义这些排除项,则可以使用该spring.devtools.restart.exclude 属性。例如,仅排除/static , /public 您将设置以下属性:
spring.devtools.restart.exclude=static/**,public/**
1.5 全局配置文件
1.5.1 全局配置文件概述及优先级
全局配置文件能够对一些默认配置值进行修改及自定义配置。
Spring Boot使用一个application.properties或者application.yaml的文件作为全局配置文件
也可以从ConfigFileApplicationListener这类便可看出,其中DEFAULT_SEARCH_LOCATIONS属性设置了加载的目录:
翻译成文件系统:
–file:./config/
–file:./
–classpath:/config/
–classpath:/
翻译成语言如下( 按照优先级从高到低的顺序):
- 先去项目根目录找config文件夹下找配置文件件
- 再去根目录下找配置文件
- 去resources下找cofnig文件夹下找配置文件
- 去resources下找配置文
整个设计非常巧妙。SpringBoot会从这四个位置全部加载主配置文件,如果高优先级中配置文件属性与低优先级配置文件不冲突的属性,则会共同存在— 互补配置。
SpringBoot会加载全部主配置文件;互补配置;
备注:
这里说的配置文件,都还是项目里面。最终都会被打进jar包里面的,需要注意。
1、如果同一个目录下,有application.yml也有application.properties,默认先读取
application.properties。
2、如果同一个配置属性,在多个配置文件都配置了,默认使用第1个读取到的,后面读取的不覆盖前面读取到的。
3、创建SpringBoot项目时,一般的配置文件放置在“项目的resources目录下”
如果我们的配置文件名字不叫application.properties或者application.yml,可以通过以下参数来指定配置文件的名字,myproject是配置文件名
$ java -jar myproject.jar --spring.config.name=myproject
我们同时也可以指定其他位置的配置文件来生效指定配置文件和默认加载的这些配置文件共同起作用形成互补配置。
java -jar run-0.0.1-SNAPSHOT.jar --spring.config.location=D:/application.properties
接下来,将针对这两种全局配置文件application.properties及application.yml进行讲解 :
知识点补充!
Spring Boot 2.4 改进了处理 application.properties 和 application.yml 配置文件的方式,如果是2.4.0之前版本,优先级properties>yaml, 但是如果是2.4.0的版本,优先级yaml>properties如果想继续使用 Spring Boot 2.3 的配置逻辑,也可以通过在 application.properties 或者application.yml 配置文件中添加以下参数:
spring.config.use-legacy-processing = true
1.5.2 application.properties配置文件
使用Spring Initializr方式构建Spring Boot项目时,会在resource目录下自动生成一个空的application.properties文件,Spring Boot项目启动时会自动加载application.properties文件。
我们可以在application.properties文件中定义Spring Boot项目的相关属性,当然,这些相关属性可以是系统属性、环境变量、命令参数等信息,也可以是自定义配置文件名称和位置
server.port=8081
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.config.name=application
接下来,通过一个案例对Spring Boot项目中application.properties配置文件的具体使用进行讲解
演示:
预先准备了两个实体类文件,后续会演示将application.properties配置文件中的自定义配置属性注入到Person实体类的对应属性中
- (1)先在项目的com.lagou包下创建一个pojo包,并在该包下创建两个实体类Pet和Person
public class Pet { private String type; private String name; // 省略属性getXX()和setXX()方法 // 省略toString()方法 }
@Component //用于将Person类作为Bean注入到Spring容器中 @ConfigurationProperties(prefix = "person") //将配置文件中以person开头的属性注入到该类中 public class Person { private int id; //id private String name; //名称 private List hobby; //爱好 private String[] family; //家庭成员 private Map map; private Pet pet; //宠物 // 省略属性getXX()和setXX()方法 // 省略toString()方法 }
@ConfigurationProperties(prefix = “person”)注解的作用是将配置文件中以person开头的属性值通过setXX()方法注入到实体类对应属性中
@Component注解的作用是将当前注入属性值的Person类对象作为Bean组件放到Spring容器中,只有这样才能被@ConfigurationProperties注解进行赋值
-
(2)打开项目的resources目录下的application.properties配置文件,在该配置文件中编写需要对Person类设置的配置属性
编写application.properties配置文件时,由于要配置的Person对象属性是我们自定义的,SpringBoot无法自动识别,所以不会有任何书写提示。在实际开发中,为了出现代码提示的效果来方便配置,在使用@ConfigurationProperties注解进行配置文件属性值注入时,可以在pom.xml文件中添加一个Spring Boot提供的配置处理器依赖:<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency>
在pom.xml中添加上述配置依赖后,还需要重新运行项目启动类或者使用“Ctrl+F9”快捷键(即Build Project)重构当前Spring Boot项目方可生效
-
(3)查看application.properties配置文件是否正确,同时查看属性配置效果,打开通过IDEA工具创建的项目测试类,在该测试类中引入Person实体类Bean,并进行输出测试
@RunWith(SpringRunner.class) // 测试启动器,并加载Spring Boot测试注解 @SpringBootTest // 标记为Spring Boot单元测试类,并加载项目的ApplicationContext上下文环境 class SpringbootDemoApplicationTests { // 配置测试 @Autowired private Person person; @Test void configurationTest() { System.out.println(person); } }
打印结果:
可以看出,测试方法configurationTest()运行成功,同时正确打印出了Person实体类对象。至此,说明application.properties配置文件属性配置正确,并通过相关注解自动完成了属性注入
1.5.3 application.yaml配置文件
YAML文件格式是Spring Boot支持的一种JSON超集文件格式,以数据为中心,比properties、xml等更适合做配置文件
- yml和xml相比,少了一些结构化的代码,使数据更直接,一目了然
- yml相比properties文件更简洁
- YAML文件的扩展名可以使用.yml或者.yaml。
- application.yml文件使用 “key:(空格)value”格式配置属性,使用缩进控制层级关系。
这里,针对不同数据类型的属性值,介绍一下YAML
-
(1)value值为普通数据类型(例如数字、字符串、布尔等)
当YAML配置文件中配置的属性值为普通数据类型时,可以直接配置对应的属性值,同时对于字符串类型的属性值,不需要额外添加引号,示例代码如下server: port: 8080 servlet: context-path: /hello
-
(2)value值为数组和单列集合
当YAML配置文件中配置的属性值为数组或单列集合类型时,主要有两种书写方式:缩进式写法和行内式写法。其中,缩进式写法还有两种表示形式,示例代码如下
person: hobby: - play - read - sleep
或者使用如下示例形式
person: hobby: play, read, sleep
上述代码中,在YAML配置文件中通过两种缩进式写法对person对象的单列集合(或数组)类型的爱好hobby赋值为play、read和sleep。其中一种形式为“-(空格)属性值”,另一种形式为多个属性值之前加英文逗号分隔(注意,最后一个属性值后不要加逗号)。
person: hobby: [play,read,sleep]
通过上述示例对比发现,YAML配置文件的行内式写法更加简明、方便。另外,包含属性值的中括号“[]”还可以进一步省略,在进行属性赋值时,程序会自动匹配和校对
-
(3)value值为Map集合和对象
当YAML配置文件中配置的属性值为Map集合或对象类型时,YAML配置文件格式同样可以分为两种书写方式:缩进式写法和行内式写法。其中,缩进式写法的示例代码如下
person: map: k1: v1 k2: v2
对应的行内式写法示例代码如下
person: map: {k1: v1,k2: v2}
在YAML配置文件中,配置的属性值为Map集合或对象类型时,缩进式写法的形式按照YAML文件格式编写即可,而行内式写法的属性值要用大括号“{}”包含。
接下来,在Properties配置文件演示案例基础上,通过配置application.yaml配置文件对Person对象进行赋值,具体使用如下
- (1)在项目的resources目录下,新建一个application.yaml配置文件,在该配置文件中编写为Person类设置的配置属性
#对实体类对象Person进行属性配置 person: id: 1 name: lucy hobby: [吃饭,睡觉,打豆豆] family: [father,mother] map: {k1: v1,k2: v2} pet: {type: dog,name: 旺财}
- (2)再次执行测试
可以看出,测试方法configurationTest()同样运行成功,并正确打印出了Person实体类对象。
1.6 属性注入
使用Spring Boot全局配置文件设置属性时:
如果配置属性是Spring Boot已有属性,例如服务端口server.port,那么Spring Boot内部会自动扫描并读取这些配置文件中的属性值并覆盖默认属性。
如果配置的属性是用户自定义属性,例如刚刚自定义的Person实体类属性,还必须在程序中注入这些配置属性方可生效。
1.6.1 属性注入常用注解
@Configuration:声明一个类作为配置类
@Bean:声明在方法上,将方法的返回值加入Bean容器
@Value:属性注入
@ConfigurationProperties(prefix = “jdbc”):批量属性注入
@PropertySource(“classpath:/jdbc.properties”)指定外部属性文件。在类上添加
1.6.2 @Value属性值注入
-
1.引入数据源连接依赖
<dependency> <groupId>com.github.drtrang</groupId> <artifactId>druid-spring-boot2-starter</artifactId> <version>1.1.10</version> </dependency>
-
2.application.properties添加信息
jdbc.driverClassName=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://127.0.0.1:3306/springboot_h jdbc.username=root jdbc.password=123
-
3.配置数据源
创建JdbcConfiguration类: 使用spring中的value注解对每个属性进行注入,用bean注解将返回值添加到容器中@Configuration public class JdbcConfiguration { @Value("${jdbc.url}") String url; @Value("${jdbc.driverClassName}") String driverClassName; @Value("${jdbc.username}") String username; @Value("${jdbc.password}") String password; @Bean public DataSource dataSource() { DruidDataSource dataSource = new DruidDataSource(); dataSource.setUrl(url); dataSource.setDriverClassName(driverClassName); dataSource.setUsername(username); dataSource.setPassword(password); return dataSource; } }
1.6.3 @ConfigurationProperties批量注入
-
1.新建JdbcProperties ,用来进行属性注入:
@ConfigurationProperties(prefix = "jdbc") //这里需要定义出在application文件中定义属性值得前缀信息 public class JdbcProperties { private String url; private String driverClassName; private String username; private String password; // 注:要生成属性的set方法 }
注:添加@ConfigurationProperties注解后有警告:springboot 配置注释处理器未配置(编写配置文件此时无提示)
添加spring-boot-configuration-processor后出现提示,加完依赖后通过Ctrl+F9来使之生效<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency>
接着发现,仍然有红色警告
@EnableConfigurationProperties 是Spring Boot 提供的一个注解,使用该注解用于启用应用对另外一个注解@ConfigurationProperties 的支持,用于设置一组使用了注解@ConfigurationProperties 的类,用于作为 bean 定义注册到容器中。
-
2.application.properties添加信息
jdbc.driverClassName=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://127.0.0.1:3306/springboot_h jdbc.username=root jdbc.password=123
注意:将配置信息添加到这里,通过前缀进行区分,进行引用
-
3.查看效果
@RunWith(SpringRunner.class) @SpringBootTest class Springbootlesson1ApplicationTests { @Autowired private JdbcProperties jdbcProperties; @Test public void jdbcPropertiesTest(){ System.out.println(jdbcProperties); } }
打印结果:JdbcProperties{url=‘jdbc:mysql://127.0.0.1:3306/springboot_h’,
driverClassName=‘com.mysql.jdbc.Driver’, username=‘root’, password=‘root’}
1.6.4 第三方配置
除了@ConfigurationProperties 用于注释类之外,您还可以在公共@Bean 方法上使用它。当要将属性绑定到控件之外的第三方组件时,这样做特别有用。
效果演示:
创建一个其他组件类
@Data
public class AnotherComponent {
private boolean enabled;
private InetAddress remoteAddress;
}
创建MyService
@Configuration
public class MyService {
@ConfigurationProperties("another")
@Bean
public AnotherComponent anotherComponent(){
return new AnotherComponent();
}
}
配置文件
another.enabled=true
another.remoteAddress=192.168.10.11
测试类
@Autowired
private AnotherComponent anotherComponent;
@Test
public void myServiceTest(){
System.out.println(anotherComponent);
}
我们通过测试可以获得AnotherComponent组件的实例对象。
1.6.5 松散绑定
Spring Boot使用一些宽松的规则将环境属性绑定到@ConfigurationProperties bean,因此环境属性名和bean属性名之间不需要完全匹配
例如属性类:
@Data
@Component
@ConfigurationProperties("acme.my-person.person")
public class OwnerProperties {
private String firstName;
}
acme:
my-person:
person:
first-name: 泰森
属性文件中配 | 说明 |
---|---|
acme.my-project.person.first-name | 羊肉串模式case, 推荐使用 |
acme.myProject.person.firstName | 标准驼峰模式 |
acme.my_project.person.first_name | 下划线模式 |
ACME_MYPROJECT_PERSON_FIRSTNAME | 大写下划线,如果使用系统环境时候推荐使用 |
1.6.6 @ConfigurationProperties vs @Value
特征 | @ConfigurationProperties | @Value |
---|---|---|
宽松的绑定 | yes | Limited(详见下方官网截图) |
元数据支持 | yes | no |
SpEL 表达式 | no | yes |
应用场景 | 批量属性绑定 | 单个属性绑定 |
1.7 SpringBoot日志框架
1.7.1 日志框架介绍
在项目的开发中,日志是必不可少的一个记录事件的组件,不管是记录运行情况还是追踪线上问题,都离不开对日志的分析,所以也会相应的在项目中实现和构建我们所需要的日志框架。
而市面上常见的日志框架有很多,比如:JCL、SLF4J、Jboss-logging、jUL、log4j、log4j2、logback等等,我们该如何选择呢?
通常情况下,日志是由一个抽象层+实现层的组合来搭建的。
日志-抽象层 | 日志-实现层 |
---|---|
JCL(Jakarta Commons Logging)、SLF4J(Simple Logging Facade for Java)、jboss-logging | jul(java.util.logging)、log4j、logback、log4j2 |
Spring 框架选择使用了 JCL 作为默认日志输出。而 Spring Boot 默认选择了 SLF4J 结合 LogBack
1.7.2 SLF4J 的使用
在开发的时候不应该直接使用日志实现类,应该使用日志的抽象层。具体参考 SLF4J 官方。
SLF4J 官方给出了简单示例。
首先要为系统导入 SLF4J 的 jar .
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HelloWorld {
public static void main(String[] args) {
LoggerFactory logger = LoggerFactory.getLogger(HelloWorld.class);
logger.info("Hello World");
}
}
下图是 SLF4J 结合各种日志框架的官方示例,从图中可以清晰的看出 SLF4J API 永远作为日志的门面,直接应用与应用程序中。
注意:由于每一个日志的实现框架都有自己的配置文件,所以在使用 SLF4j 之后,配置文件还是要使用实现日志框架的配置文件
1.7.3. 统一日志框架的使用
遗留问题:A项目(slf4J + logback): Spring(commons logging)、Hibernate(jboss-logging)、mybatis…
一般情况下,在项目中存在着各种不同的第三方 jar ,且它们的日志选择也可能不尽相同,显然这样是不利于我们使用的,那么如果我们想为项目设置统一的日志框架该怎么办呢?
在 SLF4J 官方,也给了我们参考的例子
从图中我们得到一种统一日志框架使用的方式,可以使用一种和要替换的日志框架类完全一样的 jar 进行替换,这样不至于原来的第三方 jar 报错,而这个替换的 jar 其实使用了 SLF4J API. 这样项目中的日志就都可以通过 SLF4J API 结合自己选择的框架进行日志输出。
统一日志框架使用步骤归纳如下:
- 排除系统中的其他日志框架。
- 使用中间包替换要替换的日志框架。
- 导入我们选择的 SLF4J 实现。
1.7.4. Spring Boot 的日志关系
-
① 排除其他日志框架
根据上面总结的要统一日志框架的使用,第一步要排除其他的日志框架,在 Spring Boot 的 Maven 依赖里可以清楚的看到 Spring Boot 排除了其他日志框架。
我们自行排除依赖时也只需要按照图中的方式就好了。 -
② 统一框架引入替换包
Spring Boot 是使用了 SLF4J+logback 的日志框架组合,查看 Spring Boot 项目的 Maven 依赖关系可以看到 Spring Boot 的核心启动器 spring-boot-starter 引入了 spring-boot-starter-logging.<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> <version>2.4.0.RELEASE</version> </dependency>
而 spring-boot-starter-logging 的 Maven 依赖主要引入了 logback-classic (包含了日志框架 Logback的实现),log4j-to-slf4j (在 log4j 日志框架作者开发此框架的时候还没有想到使用日志抽象层进行开发,因此出现了 log4j 向 slf4j 转换的工具),jul-to-slf4j ( Java 自带的日志框架转换为 slf4j).
<dependencies> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-to-slf4j</artifactId> <version>2.13.3</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>jul-to-slf4j</artifactId> <version>1.7.30</version> <scope>compile</scope> </dependency> </dependencies>
从上面的分析,Spring Boot 对日志框架的使用已经是清晰明了了,我们使用 IDEA 工具查看 Maven 依赖关系,可以清晰的看到日志框架的引用
由此可见,Spring Boot 可以自动的适配日志框架,而且底层使用 SLF4j + LogBack 记录日志,如果我们自行引入其他框架,需要排除其日志框架。
1.7.5. Spring Boot 的日志使用
日志级别和格式
从上面的分析,发现 Spring Boot 默认已经使用了 SLF4J + LogBack . 所以我们在不进行任何额外操作的情况下就可以使用 SLF4J + Logback 进行日志输出。
编写 Java 测试类进行测试。
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
/**
* 测试日志输出,
* SLF4J 日志级别从小到大trace,debug,info,warn,error
*
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class LogbackTest {
Logger logger = LoggerFactory.getLogger(getClass());
@Test
public void testLog() {
logger.trace("Trace 日志...");
logger.debug("Debug 日志...");
logger.info("Info 日志...");
logger.warn("Warn 日志...");
logger.error("Error 日志...");
}
}
已知日志级别从小到大为 trace < debug < info < warn < error . 运行得到输出如下。由此可见 SpringBoot 默认日志级别为 INFO.
2020-11-16 19:58:43.094 INFO 39940 --- [ main]
com.lagou.Springboot01DemoApplicationTests : Info 日志...
2020-11-16 19:58:43.094 WARN 39940 --- [ main]
com.lagou.Springboot01DemoApplicationTests : Warn 日志...
2020-11-16 19:58:43.094 ERROR 39940 --- [ main]
com.lagou.Springboot01DemoApplicationTests : Error 日志...
从上面的日志结合 Logback 日志格式可以知道 Spring Boot 默认日志格式是
%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
# %d{yyyy-MM-dd HH:mm:ss.SSS} 时间
# %thread 线程名称
# %-5level 日志级别从左显示5个字符宽度
# %logger{50} 类名
# %msg%n 日志信息加换行
至于为什么 Spring Boot 的默认日志输出格式是这样?
我们可以在 Spring Boot 的源码里找到答案。
1.7.6 自定义日志输出
可以直接在配置文件编写日志相关配置
# 日志配置
# 指定具体包的日志级别
logging.level.com.lagou=debug
# 控制台和日志文件输出格式
logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level
%logger{50} - %msg%n
logging.pattern.file=%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}
- %msg%n
# 日志输出路径,默认文件spring.log
logging.file.path=spring.log
#logging.file.name=log.log
关于日志的输出路径,可以使用 logging.file 或者 logging.path 进行定义,两者存在关系如下表。
logging.file | logging.path | 例子 | 描述 |
---|---|---|---|
(没有) | (没有) | 仅控制台记录。 | |
具体文件 | (没有) | my.log | 写入指定的日志文件,名称可以是精确位置或相对于当前目录。 |
(没有) | 具体目录 | /var/log | 写入spring.log 指定的目录,名称可以是精确位置或相对于当前目录。 |
1.7.7 替换日志框架
因为 Log4j 日志框架已经年久失修,原作者都觉得写的不好,所以下面演示替换日志框架为 Log4j2 的方式。根据官网我们 Log4j2 与 logging 需要二选一,因此修改 pom如下
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<artifactId>spring-boot-starter-logging</artifactId>
<groupId>org.springframework.boot</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>