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/

相关文章
|
2月前
|
XML Java Maven
Spring Boot学习知识点大全(一)
教程来源 https://app-a87ujc988w01.appmiaoda.com/ Spring Boot 是 Spring 家族中革命性框架,秉持“约定优于配置”理念,通过自动配置、起步依赖、嵌入式服务器等特性,大幅简化企业级 Java 应用开发。本文系统梳理其核心概念、注解、多环境配置与最佳实践,助初学者快速入门,为进阶开发者提供深度参考。
|
5月前
|
SQL 存储 关系型数据库
使用MySQL中的WITH AS子句进行高效数据库查询实践
虑到查询优化,它们是管理复杂SQL语句的有力工具。由于CTE在查询计划中的处理方式可能因MySQL的不同版本而异,建议在特定环境中测试和分析查询性能以确保达到最佳效果。
526 3
|
5月前
|
存储 关系型数据库 调度
微服务原理篇(XXLJOB-幂等-MySQL)
本课程深入讲解微服务核心组件XXL-JOB任务调度原理,涵盖其架构、分布式任务处理、幂等性设计及MySQL存储引擎、索引机制、SQL优化与分库分表策略,全面提升系统性能与可靠性。
|
8月前
|
JSON API 数据安全/隐私保护
天猫商品详情API 数据解析(附代码)
天猫商品详情API(tmall.item_get)通过商品ID获取标题、价格、库存、图片、SKU等信息,支持高并发,适用于电商数据分析与价格策略优化。接口采用HTTP请求与JSON返回,需AppKey+签名认证。附Python请求示例,含签名验证与异常处理。前往体验:c0b.cc/R4rbK2
|
Java 数据格式 微服务
2024最新首发,全网最全 Spring Boot 学习宝典(附思维导图)
📚 《滚雪球学Spring Boot》是由CSDN博主bug菌创作的全面Spring Boot教程。作者是全栈开发专家,在多个技术社区如CSDN、掘金、InfoQ、51CTO等担任博客专家,并拥有超过20万的全网粉丝。该教程分为入门篇和进阶篇,每篇包含详细的教学步骤,涵盖Spring Boot的基础和高级主题。
2870 4
2024最新首发,全网最全 Spring Boot 学习宝典(附思维导图)
|
监控 Java 数据库
SpringBoot 实现动态切换数据源:优雅之道
在微服务架构和复杂的应用系统中,随着业务规模的扩大,单个数据源往往无法满足多样化的数据访问需求。这时,动态切换数据源成为了一个重要的技术需求,它允许应用程序在运行时根据业务逻辑或用户请求,灵活地选择不同的数据源进行操作。在Spring Boot框架中,实现数据源的动态切换既是一项挑战,也是一项展现技术优雅性的机会。以下,我们将深入探讨如何在Spring Boot中实现数据源的动态切换,并追求实现的优雅性。
989 1
|
Web App开发 资源调度 JavaScript
node.js详解
node.js详解
493 0
|
算法 搜索推荐 Java
基于SpringBoot+协同过滤算法的家政服务平台设计和实现(源码+LW+调试文档+讲解等)
基于SpringBoot+协同过滤算法的家政服务平台设计和实现(源码+LW+调试文档+讲解等)
Collectors.toMap空指针问题
Collectors.toMap空指针问题
506 1
|
SQL 测试技术 数据库
`SELECT ... FOR UPDATE` 语句是如何工作的?
`SELECT ... FOR UPDATE` 语句是如何工作的?
1238 0

热门文章

最新文章