Spring Boot入门教程:60分钟快速上手
上周五晚上9点半,我瘫在工位上盯着IDEA里满屏的红色报错,脑子里只有一个念头:“早知道就用 Go 写这个爬虫了。”
这事儿得从头说起——我们组最近被临时塞了个需求:搞一个内部内容聚合服务,定时从几个公开站点抓取商品信息(别问为啥不直接调API,问就是产品说“这些站没开放接口”)。Deadline是三天后,而我刚好在双11大促压测完的第二天接到这个活儿。人还飘着,脑子还在处理Redis集群的慢查询日志,结果产品经理一句“很简单吧?你不是会Java吗?”直接把我钉死在椅子上。
我是京东上海后端团队的五年老狗,经历过618和双11的流量洪峰,日常开发用Mac(Windows只留着测试IE兼容性,虽然现在基本没人用了),对前端动画还挺感兴趣(毕竟看着loading转圈也得有点审美)。但说实话,Spring Boot 这玩意儿,我一直用得挺佛系——公司主框架早就封装好了starter、中间件、配置中心,我平时写业务代码就像搭乐高,很少从零搭建。这次突然要裸写一个独立服务,还得集成爬虫逻辑,瞬间有点懵。
为什么不用 Go?别急,先比一比
很多人第一反应是:“这种IO密集型任务,Go不香吗?” 我一开始也这么想。毕竟Go的goroutine天生适合高并发爬虫,轻量、编译快、部署简单。但在实际业务场景里,技术选型从来不是“哪个更酷”,而是“哪个更稳、更快交付、更符合团队栈”。
我把两个方案拉出来遛了遛:
| 维度 | Spring Boot (Java 17) | Go (1.22) |
|---|---|---|
| 开发速度 | ⭐⭐⭐⭐(有大量现成生态) | ⭐⭐⭐(需手动集成很多轮子) |
| 团队熟悉度 | ⭐⭐⭐⭐⭐(全员Java栈) | ⭐(只有俩人会) |
| 爬虫库成熟度 | Jsoup/HttpClient + 定时任务 | Colly + Cron |
| 部署运维 | Docker + K8s(公司标准) | 同左,但监控埋点要重写 |
| 与现有系统集成 | 直接调用公司统一认证、日志、配置中心 | 需要额外适配 |
| 内存占用 | 较高(~500MB起) | 极低(~20MB) |
结论很现实:虽然Go更轻量,但为了三天交付,用Spring Boot能复用公司已有的中间件SDK、统一告警、链路追踪,省下至少一天调试时间。而且——我司运维看到Go服务会皱眉(他们只认Java应用的JMX指标),测试同学也不会写Go的Mock Server。算了,忍一忍,上Spring Boot。
60分钟实操:从 start.spring.io 到跑起来
Step 1:初始化项目(5分钟)
打开 https://start.spring.io,填好Group、Artifact,语言选Java,打包选Jar,Java版本17(公司新项目标配),依赖勾上:
- Spring Web(必须)
- Spring Data JPA(后面存数据用)
- H2 Database(本地开发用内存DB,上线切MySQL)
- Spring Boot DevTools(热加载,救命神器)
- Quartz Scheduler(定时爬虫)
⚠️ 吐槽:别信网上那些“Spring Initializr太重”的说法——对我们这种赶deadline的人来说,它就是生产力核弹。
生成下载,解压,IDEA打开。搞定。
Step 2:写个最简Controller验证通路(10分钟)
@RestController
public class HealthController {
@GetMapping("/ping")
public String ping() {
return "pong! - built at " + new Date();
}
}
终端敲:
./mvnw spring-boot:run
浏览器访问 http://localhost:8080/ping,看到返回就算通了。千万别跳过这一步! 去年双11前我就因为跳过健康检查,上线后发现端口被占,差点背锅。
Step 3:集成爬虫逻辑(20分钟)
这里用 Jsoup(HTML解析) + Apache HttpClient(高性能HTTP客户端)。在 pom.xml 加依赖:
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.17.2</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
</dependency>
写个Service:
@Service
public class ProductCrawlerService {
private final CloseableHttpClient httpClient = HttpClients.createDefault();
public List<Product> crawlFromSite(String url) throws IOException {
// 设置User-Agent防反爬
HttpGet request = new HttpGet(url);
request.setHeader("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36");
try (CloseableHttpResponse response = httpClient.execute(request)) {
String html = EntityUtils.toString(response.getEntity());
Document doc = Jsoup.parse(html);
// 假设页面结构是 <div class="product">...<span class="price">¥100</span></div>
return doc.select(".product").stream()
.map(el -> new Product(
el.select(".name").text(),
el.select(".price").text().replace("¥", "")
))
.collect(Collectors.toList());
}
}
}
💡 注意:真实爬虫要考虑重试、代理、验证码、动态渲染(这时候可能得上Selenium或Playwright),但内部工具先跑通再说。产品经理说“就几个静态页”,信他个鬼!
Step 4:存数据库 + 定时任务(15分钟)
先建Entity:
@Entity
@Table(name = "products")
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String price;
// constructor, getters, setters...
}
Repository就一行:
public interface ProductRepository extends JpaRepository<Product, Long> {}
然后写定时任务:
@Component
public class CrawlScheduler {
@Autowired
private ProductCrawlerService crawlerService;
@Autowired
private ProductRepository productRepo;
// 每30分钟跑一次
@Scheduled(fixedRate = 30 * 60 * 1000)
public void crawlAndSave() {
log.info("Starting crawl job...");
try {
List<Product> products = crawlerService.crawlFromSite("https://example-shop.com/list");
productRepo.saveAll(products);
log.info("Saved {} products", products.size());
} catch (Exception e) {
log.error("Crawl failed!", e);
// 这里应该发企业微信告警,但demo先打印
}
}
}
别忘了在主类加 @EnableScheduling!
Step 5:配置文件(5分钟)
application.yml:
spring:
datasource:
url: jdbc:h2:mem:testdb
driver-class-name: org.h2.Driver
jpa:
hibernate:
ddl-auto: update
show-sql: true
logging:
level:
com.yourpackage: debug
🤫 秘密:
ddl-auto: update在生产环境是大忌!我们上线前一定会改成validate并手动管理SQL脚本。但本地开发图快,谁管那么多?
Step 6:跑起来 & 验证(5分钟)
./mvnw spring-boot:run
等30秒,看控制台有没有 Saved X products。再查H2数据库(Spring Boot自动启H2 Console,访问 /h2-console,JDBC URL填 jdbc:h2:mem:testdb)。
踩坑实录:那些让我想砸Mac的瞬间
- Jsoup解析乱码:目标站是GBK编码,Jsoup默认UTF-8。解决方案:
Jsoup.parse(html, "GBK")。 - HttpClient连接池耗尽:没close response,线程卡死。务必用try-with-resources!
- 定时任务阻塞主线程:Quartz默认单线程。加配置:
spring: task: scheduling: pool: size: 5 - H2和MySQL语法差异:本地用H2的
AUTO_INCREMENT,上线MySQL报错。后来统一用GenerationType.IDENTITY才解决。
最惨的是——忘了加 @Transactional,导致批量save时每个insert开一个事务,DB CPU飙到100%。还好是在测试环境,不然又是一次P0事故。
为什么我还是推荐Spring Boot给新人?
虽然Go在爬虫场景确实更轻量,但Spring Boot的工程化能力太强了:
- 自动配置(AutoConfiguration)省去90%样板代码
- Actuator提供
/health,/metrics等生产级端点 - Starter机制让集成Redis、RabbitMQ像喝水一样简单
- Spring Cloud全家桶无缝对接微服务
更重要的是——在大厂,稳定性和可维护性永远排在“技术时髦”前面。我们组去年尝试用Go重写一个边缘服务,结果发现:
- 日志格式和公司ELK不兼容
- 链路追踪ID透传要自己实现
- 压测时GC停顿没了,但goroutine泄漏更难查
最后还是回滚到Java。血泪教训啊!
最后几句真心话
写这篇教程,其实是因为上周帮实习生搭环境时,发现很多人对Spring Boot还停留在“XML配置地狱”的印象。其实现在的Spring Boot,真的可以60分钟跑通一个生产级雏形。
当然,如果你只是写个一次性爬虫脚本,Go or Python definitely better。但一旦涉及用户体系、权限控制、事务、监控告警、灰度发布——Spring Boot这套组合拳,依然是Java世界的最优解。
哦对了,今天那个爬虫服务已经上线了。虽然产品经理昨天又提了新需求:“能不能把图片也下载下来?”……算了,不说了,我去改代码了。
作者:京东上海后端,Mac党,双11幸存者。
本文所有代码已脱敏,可在GitHub Gist查看(假的,别点)。
下期预告:《如何在Spring Boot里优雅地接入公司统一登录?》——求产品经理别再让我重复造轮子了!

评论 0