版本:0.4.0 日期:2025-08-31

框架概述

本框架集成第三开源工具,开发Spring Boot Starter风格的包,方便引入及使用

主要目标:

  • 只集成工具,适当的扩展工具,使其更容易与Spring Boot集成

  • 每个Starter功能单一,或者只专注某一方面,如:ID生成器,验证码生成器等等

  • 积累知识,技术,经验

注意,Gitee是主库,发布时会同步到Github

主要依赖及版本对应

  • JDK: 17.0.12

  • Spring Boot 3.0.13

本框架版本 Spring Boot 版本

< = 0.4.0

3.0.13

如何使用

有两个方法引入本框架,分别是继承`starter-parent`项目和导入`dependencies`项目。第一个方法更方便;第二个方法更有灵活性。

如果是示例或小型系统(单一项目),推荐使用第一种方法。大型系统(多项目)就使用第二种方法

继承`starter-parent`项目

`starter-parent`继承了`spring-boot-starter-parent`项目,在此基础上扩展功能,如:导入本框架,添加插件等等

在项目`pom.xml`中添加:

<parent>
    <groupId>io.gitee.yunjiao-source.spring-boot</groupId>
    <artifactId>starter-parent</artifactId>
    <version>3.0.13</version>
</parent>

`starter-parent`的版本与Spring Boot的版本保持一致。对应本框架的版本,请查看主要依赖及版本对应

导入`dependencies`项目

`dependencies`项目包含本框架的所有包,在项目中导入即可

在项目`pom.xml`中添加:

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>io.gitee.yunjiao-source.spring-boot</groupId>
            <artifactId>dependencies</artifactId>
            <version>0.4.0</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

编译源码

源码目录中包含多个独立项目,需要分别编译,打包

因为是独立的项目,IDE中只能自动导入主项目,其他项目需要手工导入

目录结构

项目名 目录及说明

Spring Boot

autoconfigure(自动配置包), dependencies(所有的依赖包), extensions(扩展包), starters(启动器包)

Spring Boot :: Starter Parent

starter-parent, 此项目集成`Spring Boot`的`spring-boot-starter-parent`项目

Spring Boot :: Examples

examples, 示例项目

Spring Boot :: Projects

projects, 示例应用

编译打包

首先编译打包主项目

# cd yunjiao-spring-boot
# mvn clean install

其次打包starter-parent

# cd starter-parent
# mvn clean install

其他的项目,编译,打包方法是一样的

ID扩展

ID生成是企业信息系统需要的,重要的,必须的功能。目前使用雪花算法生成ID是最流行的,本框架集成`Hutool`,`uid-generator`等框架, 这些框架实现了雪花算法。

`uid-generator`框架( 官网 )是百度开源的,此框架没有发布到Maven中心仓库,所以 在pom中无法引入。本框架引入的是`uid-core`包 ( 官网

雪花算法需要一个中心化服务分配机器ID,在不同的应用中实现统一的ID(不重复),但这并不常见,实现也很复杂。一般情况下,比如在用户应用与订单应用, 用户表的ID与订单表的ID是可以重复的。

在微服务场景中,需要在多台服务器上部署相同的应用,如用户应用,必须保证每个用户应用新增用户的ID是不重复的。

根据以上分析,可以把每台服务当做中心化服务,分配机器ID。只需要在系统定义环境变量,雪花ID算法工具获取环境变量,就可以保证相同的应用统一的ID(不重复), 这个思路我称为`有限的分布式ID`。 本框架实现了`有限的分布式ID`, 支持在系统中定义环境变量`SNOWFLAKE_WORKER_ID`和`SNOWFLAKE_DATACENTER_ID`。

如果需要真正的分布式ID,推荐`uid-generator-starter` ( 官网 ), 它支持中心化服务,如 数据库,redis, zk等

雪花ID算法

是一个在分布式系统中广泛使用的全局唯一ID生成算法,由Twitter公司提出,旨在解决在分布式环境下高并发、高性能地生成唯一ID的问题

优点

  • 全局唯一:通过时间戳+机器ID+序列号的组合,在分布式系统下几乎不可能重复。

  • 趋势递增:由于时间戳在最前面,生成的ID整体上是随时间递增的。这对于数据库索引(如MySQL的B+Tree)非常友好,插入性能高。

  • 高性能:本地生成,无需远程调用数据库或Redis等中间件,速度极快(每毫秒每台机器可生成4096个ID)。

  • 无需中心化服务:不像UUID需要中心化的发号器,避免了单点故障和性能瓶颈。(但分配机器ID可能需要一个中心化服务)。

  • 信息隐含:ID本身包含时间戳和机器ID信息,在排查问题时可以根据ID反推出生成时间和生成机器。

(信息来源:DeepSeek)

示例

Gitee: examples/example-id

如何使用

在pom.xml文件中添加

<dependency>
    <groupId>io.gitee.yunjiao-source.spring-boot</groupId>
    <artifactId>starter-id</artifactId>
</dependency>

配置指南

在`application.yml`中配置参数

配置参数 默认值 说明

spring.id.uid-generator.time-bits

28

spring.id.uid-generator.worker-bits

22

spring.id.uid-generator.seq-bits

13

spring.id.uid-generator.epoch-str

2025-08-20

开发指南

在服务中注入

@Slf4j
@Component
public class IdCommandLineRunner implements CommandLineRunner {
    @Autowired
    private Snowflake snowflake;
    @Autowired
    private UidGeneratorCached uidGeneratorCached;
    @Autowired
    private UidGeneratorDefault uidGeneratorDefault;

    @Override
    public void run(String... args) throws Exception {
        TimeUnit.SECONDS.sleep(3);

        log.info("Snowflake = {}",  generator(() -> snowflake.nextId()).size());
        log.info("uidGeneratorCached = {}", generator(() -> uidGeneratorCached.getUID()).size() );
        log.info("uidGeneratorDefault = {}", generator(() -> uidGeneratorDefault.getUID()).size() );
    }

    private Set<Long> generator(Supplier<Long> supplier) {
        return IntStream.range(0, 4000).mapToObj(i -> supplier.get())
                .peek(l -> System.out.print(l + ","))
                .collect(Collectors.toSet());
    }
}

在使用中,日志中会出现警告信息

WARN   --- [           main] y.s.e.i.u.EnvironmentWorkerIdAssigner    : Uid-Generator 框架雪花算法配置使用默认参数。如需支持分布式,请设置系统环境变量:SNOWFLAKE_WORKER_ID
WARN   --- [           main] y.s.a.id.HutoolIdConfiguration           : Hutool 框架雪花算法配置使用默认参数。如需支持分布式,请设置系统环境变量:SNOWFLAKE_WORKER_ID 与 SNOWFLAKE_DATACENTER_ID

如果不是微服务或集群部署应用,可以忽略此警告

本框架提供的雪花ID工具,没有必要都使用,使用其中的一种就可以满足大部分场景

使用`CachedUidGeneratorConfigurer`自定义配置

@Slf4j
@Configuration
public class IdConfiguration {

    @Bean
    CachedUidGeneratorConfigurer CachedUidGeneratorConfigurer() {
        return generator -> {
            log.info("自定义配置");
            generator.setBoostPower(3);
            generator.setPaddingFactor(50);
            generator.setScheduleInterval(60);
        };
    }

}

Captcha(验证码)扩展

Captcha(验证码)是很常见的需求,广泛应用在登录,下载等功能中。本框架集成Hutool,aj-captcha等开源验证码工具,提供多种验证码,如:线段干扰验证码, 圆圈干扰验证码,扭曲干扰验证码,GIF验证码,滑块拼图验证码,文字点选验证码,旋转拼图验证码等等

本框架验证码服务只有生成,校验功能。不涉及验证码缓存及失效

示例

Gitee: examples/example-captcha

如何使用

在pom.xml文件中添加

<dependency>
    <groupId>io.gitee.yunjiao-source.spring-boot</groupId>
    <artifactId>starter-captcha</artifactId>
</dependency>

配置指南

在`application.yml`中配置参数

线段干扰验证码(line)配置

配置参数 默认值 说明

spring.captcha.hutool.line.width

250

图片的宽度

spring.captcha.hutool.line.height

50

图片的高度

spring.captcha.hutool.line.interfereCount

60

验证码干扰元素个数

spring.captcha.hutool.line.backgroundColor

ColorType.white

背景色

spring.captcha.hutool.line.transparency

null

文字透明度,取值0~1,1表示不透明

spring.captcha.hutool.line.fuzziness

2

模糊度(0 - 30)

spring.captcha.hutool.line.validIgnoreCase

true

校验时忽略大小写

spring.captcha.hutool.line.font.name

null

字体名称, 为空表示使用系统默认字体

spring.captcha.hutool.line.font.style

FontStyle.plain

字体风格

spring.captcha.hutool.line.font.size

36

字体大小

spring.captcha.hutool.line.code.generator

CodeGeneratorType.numAndChar

码生成类型

spring.captcha.hutool.line.code.length

5

字符长度

圆圈干扰验证码(circle)配置

配置参数 默认值 说明

spring.captcha.hutool.circle.width

250

图片的宽度

spring.captcha.hutool.circle.height

50

图片的高度

spring.captcha.hutool.circle.interfereCount

30

验证码干扰元素个数

spring.captcha.hutool.circle.backgroundColor

ColorType.white

背景色

spring.captcha.hutool.circle.transparency

null

文字透明度,取值0~1,1表示不透明

spring.captcha.hutool.circle.fuzziness

2

模糊度(0 - 30)

spring.captcha.hutool.circle.validIgnoreCase

true

校验时忽略大小写

spring.captcha.hutool.circle.font.name

null

字体名称, 为空表示使用系统默认字体

spring.captcha.hutool.circle.font.style

FontStyle.plain

字体风格

spring.captcha.hutool.circle.font.size

36

字体大小

spring.captcha.hutool.circle.code.generator

CodeGeneratorType.numAndChar

码生成类型

spring.captcha.hutool.circle.code.length

5

字符长度

扭曲干扰验证码(shear)配置

配置参数 默认值 说明

spring.captcha.hutool.shear.width

250

图片的宽度

spring.captcha.hutool.shear.height

50

图片的高度

spring.captcha.hutool.shear.interfereCount

10

干扰线宽度

spring.captcha.hutool.shear.backgroundColor

ColorType.white

背景色

spring.captcha.hutool.shear.transparency

null

文字透明度,取值0~1,1表示不透明

spring.captcha.hutool.shear.fuzziness

2

模糊度(0 - 30)

spring.captcha.hutool.shear.validIgnoreCase

true

校验时忽略大小写

spring.captcha.hutool.shear.font.name

null

字体名称, 为空表示使用系统默认字体

spring.captcha.hutool.shear.font.style

FontStyle.plain

字体风格

spring.captcha.hutool.shear.font.size

36

字体大小

spring.captcha.hutool.shear.code.generator

CodeGeneratorType.numAndChar

码生成类型

spring.captcha.hutool.shear.code.length

5

字符长度

Gif验证码(gif)配置

配置参数 默认值 说明

spring.captcha.hutool.gif.width

250

图片的宽度

spring.captcha.hutool.gif.height

50

图片的高度

spring.captcha.hutool.gif.interfereCount

10

干扰线宽度

spring.captcha.hutool.gif.backgroundColor

ColorType.white

背景色

spring.captcha.hutool.gif.transparency

null

文字透明度,取值0~1,1表示不透明

spring.captcha.hutool.gif.fuzziness

2

模糊度(0 - 30)

spring.captcha.hutool.gif.validIgnoreCase

true

校验时忽略大小写

spring.captcha.hutool.gif.font.name

null

字体名称, 为空表示使用系统默认字体

spring.captcha.hutool.gif.font.style

FontStyle.plain

字体风格

spring.captcha.hutool.gif.font.size

36

字体大小

spring.captcha.hutool.gif.code.generator

CodeGeneratorType.numAndChar

码生成类型

spring.captcha.hutool.gif.code.length

5

字符长度

spring.captcha.hutool.gif.quality

10

量化器取样间隔, 1 ~ 20 值之间 - 默认是10ms

spring.captcha.hutool.gif.repeat

0

帧循环次数,默认是 0, 意味着无限循环

spring.captcha.hutool.gif.min-color

0

设置随机颜色时,最小的取色范围

spring.captcha.hutool.gif.max-color

255

设置随机颜色时,最大的取色范围

滑块拼图验证码(blockPuzzle)及文字点选验证码(clickWord)配置

配置参数 默认值 说明

spring.captcha.anji.water-mark

我的水印

右下角水印文字

spring.captcha.anji.water-font

WenQuanZhengHei.ttf

右下角水印字体

spring.captcha.anji.block-puzzle.jigsaw

滑动拼图底图路径

spring.captcha.anji.block-puzzle.slip-offset

5

校验滑动拼图允许误差偏移量

spring.captcha.anji.block-puzzle.interference-options

0

滑块干扰项

spring.captcha.anji.click-word.pic-click

点选文字底图路径

spring.captcha.anji.click-word.font-type

WenQuanZhengHei.ttf

点选文字验证码的文字字体

spring.captcha.anji.click-word.font-style

FontStyle.bold

点选字体样式

spring.captcha.anji.click-word.font-size

25

点选字体大小

spring.captcha.anji.click-word.click-word-count

4

点选文字个数

spring.captcha.anji.click-word.slip-offset

13

校验拼图允许误差(默认13像素).

开发指南

使用验证码服务工厂(CaptchaServiceFactory),可以很方便获取验证码服务

@Slf4j
@Component
public class CaptchaCommandLineRunner implements CommandLineRunner {
    @Autowired
    private CaptchaServiceFactory factory;

    @Override
    public void run(String... args) throws Exception {
        CaptchaService circleService = factory.findService(CaptchaCategory.circle);
        CaptchaData data = circleService.draw();
        log.info(GsonUtils.toJson(data));
        saveImage(data);

        CaptchaService lineService = factory.findService(CaptchaCategory.line);
        data = lineService.draw();
        saveImage(data);

        CaptchaService gifService = factory.findService(CaptchaCategory.gif);
        data = gifService.draw();
        saveImage(data);

        CaptchaService shearService = factory.findService(CaptchaCategory.shear);
        data = shearService.draw();
        saveImage(data);

        CaptchaService blockPuzzleService = factory.findService(CaptchaCategory.blockPuzzle);
        data = blockPuzzleService.draw();
        saveImage(data);

        CaptchaService clickWordService = factory.findService(CaptchaCategory.clickWord);
        data = clickWordService.draw();
        saveImage(data);
    }

    private void saveImage(CaptchaData data) throws IOException {
        Path targetDir = Paths.get("target", "processed-images");
        if (!Files.exists(targetDir)) {
            Files.createDirectories(targetDir);
        }

        Path filePath = targetDir.resolve(data.category().name() + "-captcha." + data.category().getExt());
        Files.write(filePath, data.captchaImage());

        if (data.backgroundImage() != null) {
            filePath = targetDir.resolve(data.category().name() + "-background." + data.category().getExt());
            Files.write(filePath, data.backgroundImage());
        }
    }
}

所有的验证码服务生成的验证码信息是统一的,使用更加方便

验证码信息(json)格式

{"key":"5e1bf15c178b4ea3a6d0599ba8fb3743","captchaImage":[-119,80,78,71,13,10,....],"backgroundImage":null,"code":"z8yOk","category":"circle"}

验证码信息属性说明

属性 说明

key

String类型,验证码ID,生成的每个验证码都有唯一的ID

captchaImage

byte[]类型,验证码图片。

backgroundImage

byte[]类型,验证码背景图片。分类是blockPuzzle才有值

code

String类型,校验码,

category

枚举类型,分类

如果需要Base64类型的图片信息,可以调用`CaptchaData.captchaImageBase64Url`或`CaptchaData.backgroundImageBase64Url`方法

使用验证码缓存工厂(CaptchaCacheFactory)存储验证码信息,支持`Hutool`框架的`TimedCache`缓存和`Guava`框架的`LoadingCache`缓存,可以自由 选择缓存,而且可以自定义配置

@Slf4j
@Configuration
public class CaptchaConfiguration {

    @PostConstruct
    void postConstruct() {
        // 设置过期时间
        CaptchaCacheFactory.initHtoolCache(Duration.ofSeconds(30));
    }

}

验证码缓存工厂(CaptchaCacheFactory)功能与 验证码服务工厂(CaptchaServiceFactory)功能没有关联

写一个简单的验证码生成及校验接口

@RestController
@RequiredArgsConstructor
public class CaptchaController {
    private final CaptchaServiceFactory factory;
    private final CaptchaCache cache = CaptchaCacheFactory.getInstance();

    @GetMapping("/captcha")
    public CaptchaReponse get(@RequestParam(name = "category") CaptchaCategory category) {
        CaptchaService service = factory.findService(category);
        CaptchaData data = service.draw();

        CaptchaReponse reponse = new CaptchaReponse();
        reponse.setKey(data.key());
        reponse.setCategory(service.getCategory());
        reponse.setCaptchaImageBase64(data.captchaImageBase64Url());
        reponse.setBackgroundImageBase64(data.backgroundImageBase64Url());

        cache.put(data.key(), data.code());
        System.out.println("code=" + data.code());
        return reponse;
    }

    @PostMapping("/captcha")
    public String post(@RequestBody CaptchaValidate validate) {
        String cacheCode = cache.get(validate.getKey()).orElse(null);
        if (!StringUtils.hasText(cacheCode)) {
            return "验证码校验失败";
        }

        CaptchaService service = factory.findService(validate.getCategory());
        boolean pass = service.verify(cacheCode, validate.getCode());
        if (pass) {
            return "验证码校验成功";
        } else {
            return "验证码校验失败";
        }
    }
}

获取验证码请求

curl --location --request GET 'http://localhost:8080/captcha?category=line' \
--header 'Accept: /' \
--header 'Host: localhost:8080' \
--header 'Connection: keep-alive'

校验验证码请求

curl --location --request POST 'http://localhost:8080/captcha' \
--header 'Content-Type: application/json' \
--header 'Accept: /' \
--header 'Host: localhost:8080' \
--header 'Connection: keep-alive' \
--data-raw '{
    "key": "e3d15841b93543f88e2404fc93cd8edb",
    "category": "line",
    "code": "1kNk9"
}'

*注意*编写生成验证码接口时,不要将校验码(CaptchaData.code属性)返回给前端