Spring Boot 热部署与日志

381 阅读5分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第28天,点击查看活动详情

一、springboot中devtools热部署

  为了进一步提高开发效率,springboot为我们提供了全局项目热部署,日后在开发过程中修改了部分代码以及相关配置文件后,不需要每次重启使修改生效,在项目中开启了springboot全局热部署之后只需要在修改之后等待几秒即可使修改生效。

1-1、开启热部署

1-1-1、项目中引入maven依赖

<dependency>   
    <groupId>org.springframework.boot</groupId>   
    <artifactId>spring-boot-devtools</artifactId>   
    <optional>true</optional> 
</dependency>

1-1-2、idea中的配置

当我们修改了类文件后,idea不会自动编译,得修改idea设置

  • (1)File-Settings-Compiler-Build Project automatically

image.png

  • (2)ctrl + shift + alt + / ,选择Registry,勾上 Compiler autoMake allow when app running

1-1-2、启动项目检测热部署是否生效

1.启动出现如下日志代表生效

image.png 注意:日志出现restartedMain代表已经生效,在使用热部署时如果遇到修改之后不能生效,请重试重启项目在试

二、混乱的JAVA日志体系!

还在为弄不清commons-logging.jar、log4j.jar、sl4j-api.jar等日志框架之间复杂的关系而感到烦恼吗?

还在为如何统一系统的日志输出而感到不知所措嘛?

您是否依然存在这样的烦恼。比如,要更改spring的日志输出为log4j 2,却不知该引哪些jar包,只知道去百度一下,照着人家复制,却无法弄懂其中的原理?

日志实现日志门面
log4j 淘汰JCL
jul java.util.logging 别的SJF4J
log4j2
logback

早年,你工作的时候,在日志里使用了log4j框架来输出,于是你代码是这么写的

import org.apache.log4j.Logger;
Logger logger = Logger.getLogger(Test.class);
logger.trace("trace");

但是,岁月流逝,sun公司对于log4j的出现内心隐隐表示嫉妒。于是在jdk1.4版本后,增加了一个包为java.util.logging,简称为jul,用以对抗log4j。于是,你的领导要你把日志框架改为jul,这时候你只能一行行的将log4j的api改为jul的api,如下所示

import java.util.logging.Logger; 
\\省略
Logger loggger = Logger.getLogger(Test.class.getName());  logger.finest("finest");

可以看出,api完全是不同的。那有没有办法,将这些api抽象出接口,这样以后调用的时候,就调用这些接口就好了呢?

这个时候jcl(Jakarta Commons Logging)出现了,说jcl可能大家有点陌生,讲commons-logging-xx.jar组件,大家总有印象吧。JCL 只提供 log 接口,具体的实现则在运行时动态寻找。这样一来组件开发者只需要针对 JCL 接口开发,而调用组件的应用程序则可以在运行时搭配自己喜好的日志实践工具。JCL可以实现的集成方案如下图所示

image.png

jcl默认的配置:如果能找到Log4j 则默认使用log4j 实现,如果没有则使用jul(jdk自带的) 实现,再没有则使用jcl内部提供的SimpleLog 实现。

于是,你在代码里变成这么写了

import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory;
\\省略
Log log =LogFactory.getLog(Test.class); 
log.trace('trace');

至于这个Log具体的实现类,JCL会在ClassLoader中进行查找。这么做,有三个缺点,缺点一是效率较低,二是容易引发混乱,三是在使用了自定义ClassLoader的程序中,使用JCL会引发内存泄露。

JCL动态查找机制进行日志实例化,执行顺序为:commons-logging.properties---->系统环境变量------->log4j--->jul--->simplelog---->nooplog

于是log4j的作者觉得jcl不好用,自己又写了一个新的接口api,那么就是slf4j。关于slf4j的集成图如下所示

image.png

理解slf4j日志门面了吗,它跟jcl机制不一样。 它就相当于这个游戏机, 我本身没有游戏, 只提供一个运行游戏的平台(门面)

要运行哪个游戏我不管, 你给我放哪块光盘我就运行哪个游戏。 JCL 背靠JDK 自带jul .

image.png

Slf4j与其他各种日志组件的桥接说明

jar包名说明
slf4j-log4j12-1.7.13.jarLog4j1.2版本的桥接器,你需要将Log4j.jar加入Classpath。
log4j-slf4j-impl.jarLog4j2版本的桥接器,还需要log4j-api.jar log4j-core.jar
slf4j-jdk14-1.7.13.jarjava.util.logging的桥接器,Jdk原生日志框架。
slf4j-nop-1.7.13.jarNOP桥接器,默默丢弃一切日志。
slf4j-simple-1.7.13.jar一个简单实现的桥接器,该实现输出所有事件到System.err. 只有Info以及高于该级别的消息被打印,在小型应用中它也许是有用的。
slf4j-jcl-1.7.13.jarJakarta Commons Logging 的桥接器. 这个桥接器将Slf4j所有日志委派给Jcl。
logback-classic-1.0.13.jar(requires logback-core-1.0.13.jar)Slf4j的原生实现,Logback直接实现了Slf4j的接口,因此使用Slf4j与Logback的结合使用也意味更小的内存与计算开销

如图所示,应用调了sl4j-api,即日志门面接口。日志门面接口本身通常并没有实际的日志输出能力,它底层还是需要去调用具体的日志框架API的,也就是实际上它需要跟具体的日志框架结合使用。由于具体日志框架比较多,而且互相也大都不兼容,日志门面接口要想实现与任意日志框架结合可能需要对应的桥接器,上图红框中的组件即是对应的各种桥接器!

我们在代码中需要写日志,变成下面这么写

import org.slf4j.Logger; 
import org.slf4j.LoggerFactory;
//省略
Logger logger = LoggerFactory.getLogger(Test.class); 
// 省略
logger.info("info");

在代码中,并不会出现具体日志框架的api。程序根据classpath中的桥接器类型,和日志框架类型,判断出logger.info应该以什么框架输出!注意了,如果classpath中不小心引了两个桥接器,那会直接报错的!