缩短 url 是现代应用程序中常见的需求,通常用于减少长 url 的长度,使其更易于分享。url 缩短服务的核心思路是将长 url 映射到一个唯一的短代码。较为复杂的场景可能涉及多种功能,例如:
- 缩短的 url 自动过期(即在一定时间后失效)。
- 统计 url 的访问量。
- 检查并避免短 url 重复。
- 添加安全机制,如防止恶意链接。
场景案例
我们可以设计一个场景:
- 用户通过 api 提交长 url。
- 系统生成短 url,短 url 有有效期(例如 7 天),并存储在数据库中。
- 用户可以通过 api 查询短 url 的访问次数。
- 每当有人访问短 url,系统会记录访问量,并自动重定向到原始的长 url。
- 在短 url 过期后,无法再进行重定向。
技术栈
- spring boot: 用于快速构建 restful api 服务。
- h2 数据库: 用于存储 url 和相关元数据。
- java uuid: 生成唯一短码。
- java 定时任务: 自动清理过期 url。
step 1: 项目结构
src/ ├── main/ │ ├── java/ │ │ └── com/ │ │ └── example/ │ │ ├── urlshortenerapplication.java │ │ ├── controller/ │ │ │ └── urlcontroller.java │ │ ├── model/ │ │ │ └── url.java │ │ ├── repository/ │ │ │ └── urlrepository.java │ │ └── service/ │ │ └── urlservice.java │ └── resources/ │ └── application.properties
step 2: 创建实体类 url
url.java 是用于存储长 url、短 url 以及相关元数据的实体类。
package com.example.model; import javax.persistence.*; import java.time.localdatetime; @entity public class url { @id @generatedvalue(strategy = generationtype.identity) private long id; private string originalurl; private string shortcode; private localdatetime createdat; private localdatetime expiresat; private int visitcount; public url() {} public url(string originalurl, string shortcode, localdatetime createdat, localdatetime expiresat) { this.originalurl = originalurl; this.shortcode = shortcode; this.createdat = createdat; this.expiresat = expiresat; this.visitcount = 0; } // getters and setters }
step 3: 创建 repository 接口
使用 spring data jpa,我们可以快速创建一个操作数据库的接口。
package com.example.repository; import com.example.model.url; import org.springframework.data.jpa.repository.jparepository; import java.util.optional; public interface urlrepository extends jparepository<url, long> { optional<url> findbyshortcode(string shortcode); void deletebyexpiresatbefore(localdatetime datetime); }
step 4: 编写服务类 urlservice
urlservice.java 包含业务逻辑,例如生成短 url、处理 url 重定向、统计访问量等。
package com.example.service; import com.example.model.url; import com.example.repository.urlrepository; import org.springframework.stereotype.service; import java.time.localdatetime; import java.util.optional; import java.util.uuid; @service public class urlservice { private final urlrepository urlrepository; public urlservice(urlrepository urlrepository) { this.urlrepository = urlrepository; } public string shortenurl(string originalurl, int expirationdays) { string shortcode = uuid.randomuuid().tostring().substring(0, 8); // 生成短码 localdatetime createdat = localdatetime.now(); localdatetime expiresat = createdat.plusdays(expirationdays); url url = new url(originalurl, shortcode, createdat, expiresat); urlrepository.save(url); return shortcode; } public optional<url> getoriginalurl(string shortcode) { optional<url> urloptional = urlrepository.findbyshortcode(shortcode); urloptional.ifpresent(url -> { if (url.getexpiresat().isafter(localdatetime.now())) { url.setvisitcount(url.getvisitcount() + 1); urlrepository.save(url); } }); return urloptional.filter(url -> url.getexpiresat().isafter(localdatetime.now())); } public int getvisitcount(string shortcode) { return urlrepository.findbyshortcode(shortcode) .map(url::getvisitcount) .orelse(0); } // 定时任务清理过期 url public void cleanupexpiredurls() { urlrepository.deletebyexpiresatbefore(localdatetime.now()); } }
step 5: 编写 controller
urlcontroller.java 是与前端或其他客户端交互的层。
package com.example.controller; import com.example.model.url; import com.example.service.urlservice; import org.springframework.http.responseentity; import org.springframework.web.bind.annotation.*; import java.util.optional; @restcontroller @requestmapping("/api/url") public class urlcontroller { private final urlservice urlservice; public urlcontroller(urlservice urlservice) { this.urlservice = urlservice; } @postmapping("/shorten") public responseentity<string> shortenurl(@requestparam string originalurl, @requestparam(defaultvalue = "7") int expirationdays) { string shortcode = urlservice.shortenurl(originalurl, expirationdays); return responseentity.ok("shortened url: http://localhost:8080/api/url/" + shortcode); } @getmapping("/{shortcode}") public responseentity<?> redirecturl(@pathvariable string shortcode) { optional<url> urloptional = urlservice.getoriginalurl(shortcode); return urloptional .map(url -> responseentity.status(302).header("location", url.getoriginalurl()).build()) .orelse(responseentity.notfound().build()); } @getmapping("/{shortcode}/stats") public responseentity<integer> geturlstats(@pathvariable string shortcode) { int visitcount = urlservice.getvisitcount(shortcode); return responseentity.ok(visitcount); } }
step 6: 定时清理任务
spring boot 可以通过 @scheduled 注解定期执行任务。我们可以创建一个任务来清理过期的 url。
package com.example.service; import org.springframework.scheduling.annotation.scheduled; import org.springframework.stereotype.component; @component public class cleanuptask { private final urlservice urlservice; public cleanuptask(urlservice urlservice) { this.urlservice = urlservice; } @scheduled(cron = "0 0 0 * * ?") // 每天午夜执行一次 public void cleanexpiredurls() { urlservice.cleanupexpiredurls(); } }
step 7: 配置文件
在 application.properties 中配置 h2 数据库以及其他 spring boot 配置。
spring.datasource.url=jdbc:h2:mem:testdb spring.datasource.driverclassname=org.h2.driver spring.datasource.username=sa spring.datasource.password=password spring.h2.console.enabled=true spring.jpa.hibernate.ddl-auto=update
step 8: 启动类 urlshortenerapplication.java
package com.example; import org.springframework.boot.springapplication; import org.springframework.boot.autoconfigure.springbootapplication; import org.springframework.scheduling.annotation.enablescheduling; @springbootapplication @enablescheduling public class urlshortenerapplication { public static void main(string[] args) { springapplication.run(urlshortenerapplication.class, args); } }
运行服务
- 使用 post 请求 /api/url/shorten 提交长 url 并获取短 url。
- 使用 get 请求 /api/url/{shortcode} 重定向到原始 url。
- 使用 get 请求 /api/url/{shortcode}/stats 获取短 url 的访问量。
- 每天定时任务会清理过期的 url。
总结
通过 spring boot 框架,我们可以快速构建一个带有定时任务、访问统计以及过期处理的 url 缩短服务。在真实场景中,可能还会涉及更多的功能,如用户身份验证、url 黑名单过滤等。
到此这篇关于springboot中缩短一个url链接的实现的文章就介绍到这了,更多相关springboot缩短url链接内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论