使用 SpotBugs + Maven + Flyway 构建可维护的 Java 爬虫项目
在 Java 项目开发中,代码质量、数据库版本管理以及架构解耦都是非常重要的工程实践。本文通过一个 新闻爬虫项目,介绍以下几个核心知识:
- SpotBugs 静态代码检查工具
- Maven 生命周期与插件机制
- Flyway 数据库版本管理
- JDBC 到 DAO 架构重构
- 向 ORM 框架(MyBatis)演进
通过这些技术,可以逐步把一个简单脚本式项目,升级成 工程化、可维护的 Java 应用。
一、SpotBugs:Java 静态代码检查工具
1 SpotBugs 是什么
SpotBugs 是一个 Java 静态代码分析工具,用于发现潜在问题,例如:
- 空指针风险
- 资源未关闭
- 不安全的代码
- 错误的 API 使用
- 性能问题
例如:
DMI_CONSTANT_DB_PASSWORD
表示:
数据库密码被硬编码在代码中。
2 在 Maven 中运行 SpotBugs
SpotBugs 可以直接通过 Maven 插件运行:
mvn spotbugs:check
执行后:
- 扫描 Java 字节码
- 分析潜在 Bug
- 如果发现问题,会在控制台输出报告
3 忽略 SpotBugs 警告
有些警告在实际项目中是 可接受的,可以使用注解忽略:
@SuppressFBWarnings("DMI_CONSTANT_DB_PASSWORD")
示例:
@SuppressFBWarnings("DMI_CONSTANT_DB_PASSWORD")
public static void main(String[] args) {
}
如果不知道警告含义,可以直接 Google:
spotbugs SuppressFBWarnings
二、Maven 生命周期(Lifecycle)
Maven 的核心是 生命周期驱动构建。
1 Maven 有三种生命周期
| 生命周期 | 作用 |
|---|---|
| clean | 清理项目 |
| default | 构建项目(最常用) |
| site | 生成文档 |
最常用的是:
default lifecycle
2 default 生命周期阶段
主要阶段:
validate
compile
test
package
verify
install
deploy
执行某个阶段时:
Maven 会从头执行到该阶段
例如:
mvn verify
实际执行顺序:
validate
compile
test
package
verify
3 Maven 插件与 Goal
Maven 的所有功能都是 插件提供的。
例如:
maven-surefire-plugin
负责执行测试。
每个插件包含多个 goal:
mvn spotbugs:check
其中:
spotbugs -> 插件
check -> goal
4 Goal 与生命周期绑定
有两种插件:
1 默认绑定插件
例如:
maven-surefire-plugin
默认绑定:
test phase
执行:
mvn test
会自动运行单元测试。
2 需要手动绑定插件
例如:
- checkstyle
- spotbugs
需要手动绑定生命周期:
<executions>
<execution>
<id>verify</id>
<phase>verify</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
表示:
在 verify 阶段执行 spotbugs:check
运行:
mvn verify
SpotBugs 就会自动执行。
三、使用 Flyway 管理数据库版本
数据库结构同样需要 版本管理。
Flyway 是一个数据库迁移工具,可以理解为:
Git for Database
1 SQL 版本文件命名规则
Flyway 约定:
V1__Create_tables.sql
V2__Init_data.sql
规则:
V版本号__描述.sql
2 创建数据库表
新闻表
create table news
(
id bigint primary key auto_increment,
title text,
content text,
url varchar(1000),
created_at timestamp,
modified_at timestamp
);
说明:
text 类型适合存储新闻内容
因为新闻通常:
只读 + 长文本
待处理链接池
create table LINKS_TO_BE_PROCESSED(
link varchar(100)
);
已处理链接池
create table LINKS_ALREADY_PROCESSED(
link varchar(100)
);
3 初始化数据
V2__Init_data.sql
insert into LINKS_TO_BE_PROCESSED (link)
values ('https://sina.cn');
4 Maven 配置 Flyway
<plugin>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-maven-plugin</artifactId>
<version>5.2.4</version>
<configuration>
<url>
jdbc:h2:file:/Users/.../news
</url>
<user>root</user>
<password>root</password>
</configuration>
</plugin>
执行:
mvn flyway:migrate
Flyway 会:
- 检查数据库版本
- 执行未执行的 SQL
- 记录执行历史
四、JDBC 版爬虫架构
初始版本爬虫逻辑:
Main.java
包含:
- 网络请求
- HTML解析
- 数据库操作
问题:
业务逻辑 + 数据库逻辑混在一起
违反:
单一职责原则
五、重构:DAO 架构
重构目标:
解耦 爬虫逻辑 和 数据库逻辑
拆成三个类:
Crawler.java
CrawlerDao.java
JdbcCrawlerDao.java
1 DAO 接口
CrawlerDao.java
public interface CrawlerDao {
String getNextLink(String sql) throws SQLException;
String getNextLinkThenDelete() throws SQLException;
void updateDatabase(String link, String sql) throws SQLException;
void insertNewsIntoDatabase(String url, String title, String content) throws SQLException;
boolean isLinkProcessed(String link) throws SQLException;
}
作用:
定义数据库操作规范
2 JDBC 实现
JdbcCrawlerDao.java
负责:
JDBC 操作数据库
例如:
public void insertNewsIntoDatabase(String url, String title, String content) throws SQLException {
try (PreparedStatement statement =
connection.prepareStatement(
"insert into news (url,title,content,CREATED_AT,MODIFIED_AT) values (?,?,?,now(),now())")) {
statement.setString(1, url);
statement.setString(2, title);
statement.setString(3, content);
statement.executeUpdate();
}
}
3 爬虫核心逻辑
Crawler.java
只负责:
爬虫逻辑
例如:
private CrawlerDao dao = new JdbcCrawlerDao();
执行流程:
1 获取待处理链接
2 下载页面
3 解析HTML
4 提取新链接
5 保存新闻
6 标记链接已处理
六、为什么要做 DAO 抽象?
如果未来需要更换数据库:
例如:
H2 -> MySQL
只需要修改:
JdbcCrawlerDao.java
爬虫代码:
Crawler.java
完全不需要修改
这就是:
依赖倒置原则
七、向 ORM 框架演进
JDBC 有很多缺点:
- SQL 写在代码中
- 映射麻烦
- 可维护性差
Java 世界常见 ORM:
| 框架 | 特点 |
|---|---|
| MyBatis | 半自动 ORM |
| Hibernate | 全自动 ORM |
| JPA | Java 标准 |
本项目准备使用:
MyBatis
八、MyBatis DAO 骨架
public class MyBatisCrawlerDao implements CrawlerDao {
@Override
public String getNextLink(String sql) throws SQLException {
return null;
}
@Override
public String getNextLinkThenDelete() throws SQLException {
return null;
}
@Override
public void updateDatabase(String link, String sql) throws SQLException {
}
@Override
public void insertNewsIntoDatabase(String url, String title, String content) throws SQLException {
}
@Override
public boolean isLinkProcessed(String link) throws SQLException {
return false;
}
}
后续只需要:
CrawlerDao dao = new MyBatisCrawlerDao();
就可以完成:
JDBC → ORM
架构升级。
九、总结
本文通过一个 Java 爬虫项目介绍了完整的工程实践:
代码质量
使用
SpotBugs
进行静态代码检测。
构建工具
理解
Maven 生命周期
插件
Goal
数据库管理
使用
Flyway
进行数据库版本控制。
架构优化
通过 DAO 模式:
爬虫逻辑
数据库逻辑
彻底解耦。
技术演进路线
JDBC
↓
DAO
↓
MyBatis
↓
Elasticsearch
逐步构建一个 完整的新闻搜索引擎系统。