后端代码质量门禁
后端代码质量面临的问题
项目中经常会遇到由于后端代码基础质量不高造成工作反复、后期维护难的问题。 这些问题大致可分为 4 类:
- 代码风格
- 语法规范
- 潜在语法与逻辑风险
- 潜在安全风险
问题示例
- 代码风格
public static void main(String[] args) {
// 没有缩进 4 个空格
String say = "hello";
// 运算符的左右没有空格
int flag=0;
// 关键词 if 与括号之间没有空格
if(flag == 0) {
System.out.println(say);
}
StringBuilder sb = new StringBuilder();
// 超过 120 个字符的情况下,在括号前换行
sb.append("you").append("are")...append
("lucky");
}
- 语法规范
public static void main(String[] args){
Map<String, Object> map=new HashMap<>();
// 集合内部的元素是否为空,使用size()==0的方式
if(map.size()==0){
System.out.println("no element in this map.");
}
}
private int x = 0;
public int checkReturn() {
try {
return ++x;
} finally {
// finally 块中使用 return
return ++x;
}
}
后端代码质量门禁
我们通过 CheckStyle、SonarQube 2款工具建立后端代码质量门禁,提升代码质量,达到质量内建的效果,从而从根本上解决上述的问题。
质量门禁指标
指标名 | 目标 | 指标说明 | 度量方法 | |
---|---|---|---|---|
编码风格 | 编码风格 | 遵循工具标准 | 保证语法规则和代码风格的一致性,提高可读性和可维护性。 | CheckStyle |
代码质量 | 圈复杂度 | <=20 | 圈复杂度可以用来衡量一个模块判定结构的复杂程度,其数量上表现为独立路径的条数,也可理解为覆盖所有的可能情况最少使用的测试用例个数。 | SonarQube |
单元测试覆盖率 (行覆盖+分支覆盖) | >= 85% | 自动化测试是重要的质量保证手段,覆盖率高表示回归成本低。 | Junit、Jacoco SonarQube | |
可靠性 | A级 | 根据项目bug数给与的项目评级: A = 0 Bug 最高等级A,表示代码无bug B = at least 1 Minor Bug 代码只要有一个次要bug,等级就为B C = at least 1 Major Bug 只要包含一个重要bug,等级将为C D = at least 1 Critical Bug 只要有一个严重bug,等级评估为D E = at least 1 Blocker Bug 只要有一个最高等级的阻断级别的bug,可靠性评估为E,最低级别。 | SonarQube | |
安全评级 | A级 | 根据项目安全漏洞给与的项目评级: A = 0 Vulnerability 没有漏洞时,项目评估为最高级别A B = at least 1 Minor Vulnerability 只要包含一个次要漏洞,项目评估为级别B C = at least 1 Major Vulnerability 只要包含一个重要漏洞,项目评估为级别C D = at least 1 Critical Vulnerability 只要包含一个严重漏洞,项目评估为级别D E = at least 1 Blocker Vulnerability 只要包含一个阻断漏洞,项目评估为最低级别E | SonarQube | |
维护性 (技术债比例) | A级 | 根据项目技术债务比率而给与的项目评级: A = 0 - 0.05,即在0到5%之间 B = 0.06 - 0.1,即在6%到10%之间 C = 0.11 - 0.2,即在11%到20%之间 D = 0.21 - 0.5,即在21%到50%之间 E = 0.51 - 1,即超过50% 注:1.开发成本与修复成本的比率。该比率=修复成本/开发成本,也=修复成本/(代码行数*每行代码开发成本) 2.每行代码开发成本为0.06天。 | SonarQube | |
开放性issue | 0 | 开放性 issue 是指未解决的问题总数。问题包括缺陷(Bugs),漏洞(Vulnerabilities),坏味道(Code Smells)。 | SonarQube | |
重复率 | <=10% | 代码重复率高表示Bug可能性多,违背了 Don't Repeat Yourself 原则 | SonarQube | |
坏味道 | 尽量修改 | 代码坏味道是阻碍响应变化的一系列代码问题 | SonarQube | |
可读性 | 命名精准 | > 90% | 命名要与业务意义匹配 | 人工评审 |
必要的注释 | > 90% | 方法要有相关的注释,算法逻辑复杂时要有额外注释,减少不必要注释 | 人工评审 | |
小函数 | < 100行 | 每个函数小于100行 | 人工评审 | |
性能 | 代码支持自动扩展 | 代码支持自动扩展,并且达到10tps | Zipkin、Jmeter | |
请求响应时间 | > 90% | 90分位低于1.5秒 | Zipkin、Jmeter | |
可维护性 | Restful API Style | 100% | Restful API Style 命名空间丰富、权限控制与日志审计实施便利 | 人工评审 |
Swagger API 文档 (功能、参数、返回值) | 100% | 每一个接口需要完备API文档说明,内容包括所实现功能描述、参数与返回的意义 | 人工评审 | |
功能测试 | 接口覆盖率 | 100% | 自动化测试实现功能测试 正确参数,正确返回; 正确参数,空返回(如果有); 正确参数,依赖错误(如果有); 错误参数,错误提示; 无参数,错误提示; | 人工评审 |
检查规则项
2款工具共提供了约 2280+ 项规则,考虑到投入产出比,我们整理了更为常用的 500+ 项规则用于支撑后端代码质量门禁指标,并通过 CheckStyle、SonarQube 落地实施。
工具名称 | 使用 | 未使用 | 小计 |
---|---|---|---|
CheckStyle | 61 | 120+ | 180+ |
SonarQube | 442 | 1700+ | 2100+ |
总计 | 503 | 1820+ | 2280+ |
CheckStyle 检查规则项 (61项)
代码风格采用谷歌公司提供的标准,共计 61 项。
Name | 规则描述 |
---|---|
FileTabCharacter | 检查代码中是否存在 Tab('\t') |
LineLength | 检查每一行的长度 |
OuterTypeFilename | 检查外部类型名称与文件名是否一致 |
IllegalTokenText | 检查指定的标记文本是否匹配非法的模式 |
AvoidEscapedUnicodeCharacters | 限制使用Unicode转义(例如\u221e) |
AvoidStarImport | 检查是否没有使用 * 符号的import语句 |
OneTopLevelClass | 检查每个顶级类、接口、枚举或注释是否驻留在自己的源文件中 |
NoLineWrap | 检查所选语句是否没有行换行 (package, import, static import) |
EmptyBlock | 检查是否为空代码块 |
NeedBraces | 检查代码块周围的括号 |
LeftCurly | 检查代码块的左花括号('{')位置 |
RightCurly | 检查代码块的右花括号('}')位置 |
WhitespaceAfter | 检查标记后面是否有空格,但不检查for循环迭代器中的分号后面是否有空格 |
WhitespaceAround | 检查标记是否被空格包围 |
OneStatementPerLine | 检查每行是否只有一条语句 |
MultipleVariableDeclarations | 检查每个变量声明是否在自己的语句中并在自己的行中 |
ArrayTypeStyle | 检查数组类型定义的样式 |
MissingSwitchDefault | 检查switch语句是否有default |
FallThrough | 检查switch语句中是否有结束语句(break, return, throw, continue) |
UpperEll | 检查long型定义是否有大写的“L” |
ModifierOrder | 检查修饰符的顺序是否符合Java语言规范 |
EmptyLineSeparator | 检查头、包、所有导入声明、字段、构造函数、方法、嵌套类、静态初始化器和实例初始化器之后的空行分隔符。 |
SeparatorWrap | 检查换行 |
PackageName | 检查包名是否符合指定的格式 ^[a-z]+(\.[a-z][a-z0-9]*)*$ |
TypeName | 检查类型名称是否符合指定的格式 ^[A-Z][a-zA-Z0-9]*$ |
MemberName | 检查实例变量名称是否符合指定的格式 ^[a-z][a-z0-9][a-zA-Z0-9]*$ |
ParameterName | 检查方法参数名称是否符合指定的格式 ^[a-z]([a-z0-9][a-zA-Z0-9]*)?$ |
LambdaParameterName | 检查lambda参数名称是否符合指定的格式 ^[a-z]([a-z0-9][a-zA-Z0-9]*)?$ |
CatchParameterName | 检查catch参数名称是否符合指定的格式 ^[a-z]([a-z0-9][a-zA-Z0-9]*)?$ |
LocalVariableName | 检查局部的非final变量名是否符合指定的格式 ^[a-z]([a-z0-9][a-zA-Z0-9]*)?$ |
PatternVariableName | 检查pattern变量名是否符合指定的格式 ^[a-z]([a-z0-9][a-zA-Z0-9]*)?$ |
ClassTypeParameterName | 检查类类型参数名是否符合指定的格式 (^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$) |
RecordComponentName | 检查Record组件名称是否符合指定的格式 ^[a-z]([a-z0-9][a-zA-Z0-9]*)?$ |
RecordTypeParameterName | 检查Record类型参数名称是否符合指定的格式 (^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$) |
MethodTypeParameterName | 检查方法类型参数名称是否符合指定的格式 (^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$) |
InterfaceTypeParameterName | 检查接口类型参数名是否符合指定的格式 (^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$) |
NoFinalizer | 检查没有参数的finalize方法 |
GenericWhitespace | 检查"<"和">"周围的空格是否正确 |
Indentation | 检查Java代码的正确缩进 |
AbbreviationAsWordInName | 检查标识符名称中的缩写(连续大写字母)长度 |
OverloadMethodsDeclarationOrder | 检查是否将重载方法分组在一起 |
VariableDeclarationUsageDistance | 检查变量的声明与第一次使用之间的距离 |
CustomImportOrder | 检查导入声明是否按用户指定的顺序出现 |
MethodParamPad | 检查方法定义、构造函数定义、方法调用或构造函数调用的标识符之间的填充;以及参数列表的左括号 |
NoWhitespaceBefore | 检查标记之前是否有空格 |
ParenPad | 检查圆括号内填充的策略;也就是左括号和右括号之前是否需要空格,或者这样的空格是被禁止的 |
OperatorWrap | 检查在操作符上换行的策略 |
AnnotationLocation | 检查语言元素上注解的位置 |
NonEmptyAtclauseDescription | 检查块标签后面是否有描述 |
InvalidJavadocPosition | 检查javadoc是否位于正确的位置 |
JavadocTagContinuationIndentation | 检查块标记中延续行的缩进 |
SummaryJavadoc | 检查Javadoc总结语句是否包含不推荐使用的短语 |
JavadocParagraph | 检查Javadoc段落 |
AtclauseOrder | 检查javadoc块标记或javadoc标记的顺序 |
JavadocMethod | 检查方法或构造函数的Javadoc |
MissingJavadocMethod | 检查方法或构造函数的Javadoc注释是否缺失 |
MissingJavadocType | 检查类、枚举、接口和注释接口定义的Javadoc注释 |
MethodName | 检查方法名称是否符合指定的格式 ^[a-z][a-z0-9][a-zA-Z0-9_]*$ |
SingleLineJavadoc | 检查Javadoc块是否可以放在一行中,并且不包含块标记 |
EmptyCatchBlock | 检查空的catch代码块 |
CommentsIndentation | 控制注释和周围代码之间的缩进 |
SonarQube 检查规则项 (442项)
SonarQube 为 Java 语言提供了 2100+ 项扫描规则,默认的 Quality Profiles
配置文件激活了其中更为常用的 442 项规则。
规则类别 | 使用 | 未使用 | 小计 |
---|---|---|---|
缺陷(Bugs) | 142 | 483 | 625 |
漏洞(Vulnerabilities) | 29 | 140 | 169 |
坏味道(Code Smells) | 248 | 1100+ | 1300+ |
安全热点(Security Hotspots) | 23 | 14 | 37 |
总计 | 442 | 1700+ | 2100+ |
具体规则可以在 SonarQube 中的
Quality Profiles
菜单中查看
工具安装与配置
CheckStyle 安装
通过 Preferences > Plugins > Marketplace
查找 CheckStyle-IDEA
插件,安装并重新启动。
version: 8.41.1
CheckStyle 配置
创建 CheckStyle 配置文件 custom-google-check.xml 并设置以下内容:
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
"-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
"https://checkstyle.org/dtds/configuration_1_3.dtd">
<!--
Checkstyle configuration that checks the Google coding conventions from Google Java Style
that can be found at https://google.github.io/styleguide/javaguide.html
Checkstyle is very configurable. Be sure to read the documentation at
http://checkstyle.org (or in your downloaded distribution).
To completely disable a check, just comment it out or delete it from the file.
To suppress certain violations please review suppression filters.
Authors: Max Vetrenko, Ruslan Diachenko, Roman Ivanov.
-->
<module name="Checker">
<property name="charset" value="UTF-8"/>
<property name="severity" value="warning"/>
<property name="fileExtensions" value="java, properties, xml"/>
<!-- Excludes all 'module-info.java' files -->
<!-- See https://checkstyle.org/config_filefilters.html -->
<module name="BeforeExecutionExclusionFileFilter">
<property name="fileNamePattern" value="module\-info\.java$"/>
</module>
<!-- https://checkstyle.org/config_filters.html#SuppressionFilter -->
<module name="SuppressionFilter">
<property name="file" value="${org.checkstyle.google.suppressionfilter.config}"
default="checkstyle-suppressions.xml"/>
<property name="optional" value="true"/>
</module>
<!-- Checks for whitespace -->
<!-- See http://checkstyle.org/config_whitespace.html -->
<module name="FileTabCharacter">
<property name="eachLine" value="true"/>
</module>
<module name="LineLength">
<property name="fileExtensions" value="java"/>
<property name="max" value="120"/>
<property name="ignorePattern" value="^package.*|^import.*|a href|href|http://|https://|ftp://"/>
</module>
<module name="TreeWalker">
<module name="OuterTypeFilename"/>
<module name="IllegalTokenText">
<property name="tokens" value="STRING_LITERAL, CHAR_LITERAL"/>
<property name="format"
value="\\u00(09|0(a|A)|0(c|C)|0(d|D)|22|27|5(C|c))|\\(0(10|11|12|14|15|42|47)|134)"/>
<property name="message"
value="Consider using special escape sequence instead of octal value or Unicode escaped value."/>
</module>
<module name="AvoidEscapedUnicodeCharacters">
<property name="allowEscapesForControlCharacters" value="true"/>
<property name="allowByTailComment" value="true"/>
<property name="allowNonPrintableEscapes" value="true"/>
</module>
<module name="AvoidStarImport"/>
<module name="OneTopLevelClass"/>
<module name="NoLineWrap">
<property name="tokens" value="PACKAGE_DEF, IMPORT, STATIC_IMPORT"/>
</module>
<module name="EmptyBlock">
<property name="option" value="TEXT"/>
<property name="tokens"
value="LITERAL_TRY, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE, LITERAL_SWITCH"/>
</module>
<module name="NeedBraces">
<property name="tokens"
value="LITERAL_DO, LITERAL_ELSE, LITERAL_FOR, LITERAL_IF, LITERAL_WHILE"/>
</module>
<module name="LeftCurly">
<property name="tokens"
value="ANNOTATION_DEF, CLASS_DEF, CTOR_DEF, ENUM_CONSTANT_DEF, ENUM_DEF,
INTERFACE_DEF, LAMBDA, LITERAL_CASE, LITERAL_CATCH, LITERAL_DEFAULT,
LITERAL_DO, LITERAL_ELSE, LITERAL_FINALLY, LITERAL_FOR, LITERAL_IF,
LITERAL_SWITCH, LITERAL_SYNCHRONIZED, LITERAL_TRY, LITERAL_WHILE, METHOD_DEF,
OBJBLOCK, STATIC_INIT, RECORD_DEF, COMPACT_CTOR_DEF"/>
</module>
<module name="RightCurly">
<property name="id" value="RightCurlySame"/>
<property name="tokens"
value="LITERAL_TRY, LITERAL_CATCH, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE,
LITERAL_DO"/>
</module>
<module name="RightCurly">
<property name="id" value="RightCurlyAlone"/>
<property name="option" value="alone"/>
<property name="tokens"
value="CLASS_DEF, METHOD_DEF, CTOR_DEF, LITERAL_FOR, LITERAL_WHILE, STATIC_INIT,
INSTANCE_INIT, ANNOTATION_DEF, ENUM_DEF, INTERFACE_DEF, RECORD_DEF,
COMPACT_CTOR_DEF"/>
</module>
<module name="SuppressionXpathSingleFilter">
<!-- suppresion is required till https://github.com/checkstyle/checkstyle/issues/7541 -->
<property name="id" value="RightCurlyAlone"/>
<property name="query" value="//RCURLY[parent::SLIST[count(./*)=1]
or preceding-sibling::*[last()][self::LCURLY]]"/>
</module>
<module name="WhitespaceAfter">
<property name="tokens"
value="COMMA, SEMI, TYPECAST, LITERAL_IF, LITERAL_ELSE,
LITERAL_WHILE, LITERAL_DO, LITERAL_FOR, DO_WHILE"/>
</module>
<module name="WhitespaceAround">
<property name="allowEmptyConstructors" value="true"/>
<property name="allowEmptyLambdas" value="true"/>
<property name="allowEmptyMethods" value="true"/>
<property name="allowEmptyTypes" value="true"/>
<property name="allowEmptyLoops" value="true"/>
<property name="ignoreEnhancedForColon" value="false"/>
<property name="tokens"
value="ASSIGN, BAND, BAND_ASSIGN, BOR, BOR_ASSIGN, BSR, BSR_ASSIGN, BXOR,
BXOR_ASSIGN, COLON, DIV, DIV_ASSIGN, DO_WHILE, EQUAL, GE, GT, LAMBDA, LAND,
LCURLY, LE, LITERAL_CATCH, LITERAL_DO, LITERAL_ELSE, LITERAL_FINALLY,
LITERAL_FOR, LITERAL_IF, LITERAL_RETURN, LITERAL_SWITCH, LITERAL_SYNCHRONIZED,
LITERAL_TRY, LITERAL_WHILE, LOR, LT, MINUS, MINUS_ASSIGN, MOD, MOD_ASSIGN,
NOT_EQUAL, PLUS, PLUS_ASSIGN, QUESTION, RCURLY, SL, SLIST, SL_ASSIGN, SR,
SR_ASSIGN, STAR, STAR_ASSIGN, LITERAL_ASSERT, TYPE_EXTENSION_AND"/>
<message key="ws.notFollowed"
value="WhitespaceAround: ''{0}'' is not followed by whitespace. Empty blocks may only be represented as '{}' when not part of a multi-block statement (4.1.3)"/>
<message key="ws.notPreceded"
value="WhitespaceAround: ''{0}'' is not preceded with whitespace."/>
</module>
<module name="OneStatementPerLine"/>
<module name="MultipleVariableDeclarations"/>
<module name="ArrayTypeStyle"/>
<module name="MissingSwitchDefault"/>
<module name="FallThrough"/>
<module name="UpperEll"/>
<module name="ModifierOrder"/>
<module name="EmptyLineSeparator">
<property name="tokens"
value="PACKAGE_DEF, IMPORT, STATIC_IMPORT, CLASS_DEF, INTERFACE_DEF, ENUM_DEF,
STATIC_INIT, INSTANCE_INIT, METHOD_DEF, CTOR_DEF, VARIABLE_DEF, RECORD_DEF,
COMPACT_CTOR_DEF"/>
<property name="allowNoEmptyLineBetweenFields" value="true"/>
</module>
<module name="SeparatorWrap">
<property name="id" value="SeparatorWrapDot"/>
<property name="tokens" value="DOT"/>
<property name="option" value="nl"/>
</module>
<module name="SeparatorWrap">
<property name="id" value="SeparatorWrapComma"/>
<property name="tokens" value="COMMA"/>
<property name="option" value="EOL"/>
</module>
<module name="SeparatorWrap">
<!-- ELLIPSIS is EOL until https://github.com/google/styleguide/issues/259 -->
<property name="id" value="SeparatorWrapEllipsis"/>
<property name="tokens" value="ELLIPSIS"/>
<property name="option" value="EOL"/>
</module>
<module name="SeparatorWrap">
<!-- ARRAY_DECLARATOR is EOL until https://github.com/google/styleguide/issues/258 -->
<property name="id" value="SeparatorWrapArrayDeclarator"/>
<property name="tokens" value="ARRAY_DECLARATOR"/>
<property name="option" value="EOL"/>
</module>
<module name="SeparatorWrap">
<property name="id" value="SeparatorWrapMethodRef"/>
<property name="tokens" value="METHOD_REF"/>
<property name="option" value="nl"/>
</module>
<module name="PackageName">
<property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$"/>
<message key="name.invalidPattern"
value="Package name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="TypeName">
<property name="tokens" value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF,
ANNOTATION_DEF, RECORD_DEF"/>
<message key="name.invalidPattern"
value="Type name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="MemberName">
<property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9]*$"/>
<message key="name.invalidPattern"
value="Member name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="ParameterName">
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
<message key="name.invalidPattern"
value="Parameter name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="LambdaParameterName">
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
<message key="name.invalidPattern"
value="Lambda parameter name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="CatchParameterName">
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
<message key="name.invalidPattern"
value="Catch parameter name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="LocalVariableName">
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
<message key="name.invalidPattern"
value="Local variable name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="PatternVariableName">
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
<message key="name.invalidPattern"
value="Pattern variable name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="ClassTypeParameterName">
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
<message key="name.invalidPattern"
value="Class type name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="RecordComponentName">
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
<message key="name.invalidPattern"
value="Record component name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="RecordTypeParameterName">
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
<message key="name.invalidPattern"
value="Record type name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="MethodTypeParameterName">
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
<message key="name.invalidPattern"
value="Method type name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="InterfaceTypeParameterName">
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
<message key="name.invalidPattern"
value="Interface type name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="NoFinalizer"/>
<module name="GenericWhitespace">
<message key="ws.followed"
value="GenericWhitespace ''{0}'' is followed by whitespace."/>
<message key="ws.preceded"
value="GenericWhitespace ''{0}'' is preceded with whitespace."/>
<message key="ws.illegalFollow"
value="GenericWhitespace ''{0}'' should followed by whitespace."/>
<message key="ws.notPreceded"
value="GenericWhitespace ''{0}'' is not preceded with whitespace."/>
</module>
<module name="Indentation">
<property name="basicOffset" value="4"/>
<property name="braceAdjustment" value="0"/>
<property name="caseIndent" value="4"/>
<property name="throwsIndent" value="4"/>
<property name="lineWrappingIndentation" value="4"/>
<property name="arrayInitIndent" value="4"/>
</module>
<module name="AbbreviationAsWordInName">
<property name="ignoreFinal" value="false"/>
<property name="allowedAbbreviationLength" value="3"/>
<property name="tokens"
value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, ANNOTATION_DEF, ANNOTATION_FIELD_DEF,
PARAMETER_DEF, VARIABLE_DEF, METHOD_DEF, PATTERN_VARIABLE_DEF, RECORD_DEF,
RECORD_COMPONENT_DEF"/>
</module>
<module name="OverloadMethodsDeclarationOrder"/>
<module name="VariableDeclarationUsageDistance"/>
<module name="CustomImportOrder">
<property name="sortImportsInGroupAlphabetically" value="false"/>
<property name="separateLineBetweenGroups" value="true"/>
<property name="customImportOrderRules" value="STATIC ### THIRD_PARTY_PACKAGE ### STANDARD_JAVA_PACKAGE"/>
<property name="tokens" value="IMPORT, STATIC_IMPORT, PACKAGE_DEF"/>
</module>
<module name="MethodParamPad">
<property name="tokens"
value="CTOR_DEF, LITERAL_NEW, METHOD_CALL, METHOD_DEF,
SUPER_CTOR_CALL, ENUM_CONSTANT_DEF, RECORD_DEF"/>
</module>
<module name="NoWhitespaceBefore">
<property name="tokens"
value="COMMA, SEMI, POST_INC, POST_DEC, DOT,
LABELED_STAT, METHOD_REF"/>
<property name="allowLineBreaks" value="true"/>
</module>
<module name="ParenPad">
<property name="tokens"
value="ANNOTATION, ANNOTATION_FIELD_DEF, CTOR_CALL, CTOR_DEF, DOT, ENUM_CONSTANT_DEF,
EXPR, LITERAL_CATCH, LITERAL_DO, LITERAL_FOR, LITERAL_IF, LITERAL_NEW,
LITERAL_SWITCH, LITERAL_SYNCHRONIZED, LITERAL_WHILE, METHOD_CALL,
METHOD_DEF, QUESTION, RESOURCE_SPECIFICATION, SUPER_CTOR_CALL, LAMBDA,
RECORD_DEF"/>
</module>
<module name="OperatorWrap">
<property name="option" value="NL"/>
<property name="tokens"
value="BAND, BOR, BSR, BXOR, DIV, EQUAL, GE, GT, LAND, LE, LITERAL_INSTANCEOF, LOR,
LT, MINUS, MOD, NOT_EQUAL, PLUS, QUESTION, SL, SR, STAR, METHOD_REF,
TYPE_EXTENSION_AND "/>
</module>
<module name="AnnotationLocation">
<property name="id" value="AnnotationLocationMostCases"/>
<property name="tokens"
value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF,
RECORD_DEF, COMPACT_CTOR_DEF"/>
</module>
<module name="AnnotationLocation">
<property name="id" value="AnnotationLocationVariables"/>
<property name="tokens" value="VARIABLE_DEF"/>
<property name="allowSamelineMultipleAnnotations" value="true"/>
</module>
<module name="NonEmptyAtclauseDescription"/>
<module name="InvalidJavadocPosition"/>
<module name="JavadocTagContinuationIndentation"/>
<module name="SummaryJavadocCheck">
<property name="period" value="。"/>
</module>
<module name="JavadocParagraph"/>
<module name="AtclauseOrder">
<property name="tagOrder" value="@param, @return, @throws, @deprecated"/>
<property name="target"
value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF, VARIABLE_DEF"/>
</module>
<module name="JavadocMethod">
<property name="allowMissingParamTags" value="true"/>
<property name="allowMissingReturnTag" value="true"/>
<property name="allowedAnnotations" value="Override, Test"/>
<property name="tokens" value="METHOD_DEF, CTOR_DEF, ANNOTATION_FIELD_DEF, COMPACT_CTOR_DEF"/>
</module>
<module name="MissingJavadocMethod">
<property name="scope" value="public"/>
<property name="minLineCount" value="2"/>
<property name="allowedAnnotations" value="Override, Test"/>
<property name="tokens" value="METHOD_DEF, CTOR_DEF, ANNOTATION_FIELD_DEF,
COMPACT_CTOR_DEF"/>
</module>
<module name="MissingJavadocType">
<property name="scope" value="protected"/>
<property name="tokens"
value="ENUM_DEF, RECORD_DEF, ANNOTATION_DEF"/>
<property name="excludeScope" value="nothing"/>
</module>
<module name="MethodName">
<property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9_]*$"/>
<message key="name.invalidPattern"
value="Method name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="SingleLineJavadoc"/>
<module name="EmptyCatchBlock">
<property name="exceptionVariableName" value="expected"/>
</module>
<module name="CommentsIndentation">
<property name="tokens" value="SINGLE_LINE_COMMENT, BLOCK_COMMENT_BEGIN"/>
</module>
<!-- https://checkstyle.org/config_filters.html#SuppressionXpathFilter -->
<module name="SuppressionXpathFilter">
<property name="file" value="${org.checkstyle.google.suppressionxpathfilter.config}"
default="checkstyle-xpath-suppressions.xml"/>
<property name="optional" value="true"/>
</module>
</module>
</module>
CheckStyle插件安装完成后,通过 Preferences > Tools > Checkstyle
进行配置。
点击 Configuration File
下的 +
,在弹出框内输入 Description
并选中 Use a local Checkstyle file
, 点击 Browse
选择custom-google-check.xml配置文件。
点击 Next
对选择的文件进行校验,校验通过后点击 Finish
添加文件。
添加后在配置页面选择刚导入的文件,勾选Active。然后点击 Apply
。
SonarQube 安装
参照 SonarQube 安装
SonarQube 配置
质量门禁配置
用管理员
登录 SonarQube, 在首页中点击 Quality Gates
, 在该画面点击 Create
添加质量门禁。 在弹出框中输入门禁名称后点击 Save
创建质量门禁。
点击 Add Condition
添加门禁指标。 在弹出框选择作用代码、门禁项目,并输入指标值。
在画面下方 Projects
区选择质量门禁应用的项目。
扫描项配置
用管理员
登录 SonarQube, 在首页中点击 Quality Profiles
, 在该画面点击 Create
添加扫描项配置文件。 在弹出框中输入配置名称,选择语言,选择父级配置后点击 Create
创建扫描项配置文件。
点击 Active More
按钮,激活更多扫描规则项。
点击 Change Projects
按钮,选择应用的项目。
SonarLint 安装
通过 Preferences > Plugins > Marketplace
查找 SonarLint
插件,安装并重新启动。
version: 4.14.2
SonarLint 配置
SonarLint安装完成后,通过 Preferences > Tools > SonarLint
添加 SonarQube 服务链接。 点击 +
在弹出框中输入链接名称,选择 Connect to a server
,并在下方输入 SonarQube 服务器的url后点击Next
。
选择 Login / Password
输入 SonarQube 服务的用户名密码后点击 Next
,认证成功后点击 Next
再点击 Finish
同步服务信息。
可以通过 Preferences > Tools > SonarLint > Project Setting
绑定 SonarQube 服务上的项目,同步项目的扫描规则。
注:当服务器上的扫描项配置有更新时,需要在
Preferences > Tools > SonarLint
点击Update binding
按钮以同步最新的扫描规则。
作业流程
1.安装编辑器插件
安装 CheckStyle、SonarLint等插件并配置相关参数(参考工具安装与配置)。
2.在提交前代对代码进行检查
CheckStyle 检查
在要提交的文件编辑区域点击右键,点击 Check Current File
。
SonarLint 检查
在要提交的文件编辑区域点击右键,点击 Analyze with SonarLint
。
在项目上点击右键,点击 SonarLint > Analyze with SonarLint
扫描整个项目。