跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
JavaSaaSjava算法

Spring Boot 实战:从零设计短链系统(附代码与数据库设计)

短链系统通过映射长链接为简短标识来优化分享体验,常用于社交平台、短信营销及流量追踪。基于 Spring Boot 实现简易短链服务,对比自增 ID 转 Base62 编码与哈希算法两种生成策略。内容涵盖数据库模型设计、实体类构建、工具类封装及控制层接口实现,重点解析短码唯一性校验、302 重定向逻辑及过期时间处理。提供完整代码示例与测试步骤,助开发者快速理解短链系统核心架构与落地实践。

leon发布于 2026/3/27更新于 2026/6/1124 浏览
Spring Boot 实战:从零设计短链系统(附代码与数据库设计)

Spring Boot 实战:从零设计短链系统

问题分析:为什么我们需要'短链'?

相信很多小伙伴在日常生活中都收到过一些营销短信,里面附带了一些链接地址。是否发现链接实际上很短,点击后会再次访问一个相对长的链接。

例如我们收到的短链可能是这样子:

https://www.example.com/aBcDeF 

点开它,会跳转到以下原始长链接(甚至更长…参数更多):

https://www.example.com/product/detail?id=134985&from=share&ref=longtext&utm_source=wechat 

那就有小伙伴问了,直接分享这个长链接不行么?为何多此一举通过短链条转长链?

那我们就来看看常见的短链应用场景:

  • 社交平台分享:如微博、朋友圈、抖音简介中限制字符数;
  • 短信营销:短链可以有效减少短信的字符数,从而减少短信费用;
  • 营销追踪:每个渠道生成不同短链,统计访问量;
  • 二维码应用:短链生成二维码更清晰、美观;
  • 安全控制:短链服务可做访问限制、过期时间等策略。

系统设计分析

要实现一个简易的短链系统,我们至少需要解决以下问题:

模块说明
短链生成策略如何将长链接映射成短码?并保证唯一性和随机性
存储设计如何保存短码与原始链接的映射关系?
重定向服务访问短链时,如何高效跳转到原始链接?
过期策略(可选)是否允许短链设置有效期?
统计分析(可选)统计点击量、来源等指标。

如果你要实现一个更高性能的短链系统,你还需要引入缓存设计,如 redis。本章节更多的是探讨短链系统,小伙伴们可以自行在业务层追加缓存来实现减少数据库的访问。

数据库设计

这里我们只实现最核心的短链映射功能。

字段名类型说明
idbigint主键
short_codevarchar(10)短链编码(唯一索引)
original_urlvarchar(500)原始长链接
expire_timedatetime过期时间(可选)
create_timedatetime创建时间

SQL 建表语句:

CREATE TABLE short_link (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    short_code VARCHAR(10) NOT NULL UNIQUE,
    original_url VARCHAR(500) NOT NULL,
    expire_time DATETIME NULL,
    create_time DATETIME DEFAULT CURRENT_TIMESTAMP
);

短链生成策略设计

短链生成核心是将长链接转换成唯一的短码,常见方式有:

  • 自增 ID + Base62 编码(简单可靠,唯一性高)
  • 哈希算法(MD5/SHA)(可重复生成,长度可控)

自增 ID + Base62 编码:Base62 使用 [0-9][a-z][A-Z] 共 62 个字符编码,可生成短而唯一的字符串。

哈希算法(如 MD5 或 SHA-256)的主要作用是将一个长度不定的输入(如 URL)映射为固定长度的输出(哈希值)。

实战开始

下面讲解以上两种的实现方式。

❶ 项目依赖

在正式开发前我们先在 SpringBoot 项目引入相关依赖:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>com.mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
</dependencies>

❷ 构建实体类

import jakarta.persistence.*;
import lombok.Data;
import java.time.LocalDateTime;

@Data
@Entity
@Table(name = "short_link")
public class ShortLink {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(unique = true, nullable = false)
    private String shortCode;

    @Column(nullable = false, length = 500)
    private String originalUrl;

    private LocalDateTime expireTime;
    private LocalDateTime createTime = LocalDateTime.now();
}

❸ 工具类

Base62 工具类:使用自增 ID + Base62 编码。

public class Base62Utils {
    private static final String CHARSET = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

    public static String encode(long num) {
        StringBuilder sb = new StringBuilder();
        while (num > 0) {
            int index = (int) (num % 62);
            sb.append(CHARSET.charAt(index));
            num /= 62;
        }
        return sb.reverse().toString();
    }
}

哈希生成工具类(MD5/SHA):我们将实现一个工具类,通过 MD5 或 SHA-256 来生成哈希值,然后从中截取短链。

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class HashUtils {
    // 使用 MD5 算法生成短链
    public static String generateHash(String input, String algorithm) {
        try {
            MessageDigest md = MessageDigest.getInstance(algorithm);
            byte[] hashBytes = md.digest(input.getBytes());
            // 转换为 16 进制表示
            StringBuilder hexString = new StringBuilder();
            for (byte b : hashBytes) {
                String hex = Integer.toHexString(0xff & b);
                if (hex.length() == 1) {
                    hexString.append('0');
                }
                hexString.append(hex);
            }
            return hexString.toString();
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("哈希算法异常:" + e.getMessage());
        }
    }

    // 使用 MD5 生成短链的前 8 位
    public static String generateShortLink(String originalUrl) {
        String md5Hash = generateHash(originalUrl, "MD5");
        return md5Hash.substring(0, 8); // 返回前 8 位
    }

    // 使用 SHA-256 生成短链的前 10 位
    public static String generateShortLinkSHA(String originalUrl) {
        String sha256Hash = generateHash(originalUrl, "SHA-256");
        return sha256Hash.substring(0, 10); // 返回前 10 位
    }
}

❹ 服务层 Service

为了更好的区分两种方式,这里我们将用两个 Service 来方便区分。

Base62ShortLinkService

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
public class Base62ShortLinkService {
    private final ShortLinkRepository repository;

    @Transactional
    public String createShortLink(String originalUrl) {
        ShortLink entity = new ShortLink();
        entity.setOriginalUrl(originalUrl);
        ShortLink saved = repository.save(entity);
        String shortCode = Base62Utils.encode(saved.getId());
        saved.setShortCode(shortCode);
        repository.save(saved);
        return shortCode;
    }

    public String getOriginalUrl(String shortCode) {
        ShortLink link = repository.findByShortCode(shortCode);
        if (link == null) {
            throw new RuntimeException("短链不存在或已失效");
        }
        return link.getOriginalUrl();
    }
}

HashShortLinkService

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
public class HashShortLinkService {
    private final ShortLinkRepository repository;

    @Transactional
    public String createShortLink(String originalUrl) {
        // 先生成哈希值(使用 MD5 或 SHA-256)
        String shortCode = HashUtils.generateShortLink(originalUrl);
        // 检查数据库中是否已经存在该短链,避免重复
        if (repository.findByShortCode(shortCode) != null) {
            shortCode = HashUtils.generateShortLinkSHA(originalUrl); // 如果冲突,尝试用 SHA-256 生成短链
        }
        // 保存短链映射
        ShortLink entity = new ShortLink();
        entity.setOriginalUrl(originalUrl);
        entity.setShortCode(shortCode);
        repository.save(entity);
        return shortCode;
    }

    public String getOriginalUrl(String shortCode) {
        ShortLink link = repository.findByShortCode(shortCode);
        if (link == null) {
            throw new RuntimeException("短链不存在或已失效");
        }
        return link.getOriginalUrl();
    }
}

❺ 控制层 Controller

import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.view.RedirectView;

@RestController
@RequiredArgsConstructor
public class ShortLinkController {
    private final Base62ShortLinkService base62ShortLinkService;
    private final HashShortLinkService hashShortLinkService;

    // 创建短链
    @PostMapping("/api/shorten")
    public String create(@RequestParam String url) {
        String code = base62ShortLinkService.createShortLink(url);
        // 切换 Hash
        // String code = hashShortLinkService.createShortLink(url);
        return "短链生成成功:" + "http://localhost:8080/" + code;
    }

    // 访问短链
    @GetMapping("/{code}")
    public RedirectView redirect(@PathVariable String code) {
        String originalUrl = base62ShortLinkService.getOriginalUrl(code);
        // 切换 Hash
        // String originalUrl = hashShortLinkService.getOriginalUrl(code);
        // 302 重定向
        return new RedirectView(originalUrl);
    }
}

❻ 运行与测试

根据控制层的代码演示,小伙伴们可以自行切换两种实现方式,进行测试。

以 Hash 为例生成短链接口:

POST http://localhost:8080/api/shorten?url=https://www.example.com/article/long-url 

返回:

短链生成成功:http://localhost:8080/d41d8cd9 

浏览器中打开短链,直接看跳转效果!

总结

本文通过讲解了两种常见的短链生成方式:自增 ID + Base62 编码 和 哈希算法。并通过完整的简易示例代码,让开发者可以快速理解并掌握短链系统的设计!

目录

  1. Spring Boot 实战:从零设计短链系统
  2. 问题分析:为什么我们需要“短链”?
  3. 系统设计分析
  4. 数据库设计
  5. 短链生成策略设计
  6. 实战开始
  7. ❶ 项目依赖
  8. ❷ 构建实体类
  9. ❸ 工具类
  10. ❹ 服务层 Service
  11. ❺ 控制层 Controller
  12. ❻ 运行与测试
  13. 总结
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • 腾讯云服务器部署 OpenClaw 对接飞书实战
  • Ubuntu 22.04 源码编译安装 CARLA 0.9.15 教程
  • Python Web 开发:基于 Flask 与 MicroPython 的 IoT 边缘推理平台
  • 2026自愈式爬虫开发:Python+AI应对页面改版
  • Chrome 浏览器集成 Gemini 功能:网页浏览实时 AI 问答
  • AI 网络技术编程测试:从理论到实践
  • DeepSeek 内容导出 Word 方案:HTML 转换与自动化脚本实践
  • SQLBot:基于大模型与 RAG 的智能问数系统架构
  • Apache IoTDB 在工业物联网时序数据管理中的核心优势
  • MySQL 与 MCP 集成:从环境构建到 AI 驱动的数据交互
  • SQL 自动生成 ER 图与数据库设计基础
  • 时序数据库选型指南:Apache IoTDB 核心优势分析
  • Docker Compose 部署 PostgreSQL 18 实战指南
  • Dify 与 MySQL 深度整合:基于 MCP 协议的数据交互实践
  • C++ STL 进阶:unordered_set 与 unordered_map 模拟实现
  • Minecraft RCON Web 控制台远程管理指南
  • 网络安全红队与蓝队概念及技能解析
  • openGauss 向量数据库能力与 RAG 应用场景解析
  • 在 Trae 中配置 MySQL-MCP 服务器的完整指南
  • NL2SQL 领域研究报告:融合大语言模型的先进技术与代码实践

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online