使用Gradle构建项目,尝试用的属性,与数据库、缓存,MQ搭配,以及构建模板。
Gradle介绍
Gradle 是一个构建自动化工具
用来做三件事:
- 编译 Java / Kotlin
- 管理依赖(类似 Maven)
- 组织多模块工程
一个 Gradle 项目本质是:1
Gradle = 一堆 Project + 每个 Project 的 build.gradle
每个 build.gradle 里都是“配置块”。
Gradle核心模型
Gradle 有 3 个核心对象:
Project(项目)
每个模块都是一个 Project:1
2
3
4root project
├── api
├── service
├── repository
每个 build.gradle = 一个 Project
Task(任务)
Gradle 的执行单位:1
2
3
4compileJava
test
bootJar
build
Dependency(依赖)
Java 代码依赖:1
implementation 'org.springframework.boot:spring-boot-starter'
生命周期
1 | 1. 初始化(init) |
关键点: build.gradle 在“配置阶段”全部执行
所以:1
println("hello")
会在执行 task 之前就打印。
一个项目结构让设计如下1
2
3
4
5
6
7purchase-system
│
├── common (基础模型)
├── contract (RPC接口)
├── repository (DAO)
├── service (业务 + Dubbo provider)
└── api (HTTP + Dubbo consumer)
插件块
给 build.gradle 这个脚本本身加依赖,通过buildscript或者plugins.给项目“增加能力”
比如:
- java → 可以编译 Java
- spring boot → 可以打 bootJar
plugins {}:现代插件机制(声明式、Gradle 官方推荐)
buildscript {}:旧插件机制(手动把依赖塞进构建脚本 classpath)1
2
3
4
5
6
7
8buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath "org.springframework.boot:spring-boot-gradle-plugin:3.3.0"
}
}
通过repositories中的仓库查找对应插件并放到classpath.然后才apply plugin使用插件. Gradle本身自带一些插件,比如java,可以直接apply
而`plugins自己去“插件仓库”下载 plugin
然后自动解析 plugin marker,找找 implementation jar,自动 apply. 配置下载仓库在settings.gradle中,默认使用Gradle官方的1
2
3plugins {
id "org.springframework.boot" version "3.3.0"
}
仓库块
1 | repositories { |
告诉 Gradle 去哪里下载依赖
依赖块
1 | dependencies { |
给 Java 代码引入库
| 关键字 | 作用 |
|---|---|
| implementation | 运行 + 编译 |
| compileOnly | 只编译 |
| testImplementation | 测试用 |
| runtimeOnly | 运行时 |
ext
1 | ext { |
作用: 定义“全局变量
现在推荐替代方案1
gradle.properties
allprojects
1 | allprojects { |
作用: 对“所有 project(包括 root + 子模块)”生效
subprojects
1 | subprojects { |
作用: 只对“子模块”生效(不包括 root)
作用:
统一 Java plugin
统一 Lombok
统一 test 配置
project(“:xxx”) 1
2
3
4
5project(":api") {
dependencies {
implementation project(":common")
}
}
配置“某一个指定模块”
dependencyManagement {}1
2
3
4
5dependencyManagement {
imports {
mavenBom "org.springframework.boot:spring-boot-dependencies:3.3.0"
}
}
作用:统一版本号(不在 dependencies 写版本)
tasks (任务块)1
2
3
4
5tasks.register('hello') {
doLast {
println("hello gradle")
}
}
作用:定义构建步骤
常用插件
自带插件
1)java(最重要)1
2
3plugins {
id 'java'
}
提供能力:
compileJava
jar
test
sourceSets
Java 项目基础插件
2)java-library(更专业)1
2
3plugins {
id 'java-library'
}
多了:
- api / implementation 区分
更适合模块化项目
推荐多模块项目用这个
maven-publish
Gradle Maven Publish Plugin1
2
3plugins {
id 'maven-publish'
}
用于发布 jar 到 Maven 仓库
数据库 / ORM 常见插件
MyBatis Generator1
2
3plugins {
id 'org.mybatis.generator'
}
自动生成 mapper / entity
Spring 生态插件
Spring Boot Plugin
Spring Boot Gradle Plugin1
2
3plugins {
id 'org.springframework.boot' version '3.3.0'
}
提供:
- bootJar(fat jar)
- bootRun
- 自动依赖管理
Spring Dependency Management1
2
3plugins {
id 'io.spring.dependency-management'
}
提供:BOM 管理(不写版本号)
基于Spring-boot-starter与Mybatis整合
mybatis-spring-boot-autoconfigure – 简介1
2
3dependencies {
implementation("org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.2")
}
MyBatis-Spring-Boot-Starter 将会:
- 自动探测存在的
DataSource - 将使用
SqlSessionFactoryBean创建并注册一个SqlSessionFactory的实例,并将探测到的DataSource作为数据源 - 将创建并注册一个从
SqlSessionFactory中得到的SqlSessionTemplate的实例 - 自动扫描你的 mapper,将它们与
SqlSessionTemplate相关联,并将它们注册到Spring 的环境(context)中去,这样它们就可以被注入到你的 bean 中
数据源是基础底层,SqlSessionFactory 利用数据源创建连接并作为核心工厂,生产出具体的执行器 SqlSessionTemplate,而 SqlSessionTemplate 在幕后真正驱动并支撑 Mapper 接口的方法执行。
它们之间的分层依赖与调用关系如下:1
2
3
4
5
6
7
8
9
10
11
12
13[DataSource (数据源)]
▲
│ 提供数据库连接池 (如 HikariCP)
│
[SqlSessionFactory (会话工厂)]
▲
│ 作为单例核心引擎,用来创建会话
│
[SqlSessionTemplate (会话模板)]
▲
│ 线程安全,代理并管理实际的 SqlSession 会话与生命周期
│
[Mapper 接口 (动态代理对象)]
mybatis-spring-boot-autoconfigure – 简介
配置
引入依赖
在 pom.xml 中引入 MyBatis 自己的 Starter(它会顺着依赖把需要的工厂类和模板类全部下载下来):1
2
3
4
5<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
- 核心配置文件 (
application.yml)
只需要配置两部分:一是数据源(让它能自动探测到 DataSource),二是告诉它你的 SQL 映射文件(XML)放在哪。1
2
3
4
5
6
7
8
9
10
11
12
13spring:
datasource:
# 1. 对应第一句:“自动探测存在的 DataSource”
url: jdbc:mysql://localhost:3306/your_db?useUnicode=true&characterEncoding=utf-8
username: root
password: your_password
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
# 2. 对应第四句:告诉系统你的 XML 映射文件在哪里,好让它自动扫描并关联
mapper-locations: classpath:mapper/*.xml
configuration:
map-underscore-to-camel-case: true # 顺便开启驼峰命名转换(下划线转驼峰)
- 在代码中打个“招呼”
最后在Spring Boot 启动类(或者配置类)上,加上一个 @MapperScan 注解。
这是为了告诉它:“我的 Mapper 接口都放在这个包底下了,你快去自动扫描它们,把它们和 SqlSessionTemplate 关联起来吧!”
Java1
2
3
4
5
6
7@SpringBootApplication
@MapperScan("com.example.ecommerce.mapper") // 告诉系统扫描哪里的 Mapper 接口
public class EcommerceApplication {
public static void main(String[] args) {
SpringApplication.run(EcommerceApplication.class, args);
}
}
整合springBoot与Redis
快速导览 :: Spring Kafka - Spring 框架
在 Spring Boot 中,spring-boot-starter-data-redis(通常简称为 spring-redis-starter)是整合 Redis 的核心组件。它不仅引入了 Redis 客户端驱动,还通过 Spring Boot 的 Auto-Configuration(自动装配) 机制,让开发者几乎可以零配置直接使用。
- 默认客户端驱动:Lettuce
- 在老的 Spring Boot 1.x 版本中,默认使用的是 Jedis 客户端。
- 从 Spring Boot 2.x/3.x 开始,官方默认将驱动切换为了 Lettuce。
- 为什么用 Lettuce? Jedis 的多线程并发是基于连接池的(线程不安全),而 Lettuce 是基于 Netty 的,它的连接可以在多个线程间共享,属于异步非阻塞的架构,在高并发场景下性能更优、资源消耗更小。
- 自动装配类:
RedisAutoConfiguration
当你引入了 Starter 后,Spring Boot 的类路径扫描会激活 RedisAutoConfiguration 这个自动配置类。它在幕后默默做了两件事:
- 自动创建连接工厂(
RedisConnectionFactory):它会读取你在yml中配置的 host、port 等参数,实例化一个LettuceConnectionFactory,负责建立与 Redis 服务器的物理连接。 - 自动注入两个核心模板类:
RedisTemplate<Object, Object>:通用模板,键值对都可以是任意对象。StringRedisTemplate:专门处理String类型的模板(因为 Redis 中 90% 的场景都是字符串)。
- 序列化机制(关键踩坑点)
Redis 底层只存储字节数组(byte[]),而 Java 是一门面向对象的语言。因此,Java 对象存入 Redis 必须经过序列化。
StringRedisTemplate:默认使用StringRedisSerializer。它存进去的就是人类直观可见的普通字符串。RedisTemplate:默认使用JdkSerializationSerializer。这会导致两个严重问题:- 存入 Redis 的 Key 和 Value 前面会带有一串乱码般的十六进制前缀(如
\xac\xed\x00\x05t\x00...)。 - 难以通过可视化工具(如 RDM)直接查看,且跨语言(如 Python 读写)极不友好。
- 存入 Redis 的 Key 和 Value 前面会带有一串乱码般的十六进制前缀(如
配置
引入依赖 (POM)
除了 Starter 本身,如果需要配置连接池,必须引入 commons-pool2。1
2
3
4
5
6
7
8
9<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 必须引入,否则 lettuce 连接池配置无法生效 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
- 核心配置文件 (
application.yml)
标准 Lettuce 连接池配置:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15spring:
data:
redis:
host: 127.0.0.1
port: 6379
password: your_password
database: 0 # 默认使用 0 号库
timeout: 5000ms # 连接超时时间
connect-timeout: 5000ms # 路由建立连接超时时间
lettuce:
pool:
max-active: 32 # 连接池最大连接数(高并发下调大)
max-idle: 16 # 连接池中的最大空闲连接
min-idle: 4 # 连接池中的最小空闲连接
max-wait: 2000ms # 连接耗尽时,消费者最大等待时间
- 自定义配置类:重写
RedisTemplate(必做步骤)
为了解决上面提到的 “JDK 序列化乱码” 问题,在实际开发中,我们必须自己写一个配置类,把默认的序列化方式替换为 JSON 序列化。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class RedisConfig {
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
// 1. 使用 JSON 工具(这里以 GenericJackson2JsonRedisSerializer 为例)
GenericJackson2JsonRedisSerializer jsonSerializer = new GenericJackson2JsonRedisSerializer();
// 2. 设置 Key 和 HashKey 采用 String 序列化(保证人类可读,无乱码)
template.setKeySerializer(RedisSerializer.string());
template.setHashKeySerializer(RedisSerializer.string());
// 3. 设置 Value 和 HashValue 采用 JSON 序列化
template.setValueSerializer(jsonSerializer);
template.setHashValueSerializer(jsonSerializer);
template.afterPropertiesSet();
return template;
}
}
三、 总结:如何选择 RedisTemplate 和 StringRedisTemplate?
在代码中注入并使用它们时,遵循以下基本原则:
- 如果你只存纯文本、数字、或者你自己手动转好的 JSON 字符串: 直接使用
StringRedisTemplate。它免去了任何框架层的序列化开销,性能最高,且在 Redis 内呈现最干净的字符串。 - 如果你想直接把一个 Java 对象(如
User实体、Order实体)丢进 Redis: 使用你刚才自定义好序列化器的RedisTemplate<String, Object>。它会在底层自动帮你把对象转成标准 JSON 串存入,取出来时自动反序列化回 Java 对象,开发体验最顺畅。
整合springBoot与Kafka
引入依赖1
2
3<dependency>
<groupId>org.springframework.kafka</groupId> <artifactId>spring-kafka</artifactId> <version>4.0.0</version>
</dependency>
spring-kafka 的自动装配核心是 KafkaAutoConfiguration 类,它在项目启动时主要帮你完成了以下几件大事:
- 自动构建三大工厂(基础层)
它会读取 yml 中的集群地址等参数,在后台自动实例化:
ProducerFactory:生产者工厂,负责专门创建、管理和复用低层的 Kafka 生产者实例(KafkaProducer)。ConsumerFactory:消费者工厂,负责配置消费组 ID、反序列化器,并创建原生的KafkaConsumer。KafkaListenerContainerFactory:监听器容器工厂(最核心)。它负责把你的@KafkaListener注解转换为后台不断轮询(poll())消息的异步线程池容器。
- 自动注入核心发送模板:
KafkaTemplate
类似于 RedisTemplate,Spring 自动为你提供了一个 KafkaTemplate<K, V>。
- 它对原生的生产者进行了高级封装。
- 它默认是线程安全的,你可以在任何 Service 里直接注入并调用
.send(topic, message)。 - 支持同步发送(等待 Broker 回应)和异步发送(通过
CompletableFuture回调接收发送结果)。
@KafkaListener幕后的死循环轮询
当你在某个方法上加上 @KafkaListener(topics = "my-topic") 时:
- Spring 启动后,监听器容器工厂会为这个方法单独开启独立的线程。
- 线程内部会进入一个
while(true)的死循环,源源不断地调用 Kafka 原生的consumer.poll(timeout)方法去拉取消息。 - 一旦拉取到消息,通过反射调用你写的方法,将消息作为参数传入。
引入依赖 (POM)1
2
3
4<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
</dependency>
- 核心配置文件 (
application.yml)
在实际的业务场景中,我们必须精细控制ACK应答级别以防丢消息,以及关闭自动提交改为手动提交。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
28spring:
kafka:
bootstrap-servers: 127.0.0.1:9092 # Kafka 集群地址,多个用逗号隔开
# ==================== 1. 生产者配置 ====================
producer:
retries: 3 # 消息发送失败后的重试次数
acks: all # 应答级别。all(或-1) 代表所有副本都同步完才算成功,最安全
batch-size: 16384 # 批量发送大小 (16KB),提升吞吐量
buffer-memory: 33554432 # 生产端本地缓冲区大小 (32MB)
key-serializer: org.apache.kafka.common.serialization.StringSerializer
value-serializer: org.apache.kafka.common.serialization.StringSerializer
# ==================== 2. 消费者配置 ====================
consumer:
group-id: ecommerce-order-group # 默认消费组 ID
enable-auto-commit: false # 关闭自动提交!推荐手动提交,防止业务代码报错时消息丢失
auto-offset-reset: earliest # 当各分区没有初始偏移量时的策略:earliest/latest
key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
max-poll-records: 500 # 单次 poll 轮询最大拉取条数
# ==================== 3. 监听Container配置 ====================
listener:
# 手动立即提交偏移量。配合 enable-auto-commit: false 使用。
# 业务代码正常执行完后,Spring 会自动帮你调用 commitSync()
ack-mode: manual_immediate
concurrency: 3 # 消费者并发度。建议设置为和你 Topic 的 Partition(分区)数量一致,充分利用多线程并行消费
三、 核心代码使用示例
- 生产者:发送消息
1 |
|
- 消费者:监听并手动确认(Ack)
当你在上面配置了 ack-mode: manual_immediate,你的消费方法里可以引入 Acknowledgment 对象。当业务代码顺利跑完,调用 ack.acknowledge() 告诉 Kafka:“这条消息我吃下了,你可以推进 Offset 了”。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class OrderConsumer {
// concurrency = "3" 表示启动 3 个线程同时消费,对应 yml 中的全局配置,也可以在这里局部覆盖
public void onMessage(ConsumerRecord<String, String> record, Acknowledgment ack) {
try {
// 1. 收到消息,打印日志
System.out.printf("线程 %s 收到消息 -> 分区: %d, 消息体: %s%n",
Thread.currentThread().getName(), record.partition(), record.value());
// 2. 执行你的电商核心业务逻辑(如:扣减库存、发送通知等)
// doYourBusiness(record.value());
// 3. 业务成功,手动提交
ack.acknowledge();
} catch (Exception e) {
// 4. 业务异常,这里千万别 ack!可以流转到死信队列(DLQ)或者触发重试机制
System.err.println("消费异常,暂不提交偏移量: " + e.getMessage());
}
}
}
