Skip to content
第 84 / 250 章后端⏱ 10 分钟阅读

第 84 章:项目初始化与基础配置

学习目标

  • 搭建 TaskFlow 项目的 Maven 多模块结构
  • 配置 application.yml 多环境
  • 集成所有基础设施依赖

一、项目结构

二、父 POM

xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.0</version>
        <relativePath/>
    </parent>

    <groupId>com.taskflow</groupId>
    <artifactId>taskflow-parent</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>pom</packaging>
    <name>taskflow-parent</name>

    <modules>
        <module>taskflow-common</module>
        <module>taskflow-system</module>
        <module>taskflow-generator</module>
        <module>taskflow-application</module>
    </modules>

    <properties>
        <java.version>17</java.version>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>

        <mybatis-plus.version>3.5.5</mybatis-plus.version>
        <hutool.version>5.8.25</hutool.version>
        <jjwt.version>0.12.5</jjwt.version>
        <springdoc.version>2.6.0</springdoc.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.taskflow</groupId>
                <artifactId>taskflow-common</artifactId>
                <version>${project.version}</version>
            </dependency>
            <dependency>
                <groupId>com.taskflow</groupId>
                <artifactId>taskflow-system</artifactId>
                <version>${project.version}</version>
            </dependency>
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
                <version>${mybatis-plus.version}</version>
            </dependency>
            <dependency>
                <groupId>cn.hutool</groupId>
                <artifactId>hutool-all</artifactId>
                <version>${hutool.version}</version>
            </dependency>
            <dependency>
                <groupId>io.jsonwebtoken</groupId>
                <artifactId>jjwt-api</artifactId>
                <version>${jjwt.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <configuration>
                        <source>17</source>
                        <target>17</target>
                        <annotationProcessorPaths>
                            <path>
                                <groupId>org.projectlombok</groupId>
                                <artifactId>lombok</artifactId>
                            </path>
                        </annotationProcessorPaths>
                    </configuration>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>
</project>

三、application.yml(启动模块)

yaml
spring:
  profiles:
    active: dev
  application:
    name: taskflow
  jackson:
    time-zone: GMT+8
    date-format: yyyy-MM-dd HH:mm:ss

  # ============ 数据源 ============
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/${DB_NAME:taskflow}?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
    username: ${DB_USER:root}
    password: ${DB_PASSWORD:root123}
    hikari:
      pool-name: TaskflowHikariCP
      minimum-idle: 10
      maximum-pool-size: 20
      idle-timeout: 600000
      max-lifetime: 1740000
      connection-timeout: 30000
      leak-detection-threshold: 60000

  # ============ Redis ============
  data:
    redis:
      host: ${REDIS_HOST:localhost}
      port: ${REDIS_PORT:6379}
      password: ${REDIS_PASSWORD:}
      database: 0
      timeout: 3000
      lettuce:
        pool:
          max-active: 16
          max-idle: 8
          min-idle: 4

  # ============ RabbitMQ ============
  rabbitmq:
    host: ${RABBITMQ_HOST:localhost}
    port: ${RABBITMQ_PORT:5672}
    username: ${RABBITMQ_USER:admin}
    password: ${RABBITMQ_PASSWORD:admin123}
    virtual-host: /
    publisher-confirm-type: correlated
    publisher-returns: true
    listener:
      simple:
        acknowledge-mode: manual
        prefetch: 10
        concurrency: 4
        max-concurrency: 8

  # ============ 文件上传 ============
  servlet:
    multipart:
      max-file-size: 100MB
      max-request-size: 200MB

# ============ MyBatis-Plus ============
mybatis-plus:
  mapper-locations: classpath*:/mapper/**/*.xml
  type-aliases-package: com.taskflow.**.entity
  configuration:
    map-underscore-to-camel-case: true
    cache-enabled: false
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  global-config:
    banner: false
    db-config:
      id-type: assign_id
      logic-delete-field: deleted
      logic-delete-value: 1
      logic-not-delete-value: 0
      update-strategy: not_null

# ============ JWT ============
jwt:
  secret: ${JWT_SECRET:taskflow-secret-key-must-be-at-least-32-bytes-long}
  access-expire: 7200          # 2 小时
  refresh-expire: 2592000      # 30 天
  issuer: taskflow

# ============ Springdoc OpenAPI ============
springdoc:
  api-docs:
    path: /v3/api-docs
  swagger-ui:
    path: /swagger-ui.html
    operations-sorter: alpha
    tags-sorter: alpha

# ============ Actuator ============
management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics,prometheus
      base-path: /actuator
  endpoint:
    health:
      probes:
        enabled: true
      show-details: when-authorized
      group:
        liveness:
          include: livenessState
        readiness:
          include: readinessState,db,redis
  metrics:
    tags:
      application: ${spring.application.name}
  prometheus:
    metrics:
      export:
        enabled: true

# ============ 日志 ============
logging:
  level:
    root: INFO
    com.taskflow: DEBUG
    org.springframework.security: INFO
  file:
    path: ./logs

server:
  port: 8080
  shutdown: graceful
  compression:
    enabled: true
  tomcat:
    threads:
      max: 200

四、多环境配置

yaml
# application-dev.yml(开发)
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/taskflow?...

logging:
  level:
    com.taskflow: DEBUG

# application-test.yml(测试)
spring:
  datasource:
    url: jdbc:mysql://test-db:3306/taskflow_test?...

logging:
  level:
    com.taskflow: INFO

# application-prod.yml(生产)
spring:
  datasource:
    url: jdbc:mysql://${DB_HOST}:3306/taskflow?...
    hikari:
      maximum-pool-size: 50

springdoc:
  api-docs:
    enabled: false
  swagger-ui:
    enabled: false

logging:
  level:
    root: WARN
    com.taskflow: INFO

激活环境:

bash
java -jar taskflow.jar --spring.profiles.active=prod

五、启动类

java
package com.taskflow;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication(scanBasePackages = "com.taskflow")
@MapperScan("com.taskflow.**.mapper")
@EnableCaching
@EnableAsync
@EnableScheduling
public class TaskflowApplication {

    public static void main(String[] args) {
        SpringApplication.run(TaskflowApplication.class, args);
        System.out.println("""
            ╔═══════════════════════════════════════╗
            ║   TaskFlow started successfully       ║
            ║   Swagger: http://localhost:8080/swagger-ui.html  ║
            ╚═══════════════════════════════════════╝
            """);
    }
}

六、核心配置类

java
package com.taskflow.config;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MybatisPlusConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
        return interceptor;
    }
}
java
@Configuration
public class JacksonConfig {

    @Bean
    public Jackson2ObjectMapperBuilderCustomizer customizer() {
        return builder -> builder
            .serializerByType(Long.class, ToStringSerializer.instance)
            .serializerByType(Long.TYPE, ToStringSerializer.instance)
            .modules(new JavaTimeModule())
            .featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
    }
}
java
@Configuration
public class AsyncConfig implements AsyncConfigurer {

    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(8);
        executor.setMaxPoolSize(16);
        executor.setQueueCapacity(200);
        executor.setThreadNamePrefix("async-");
        executor.setRejectedExecutionHandler(
                new ThreadPoolExecutor.CallerRunsPolicy());
        executor.setWaitForTasksToCompleteOnShutdown(true);
        executor.setAwaitTerminationSeconds(60);
        executor.initialize();
        return executor;
    }
}

七、日志配置

xml
<!-- logback-spring.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <include resource="org/springframework/boot/logging/logback/defaults.xml"/>

    <springProperty scope="context" name="appName" source="spring.application.name"/>
    <property name="LOG_PATH" value="${LOG_PATH:-./logs}"/>

    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %X{traceId} %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_PATH}/${appName}.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/${appName}.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
            <maxFileSize>100MB</maxFileSize>
            <maxHistory>30</maxHistory>
            <totalSizeCap>20GB</totalSizeCap>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %X{traceId} %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <appender name="ASYNC_FILE" class="ch.qos.logback.classic.AsyncAppender">
        <appender-ref ref="FILE"/>
        <queueSize>2048</queueSize>
        <discardingThreshold>0</discardingThreshold>
    </appender>

    <logger name="com.taskflow" level="DEBUG"/>
    <logger name="org.springframework" level="INFO"/>
    <logger name="com.zaxxer.hikari" level="INFO"/>

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

八、Docker Compose 一键启动

yaml
# docker-compose.yml
version: '3.8'

services:
  mysql:
    image: mysql:8.0
    container_name: taskflow-mysql
    environment:
      MYSQL_ROOT_PASSWORD: root123
      MYSQL_DATABASE: taskflow
      TZ: Asia/Shanghai
    command:
      - --character-set-server=utf8mb4
      - --collation-server=utf8mb4_unicode_ci
    volumes:
      - ./sql/init.sql:/docker-entrypoint-initdb.d/init.sql:ro
      - mysql-data:/var/lib/mysql
    ports:
      - "3306:3306"
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-uroot", "-proot123"]
      interval: 10s
      timeout: 5s
      retries: 10
    networks:
      - taskflow-net

  redis:
    image: redis:7-alpine
    container_name: taskflow-redis
    command: redis-server --appendonly yes
    volumes:
      - redis-data:/data
    ports:
      - "6379:6379"
    networks:
      - taskflow-net

  rabbitmq:
    image: rabbitmq:3-management-alpine
    container_name: taskflow-rabbitmq
    environment:
      RABBITMQ_DEFAULT_USER: admin
      RABBITMQ_DEFAULT_PASS: admin123
    volumes:
      - rabbitmq-data:/var/lib/rabbitmq
    ports:
      - "5672:5672"
      - "15672:15672"
    networks:
      - taskflow-net

  app:
    build:
      context: .
      dockerfile: Dockerfile
    image: taskflow:1.0.0
    container_name: taskflow-app
    depends_on:
      mysql:
        condition: service_healthy
      redis:
        condition: service_started
      rabbitmq:
        condition: service_started
    environment:
      SPRING_PROFILES_ACTIVE: docker
      DB_HOST: mysql
      DB_PORT: 3306
      DB_USER: root
      DB_PASSWORD: root123
      DB_NAME: taskflow
      REDIS_HOST: redis
      REDIS_PORT: 6379
      RABBITMQ_HOST: rabbitmq
      RABBITMQ_USER: admin
      RABBITMQ_PASSWORD: admin123
    ports:
      - "8080:8080"
    networks:
      - taskflow-net

volumes:
  mysql-data:
  redis-data:
  rabbitmq-data:

networks:
  taskflow-net:
    driver: bridge
bash
# 一键启动
docker compose up -d

# 查看日志
docker compose logs -f app

# 访问
# 应用:http://localhost:8080
# Swagger UI:http://localhost:8080/swagger-ui.html
# RabbitMQ:http://localhost:15672(admin/admin123)

九、本章小结

要点关键
多模块parent / common / system / generator / application
配置application-{env}.yml 多环境
启动@SpringBootApplication + 各种 @Enable*
核心配置MyBatisPlus / Jackson / Async / Logback
一键启动Docker Compose(MySQL + Redis + MQ + App)
健康检查Actuator + K8s 探针

动手练习

练习 1:基础题

按本章结构创建 TaskFlow 多模块项目,能在 IDE 中正常导入并编译通过。

练习 2:进阶题

完成 Docker Compose 配置,启动 MySQL + Redis + RabbitMQ + 应用,浏览器访问 /swagger-ui.html 能看到空文档。

练习 3:思考题

你的项目有 3 个环境:dev / staging / prod,每个环境的配置差异很大。如何用 Spring Cloud Config 或 Nacos 做集中配置?


下一章第 85 章:通用模块(异常/响应/工具)

本站基于 VitePress 构建 · 由 Codebook 团队维护