第 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: bridgebash
# 一键启动
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 章:通用模块(异常/响应/工具) →