扩展阿里巴巴 Java 开发规约插件,打造团队专属风格!

逆锋起笔

共 7401字,需浏览 15分钟

 ·

2021-12-10 02:08

关注不迷路

1.前言

工作中难免会遇到维护别人代码的情况,那么首先就得看懂别人写的代码。如果对方写的代码混乱臃肿,维护成本必然很高,如果对方写的代码优雅清晰,那维护的人看起来必然心情愉悦。正所谓“前人栽树,后人乘凉;前人埋坑,后人骂娘”。
代码首先是给人看的,其次才是给机器看到,如何编写出任何人都看到懂的代码?答案是制定规范!
每个公司都会有自己的编码规范,但是往往的情况是赶项目进度或者懒惰或者个人水平习惯等原因,加上没有code review,最后代码就写的千奇百怪了。原因就在于规范是有了,但是没人遵守。所以,编码规范需要强制执行,交给工具来强制执行。
本文将通过介绍java静态代码检查工具PMD、阿里巴巴p3c开源项目到最后编写自定义编码规约来学习如何规范代码的编写。

2.PMD静态代码扫描

2.1.PMD官网

https://pmd.github.io/

2.2.概述

PMD是一种开源分析Java代码错误的工具。它通过静态分析获知代码错误。也就是说,在不运行Java程序的情况下报告错误。PMD附带了许多可以直接使用的规则,利用这些规则可以找出Java源程序的许多问题,例如:
  • 潜在的bug:空的try/catch/finally/switch语句
  • 未使用的代码:未使用的局部变量、参数、私有方法等
  • 可选的代码:String/StringBuffer的滥用
  • 复杂的表达式:不必须的if语句、可以使用while循环完成的for循环
  • 重复的代码:拷贝/粘贴代码意味着拷贝/粘贴bugs
  • 循环体创建新对象:尽量不要再for或while循环体内实例化一个新对象
  • 资源关闭:Connect,Result,Statement等使用之后确保关闭掉
此外,用户还可以自己定义规则,检查Java代码是否符合某些特定的编码规范。例如,你可以编写一个规则,要求PMD找出所有创建Thread和Socket对象的操作。

2.3.工作原理

PMD的核心是JavaCC解析器生成器。PMD结合运用JavaCC和EBNF(扩展巴科斯-诺尔范式,Extended Backus-Naur Formal)语法,再加上JJTree,把Java源代码解析成抽象语法树(AST,Abstract Syntax Tree)
从根本上看,Java源代码只是一些普通的文本。不过,为了让解析器承认 这些普通的文本是合法的Java代码,它们必须符合某种特定的结构要求。这种结构可以用一种称为EBNF的句法元语言表示,通常称为“语法” (Grammar)。JavaCC根据语法要求生成解析器,这个解析器就可以用于解析用Java编程语言编写的程序。

2.4.规则分类

  • 最佳实践:公认的最佳实践的规则。
  • 代码风格:这些规则强制执行特定的编码风格。
  • 设计:帮助您发现设计问题的规则。
  • 文档:这些规则与代码文档有关。
  • 容易出错的规则:用于检测被破坏的、非常混乱的或容易发生运行时错误的结构的规则。
  • 多线程:这些规则在处理多个执行线程时标记问题。
  • 性能:标记存在性能问题的代码的规则。
  • 安全:显示潜在安全缺陷的规则。

2.5.编写PMD自定义规则

  • https://pmd.github.io/pmd-5.4.1/customizing/howtowritearule.html

  • https://testerhome.com/topics/4918

  • http://www.w3school.com.cn/xpath/index.asp

3.阿里巴巴Java开发规约插件p3c

3.1.GITHUB地址

https://github.com/alibaba/p3c

3.2.概述

阿里巴巴p3c项目包含三个部分:
  • p3c-pmd,提供大部分规则实现,基于PMD框架开发,如果想实现自己的规则,可以基于该模块开发(该模块基于maven编译打包)
  • IntelliJ IDEA插件,即idea-plugin模块(该模块基于gradle编译打包)
  • Eclipse插件,即eclipse-plugin,本文不介绍

3.3.阿里编码规约IDEA插件使用

传送门:https://github.com/alibaba/p3c/wiki/IDEA%E6%8F%92%E4%BB%B6%E4%BD%BF%E7%94%A8%E6%96%87%E6%A1%A3

4.基于p3c编写自定义编码规则

4.1.自定义规则

假设现在需要开发这么一个规则:方法请求参数列表不允许超过(含)5个

4.2.开发步骤

4.2.1.找出问题代码,使用pmd图形化工具解析成抽象语法树

代码示例:
package org.p3c.demo;

public class Demo {

    public void methodA(int a) {

    }

    public void methodB(int a, int b, int c, int d, int e) {

    }
}
将源码放入Source Code框,点击Go按钮,解析结果显示在左下框

4.2.2.分析抽象语法树

可以看到,整棵树的根节点是CompilationUnit,即编译单元,代表每个java源文件。我们首先要找到所有的方法声明,根据树节点名称大概也能看出来是MethodDeclaration,点击相应的节点,看看光标是否定位到源码方法声明位置。
仔细分析MethodDeclaration节点,可以看到他有以下几个直接子节点:ResultType、MethodDeclarator、Block,即返回类型、方法声明、方法体
MethodDeclarator是我们想找的节点XPATH表达式可以这么写:
//CompilationUnit//MethodDeclarator
验证表达式是否正确,将它写到PMD图形界面XPATH Query框中,点击Go按钮
接下来,我们需要找到每个方法对应的参数列表,参数列表节点是方法节点的直接子节点,完整XPATH表达式为:
//CompilationUnit//MethodDeclarator/FormalParameters
获取到参数列表节点后,我们查看该节点的属性,找出参数个数的属性,观察可以发现是ParameterCount属性。
到现在为止,抽象语法树已经分析完,我们知道这么找出代码中参数列表大于等于5个的方法了。

4.2.3.p3c-pmd项目编写自定义代码规则

打开阿里p3c-pmd工程,开始编写我们的自定义规则。
阿里已经写了很多规则,我们现在要编写的规则属于面向对象范畴,可以把规则写到opp包下,新建一个规则类MethodParameterCountRule,继承自AbstractAliRule,重写 visit方法:
package com.alibaba.p3c.pmd.lang.java.rule.oop;

import com.alibaba.p3c.pmd.lang.java.rule.AbstractAliRule;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
import net.sourceforge.pmd.lang.java.ast.ASTFormalParameters;

import java.util.List;

/**
 * 方法参数列表个数不宜过长
 *
 * @auther qingjian.wu
 * @create 2018-01-27 14:59
 */

public class MethodParameterCountRule extends AbstractAliRule{


    private static final String METHOD_XPATH = "//MethodDeclarator";

   private static final Integer PARAMETER_COUNT_LIMIT = 5;


    @Override
    public Object visit(ASTCompilationUnit node, Object data) {
        try {
            // 找到所方法节点
            List methodNodes = node.findChildNodesWithXPath(METHOD_XPATH);
            if (methodNodes != null && methodNodes.size() > 0) {
                for (Node methodNode : methodNodes) {
                    // 找到每个方法的参数列表声明
                    List formalParameters = methodNode.findChildrenOfType(ASTFormalParameters.class);
                    if (formalParameters.get(0).getParameterCount() >= PARAMETER_COUNT_LIMIT) {
                        // 违反规则提示信息,第二个参数是提示信息位置,第三个参数是提示信息key,第四个参数用来替换提示信息
                        // 中的占位符,这里获取的节点image属性就是方法名称
                        addViolationWithMessage(data, methodNode,
                                "java.oop.MethodParameterCountRule.violation.msg",
                                new Object[]{methodNode.getImage()});
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return super.visit(node, data);
    }
}

4.2.4.p3c-pmd项目配置规则

将编写好规则配置到ali-oop.xml文件中
    "MethodParameterCountRule"
          language="java"
          message="java.oop.MethodParameterCountRule.rule.msg"
          class="com.alibaba.p3c.pmd.lang.java.rule.oop.MethodParameterCountRule">
        
        1

        
            Negative example:
    public void methodB(int a, int b, int c, int d, int e) {

    }
]]>
        

        
            Positive example:
    public void methodA() {

    }
]]>
        

    

4.2.5.p3c-pmd项目编写提示信息

上两步使用的提示信息和规则信息需要编写到message.xml配置文件中,message_en.xml中是英文提示,这里就先不演示了
    "java.oop.MethodParameterCountRule.violation.msg">
        
    
    "java.oop.MethodParameterCountRule.rule.msg">
        
    

4.2.6.单元测试

编写测试样例,将要测试的源代码写到test目录对应的xml文件中

<test-data>

 <code-fragment id="测试样例">
   package org.p3c.demo;

 public class Demo {

  public void methodA(int a) {

  }

  public void methodB(int a, int b, int c, int d, int e) {

  }
 }
  ]]>
 code-fragment>
 <test-code>
  
  <expected-problems>0expected-problems>
  <code-ref id="测试样例" />
 test-code>
test-data>
编写单元测试
运行单元测试,因为样例代码中methodB不符合规范,但是我们预期问题个数写的是0,所以单元测试会不通过:

4.3.配置插件

4.3.1.p3c-pmd打包安装到本地maven仓库

先把用不到的插件maven-javadoc-plugin和maven-gpg-plugin注释掉,然后运行mvn命令:
mvn -DskipTests=true clean install

4.3.2.idea-plugin项目打包插件

idea-plugin项目基于gradle构建,配置根目录下build.gradle,让构建使用本地私有maven仓库构建
然后运行开始gradle构建:
clean buildDependents build
打包成功后会在idea-plugin\p3c-idea\build\distributions\目录下生成Alibaba Java Coding Guidelines-1.0.0.zip文件,这个就是我们加入了自己拓展阿里开发规约的插件,IDEA中安装此插件
Settings->Plugins->Install plugin from disk

4.3.3.IDEA中使用编码规约插件

安装完插件重启IDEA,用之前的代码测试下插件是否生效。
右键点击“编码规约扫描”
结果:

5.Maven打包加入PMD校验

到目前为止,我们已经做到了能在开发阶段实时校验自己的代码了,最后我们需要把规约检查加入到代码打包中,这样才能做到部署到生产环境的代码都是符合规范的,如果不符合规范,打包会失败。
考虑到大多数项目使用maven管理,可以把自定义pmd规则整合到maven,这样就可以使用maven校验代码规则了
在maven项目中加入pmd插件,配置插件在package阶段执行。通常我们的项目都有一个公共的父pom,那将插件加入到父pom中就行
<build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.pluginsgroupId>
                <artifactId>maven-pmd-pluginartifactId>
                <version>3.8version>

                <configuration>
                    <rulesets>
                        <ruleset>rulesets/java/ali-comment.xmlruleset>
                        <ruleset>rulesets/java/ali-concurrent.xmlruleset>
                        <ruleset>rulesets/java/ali-constant.xmlruleset>
                        <ruleset>rulesets/java/ali-exception.xmlruleset>
                        <ruleset>rulesets/java/ali-flowcontrol.xmlruleset>
                        <ruleset>rulesets/java/ali-naming.xmlruleset>
                        <ruleset>rulesets/java/ali-oop.xmlruleset>
                        <ruleset>rulesets/java/ali-orm.xmlruleset>
                        <ruleset>rulesets/java/ali-other.xmlruleset>
                        <ruleset>rulesets/java/ali-set.xmlruleset>
                    rulesets>
                    <printFailingErrors>trueprintFailingErrors>
                    
                    <minimumPriority>1minimumPriority>
                configuration>
                <executions>
                    <execution>
                        <phase>packagephase>
                        <goals>
                            <goal>checkgoal>
                        goals>
                    execution>
                executions>
                <dependencies>
                    <dependency>
                        <groupId>com.alibaba.p3cgroupId>
                        <artifactId>p3c-pmdartifactId>
                        <version>1.3.3version>
                    dependency>
                dependencies>
            plugin>
        plugins>
    build>
如果存在不符合规范代码打包将失败:
关于maven pmd插件更详细介绍参考官网

6.总结

本文篇幅确实有点长,看懂需要有点耐心。不过其实也挺简单,关键点就是分析抽象语法树,找出问题代码节点,剩下的工作就很简单了。
PMD也有局限性,比如只能校验java源文件,对于XML等配置规约就没辙了。还有最最重要的,代码逻辑等关键性问题是没法校验的,也没法做。PMD只是一定程度上规范了我们的代码,要写出优雅的代码,还得多思考多实践呐。

来源:blog.csdn.net/u014513883/

article/details/79186893

逆锋起笔是一个专注于程序员圈子的技术平台,你可以收获最新技术动态最新内测资格BAT等大厂的经验精品学习资料职业路线副业思维,微信搜索逆锋起笔关注!

20 个实例玩转 Java 8 Stream,写的太好了!
GitHub 2021 年度报告发布
Java8 中的真的 Optional 很强大,你用对了吗?
Java8 中的 Stream 那么强大,那你知道它的原理是什么吗?


浏览 77
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报