Spring Boot学习知识点大全(三)

简介: 教程来源 https://app-a6nw7st4g741.appmiaoda.com/ 系统梳理Spring Boot核心实践:涵盖日志分级配置与异步输出、单元/集成测试、Actuator监控与自定义指标、Docker/K8s部署、Spring Boot 3.x Jakarta迁移及虚拟线程等新特性,助力构建高可用生产级应用。

七、日志管理

7.1 日志配置

yaml
# application.yml
logging:
  level:
    root: INFO
    com.example: DEBUG
    org.springframework.web: INFO
    org.hibernate: WARN
    org.mybatis: DEBUG
  file:
    name: logs/application.log
    path: logs/
    max-size: 10MB
    max-history: 30
  pattern:
    console: '%d{yyyy-MM-dd HH:mm:ss} - %msg%n'
    file: '%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n'
    dateformat: yyyy-MM-dd HH:mm:ss.SSS
xml
<!-- logback-spring.xml -->
<configuration>
    <property name="LOG_PATH" value="${LOG_PATH:-${user.home}/logs}"/>
    <property name="LOG_PATTERN" 
              value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"/>

    <!-- 控制台输出 -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>${LOG_PATTERN}</pattern>
        </encoder>
    </appender>

    <!-- 文件输出 -->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_PATH}/app.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/app.%d{yyyy-MM-dd}.log.gz</fileNamePattern>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>${LOG_PATTERN}</pattern>
        </encoder>
    </appender>

    <!-- 错误日志单独文件 -->
    <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_PATH}/error.log</file>
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>ERROR</level>
        </filter>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/error.%d{yyyy-MM-dd}.log.gz</fileNamePattern>
        </rollingPolicy>
        <encoder>
            <pattern>${LOG_PATTERN}</pattern>
        </encoder>
    </appender>

    <!-- 异步日志 -->
    <appender name="ASYNC_FILE" class="ch.qos.logback.classic.AsyncAppender">
        <queueSize>512</queueSize>
        <discardingThreshold>0</discardingThreshold>
        <appender-ref ref="FILE"/>
    </appender>

    <root level="INFO">
        <appender-ref ref="CONSOLE"/>
        <appender-ref ref="ASYNC_FILE"/>
        <appender-ref ref="ERROR_FILE"/>
    </root>

    <logger name="com.example" level="DEBUG"/>
</configuration>

7.2 日志使用

java
@RestController
@Slf4j
public class UserController {

    @GetMapping("/users/{id}")
    public User getUser(@PathVariable Long id) {
        log.debug("查询用户: id={}", id);

        User user = userService.findById(id);

        if (user == null) {
            log.warn("用户不存在: id={}", id);
        } else {
            log.info("用户信息: {}", user);
        }

        return user;
    }

    @PostMapping("/users")
    public User createUser(@RequestBody User user) {
        log.info("创建用户: {}", user);

        try {
            User result = userService.create(user);
            log.info("创建成功: id={}", result.getId());
            return result;
        } catch (Exception e) {
            log.error("创建用户失败: {}", user, e);
            throw e;
        }
    }
}

八、测试

8.1 单元测试

@SpringBootTest
@AutoConfigureMockMvc
@Transactional
@Rollback
public class UserControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Autowired
    private ObjectMapper objectMapper;

    @Test
    public void testGetUser() throws Exception {
        mockMvc.perform(get("/api/users/1")
                .contentType(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200))
                .andExpect(jsonPath("$.data.id").value(1))
                .andDo(print());
    }

    @Test
    public void testCreateUser() throws Exception {
        User user = new User("张三", 25);

        mockMvc.perform(post("/api/users")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(user)))
                .andExpect(status().isCreated())
                .andExpect(jsonPath("$.data.username").value("张三"));
    }

    @Test
    public void testValidationError() throws Exception {
        User user = new User("", -1);

        mockMvc.perform(post("/api/users")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(user)))
                .andExpect(status().isBadRequest())
                .andExpect(jsonPath("$.code").value(400));
    }

    @Test
    public void testPageQuery() throws Exception {
        mockMvc.perform(get("/api/users")
                .param("page", "1")
                .param("size", "10")
                .param("keyword", "张三"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.data.records").isArray())
                .andExpect(jsonPath("$.data.total").isNumber());
    }
}

8.2 集成测试

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Testcontainers
public class IntegrationTest {

    @LocalServerPort
    private int port;

    @Container
    static MySQLContainer<?> mysql = new MySQLContainer<>("mysql:8.0")
            .withDatabaseName("testdb")
            .withUsername("test")
            .withPassword("test");

    @Container
    static GenericContainer<?> redis = new GenericContainer<>("redis:7")
            .withExposedPorts(6379);

    @Test
    public void testDatabaseConnection() {
        assertTrue(mysql.isRunning());
        assertTrue(redis.isRunning());
    }

    @Test
    public void testApiCall() {
        RestTemplate restTemplate = new RestTemplate();
        String url = "http://localhost:" + port + "/api/health";
        String response = restTemplate.getForObject(url, String.class);
        assertNotNull(response);
    }
}

九、生产就绪特性

9.1 Actuator 监控

yaml
# application.yml
management:
  server:
    port: 8081
    address: 127.0.0.1
  endpoints:
    web:
      exposure:
        include: health,info,metrics,env,beans,mappings,threaddump,heapdump
      base-path: /actuator
    jmx:
      exposure:
        include: '*'
  endpoint:
    health:
      show-details: always
      show-components: always
    info:
      git:
        mode: full
    metrics:
      enabled: true
    prometheus:
      enabled: true
  metrics:
    export:
      prometheus:
        enabled: true
    tags:
      application: ${spring.application.name}
java
// 自定义健康检查
@Component
public class CustomHealthIndicator implements HealthIndicator {

    @Autowired
    private DataSource dataSource;

    @Override
    public Health health() {
        try {
            dataSource.getConnection().isValid(5);
            return Health.up()
                .withDetail("database", "connected")
                .withDetail("timestamp", System.currentTimeMillis())
                .build();
        } catch (Exception e) {
            return Health.down()
                .withDetail("error", e.getMessage())
                .build();
        }
    }
}

// 自定义信息端点
@Component
public class CustomInfoContributor implements InfoContributor {

    @Override
    public void contribute(Builder builder) {
        builder.withDetail("app", Map.of(
            "name", "Spring Boot Demo",
            "version", "1.0.0",
            "description", "示例应用"
        ));

        builder.withDetail("build", Map.of(
            "time", Instant.now().toString(),
            "user", System.getProperty("user.name")
        ));
    }
}

// 自定义度量指标
@Component
public class CustomMetrics {

    @Autowired
    private MeterRegistry meterRegistry;

    public void recordUserLogin(String username) {
        meterRegistry.counter("user.login.total", "username", username).increment();
    }

    public void recordApiCall(String api, long duration) {
        meterRegistry.timer("api.call.duration", "api", api)
            .record(duration, TimeUnit.MILLISECONDS);
    }
}

9.2 性能监控

@Aspect
@Component
public class PerformanceAspect {

    private static final Logger log = LoggerFactory.getLogger(PerformanceAspect.class);

    @Around("@annotation(com.example.annotation.Monitor)")
    public Object monitor(ProceedingJoinPoint pjp) throws Throwable {
        String className = pjp.getTarget().getClass().getSimpleName();
        String methodName = pjp.getSignature().getName();

        long start = System.currentTimeMillis();
        try {
            Object result = pjp.proceed();
            long elapsed = System.currentTimeMillis() - start;

            if (elapsed > 1000) {
                log.warn("慢方法: {}.{} - 耗时: {}ms", className, methodName, elapsed);
            } else {
                log.debug("{}.{} - 耗时: {}ms", className, methodName, elapsed);
            }

            return result;
        } catch (Exception e) {
            long elapsed = System.currentTimeMillis() - start;
            log.error("{}.{} 执行异常, 耗时: {}ms", className, methodName, elapsed, e);
            throw e;
        }
    }
}

十、部署与运维

10.1 打包与部署

xml
<!-- Maven 打包配置 -->
<build>
    <finalName>app</finalName>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
                <executable>true</executable>
                <mainClass>com.example.Application</mainClass>
            </configuration>
        </plugin>
    </plugins>
</build>
bash
# 打包
mvn clean package

# 启动
java -jar target/app.jar

# 指定环境
java -jar target/app.jar --spring.profiles.active=prod

# 指定内存
java -Xms512m -Xmx1024m -jar target/app.jar

# 后台运行
nohup java -jar target/app.jar > app.log 2>&1 &

# 使用 Systemd 管理
# /etc/systemd/system/app.service
[Unit]
Description=Spring Boot App
After=network.target

[Service]
Type=simple
User=appuser
WorkingDirectory=/opt/app
ExecStart=/usr/bin/java -jar /opt/app/app.jar
Restart=on-failure
RestartSec=10

[Install]
WantedBy=multi-user.target

10.2 Docker 部署

dockerfile
# Dockerfile
FROM openjdk:17-jdk-slim AS builder
WORKDIR /app
COPY . .
RUN ./mvnw clean package -DskipTests

FROM openjdk:17-jre-slim
WORKDIR /app
COPY --from=builder /app/target/app.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
yaml
# docker-compose.yml
version: '3.8'
services:
  app:
    build: .
    ports:
      - "8080:8080"
    environment:
      - SPRING_PROFILES_ACTIVE=prod
      - SPRING_DATASOURCE_URL=jdbc:mysql://mysql:3306/mydb
    depends_on:
      - mysql
      - redis
    networks:
      - app-network

  mysql:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: 123456
      MYSQL_DATABASE: mydb
    volumes:
      - mysql-data:/var/lib/mysql
    ports:
      - "3306:3306"
    networks:
      - app-network

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    networks:
      - app-network

volumes:
  mysql-data:

networks:
  app-network:
    driver: bridge

10.3 Kubernetes 部署

yaml
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: spring-boot-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: spring-boot-app
  template:
    metadata:
      labels:
        app: spring-boot-app
    spec:
      containers:
      - name: app
        image: myregistry/spring-boot-app:latest
        ports:
        - containerPort: 8080
        env:
        - name: SPRING_PROFILES_ACTIVE
          value: "k8s"
        - name: SPRING_DATASOURCE_URL
          value: "jdbc:mysql://mysql-service:3306/mydb"
        resources:
          requests:
            memory: "512Mi"
            cpu: "250m"
          limits:
            memory: "1Gi"
            cpu: "500m"
        livenessProbe:
          httpGet:
            path: /actuator/health/liveness
            port: 8080
          initialDelaySeconds: 60
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /actuator/health/readiness
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 5

---
apiVersion: v1
kind: Service
metadata:
  name: spring-boot-app-service
spec:
  selector:
    app: spring-boot-app
  ports:
  - port: 80
    targetPort: 8080
  type: LoadBalancer

---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: spring-boot-app-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: spring-boot-app
  minReplicas: 2
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 80

十一、Spring Boot 新特性

11.1 Spring Boot 3.x 新特性

// Jakarta EE 迁移(javax → jakarta)
import jakarta.persistence.Entity;
import jakarta.persistence.Table;

// GraalVM 原生镜像支持
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

// 构建原生镜像
// mvn -Pnative native:compile

// 问题详细(Problem Details)支持
@RestController
public class ProblemDetailsController {

    @GetMapping("/error")
    public ProblemDetail handleError() {
        ProblemDetail problem = ProblemDetail.forStatus(HttpStatus.NOT_FOUND);
        problem.setTitle("Resource Not Found");
        problem.setDetail("The requested resource was not found");
        problem.setProperty("timestamp", Instant.now());
        return problem;
    }
}

11.2 Spring Boot 3.2 新特性

// 虚拟线程支持
@Configuration
public class VirtualThreadConfig {

    @Bean
    public TomcatProtocolHandlerCustomizer<?> protocolHandlerCustomizer() {
        return protocolHandler -> {
            protocolHandler.setExecutor(Executors.newVirtualThreadPerTaskExecutor());
        };
    }
}

// 或通过配置
spring.threads.virtual.enabled=true

// 新的 RestClient
@Configuration
public class RestClientConfig {

    @Bean
    public RestClient restClient(RestClient.Builder builder) {
        return builder
            .baseUrl("https://api.example.com")
            .defaultHeader("Accept", "application/json")
            .build();
    }
}

Spring Boot 作为 Java 生态中最受欢迎的框架,以其简洁的配置、强大的功能和出色的开发体验,极大地提升了开发效率。本文系统性地梳理了 Spring Boot 的核心知识点,从基础入门到高级特性,从数据访问到生产部署,帮助开发者建立完整的知识体系。
来源:
https://app-a6nw7st4g741.appmiaoda.com/

相关文章
|
5天前
|
人工智能 JSON 机器人
让龙虾成为你的“公众号分身” | 阿里云服务器玩Openclaw
本文带你零成本玩转OpenClaw:学生认证白嫖6个月阿里云服务器,手把手配置飞书机器人、接入免费/高性价比AI模型(NVIDIA/通义),并打造微信公众号“全自动分身”——实时抓热榜、AI选题拆解、一键发布草稿,5分钟完成热点→文章全流程!
10766 66
让龙虾成为你的“公众号分身” | 阿里云服务器玩Openclaw
|
5天前
|
人工智能 IDE API
2026年国内 Codex 安装教程和使用教程:GPT-5.4 完整指南
Codex已进化为AI编程智能体,不仅能补全代码,更能理解项目、自动重构、执行任务。本文详解国内安装、GPT-5.4接入、cc-switch中转配置及实战开发流程,助你从零掌握“描述需求→AI实现”的新一代工程范式。(239字)
3296 128
|
1天前
|
人工智能 Kubernetes 供应链
深度解析:LiteLLM 供应链投毒事件——TeamPCP 三阶段后门全链路分析
阿里云云安全中心和云防火墙已在第一时间上线相关检测与拦截策略!
1226 5
|
2天前
|
人工智能 自然语言处理 供应链
【最新】阿里云ClawHub Skill扫描:3万个AI Agent技能中的安全度量
阿里云扫描3万+AI Skill,发现AI检测引擎可识别80%+威胁,远高于传统引擎。
1213 1
|
11天前
|
人工智能 JavaScript API
解放双手!OpenClaw Agent Browser全攻略(阿里云+本地部署+免费API+网页自动化场景落地)
“让AI聊聊天、写代码不难,难的是让它自己打开网页、填表单、查数据”——2026年,无数OpenClaw用户被这个痛点困扰。参考文章直击核心:当AI只能“纸上谈兵”,无法实际操控浏览器,就永远成不了真正的“数字员工”。而Agent Browser技能的出现,彻底打破了这一壁垒——它给OpenClaw装上“上网的手和眼睛”,让AI能像真人一样打开网页、点击按钮、填写表单、提取数据,24小时不间断完成网页自动化任务。
2589 6