本章完成的效果:
- 创建一个 Spring Boot 项目
- 多环境配置,选择环境打包
- 使用logback进行日志输出,按日期+大小进行滚动压缩
- 在Windows系统使用maven进行构建打包运行
本章使用的运行环境:
- Java:JDK1.8.0_441
- Maven:3.6.0
- Spring Boot: 2.6.13
- Logback:1.2.11
- 已被
spring-boot-starter-web
间接引入
接下来我将使用idea进行创建项目:
一、新建项目
- 新建项目;
- 选择生成器:“Spring Boot”;
- 点击【服务器URL】后面的齿轮,把生成项目的地址换成阿里云,因为spring官方已经不支持Java8生成SpringBoot项目;
https://start.aliyun.com
- 填写好相关信息,点击下一步:
- 项目名称
- 项目代码位置(项目名不用填写)
- 选择Java语言
- 选择Maven管理依赖
- 组:一般是域名倒置(不能有空格)
- 工件:项目名(不能有空格)
- 软件包名称:一般由[组]和[工件]自动生成
- JDK:本地JDK8地址
- Java:8(没有8,需要修改上一步的服务器URL)
- 打包:Jar
- 选择依赖,点击创建:
- 选择Spring Boot版本:2.6.13(
- 这个版本集成logback1.2.11,足够我们使用
- 选中:Developer Tools => Lombok
- 选中:Web => Spring Web
- 选择Spring Boot版本:2.6.13(
- 项目创建好之后,删除自动生成的或不必要的文件,还原一个干净的项目结构
- 删除 src\main\java\ *** 下的整个web目录
- 删除 static\index.html
- 修改 application.properties 重命名为 application.yml
- 推荐使用yaml文件
- 修改 logback.xml 重命名为 logback-spring.xml(
- 因为后面会在这个文件中使用spring相关配置,重命名后将支持spring相关配置
- 删除 test目录下的整个java目录,后续需要再新建
- 删除 .gitignore,需要使用git时再新增
- 删除 HELP.md,需要项目帮助文档时再新增
二、pom.xml文件
新的pom.xml文件内容:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!-- 项目基本信息 -->
<groupId>com.tqazy</groupId>
<artifactId>demo</artifactId>
<version>v1.0</version>
<name>demo</name>
<description>demo</description>
<!-- 项目属性配置 -->
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.6.13</spring-boot.version>
<maven-compiler-plugin.version>3.6.0</maven-compiler-plugin.version>
</properties>
<!-- 项目依赖配置 -->
<dependencies>
<!-- Spring Boot Web 启动器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<!-- Lombok 依赖 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
<optional>true</optional>
</dependency>
</dependencies>
<!-- 依赖管理 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<!-- 项目构建配置 -->
<build>
<!-- 资源配置 -->
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<!-- 插件配置 -->
<plugins>
<!-- Maven 编译器插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<!-- Spring Boot Maven 插件 -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
<configuration>
<mainClass>com.tqazy.demo.DemoApplication</mainClass>
</configuration>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<!-- 项目环境配置 -->
<profiles>
<!-- 测试环境配置 -->
<profile>
<id>test</id>
<properties>
<profiles.active>test</profiles.active>
</properties>
<activation>
<!-- 默认环境 -->
<activeByDefault>true</activeByDefault>
</activation>
</profile>
<!-- 生产环境配置 -->
<profile>
<id>prod</id>
<properties>
<profiles.active>prod</profiles.active>
</properties>
</profile>
</profiles>
</project>
代码解析:
- 项目基本信息: 需要修改成你的配置;
- 项目属性配置: 配置了
- Java版本:1.8
- Maven 读取项目源文件的编码格式:UTF-8
- Maven 生成报告时的输出编码格式:UTF-8
- Spring Boot 版本:2.6.13
- Maven 插件版本:3.6.0
- 项目依赖配置: 配置了:
spring-boot-starter-web
:已提供Restful API风格的的依赖,同时集成了logback的依赖lombok
:简化编码,设置<scope>provided</scope>
,意为仅在编译阶段生效
- 依赖管理:
spring-boot-dependencies
:确保依赖版本的一致性和兼容性
- 项目构建配置:
- 资源配置: 此处的资源配置,是为了后面在【application.yml】文件中
@profiles.active@
配置内容能读取到【pom.xml】中的<profiles>
配置 - 插件配置: 为了配置 Maven插件 和Spring Boot Maven插件的版本和相关配置
mainClass
:配置这个项目的主文件地址,即被@SpringBootApplication
修饰的程序主入口
- 资源配置: 此处的资源配置,是为了后面在【application.yml】文件中
- 项目环境配置: 配置了两个环境:test和prod,如果有需要可以再新增一个dev。此处配置是为了方便在构建项目时,可以通过构建指令动态选择不同环境的配置进行打包。具体如何选择,在下面会讲解。
三、项目配置文件
3.1 application.yml(主配置文件)
spring:
profiles:
active: @profiles.active@
代码解析:
此处只对不同环境文件动态读取进行了配置。
后续如果有在不同环境下也需要相同配置的信息都可以在主配置文件中进行配置。
3.2 application-test.yml(分环境文件)
server:
port: 8081
# 日志配置
logging:
file:
path: D:\code\test\logs\test
level:
com.tqazy: debug
org.springframework: WARN
org.spring.springboot.dao: debug
代码解析:
- 配置项目端口:8081
- 配置日志信息:
- 日志输出地址
- 不同包下输出的日志等级
- 本地环境、测试环境可以比较宽松;
- 建议生产环境配置等级高一些,尽量不使用debug,不然生成的日志量会非常大且敏感数据打印成日志不安全
3.3 application-prod.yml(分环境文件)
server:
port: 8082
# 日志配置
logging:
file:
path: D:\code\test\logs\prod
level:
com.tqazy: info
org.springframework: WARN
org.spring.springboot.dao: info
代码解析:
- 配置项目端口:8082
- 配置日志信息:
- 日志输出地址
- 不同包下输出的日志等级
3.4 logback-spring.xml(日志配置文件)
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 从 Spring Boot 配置文件中读取日志路径 -->
<springProperty scope="context" name="log.path" source="logging.file.path"/>
<!-- 规定日志格式,
格式:月日 时分秒毫秒 [线程名称] 日志级别 记录器名称(类名) - [调用方法名,文件中行号] - 日志消息内容
示例:03-20 15:30:00.456 [http-nio-8080-exec-1] INFO com.example.controller.UserController - [getUser,50] - User retrieved successfully
-->
<property name="log.pattern" value="%d{MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{30} - [%method,%line] - %msg%n"/>
<!-- 控制台输出 -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<charset>UTF-8</charset>
<pattern>${log.pattern}</pattern>
</encoder>
</appender>
<!-- 系统日志输出 -->
<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/sys-info.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- 按天回滚 daily -->
<fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
<!--每个日志文件最大100MB-->
<maxFileSize>100MB</maxFileSize>
<!-- 总日志文件大小上限(可选) -->
<totalSizeCap>1GB</totalSizeCap>
</rollingPolicy>
<encoder>
<charset>UTF-8</charset>
<pattern>${log.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/sys-error.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- 按天回滚 daily -->
<fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
<!--每个日志文件最大100MB-->
<maxFileSize>100MB</maxFileSize>
<!-- 总日志文件大小上限(可选) -->
<totalSizeCap>1GB</totalSizeCap>
</rollingPolicy>
<encoder>
<charset>UTF-8</charset>
<pattern>${log.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 用户访问日志输出 -->
<appender name="sys-user" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/sys-user.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- 按天回滚 daily -->
<fileNamePattern>${log.path}/sys-user.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
<!--每个日志文件最大100MB-->
<maxFileSize>100MB</maxFileSize>
<!-- 总日志文件大小上限(可选) -->
<totalSizeCap>1GB</totalSizeCap>
</rollingPolicy>
<encoder>
<charset>UTF-8</charset>
<pattern>${log.pattern}</pattern>
</encoder>
</appender>
<!-- com.tqazy 包及其子包下的日志记录器的日志级别为 INFO -->
<logger name="com.tqazy" level="info"/>
<!-- 定义了全局的日志级别和默认的日志输出行为 -->
<!-- 设定级别:info,处理 INFO、WARN 和 ERROR 级别 -->
<root level="info">
<!-- 经过根日志记录器过滤后的日志消息能够输出到控制台 -->
<appender-ref ref="console"/>
<!-- 把 INFO 级别的日志输出到文件 sys-info.log 中 -->
<appender-ref ref="file_info"/>
<!-- 把 ERROR 级别的日志输出到文件 sys-info.log 中 -->
<appender-ref ref="file_error"/>
<!-- 把系统用户操作日志输出到文件 sys-user.log 中 -->
<appender-ref ref="sys-user"/>
</root>
</configuration>
代码解析:
- 从 Spring Boot 分环境配置文件中读取日志路径,实现不同环境的日志文件输出地址不同。有效实现区分windows系统和linux系统的目录地址结构不同的特性。
- 统一日志格式
- 日志滚动压缩策略:按时间+大小混合滚动
- 多维度日志输出:控制台、系统日志拆分(INFO、ERROR)、用户操作日志
- 日志级别控制
- 配置日志输出编码:UTF-8
四、编写业务代码
至此,项目的基本配置已经完成,接下来开始编写简单的业务代码,作为演示。
4.1 User.java
在src\main\java\com\tqazy\demo*\web\下新建domain.pojo.User.java。
注意:web目录要与主文件同级
import lombok.Data;
@Data
public class User {
private String name;
private String address;
}
使用Lombok之后,项目代码会变得很简洁。
4.2 UserController.java
为了演示方便,中间的逻辑层和持久层我就忽略了,这个不是本章重点。
在src\main\java\com\tqazy\demo*\web\下新建controller.UserController.java。
import com.tqazy.demo.demos.web.domain.pojo.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/user")
public class UserController {
// 创建一个 Logger 实例,使用当前类的类名作为日志记录器的名称
private static final Logger logger = LoggerFactory.getLogger(UserController.class);
// http://127.0.0.1:8080/hello?name=lisi
@RequestMapping("/hello")
@ResponseBody
public String hello(@RequestParam(name = "name", defaultValue = "unknown user") String name) {
logger.info("hello {}", name);
return "Hello " + name;
}
// http://127.0.0.1:8080/user
@RequestMapping("/getUser")
@ResponseBody
public User getUser() {
User user = new User();
user.setName("鲁班七号");
user.setAddress("王者峡谷");
System.out.println(user);
logger.error(user.toString());
return user;
}
}
为了演示,我将第一个方法的日志使用INFO级别打印,将第二个方法的日志使用ERROR级别打印。
五、不同环境运行
项目中我们配置了测试环境和生产环境的文件,接下来我们通过maven命令,动态选择不同环境文件进行打包运行。
提前声明:
本项目地址:D:\code\test\demo
生成的jar包名:demo-v1.0.jar
本项目日志输出地址:D:\code\test\logs
下面的命令你需要更换成你自己的项目地址
5.1 相关命令
具体使用在下文中实践
# 使用maven清除并重新打包项目,使用test环境文件
mvn clean package -Ptest
# 使用maven清除并重新打包项目,使用prod环境文件
mvn clean package -Pprod
# 使用java命令运行jar包,但是窗口关闭,程序停止运行
java -jar D:\code\test\demo\target\demo-v1.0.jar
# 使用javaw命令运行jar包,程序后台运行
javaw -jar D:\code\test\demo\target\demo-v1.0.jar
# 查询jar包运行的进程PID
jps -l | findstr demo-v1.0.jar
# 终止jar包运行的进程(强制终止)
taskkill /f /pid '实际的PID'
# 终止jar包运行的进程(优雅终止)
taskkill /pid '实际的PID'
5.2 测试环境运行
- 按下键盘的[Win]键,输入“Windows PowerShell”并选择
- 在命令提示框中输入:
cd D:\code\test\demo mvn clean package -Ptest
看见
BUILD SUCCESS
字样,说明构建成功。 - 先进行java方式运行,查看是否报错:
java -jar D:\code\test\demo\target\demo-v1.0.jar
我们看到,运行成功,端口显示8081,是测试环境配置的端口。
但是使用java运行,一旦关闭当前窗口,那么程序将停止运行。
下面我们将在java运行不报错的情况下进行后台运行。
此时我们先按下Ctrl + C
键停止运行。 - 我们使用
javaw
命令可以实现后台运行
在命令窗口中执行如下命令,javaw
是不会产生窗口输出的。javaw -jar D:\code\test\demo\target\demo-v1.0.jar
- 验证运行结果
我们该如何验证运行结果呢?
打开浏览器,我们尝试访问程序中的接口:http://localhost:8081/user/getUser
http://localhost:8081/user/hello?name=lisi
此时已生成日志:
sys-error.log内容:
03-20 11:15:42.402 [http-nio-8081-exec-1] ERROR c.t.d.d.w.c.UserController – [getUser,39] – User(name=鲁班七号, address=王者峡谷)
sys-info.log内容:(包含启动日志和接口访问日志)
03-20 11:15:36.016 [main] INFO com.tqazy.demo.DemoApplication – [logStarting,55] – Starting DemoApplication using Java 1.8.0_441 on DESKTOP-GB483S6 with PID 11268 (D:\code\test\demo\target\demo-v1.0.jar started by Admin in D:\code\test\demo)
03-20 11:15:36.018 [main] INFO com.tqazy.demo.DemoApplication – [logStartupProfileInfo,651] – The following 1 profile is active: "test"
03-20 11:15:36.660 [main] INFO o.a.c.http11.Http11NioProtocol – [log,173] – Initializing ProtocolHandler ["http-nio-8081"]
03-20 11:15:36.661 [main] INFO o.a.c.core.StandardService – [log,173] – Starting service [Tomcat]
03-20 11:15:36.665 [main] INFO o.a.c.core.StandardEngine – [log,173] – Starting Servlet engine: [Apache Tomcat/9.0.68]
03-20 11:15:36.752 [main] INFO o.a.c.c.C.[.[localhost].[/] – [log,173] – Initializing Spring embedded WebApplicationContext
03-20 11:15:36.937 [main] INFO o.a.c.http11.Http11NioProtocol – [log,173] – Starting ProtocolHandler ["http-nio-8081"]
03-20 11:15:37.012 [main] INFO com.tqazy.demo.DemoApplication – [logStarted,61] – Started DemoApplication in 1.284 seconds (JVM running for 1.548)
03-20 11:15:42.387 [http-nio-8081-exec-1] INFO o.a.c.c.C.[.[localhost].[/] – [log,173] – Initializing Spring DispatcherServlet ‘dispatcherServlet’
03-20 11:17:17.050 [http-nio-8081-exec-4] INFO c.t.d.d.w.c.UserController – [hello,27] – hello lisi
- 停止服务:
jps -l | findstr demo-v1.0.jar
查出PID:
执行终止进程操作taskkill /pid 11268
5.3 生产环境运行
相关步骤和测试环境相同,只是最开始的打包命令有所区别。
注意:如果上面已经成功运行了一次jar,一定要停止运行后再尝试生产环境构建运行,因为正在运行的jar不能被 clean
删除。
在命令提示框中输入:
cd D:\code\test\demo mvn clean package -Pprod
看见
BUILD SUCCESS
字样,说明构建成功。其余命令可看测试环境命令
验证运行结果
我们该如何验证运行结果呢?
打开浏览器,我们尝试访问程序中的接口:http://localhost:8082/user/hello?name=zhangsan
http://localhost:8082/user/getUser
注意:测试环境和生产环境的端口是不同的。
此时已生成日志:
具体日志内容参考 测试环境 日志内容,除了传参不同,其余相同。
- 停止服务:
参考 测试环境 停止服务方式。
5.4 优雅(脚本)的方式启停
疯狂手写命令,还要手动查找PID太过麻烦,我们可以使用脚本代为执行。
在桌面新建两个文件并重命名(文件名可以自己改,但是后缀是 .bat
),然后右击使用记事本打开。
- build_and_run.bat
- stop_project.bat
注意:当文件内容编辑完成之后,不要直接保存。
需要点击左上角 [文件] => [另存为] => 保存的左边[编码,选择:ANSI] => 保存。
否则在控制台输出,中文会乱码。
5.4.1 启动脚本
@echo off
setlocal enabledelayedexpansion
rem 进入项目目录
echo 正在进入项目目录
cd /d D:\code\test\demo
rem 定义要查找的 JAR 包名称
set "JAR_NAME=demo-v1.0.jar"
rem 查找进程 ID
for /f "tokens=1" %%i in ('jps -l ^| findstr /i "%JAR_NAME%"') do (
set "PID=%%i"
)
rem 检查是否找到进程
if defined PID (
echo 错误:%JAR_NAME% 正在运行(PID: %PID%),请先停止后再构建!
pause
exit /b 1
)
rem 执行 Maven 打包命令
echo 正在执行 Maven 打包命令
call mvn clean package -Ptest >nul 2>&1
rem 检查 Maven 打包是否成功
if %errorlevel% neq 0 (
echo Maven 打包失败,请检查错误信息
pause
exit /b 1
)
echo Maven 打包完成
rem 启动 JAR 包
echo 正在启动 JAR 包
start javaw -jar D:\code\test\demo\target\demo-v1.0.jar
echo JAR 包启动完成
echo 脚本执行完毕,按任意键返回...
pause >nul
脚本解析:
- 进入项目地址
- 检查jar运行情况
- 如果jar在运行,返回错误信息
- 如果jar没在运行,继续执行
- 使用maven对项目进行构建
- 使用javaw启动jar包
替换内容(将脚本中的信息换成你自己的):
D:\code\test\demo
:项目地址demo-v1.0.jar
:jar包名称-Ptest
:运行环境,根据上面pom中配置的<profile>
的id,根据需要换成:-Pdev
、-Ptest
、-Pprod
。D:\code\test\demo\target\demo-v1.0.jar
:jar所在的绝对路径
5.4.2 终止脚本
@echo off
setlocal enabledelayedexpansion
rem 定义要查找的 JAR 包名称
set "JAR_NAME=demo-v1.0.jar"
rem 查找进程 ID
for /f "tokens=1" %%i in ('jps -l ^| findstr /i "%JAR_NAME%"') do (
set "PID=%%i"
echo 找到进程 PID: !PID!
)
rem 检查是否找到进程
if not defined PID (
echo 未找到 %JAR_NAME% 的运行进程。
pause
exit /b 0
)
rem 强制终止进程
echo 正在强制终止进程 %PID%...
taskkill /f /pid %PID%
rem 检查终止操作是否成功
if %errorlevel% equ 0 (
echo 进程 %PID% 已成功终止。
) else (
echo 终止进程失败,错误代码:%errorlevel%
)
pause
endlocal
不得不说,java 的配置文件是真的复杂。没做过springboot 的项目,之前安卓的配置文件,各种改直接头大。
其实还好,这已经算是少的了,如果分布式和微服务搞起来那才是真正的配置地狱。不过也有解决办法,搞配置中心就可以,而且使用SpringBoot这个脚手架,已经开始了约定大于配置了,这已经减少很多配置了,SpringMVC也是配置地狱。