Gradle入门与使用

使用Gradle构建项目,尝试用的属性,与数据库、缓存,MQ搭配,以及构建模板。

Gradle介绍

Gradle 是一个构建自动化工具

用来做三件事:

  • 编译 Java / Kotlin
  • 管理依赖(类似 Maven)
  • 组织多模块工程

一个 Gradle 项目本质是:

1
Gradle = 一堆 Project + 每个 Project 的 build.gradle

每个 build.gradle 里都是“配置块”。

Gradle核心模型

Gradle 有 3 个核心对象:

Project(项目)

每个模块都是一个 Project:

1
2
3
4
root project
├── api
├── service
├── repository

每个 build.gradle = 一个 Project

Task(任务)

Gradle 的执行单位:

1
2
3
4
compileJava
test
bootJar
build

Dependency(依赖)

Java 代码依赖:

1
implementation 'org.springframework.boot:spring-boot-starter'

生命周期

1
2
3
1. 初始化(init)
2. 配置(configuration)
3. 执行(execution)

关键点: build.gradle 在“配置阶段”全部执行

所以:

1
println("hello")

会在执行 task 之前就打印。

一个项目结构让设计如下

1
2
3
4
5
6
7
purchase-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
8
buildscript {
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
3
plugins {
id "org.springframework.boot" version "3.3.0"
}

仓库块

1
2
3
4
repositories {
mavenCentral()
maven { url "https://maven.aliyun.com/repository/public" }
}

告诉 Gradle 去哪里下载依赖

依赖块

1
2
3
dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
}

给 Java 代码引入库

关键字作用
implementation运行 + 编译
compileOnly只编译
testImplementation测试用
runtimeOnly运行时

ext

1
2
3
4
ext {
lombokVersion = '1.18.34'
dubboVersion = '3.2.15'
}

作用: 定义“全局变量

现在推荐替代方案

1
gradle.properties

allprojects

1
2
3
4
5
allprojects {
repositories {
mavenCentral()
}
}

作用: 对“所有 project(包括 root + 子模块)”生效

subprojects

1
2
3
4
5
6
7
subprojects {
apply plugin: 'java'

dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter:5.10.2'
}
}

作用: 只对“子模块”生效(不包括 root)

作用:

统一 Java plugin

统一 Lombok

统一 test 配置

project(“:xxx”)

1
2
3
4
5
project(":api") {
dependencies {
implementation project(":common")
}
}

配置“某一个指定模块”

dependencyManagement {}

1
2
3
4
5
dependencyManagement {
imports {
mavenBom "org.springframework.boot:spring-boot-dependencies:3.3.0"
}
}

作用:统一版本号(不在 dependencies 写版本)

tasks (任务块)

1
2
3
4
5
tasks.register('hello') {
doLast {
println("hello gradle")
}
}

作用:定义构建步骤

常用插件

自带插件

1)java(最重要)

1
2
3
plugins {
id 'java'
}

提供能力:

  • compileJava

  • jar

  • test

  • sourceSets

    Java 项目基础插件

2)java-library(更专业)

1
2
3
plugins {
id 'java-library'
}

多了:

  • api / implementation 区分
  • 更适合模块化项目

    推荐多模块项目用这个

maven-publish

Gradle Maven Publish Plugin

1
2
3
plugins {
id 'maven-publish'
}

用于发布 jar 到 Maven 仓库

数据库 / ORM 常见插件

MyBatis Generator

1
2
3
plugins {
id 'org.mybatis.generator'
}

自动生成 mapper / entity

Spring 生态插件

Spring Boot Plugin

Spring Boot Gradle Plugin

1
2
3
plugins {
id 'org.springframework.boot' version '3.3.0'
}

提供:

  • bootJar(fat jar)
  • bootRun
  • 自动依赖管理

Spring Dependency Management

1
2
3
plugins {
id 'io.spring.dependency-management'
}

提供:BOM 管理(不写版本号)

基于Spring-boot-starter与Mybatis整合

mybatis-spring-boot-autoconfigure – 简介

1
2
3
dependencies {
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>
  1. 核心配置文件 (application.yml)

只需要配置两部分:一是数据源(让它能自动探测到 DataSource),二是告诉它你的 SQL 映射文件(XML)放在哪。

1
2
3
4
5
6
7
8
9
10
11
12
13
spring:
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 # 顺便开启驼峰命名转换(下划线转驼峰)
  1. 在代码中打个“招呼”

最后在Spring Boot 启动类(或者配置类)上,加上一个 @MapperScan 注解。

这是为了告诉它:“我的 Mapper 接口都放在这个包底下了,你快去自动扫描它们,把它们和 SqlSessionTemplate 关联起来吧!”

Java

1
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(自动装配) 机制,让开发者几乎可以零配置直接使用。

  1. 默认客户端驱动:Lettuce
  • 在老的 Spring Boot 1.x 版本中,默认使用的是 Jedis 客户端。
  • Spring Boot 2.x/3.x 开始,官方默认将驱动切换为了 Lettuce
  • 为什么用 Lettuce? Jedis 的多线程并发是基于连接池的(线程不安全),而 Lettuce 是基于 Netty 的,它的连接可以在多个线程间共享,属于异步非阻塞的架构,在高并发场景下性能更优、资源消耗更小。
  1. 自动装配类:RedisAutoConfiguration

当你引入了 Starter 后,Spring Boot 的类路径扫描会激活 RedisAutoConfiguration 这个自动配置类。它在幕后默默做了两件事:

  • 自动创建连接工厂(RedisConnectionFactory:它会读取你在 yml 中配置的 host、port 等参数,实例化一个 LettuceConnectionFactory,负责建立与 Redis 服务器的物理连接。
  • 自动注入两个核心模板类
    • RedisTemplate<Object, Object>:通用模板,键值对都可以是任意对象。
    • StringRedisTemplate:专门处理 String 类型的模板(因为 Redis 中 90% 的场景都是字符串)。
  1. 序列化机制(关键踩坑点)

Redis 底层只存储字节数组(byte[]),而 Java 是一门面向对象的语言。因此,Java 对象存入 Redis 必须经过序列化

  • StringRedisTemplate:默认使用 StringRedisSerializer。它存进去的就是人类直观可见的普通字符串。
  • RedisTemplate:默认使用 JdkSerializationSerializer。这会导致两个严重问题:
    1. 存入 Redis 的 Key 和 Value 前面会带有一串乱码般的十六进制前缀(如 \xac\xed\x00\x05t\x00...)。
    2. 难以通过可视化工具(如 RDM)直接查看,且跨语言(如 Python 读写)极不友好。

配置

引入依赖 (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>
  1. 核心配置文件 (application.yml)

标准 Lettuce 连接池配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
spring:
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 # 连接耗尽时,消费者最大等待时间
  1. 自定义配置类:重写 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
@Configuration
public class RedisConfig {

@Bean
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;
}
}

三、 总结:如何选择 RedisTemplateStringRedisTemplate

在代码中注入并使用它们时,遵循以下基本原则:

  1. 如果你只存纯文本、数字、或者你自己手动转好的 JSON 字符串: 直接使用 StringRedisTemplate。它免去了任何框架层的序列化开销,性能最高,且在 Redis 内呈现最干净的字符串。
  2. 如果你想直接把一个 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 类,它在项目启动时主要帮你完成了以下几件大事:

  1. 自动构建三大工厂(基础层)

它会读取 yml 中的集群地址等参数,在后台自动实例化:

  • ProducerFactory:生产者工厂,负责专门创建、管理和复用低层的 Kafka 生产者实例(KafkaProducer)。
  • ConsumerFactory:消费者工厂,负责配置消费组 ID、反序列化器,并创建原生的 KafkaConsumer
  • KafkaListenerContainerFactory:监听器容器工厂(最核心)。它负责把你的 @KafkaListener 注解转换为后台不断轮询(poll())消息的异步线程池容器。
  1. 自动注入核心发送模板:KafkaTemplate

类似于 RedisTemplate,Spring 自动为你提供了一个 KafkaTemplate<K, V>

  • 它对原生的生产者进行了高级封装。
  • 它默认是线程安全的,你可以在任何 Service 里直接注入并调用 .send(topic, message)
  • 支持同步发送(等待 Broker 回应)和异步发送(通过 CompletableFuture 回调接收发送结果)。
  1. @KafkaListener 幕后的死循环轮询

当你在某个方法上加上 @KafkaListener(topics = "my-topic") 时:

  1. Spring 启动后,监听器容器工厂会为这个方法单独开启独立的线程
  2. 线程内部会进入一个 while(true)死循环,源源不断地调用 Kafka 原生的 consumer.poll(timeout) 方法去拉取消息。
  3. 一旦拉取到消息,通过反射调用你写的方法,将消息作为参数传入。

引入依赖 (POM)

1
2
3
4
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
</dependency>
  1. 核心配置文件 (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
28
spring:
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. 生产者:发送消息
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
@RestController
@RequestMapping("/order")
public class OrderController {

@Autowired
private KafkaTemplate<String, String> kafkaTemplate;

@GetMapping("/create")
public String createOrder() {
String orderJson = "{\"orderId\": 10023, \"amount\": 299.00}";

// 异步发送消息
CompletableFuture<SendResult<String, String>> future = kafkaTemplate.send("order-topic", orderJson);

// 绑定发送结果回调(非阻塞)
future.whenComplete((result, ex) -> {
if (ex == null) {
System.out.println("消息发送成功,偏移量为:" + result.getRecordMetadata().offset());
} else {
System.err.println("消息发送失败:" + ex.getMessage());
}
});

return "Order processing...";
}
}
  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
@Component
public class OrderConsumer {

// concurrency = "3" 表示启动 3 个线程同时消费,对应 yml 中的全局配置,也可以在这里局部覆盖
@KafkaListener(topics = "order-topic", groupId = "ecommerce-order-group", concurrency = "3")
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());
}
}
}
-------------本文结束感谢您的阅读-------------
感谢阅读.

欢迎关注我的其它发布渠道