本文目录导读:

我将为您实现一个完整的短链接服务案例,包含核心功能和使用示例。
核心短链接生成器
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
public class ShortLinkGenerator {
// 短链接字符集
private static final String BASE62 = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
private static final int BASE = 62;
// 存储映射关系
private static final Map<String, String> shortToLongMap = new ConcurrentHashMap<>();
private static final Map<String, String> longToShortMap = new ConcurrentHashMap<>();
// 自增ID
private static final AtomicLong idGenerator = new AtomicLong(1000000);
// 短链接前缀
private static final String DOMAIN = "http://short.link/";
/**
* 方法1:基于自增ID生成短链接
*/
public static String generateByID(String longUrl) {
// 如果已经生成了,直接返回
if (longToShortMap.containsKey(longUrl)) {
return DOMAIN + longToShortMap.get(longUrl);
}
// 获取自增ID并转换为62进制
long id = idGenerator.getAndIncrement();
String shortCode = encode(id);
// 存储映射关系
shortToLongMap.put(shortCode, longUrl);
longToShortMap.put(longUrl, shortCode);
return DOMAIN + shortCode;
}
/**
* 方法2:基于MD5+Hash生成短链接
*/
public static String generateByMD5(String longUrl) {
if (longToShortMap.containsKey(longUrl)) {
return DOMAIN + longToShortMap.get(longUrl);
}
String md5 = getMD5(longUrl);
String shortCode = md5.substring(0, 8);
// 处理冲突
while (shortToLongMap.containsKey(shortCode)) {
shortCode = md5.substring(0, shortCode.length() + 1);
}
shortToLongMap.put(shortCode, longUrl);
longToShortMap.put(longUrl, shortCode);
return DOMAIN + shortCode;
}
/**
* 方法3:基于随机数生成短链接
*/
public static String generateByRandom(String longUrl) {
if (longToShortMap.containsKey(longUrl)) {
return DOMAIN + longToShortMap.get(longUrl);
}
String shortCode = generateRandomCode(6);
// 处理冲突
while (shortToLongMap.containsKey(shortCode)) {
shortCode = generateRandomCode(6);
}
shortToLongMap.put(shortCode, longUrl);
longToShortMap.put(longUrl, shortCode);
return DOMAIN + shortCode;
}
/**
* 将长链接转换为短链接代码
*/
private static String encode(long num) {
StringBuilder sb = new StringBuilder();
while (num > 0) {
sb.insert(0, BASE62.charAt((int) (num % BASE)));
num = num / BASE;
}
return sb.toString();
}
/**
* 生成MD5哈希值
*/
private static String getMD5(String input) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] messageDigest = md.digest(input.getBytes());
StringBuilder hexString = new StringBuilder();
for (byte b : messageDigest) {
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);
}
}
/**
* 生成随机短代码
*/
private static String generateRandomCode(int length) {
StringBuilder sb = new StringBuilder();
java.util.Random random = new java.util.Random();
for (int i = 0; i < length; i++) {
int index = random.nextInt(BASE);
sb.append(BASE62.charAt(index));
}
return sb.toString();
}
/**
* 根据短代码获取原始URL
*/
public static String getLongUrl(String shortCode) {
if (shortCode.startsWith(DOMAIN)) {
shortCode = shortCode.substring(DOMAIN.length());
}
return shortToLongMap.get(shortCode);
}
/**
* 获取映射表大小
*/
public static int getMapSize() {
return shortToLongMap.size();
}
}
完整短链接服务类
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class ShortLinkService {
// 短链接存储
private final Map<String, LinkInfo> shortLinkMap = new ConcurrentHashMap<>();
private final Map<String, String> longLinkMap = new ConcurrentHashMap<>();
// 配置
private final String domain;
private final int shortCodeLength;
private final boolean cacheEnabled;
private static final String CHARACTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
private static final int BASE = 62;
public ShortLinkService(String domain, int shortCodeLength, boolean cacheEnabled) {
this.domain = domain;
this.shortCodeLength = shortCodeLength;
this.cacheEnabled = cacheEnabled;
}
/**
* 链接信息类
*/
public static class LinkInfo {
private String longUrl;
private Date createdAt;
private Date expiresAt;
private long clickCount;
private Map<String, Integer> dailyClicks = new HashMap<>();
public LinkInfo(String longUrl) {
this.longUrl = longUrl;
this.createdAt = new Date();
this.clickCount = 0;
}
// Getters and Setters
public String getLongUrl() { return longUrl; }
public void setLongUrl(String longUrl) { this.longUrl = longUrl; }
public Date getCreatedAt() { return createdAt; }
public void setCreatedAt(Date createdAt) { this.createdAt = createdAt; }
public Date getExpiresAt() { return expiresAt; }
public void setExpiresAt(Date expiresAt) { this.expiresAt = expiresAt; }
public long getClickCount() { return clickCount; }
public void incrementClickCount() { this.clickCount++; }
public Map<String, Integer> getDailyClicks() { return dailyClicks; }
public void incrementDailyClick() {
String today = new java.text.SimpleDateFormat("yyyy-MM-dd").format(new Date());
dailyClicks.merge(today, 1, Integer::sum);
}
}
/**
* 生成短链接
*/
public String createShortLink(String longUrl, Integer expirationDays) {
// 检查长链接是否已存在
if (cacheEnabled && longLinkMap.containsKey(longUrl)) {
String existingShortCode = longLinkMap.get(longUrl);
return domain + existingShortCode;
}
// 生成短代码
String shortCode = generateShortCode();
while (shortLinkMap.containsKey(shortCode)) {
shortCode = generateShortCode();
}
// 创建链接信息
LinkInfo info = new LinkInfo(longUrl);
if (expirationDays != null && expirationDays > 0) {
info.setExpiresAt(new Date(System.currentTimeMillis() + expirationDays * 24 * 60 * 60 * 1000));
}
// 存储映射关系
shortLinkMap.put(shortCode, info);
longLinkMap.put(longUrl, shortCode);
return domain + shortCode;
}
/**
* 解析短链接
*/
public String resolveShortLink(String shortUrl) {
String shortCode = extractShortCode(shortUrl);
if (shortCode == null) return null;
LinkInfo info = shortLinkMap.get(shortCode);
if (info == null) return null;
// 检查过期
if (info.getExpiresAt() != null && info.getExpiresAt().before(new Date())) {
return null; // 链接已过期
}
// 更新统计数据
info.incrementClickCount();
info.incrementDailyClick();
return info.getLongUrl();
}
/**
* 从URL中提取短代码
*/
private String extractShortCode(String url) {
if (url == null || url.isEmpty()) return null;
// 如果包含域名,提取最后的短代码
if (url.contains(domain)) {
return url.substring(url.lastIndexOf('/') + 1);
}
// 如果只是短代码
return url;
}
/**
* 生成短代码
*/
private String generateShortCode() {
StringBuilder sb = new StringBuilder();
java.util.Random random = new java.util.Random();
for (int i = 0; i < shortCodeLength; i++) {
int index = random.nextInt(BASE);
sb.append(CHARACTERS.charAt(index));
}
return sb.toString();
}
/**
* 获取链接信息
*/
public LinkInfo getLinkInfo(String shortUrl) {
String shortCode = extractShortCode(shortUrl);
return shortCode != null ? shortLinkMap.get(shortCode) : null;
}
/**
* 删除短链接
*/
public boolean deleteShortLink(String shortUrl) {
String shortCode = extractShortCode(shortUrl);
LinkInfo info = shortLinkMap.remove(shortCode);
if (info != null) {
longLinkMap.remove(info.getLongUrl());
return true;
}
return false;
}
/**
* 获取统计信息
*/
public String getStats(String shortUrl) {
LinkInfo info = getLinkInfo(shortUrl);
if (info == null) {
return "短链接不存在";
}
StringBuilder stats = new StringBuilder();
stats.append("原链接: ").append(info.getLongUrl()).append("\n");
stats.append("创建时间: ").append(info.getCreatedAt()).append("\n");
if (info.getExpiresAt() != null) {
stats.append("过期时间: ").append(info.getExpiresAt()).append("\n");
}
stats.append("总点击量: ").append(info.getClickCount()).append("\n");
stats.append("最近7天点击统计:\n");
// 计算最近7天数据
for (int i = 6; i >= 0; i--) {
String date = new java.text.SimpleDateFormat("yyyy-MM-dd")
.format(new Date(System.currentTimeMillis() - i * 24 * 60 * 60 * 1000));
Integer clicks = info.getDailyClicks().get(date);
stats.append(date).append(": ").append(clicks != null ? clicks : 0).append("\n");
}
return stats.toString();
}
}
数据库存储版本(使用JDBC)
import java.sql.*;
import java.util.Optional;
public class DatabaseShortLinkService {
private final String jdbcUrl;
private final String username;
private final String password;
public DatabaseShortLinkService(String jdbcUrl, String username, String password) {
this.jdbcUrl = jdbcUrl;
this.username = username;
this.password = password;
initDatabase();
}
/**
* 初始化数据库表
*/
private void initDatabase() {
String createTableSQL = """
CREATE TABLE IF NOT EXISTS short_links (
id INT AUTO_INCREMENT PRIMARY KEY,
short_code VARCHAR(10) NOT NULL UNIQUE,
long_url TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
expires_at TIMESTAMP NULL,
click_count INT DEFAULT 0,
INDEX idx_short_code (short_code),
INDEX idx_long_url (long_url(255))
)
""";
try (Connection conn = getConnection();
Statement stmt = conn.createStatement()) {
stmt.execute(createTableSQL);
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 获取数据库连接
*/
private Connection getConnection() throws SQLException {
return DriverManager.getConnection(jdbcUrl, username, password);
}
/**
* 创建短链接
*/
public String createShortLink(String longUrl, int expirationDays) {
String shortCode = generateUniqueShortCode();
String insertSQL = """
INSERT INTO short_links (short_code, long_url, expires_at)
VALUES (?, ?, ?)
""";
try (Connection conn = getConnection();
PreparedStatement pstmt = conn.prepareStatement(insertSQL)) {
pstmt.setString(1, shortCode);
pstmt.setString(2, longUrl);
if (expirationDays > 0) {
pstmt.setTimestamp(3, new Timestamp(
System.currentTimeMillis() + expirationDays * 24 * 60 * 60 * 1000));
} else {
pstmt.setNull(3, Types.TIMESTAMP);
}
pstmt.executeUpdate();
return shortCode;
} catch (SQLException e) {
throw new RuntimeException("创建短链接失败", e);
}
}
/**
* 查询长链接
*/
public Optional<String> resolveShortLink(String shortCode) {
String querySQL = """
SELECT long_url FROM short_links
WHERE short_code = ?
AND (expires_at IS NULL OR expires_at > NOW())
""";
try (Connection conn = getConnection();
PreparedStatement pstmt = conn.prepareStatement(querySQL)) {
pstmt.setString(1, shortCode);
ResultSet rs = pstmt.executeQuery();
if (rs.next()) {
// 增加点击量
incrementClickCount(shortCode);
return Optional.of(rs.getString("long_url"));
}
} catch (SQLException e) {
throw new RuntimeException("查询短链接失败", e);
}
return Optional.empty();
}
/**
* 增加点击量
*/
private void incrementClickCount(String shortCode) {
String updateSQL = "UPDATE short_links SET click_count = click_count + 1 WHERE short_code = ?";
try (Connection conn = getConnection();
PreparedStatement pstmt = conn.prepareStatement(updateSQL)) {
pstmt.setString(1, shortCode);
pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 生成唯一的短代码
*/
private String generateUniqueShortCode() {
String characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
StringBuilder sb = new StringBuilder();
java.util.Random random = new java.util.Random();
// 生成6位短代码
for (int i = 0; i < 6; i++) {
sb.append(characters.charAt(random.nextInt(62)));
}
return sb.toString();
}
/**
* 删除过期的短链接
*/
public void deleteExpiredLinks() {
String deleteSQL = "DELETE FROM short_links WHERE expires_at < NOW()";
try (Connection conn = getConnection();
Statement stmt = conn.createStatement()) {
stmt.executeUpdate(deleteSQL);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
使用示例
public class ShortLinkDemo {
public static void main(String[] args) {
// 示例1:使用基础短链接生成器
System.out.println("=== 基础短链接生成器 ===");
String longUrl1 = "https://www.example.com/articles/2024/product-launch/announcement/details";
String shortUrl1 = ShortLinkGenerator.generateByID(longUrl1);
System.out.println("原始URL: " + longUrl1);
System.out.println("短链接ID法: " + shortUrl1);
String shortUrl2 = ShortLinkGenerator.generateByMD5(longUrl1);
System.out.println("短链接MD5法: " + shortUrl2);
String shortUrl3 = ShortLinkGenerator.generateByRandom(longUrl1);
System.out.println("短链接随机法: " + shortUrl3);
// 解析短链接
String shortCode = shortUrl1.substring(shortUrl1.lastIndexOf('/') + 1);
System.out.println("解析短链接: " + ShortLinkGenerator.getLongUrl(shortCode));
System.out.println("\n=== 高级短链接服务 ===");
// 示例2:使用高级短链接服务
ShortLinkService service = new ShortLinkService(
"https://s.example.com/",
8,
true
);
String longUrl = "https://www.example.com/very/long/path/to/article?id=123&category=tech";
// 创建短链接(7天过期)
String shortUrl = service.createShortLink(longUrl, 7);
System.out.println("创建短链接: " + shortUrl);
// 模拟访问
String resolvedUrl = service.resolveShortLink(shortUrl);
System.out.println("解析得到: " + resolvedUrl);
// 再次访问,点击量增加
service.resolveShortLink(shortUrl);
// 查看统计信息
System.out.println("\n统计信息:");
System.out.println(service.getStats(shortUrl));
// 示例3:数据库版本
System.out.println("\n=== 数据库版本示例 ===");
// 数据库配置
String jdbcUrl = "jdbc:mysql://localhost:3306/urldb";
String dbUser = "root";
String dbPassword = "password";
// 创建服务(需要MySQL驱动)
try {
DatabaseShortLinkService dbService = new DatabaseShortLinkService(
jdbcUrl, dbUser, dbPassword
);
String dbShortCode = dbService.createShortLink(longUrl, 30);
System.out.println("数据库生成的短代码: " + dbShortCode);
Optional<String> dbResolvedUrl = dbService.resolveShortLink(dbShortCode);
dbResolvedUrl.ifPresent(url ->
System.out.println("数据库解析结果: " + url)
);
// 清理过期链接
dbService.deleteExpiredLinks();
} catch (Exception e) {
System.out.println("数据库操作失败(可能需要配置MySQL)");
}
}
}
性能优化版本
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class OptimizedShortLinkService {
// 使用读写锁提高并发性能
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
private final ReentrantReadWriteLock.ReadLock readLock = lock.readLock();
private final ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock();
// 使用LRU缓存(简化版)
private final int maxCacheSize = 10000;
private final Map<String, String> cache = new java.util.LinkedHashMap<>(maxCacheSize, 0.75f, true) {
@Override
protected boolean removeEldestEntry(Map.Entry<String, String> eldest) {
return size() > maxCacheSize;
}
};
// 自增ID
private final AtomicLong counter = new AtomicLong();
// 短链接算法版本
public String generateShortLink(String longUrl) {
// 先检查缓存(读操作)
readLock.lock();
try {
if (cache.containsKey(longUrl)) {
return cache.get(longUrl);
}
} finally {
readLock.unlock();
}
// 生成新链接(写操作)
writeLock.lock();
try {
String shortCode = convertBase62(counter.incrementAndGet());
cache.put(longUrl, shortCode);
return shortCode;
} finally {
writeLock.unlock();
}
}
/**
* 转换为62进制
*/
private String convertBase62(long number) {
String chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
StringBuilder sb = new StringBuilder();
while (number > 0) {
sb.insert(0, chars.charAt((int) (number % 62)));
number /= 62;
}
return sb.toString();
}
/**
* 批量生成短链接
*/
public List<String> batchGenerate(List<String> longUrls) {
List<String> shortCodes = new ArrayList<>();
long startId = counter.getAndAdd(longUrls.size());
for (int i = 0; i < longUrls.size(); i++) {
String shortCode = convertBase62(startId + i);
cache.put(longUrls.get(i), shortCode);
shortCodes.add(shortCode);
}
return shortCodes;
}
}
这个完整的短链接服务实现包含了:
- 三种生成算法:自增ID法、MD5哈希法、随机数法
- 内存和数据库存储两种方案
- 完整的CRUD操作和统计功能
- 并发安全设计
- 过期机制和缓存优化
可以根据实际需求选择合适的实现方案,对于小型项目,使用内存版本即可;对于生产环境,建议使用数据库版本配合适当的缓存策略。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播