用 Spring Boot 4 + Thymeleaf 搭建一个高转化的技术服务落地页
一个自由职业架构师的真实项目复盘:从零搭建带线索收集功能的深色科技风落地页,包含防刷策略、自动建表、响应式布局。
为什么技术人也需要落地页?
做了 8 年云原生架构,接过 100 多个项目,我发现一个问题:技术再好,客户找不到你也没用。
朋友圈、脉脉、GitHub Profile 都是展示渠道,但当客户真的想了解你的服务、提交咨询意向时,你需要一个专业的入口——一个落地页。
不是那种花里胡哨的营销页,而是一个干净、专业、能快速传达价值的技术人官网。
这篇文章记录了我最近用 Spring Boot 4.1.0 + Thymeleaf + MyBatis-Plus + MySQL 搭建个人技术服务落地页的全过程。
技术选型:为什么是这套组合?
| 组件 | 选择 | 理由 |
|---|---|---|
| 框架 | Spring Boot 4.1.0 | 最新 LTS,Java 21 原生支持 |
| 模板引擎 | Thymeleaf | 服务端渲染,SEO 友好,不需要前后端分离 |
| ORM | MyBatis-Plus | 极简 CRUD,一个 BaseMapper 搞定 |
| 数据库 | MySQL | 稳定可靠,云服务商全支持 |
| 样式 | 纯 CSS(无框架) | 单页面不需要 Tailwind/Bootstrap 的重量 |
核心原则:能简单就不复杂。 一个落地页不需要 React,不需要 SPA,不需要微服务。Spring Boot + Thymeleaf 是最短路径。
项目结构
publicity/
├── src/main/java/com/eralong/publicity/
│ ├── PublicityApplication.java # 启动类
│ ├── controller/
│ │ └── PublicityController.java # 唯一的 Controller
│ ├── entity/
│ │ └── Lead.java # 咨询线索实体
│ └── mapper/
│ └── LeadMapper.java # MyBatis-Plus Mapper
├── src/main/resources/
│ ├── application.yaml # 配置文件
│ ├── schema.sql # 自动建表
│ ├── templates/
│ │ ├── index.html # 落地页
│ │ └── leads.html # 线索管理页(后台)
│ └── static/
│ ├── css/style.css # 深色主题样式
│ └── images/ # 案例截图
└── pom.xml
整个项目就这些。没有 service 层——对一个落地页来说,Controller 直接调 Mapper 就够了。过度工程是个人项目的头号杀手。
落地页设计:7 个区块的转化逻辑
一个好的落地页不是随便堆内容,而是有节奏地引导访客:
1. Hero 区 —— 3 秒内传达价值
<section class="hero">
<h1>云原生架构师 · 自由职业</h1>
<p class="subtitle">8 年经验 · 专注高可用、高并发系统设计与落地</p>
<a href="#contact" class="cta-button">免费咨询</a>
</section>
要点:一句话说清楚你是谁、做什么、凭什么。CTA 按钮要醒目。
2. 信任数字 —— 建立初步信任
8+ 年经验 | 100+ 项目 | 99.99% SLA | 7×24 支持
数字是最有说服力的语言。即使是占位数字,也能让页面看起来专业。
3. 服务列表 —— 你能帮客户做什么
三大类:云原生运维、后端开发、部署与交付。每个服务用图标 + 简短描述。
4. 技术栈 —— 展示硬实力
Kubernetes、Docker、Spring Cloud、Nginx、MySQL、Redis... 用 logo 墙的形式展示,一目了然。
5. 合作流程 —— 降低决策成本
需求沟通 → 方案评估 → 签约启动 → 交付验收
让客户知道合作是什么体验,消除未知恐惧。
6. 案例展示 —— 真实项目截图
放真实的案例截图(比如 Nginx SSL 证书维护的工单截图),比任何文案都有说服力。
7. FAQ + 联系表单 —— 消除疑虑,促成转化
常见问题解答 + 底部表单,让有意向的客户直接提交咨询。
核心实现:线索收集与防刷
落地页的核心功能是收集咨询线索。来看 Controller 的关键代码:
表单提交接口
@PostMapping("/contact")
public String submitLead(HttpServletRequest request,
@RequestParam String name,
@RequestParam String contact,
@RequestParam String serviceType,
@RequestParam(required = false) String message,
// 蜜罐字段,正常用户不会填
@RequestParam(required = false) String website,
Model model) {
// 1. 蜜罐检测:如果 honeypot 字段有值,说明是机器人
if (website != null && !website.isBlank()) {
return "redirect:/?submitted=bot";
}
// 2. IP 频率限制:同一 IP 每小时最多提交 3 次
String ip = getClientIp(request);
if (isRateLimited(ip)) {
model.addAttribute("error", "提交过于频繁,请稍后再试");
return "index";
}
// 3. 入库
Lead lead = new Lead();
lead.setName(name);
lead.setContact(contact);
lead.setServiceType(serviceType);
lead.setMessage(message);
lead.setIp(ip);
leadMapper.insert(lead);
return "redirect:/?submitted=ok";
}
防刷策略详解
蜜罐(Honeypot):在表单里放一个 display:none 的字段,正常用户看不到也不会填,但爬虫会无脑填所有字段。如果这个字段有值,直接丢弃。
<!-- 蜜罐字段:对正常用户不可见 -->
<div style="position:absolute;left:-9999px">
<input type="text" name="website" tabindex="-1" autocomplete="off">
</div>
IP 频率限制:用内存 Map 记录每个 IP 的提交时间,1 小时内超过 3 次就拒绝。对个人站来说这个策略足够了——不需要引入 Redis。
private final Map<String, List<LocalDateTime>> rateLimitMap = new ConcurrentHashMap<>();
private boolean isRateLimited(String ip) {
List<LocalDateTime> timestamps = rateLimitMap
.computeIfAbsent(ip, k -> new ArrayList<>());
// 清理 1 小时前的记录
timestamps.removeIf(t -> t.isBefore(LocalDateTime.now().minusHours(1)));
if (timestamps.size() >= 3) return true;
timestamps.add(LocalDateTime.now());
return false;
}
自动建表:零配置数据库初始化
在 resources/schema.sql 里放建表语句,Spring Boot 启动时自动执行:
CREATE TABLE IF NOT EXISTS t_lead (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50),
contact VARCHAR(500),
service_type VARCHAR(50),
message TEXT,
ip VARCHAR(45),
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
配合 application.yaml:
spring:
sql:
init:
mode: always # 每次启动都检查并执行
新环境部署时不需要手动建表,docker-compose up 一把梭。
深色科技风样式
技术人的官网就该有技术味。我选了一套深色主题:
:root {
--bg-primary: #0a0a0f; /* 深黑背景 */
--bg-secondary: #12121a; /* 卡片背景 */
--accent: #00d4ff; /* 科技蓝强调色 */
--accent-hover: #00b8e6;
--text-primary: #e4e4e7;
--text-secondary: #a1a1aa;
}
关键设计点:
- 渐变 Hero 背景:从深黑到深蓝的微妙渐变
- 发光边框:hover 时卡片带
box-shadow: 0 0 20px rgba(0, 212, 255, 0.15) - 无衬线字体:
-apple-system, 'Segoe UI', sans-serif,干净利落 - CSS Grid 布局:服务卡片和技术栈用 Grid 自适应,手机自动单列
响应式断点只用了两个:
@media (max-width: 768px) {
.services-grid, .tech-stack { grid-template-columns: 1fr; }
.hero h1 { font-size: 2rem; }
}
部署:单机 Docker 一把梭
FROM eclipse-temurin:21-jre
COPY target/*.jar app.jar
EXPOSE 80
ENTRYPOINT ["java", "-jar", "/app.jar"]
# docker-compose.yml
services:
publicity:
build: .
ports:
- "80:80"
environment:
- SPRING_DATASOURCE_URL=jdbc:mysql://db:3306/publicity
depends_on:
- db
db:
image: mysql:8
environment:
MYSQL_ROOT_PASSWORD: xxx
MYSQL_DATABASE: publicity
Nginx 反向代理加 HTTPS,配上域名,完事。
复盘与心得
做得好的
- 技术选型克制:没有引入 React/Vue,Thymeleaf 服务端渲染完全够用
- 防刷双保险:蜜罐 + IP 限流,上线至今零垃圾线索
- 自动建表:新环境零配置启动,降低部署心智负担
- 深色主题:技术人官网就该长这样,辨识度高
可以改进的
- 加邮件通知:线索入库后发一封邮件给自己,不用频繁刷后台
- 加访问统计:Umami 或 Plausible(隐私友好的 GA 替代品)
- SEO meta 标签:目前缺少 Open Graph 和 Twitter Card
- 多语言:如果接海外客户,可以加英文版
总结
一个技术人的落地页不需要复杂架构。Spring Boot + Thymeleaf + MySQL,5 个 Java 文件,2 个模板,1 个 CSS,就能搭出一个专业的线索收集站。
关键不是技术多牛,而是让客户在 3 秒内觉得你靠谱。
如果这篇文章对你有帮助,欢迎 Star 和分享。有技术咨询需求也可以通过页面表单联系我。