背景

本文是《Java 后端从小白到大神》修仙系列之框架学习,Java框架之 SpringBoot 框架第二篇SpringBoot框架 可以说是微服务的基石,很多复杂的系统几乎都是通过微服务构造而来,若想详细学习请点击首篇博文开始,现在开始学习。

文章概览

  1. Spring Boot 配置绑定进阶
  2. Spring Boot 中的单元测试与集成测试
  3. Spring Boot 日志体系详解
  4. Spring Boot 集成数据库
  5. Spring Boot 集成 JPA

1. Spring Boot 配置绑定进阶:@Value 与 @ConfigurationProperties

一、@Value 注解

1
2
@Value("${my.app.name}")
private String appName;

适用于简单配置注入。

二、@ConfigurationProperties 使用

1
2
3
4
5
6
@ConfigurationProperties(prefix = "my.app")
@Component
public class AppProperties {
  private String name;
  private String version;
}

配合 application.yml

1
2
3
4
my:
  app:
    name: 博客系统
    version: 1.0.0

三、优缺点对比

注解类型 特点
@Value 简单、零依赖,适合单个字段
@ConfigurationProperties 批量注入、支持复杂结构配置

2. Spring Boot 中的单元测试与集成测试

一、依赖引入

默认已包含:spring-boot-starter-test

二、基础测试案例

1
2
3
4
5
6
7
@SpringBootTest
public class DemoApplicationTests {

  @Test
  void contextLoads() {
  }
}

三、测试 Controller

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
@WebMvcTest(UserController.class)
public class UserControllerTest {

  @Autowired
  private MockMvc mockMvc;

  @Test
  void testGetUser() throws Exception {
    mockMvc.perform(get("/api/users/1"))
      .andExpect(status().isOk());
  }
}

3. Spring Boot 日志体系详解(SLF4J + Logback)

一、默认日志实现

  • Spring Boot 默认使用 Logback
  • 提供统一的 SLF4J 接口层
flowchart TD
    subgraph "传统日志实现(无门面层)"
        A1[应用代码] -->|直接依赖| B1[Log4j API]
        B1 -->|绑定到| C1[Log4j实现]
        style A1 fill:#fff6cc,stroke:#ffbc52,stroke-width:2px
        style B1 fill:#fff6cc,stroke:#ffbc52,stroke-width:2px
        style C1 fill:#fff6cc,stroke:#ffbc52,stroke-width:2px
    end
    
    subgraph "使用SLF4J门面层"
        A2[应用代码] -->|依赖| B2[SLF4J API]
        B2 -->|运行时绑定| C2[Logback实现]
        B2 -.->|可选替换| C3[Log4j实现]
        B2 -.->|其他实现| C4[JUL/Log4j2等]
        style A2 fill:#d1f2eb,stroke:#58d68d,stroke-width:2px
        style B2 fill:#d1f2eb,stroke:#58d68d,stroke-width:2px
        style C2 fill:#d1f2eb,stroke:#58d68d,stroke-width:2px
        style C3 fill:#d1f2eb,stroke:#58d68d,stroke-width:2px
        style C4 fill:#d1f2eb,stroke:#58d68d,stroke-width:2px
    end
    
    classDef issue fill:#fde8e8,stroke:#e63946,stroke-width:2px,shape:note
    classDef advantage fill:#e8f5e9,stroke:#4caf50,stroke-width:2px,shape:note
    
    I1([问题:若切换日志实现,必须修改代码并重新编译]):::issue
    A1 -.-> I1
    
    A2 -.-> A([优势:无论底层使用何种实现,代码无需改动]):::advantage

二、配置日志级别

1
2
3
4
logging:
  level:
    root: INFO
    com.example.demo: DEBUG

三、自定义 logback 配置文件

resources/ 下添加 logback-spring.xml

四、常见日志最佳实践

  • @Slf4j 是 Lombok 提供的注解,作用是自动生成 SLF4J 日志对象
传统写法与有@Slf4j对比
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
传统写法 Lombok):
import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

public class MyService {
private static final Logger logger = LoggerFactory.getLogger(MyService.class);

public void doSomething() {
    logger.info("执行操作");
    }
}

使用 @Slf4j 
import lombok.extern.slf4j.Slf4j;

@Slf4j // 自动生成 log 对象
public class MyService {
    
    public void doSomething() {
        log.info("执行操作"); // 直接使用 log 对象
    }
}

添加 Lombok 依赖
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>
  • 日志分包配置、滚动日志保存

    日志分包配置 根据不同的包名或模块设置不同的日志级别和输出策略。例如:

    • 业务代码使用 INFO 级别
    • 第三方库使用 WARN 级别
    • 敏感操作(如登录)单独输出到审计日志

    滚动日志保存 按时间或大小自动分割日志文件,防止单个日志文件过大,便于管理和归档。例如:

    • 每天生成一个新日志文件
    • 单个文件超过 100MB 时自动分割
    • 保留最近 30 天的日志

下面是一个完整的 logback-spring.xml 配置示例,包含分包配置和滚动日志:

完整日志配置示例
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="30 seconds">
    <!-- 应用名称 -->
    <property name="APP_NAME" value="spring-boot-app" />
    
    <!-- 日志文件保存路径 -->
    <property name="LOG_PATH" value="${LOG_FILE:-${LOG_PATH:-${LOG_TEMP:-${java.io.tmpdir:-/tmp}}}/logs}" />
    
    <!-- 控制台输出 -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>
    
    <!-- 业务日志:按天滚动 -->
    <appender name="APP_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_PATH}/${APP_NAME}.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/${APP_NAME}.%d{yyyy-MM-dd}.log.gz</fileNamePattern>
            <maxHistory>30</maxHistory> <!-- 保留30天的历史日志 -->
            <totalSizeCap>1GB</totalSizeCap> <!-- 日志总大小上限 -->
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>
    
    <!-- SQL日志:按天滚动,单独输出 -->
    <appender name="SQL_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_PATH}/${APP_NAME}-sql.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/${APP_NAME}-sql.%d{yyyy-MM-dd}.log.gz</fileNamePattern>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>
    
    <!-- 审计日志:记录敏感操作,按天滚动 -->
    <appender name="AUDIT_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_PATH}/${APP_NAME}-audit.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/${APP_NAME}-audit.%d{yyyy-MM-dd}.log.gz</fileNamePattern>
            <maxHistory>90</maxHistory> <!-- 审计日志保留90天 -->
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>
    
    <!-- 根日志配置 -->
    <root level="INFO">
        <appender-ref ref="CONSOLE" />
        <appender-ref ref="APP_FILE" />
    </root>
    
    <!-- 分包配置 -->
    <logger name="org.springframework" level="WARN" /> <!-- Spring框架日志设为WARN -->
    <logger name="org.hibernate" level="WARN" /> <!-- Hibernate日志设为WARN -->
    <logger name="com.zaxxer.hikari" level="WARN" /> <!-- HikariCP连接池日志设为WARN -->
    
    <!-- SQL日志 -->
    <logger name="org.springframework.jdbc.core" level="DEBUG" additivity="false">
        <appender-ref ref="SQL_FILE" />
    </logger>
    <logger name="org.hibernate.SQL" level="DEBUG" additivity="false">
        <appender-ref ref="SQL_FILE" />
    </logger>
    <logger name="org.hibernate.type.descriptor.sql" level="TRACE" additivity="false">
        <appender-ref ref="SQL_FILE" />
    </logger>
    
    <!-- 审计日志 -->
    <logger name="com.example.audit" level="INFO" additivity="false">
        <appender-ref ref="AUDIT_FILE" />
    </logger>
</configuration>

  • 配置说明

    日志文件结构

    • spring-boot-app.log:主业务日志
    • spring-boot-app-sql.log:SQL执行日志
    • spring-boot-app-audit.log:审计日志(敏感操作)

    滚动策略

    • 所有日志按天滚动(TimeBasedRollingPolicy
    • 历史日志自动压缩为.gz格式
    • 默认保留30天日志(审计日志保留90天)
    • 总日志大小限制为1GB

    分包配置

    • org.springframework:框架日志设为WARN级别
    • org.hibernate.SQL:SQL语句设为DEBUG级别
    • com.example.audit:审计日志单独输出

    动态更新

    • scan="true" 允许在运行时修改配置文件,30秒后自动生效
  • 应用建议

    在 Spring Boot 中使用: 将 logback-spring.xml 放在 src/main/resources 目录下,Spring Boot 会自动加载。

    调整日志级别

    • 开发环境:设为 DEBUG 便于调试
    • 测试环境:设为 INFO 平衡信息量和性能
    • 生产环境:设为 WARNERROR 减少磁盘IO

4. Spring Boot 集成数据库:JDBC 与 DataSource 配置

一、引入依赖

1
2
3
4
<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
</dependency>

二、配置数据源

1
2
3
4
5
6
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/test
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver

三、使用 JdbcTemplate

1
2
3
4
5
6
@Autowired
private JdbcTemplate jdbcTemplate;

public List<User> findAll() {
  return jdbcTemplate.query("select * from user", new BeanPropertyRowMapper<>(User.class));
}

5. Spring Boot 集成 JPA:快速实现 CRUD 操作

一、添加依赖

1
2
3
4
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

二、实体类定义

1
2
3
4
5
6
7
@Entity
public class User {
  @Id
  @GeneratedValue
  private Long id;
  private String name;
}

三、创建 Repository 接口

1
2
public interface UserRepository extends JpaRepository<User, Long> {
}

四、使用 Repository

1
2
3
4
5
6
@Autowired
private UserRepository userRepository;

public void createUser() {
  userRepository.save(new User(null, "李四"));
}

总结

SpringBoot 框架是微服务生态的基石,必知必会。