38、重构工具实战(二):使用静态分析工具(SonarQube, PMD)发现坏味道

说实话,很多团队做重构,第一步就卡住了——代码里到底哪些地方需要改?

靠人工Review?几百个文件,几十万行代码,看一遍眼睛都花了。我见过不少团队,开会讨论重构讨论了两周,最后连个改造清单都没列出来。说白了,缺的就是一把「扫描仪」。

静态分析工具,就是干这个的。它能自动扫描代码,把那些隐藏的坏味道一个个揪出来。今天我就拿SonarQube和PMD这两个工具,带你走一遍实战流程。

为什么需要静态分析工具?

你想想看,一个项目迭代了两年,代码里肯定有各种「历史遗留问题」:

  • 方法太长,一个函数写了300行
  • 重复代码,Ctrl+C/V出来的「双胞胎」
  • 空指针风险,变量没判空就直接用
  • 命名混乱,a1、a2、temp这种变量满天飞

这些问题,人眼很难全部发现。但工具可以。我个人的习惯是,每次重构前先跑一遍静态分析,拿到一份「坏味道地图」,再动手改。这样效率高得多。

核心观点:静态分析不是银弹,但它能帮你快速定位「哪里需要改」,让你把精力花在刀刃上。

SonarQube:代码质量的「体检中心」

SonarQube是我用得最多的工具。它不只是一个扫描器,更像一个代码质量的「体检中心」。你提交代码,它给你出一份报告,告诉你哪些地方有Bug、哪些地方有漏洞、哪些地方技术债务太高。

SonarQube能发现什么?

坏味道类型 具体表现 严重程度
代码异味 过长方法、过大类、重复代码
潜在Bug 空指针、资源未关闭、死循环
安全漏洞 SQL注入、XSS、硬编码密码 严重
技术债务 注释不足、命名不规范、未使用的导入

我记得有一次,一个老项目要重构,我先跑了SonarQube。结果发现一个类有2000多行,里面塞了十几个方法,有的方法参数多达8个。SonarQube直接标红,提示「该类复杂度超标,建议拆分」。嗯,这就是典型的「上帝类」坏味道。

SonarQube实战配置

配置SonarQube其实不复杂。我一般用Maven插件的方式集成:

<!-- pom.xml 中配置 SonarQube 插件 -->
<plugin>
    <groupId>org.sonarsource.scanner.maven</groupId>
    <artifactId>sonar-maven-plugin</artifactId>
    <version>3.9.1.2184</version>
</plugin>

然后执行命令:

mvn clean verify sonar:sonar \
  -Dsonar.host.url=http://localhost:9000 \
  -Dsonar.login=your_token

跑完之后,打开SonarQube的Web界面,就能看到一份完整的分析报告。我个人习惯先看「可靠性」和「安全性」这两个维度,因为这里面的问题通常比较严重。技术债务可以往后放一放。

小技巧:SonarQube支持自定义规则。如果你团队有特殊的编码规范,可以在「质量配置」里新建一套规则集。我曾经给一个金融项目定制过规则,把「禁止使用float做金额计算」加进去了,效果很好。

PMD:轻量级的「代码显微镜」

PMD和SonarQube不一样。SonarQube是个重型武器,需要搭服务器、建数据库。PMD轻量得多,一个命令行就能跑。适合那种不想折腾基础设施的团队。

PMD能发现什么?说白了,它擅长抓那些「小毛病」:

  • 空的catch块(catch了异常什么都不做)
  • 不必要的if-else(可以用三元表达式替代的)
  • 过长的参数列表(超过7个参数)
  • 直接操作System.out(应该用日志框架)

PMD实战配置

我一般用Maven插件集成PMD:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-pmd-plugin</artifactId>
    <version>3.15.0</version>
    <configuration>
        <rulesets>
            <ruleset>/rulesets/java/quickstart.xml</ruleset>
        </rulesets>
        <failOnViolation>true</failOnViolation>
    </configuration>
</plugin>

执行命令:

mvn pmd:pmd pmd:cpd

这里我特别说一下pmd:cpd,它是「复制粘贴检测器」。专门找重复代码的。我在项目中遇到过好几次,两个不同的类里,有完全一样的业务逻辑,只是变量名不同。CPD一跑,全给揪出来了。

避坑指南:我曾经在一个项目里直接用了PMD的默认规则集,结果跑出来几千个警告。大部分是「变量命名太短」这种无关痛痒的问题。建议你根据项目实际情况,裁剪规则集。比如老项目可以先只开「潜在Bug」和「性能」相关的规则,别一上来就开全部。

SonarQube vs PMD:怎么选?

很多新手会问:这两个工具到底用哪个?我的建议是:

  • 团队项目、长期维护:上SonarQube。它能做趋势分析,看技术债务是涨了还是降了。
  • 个人项目、快速扫描:用PMD。轻量、无依赖、跑完就出结果。
  • 最佳实践:两个都用。PMD做本地快速检查,SonarQube做CI/CD流水线里的质量门禁。

知识体系总览

下面这张图,是我梳理的静态分析工具在重构中的定位和作用流程:

静态分析工具在重构中的定位 源代码 静态分析工具 SonarQube / PMD / Checkstyle 坏味道清单 重构行动 输入 → 分析 → 输出

流程其实很简单:源代码进去,工具扫描,出来一份坏味道清单。然后你拿着这份清单,按优先级一个个改。我一般把清单按「严重程度」排序,先修那些标红的「Bug」和「漏洞」,再处理「代码异味」。

实战案例:一个真实的坏味道修复

最后分享一个我实际遇到的案例。有一次SonarQube报告里提示一个方法「Cognitive Complexity(认知复杂度)过高」,具体代码是这样的:

public String processOrder(Order order) {
    if (order != null) {
        if (order.isValid()) {
            if (order.getAmount() > 0) {
                if (order.getType() == OrderType.NORMAL) {
                    return "正常订单";
                } else if (order.getType() == OrderType.VIP) {
                    return "VIP订单";
                } else {
                    return "其他订单";
                }
            } else {
                return "金额无效";
            }
        } else {
            return "订单无效";
        }
    } else {
        return "订单为空";
    }
}

你看,嵌套了4层if-else。SonarQube直接标红,说认知复杂度超标。怎么改?用「卫语句」提前返回:

public String processOrder(Order order) {
    if (order == null) {
        return "订单为空";
    }
    if (!order.isValid()) {
        return "订单无效";
    }
    if (order.getAmount() <= 0) {
        return "金额无效";
    }
    // 正常处理
    if (order.getType() == OrderType.NORMAL) {
        return "正常订单";
    }
    if (order.getType() == OrderType.VIP) {
        return "VIP订单";
    }
    return "其他订单";
}

改完之后,嵌套深度从4层降到了1层。SonarQube再跑一次,绿色通过。这种坏味道,靠人眼看可能觉得「还行吧,能跑」,但工具会告诉你:这里需要改。

我的建议:别把静态分析工具当成「找茬的」。它其实是你的队友。每次提交代码前,本地跑一遍PMD,CI流水线里再跑一遍SonarQube。养成习惯之后,代码质量会肉眼可见地提升。

好了,这一章就聊到这儿。工具只是手段,关键还是你愿不愿意动手去改。下一章我们继续聊重构工具,不过换个角度——用IDE的自动化重构功能来加速改造。


公众号:蓝海资料掘金营,微信deep3321