使用 SpotBugs + Maven + Flyway 构建可维护的 Java 爬虫项目

0 阅读5分钟

使用 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 会:

  1. 检查数据库版本
  2. 执行未执行的 SQL
  3. 记录执行历史

四、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
JPAJava 标准

本项目准备使用:

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

逐步构建一个 完整的新闻搜索引擎系统