目 录CONTENT

文章目录

Spring Boot 短链接系统实现

在等晚風吹
2024-09-27 / 0 评论 / 0 点赞 / 26 阅读 / 0 字 / 正在检测是否收录...

Spring Boot 短链接系统实现

前言

短链接系统是一种将长URL转换为短URL的服务,方便用户分享和复制。本文使用了SpringBoot开发了一个简易的短链接转换接口,和短链接重定向接口。

一、短链接系统入门🍉

1. 什么是短链接系统?

短链接系统能够将长URL转换为短URL,用户点击后会被重定向至原始URL。这在社交媒体等字符限制的平台上尤为重要。

short_link1.png

短链接的优势:

  • 便捷分享:短链接更易于分享,外观更加美观。
  • 提升用户体验:简化用户输入,减少访问链接时的操作。
  • 推广与营销:跟踪广告点击和转化率,优化营销策略。

2. 准备工作

(1)创建 Maven 项目

short_link2.png

(2)引入相关依赖

pom.xml 中添加以下依赖:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.1.0</version>
</parent>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>com.google.guava</groupId>
        <artifactId>guava</artifactId>
        <version>30.1.1-jre</version>
    </dependency>
</dependencies>

增加 application.yaml 配置文件

server:
  port: 8888
spring:
  application:
    name: shorten-service
  datasource:
    url: jdbc:mysql://localhost:3306/shorten_db?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC
    username: root
    password: xxxx # 修改为自己的密码
  jpa:
    hibernate:
      ddl-auto: create-drop
    properties:
      hibernate:
        show_sql: true
        format_sql: true

(3)创建启动类

package org.shortenservice;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ShortenServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(ShortenServiceApplication.class, args);
    }
}

(4)自定义响应结果封装类

public class ResponseResult<T> {
    private String code;
    private String msg;
    private T data;
}

(5)创建响应工具类

package org.shortenservice.common;

public class ResultUtils {
    private ResultUtils() {}

    public static <T> ResponseResult<T> success(T data) {
        return build("200", "success", data);
    }

    public static ResponseResult<Void> success() {
        return build("200", "success", null);
    }

    public static boolean isSuccess(String code) {
        return "200".equals(code);
    }

    public static ResponseResult<Void> failure(String msg) {
        return build("500", msg, null);
    }

    public static ResponseResult<Void> failure(String code, String msg) {
        return build(code, msg, null);
    }

    public static <T> ResponseResult<T> failure(String code, String msg, T data) {
        return build(code, msg, data);
    }

    public static <T> ResponseResult<T> build(String code, String msg, T data) {
        return new ResponseResult<>(code, msg, data);
    }
}

二、核心功能实现🧁

1. 实现 Base62 编码

package org.shortenservice.utils;

public class Base62Utils {
    private static final String BASE62 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";

    private Base62Utils() {}

    public static String idToShortKey(long id) {
        StringBuilder stringBuilder = new StringBuilder();
        while (id > 0) {
            stringBuilder.append(BASE62.charAt((int) (id % 62)));
            id = id / 62;
        }
        while (stringBuilder.length() < 6) {
            stringBuilder.append(0);
        }
        return stringBuilder.reverse().toString();
    }

    public static long shortKeyToId(String shortKey) {
        long id = 0;
        for (int i = 0; i < shortKey.length(); i++) {
            id = id * 62 + BASE62.indexOf(shortKey.charAt(i));
        }
        return id;
    }
}

short_link3.png

方法解释

idToShortKey方法:

  1. 创建一个StringBuilder对象,用于存储转换后的字符串。
  2. 使用while循环,当id大于0时,执行循环体。在循环体中,首先计算id除以62的余数,然后将余数对应的BASE62字符添加到StringBuilder对象中。接着,将id除以62,更新id的值。
  3. 当id小于等于0时,跳出循环。此时,StringBuilder对象中的字符串长度可能小于6。为了确保字符串长度为6,使用另一个while循环,在StringBuilder对象的开头添加0,直到其长度达到6。
  4. 最后,将StringBuilder对象反转,并将其转换为字符串返回。

shortKeyToId方法:

  1. 创建一个名为id的长整型变量,初始值为0。
  2. 使用for循环遍历shortKey字符串中的每个字符。在循环体中,首先计算当前字符在BASE62字符串中的索引值,然后将id乘以62,再加上当前字符的索引值。将结果赋值给id。
  3. 当所有字符都遍历完毕后,返回id作为最终结果。

2. 创建实体类

package org.shortenservice.model;

import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.CreationTimestamp;

import java.time.Instant;

@Entity
@Table(name = "t_url_map", indexes = {
    @Index(columnList = "longUrl", unique = true),
    @Index(columnList = "expireTime", unique = false)
})
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class UrlMap {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String longUrl;
    private Instant expireTime;
    @CreationTimestamp
    private Instant creationTime;
}

3. 创建 DAO 层

package org.shortenservice.dao;

import org.shortenservice.model.UrlMap;
import org.springframework.data.repository.CrudRepository;

import java.time.Instant;
import java.util.List;

public interface UrlMapDao extends CrudRepository<UrlMap, Long> {
    UrlMap findFirstByLongUrl(String longUrl);
    List<UrlMap> findByExpireTimeBefore(Instant instant);
}

4. 创建 Service 层

package org.shortenservice.service;

import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.shortenservice.dao.UrlMapDao;
import org.shortenservice.model.UrlMap;
import org.shortenservice.utils.Base62Utils;
import org.springframework.stereotype.Service;

import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Optional;

@Service
@Slf4j
public class UrlMapService {
    @Resource
    UrlMapDao urlMapDao;

    public String encode(String longUrl) {
        UrlMap urlMap = urlMapDao.findFirstByLongUrl(longUrl);
        if (urlMap == null) {
            urlMap = urlMapDao.save(UrlMap.builder()
                    .longUrl(longUrl)
                    .expireTime(Instant.now().plus(30, ChronoUnit.DAYS))
                    .build());
            log.info("create urlMap:{}", urlMap);
        }
        return Base62Utils.idToShortKey(urlMap.getId());
    }

    public Optional<String> decode(String shortKey) {
        long id = Base62Utils.shortKeyToId(shortKey);
        return urlMapDao.findById(id).map(UrlMap::getLongUrl);
    }
}

5. 编写控制器

package org.shortenservice.controller;

import jakarta.annotation.Resource;
import org.shortenservice.common.ResponseResult;
import org.shortenservice.common.ResultUtils;
import org.shortenservice.service.UrlMapService;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.view.RedirectView;

import java.util.Map;

@RestController
public class UrlMapController {
    private static final String DOMAIN = "http://127.0.0.1:8888/";

    @Resource
    private UrlMapService urlMapService;

    /***
     * 长链接转短链接
     * @param longUrl 长链接
     * @return ResponseResult
     */
    @PostMapping("/shorten")
    public ResponseResult<Map> shorten(@RequestParam("longUrl") String longUrl) {
        String encode = urlMapService.encode(longUrl);
        return ResultUtils.success(Map.of("shortKey", encode, "shortUrl", DOMAIN + encode));
    }

    /***
     * 短链接重定向
     * @param shortKey 短链接
     * @return RedirectView
     */
    @GetMapping("/{shortKey}")
    public RedirectView redirect(@PathVariable("shortKey") String shortKey) {
        return urlMapService.decode(shortKey).map(RedirectView::new)
                .orElse(new RedirectView("/sorry"));
    }

    @GetMapping("/sorry")
    public String sorry() {
        return "抱歉,未找到页面!";
    }
}

6. 使用 curl 测试

将长链接转为短链接:

curl -X POST "localhost:8888/shorten?longUrl=https://i.csdn.net/#/user-center/profile?spm=1011.2415.

3001.9999.1&c=4"

访问短链接:

curl -L "http://127.0.0.1:8888/shortKey"

short_link3.png

short_link4.png

三、系统优化🍱

1. 缓存简介

缓存技术能提升数据处理速度,特别适用于高并发和频繁访问的场景。

2. 引入 Guava

UrlMapService 中利用 Guava 实现本地缓存,提升短链接的解码效率。

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;

@Service
@Slf4j
public class UrlMapService {
    private final Cache<Long, String> cache = CacheBuilder.newBuilder().maximumSize(1000).build();

    public Optional<String> decode(String shortKey) {
        long id = Base62Utils.shortKeyToId(shortKey);
        return Optional.ofNullable(cache.getIfPresent(id)).or(() -> {
            return urlMapDao.findById(id).map(urlMap -> {
                cache.put(id, urlMap.getLongUrl());
                return urlMap.getLongUrl();
            });
        });
    }
}

结论

短链接系统的实现需高效且可扩展,尤其要注重数据保护和安全标准。希望本文能为您提供一个清晰的实现思路。

0

评论区