Skip to main content

后端代码质量门禁

后端代码质量面临的问题

项目中经常会遇到由于后端代码基础质量不高造成工作反复、后期维护难的问题。 这些问题大致可分为 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
开放性issue0开放性 issue 是指未解决的问题总数。问题包括缺陷(Bugs),漏洞(Vulnerabilities),坏味道(Code Smells)。SonarQube
重复率<=10%代码重复率高表示Bug可能性多,违背了 Don't Repeat Yourself 原则SonarQube
坏味道尽量修改代码坏味道是阻碍响应变化的一系列代码问题SonarQube
可读性命名精准> 90%命名要与业务意义匹配人工评审
必要的注释> 90%方法要有相关的注释,算法逻辑复杂时要有额外注释,减少不必要注释人工评审
小函数< 100行每个函数小于100行人工评审
性能代码支持自动扩展代码支持自动扩展,并且达到10tpsZipkin、Jmeter
请求响应时间> 90%90分位低于1.5秒Zipkin、Jmeter
可维护性Restful API Style100%Restful API Style 命名空间丰富、权限控制与日志审计实施便利人工评审
Swagger API 文档
(功能、参数、返回值)
100%每一个接口需要完备API文档说明,内容包括所实现功能描述、参数与返回的意义人工评审
功能测试接口覆盖率100%自动化测试实现功能测试
正确参数,正确返回;
正确参数,空返回(如果有);
正确参数,依赖错误(如果有);
错误参数,错误提示;
无参数,错误提示;
人工评审

检查规则项

2款工具共提供了约 2280+ 项规则,考虑到投入产出比,我们整理了更为常用的 500+ 项规则用于支撑后端代码质量门禁指标,并通过 CheckStyle、SonarQube 落地实施。

工具名称使用未使用小计
CheckStyle61120+180+
SonarQube4421700+2100+
总计5031820+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)142483625
漏洞(Vulnerabilities)29140169
坏味道(Code Smells)2481100+1300+
安全热点(Security Hotspots)231437
总计4421700+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 进行配置。

browse repositories

点击 Configuration File 下的 + ,在弹出框内输入 Description 并选中 Use a local Checkstyle file, 点击 Browse 选择custom-google-check.xml配置文件。

browse repositories

点击 Next 对选择的文件进行校验,校验通过后点击 Finish 添加文件。

browse repositories

添加后在配置页面选择刚导入的文件,勾选Active。然后点击 Apply

browse repositories

SonarQube 安装

参照 SonarQube 安装

SonarQube 配置

质量门禁配置

管理员登录 SonarQube, 在首页中点击 Quality Gates, 在该画面点击 Create 添加质量门禁。 在弹出框中输入门禁名称后点击 Save 创建质量门禁。

browse repositories

点击 Add Condition 添加门禁指标。 在弹出框选择作用代码、门禁项目,并输入指标值。

browse repositories

在画面下方 Projects 区选择质量门禁应用的项目。

browse repositories

扫描项配置

管理员登录 SonarQube, 在首页中点击 Quality Profiles, 在该画面点击 Create 添加扫描项配置文件。 在弹出框中输入配置名称,选择语言,选择父级配置后点击 Create 创建扫描项配置文件。

browse repositories

点击 Active More 按钮,激活更多扫描规则项。

browse repositories

点击 Change Projects 按钮,选择应用的项目。

browse repositories

SonarLint 安装

通过 Preferences > Plugins > Marketplace 查找 SonarLint 插件,安装并重新启动。

version: 4.14.2

SonarLint 配置

SonarLint安装完成后,通过 Preferences > Tools > SonarLint 添加 SonarQube 服务链接。 点击 + 在弹出框中输入链接名称,选择 Connect to a server ,并在下方输入 SonarQube 服务器的url后点击Next

browse repositories

选择 Login / Password 输入 SonarQube 服务的用户名密码后点击 Next,认证成功后点击 Next 再点击 Finish 同步服务信息。

browse repositories

可以通过 Preferences > Tools > SonarLint > Project Setting 绑定 SonarQube 服务上的项目,同步项目的扫描规则。

browse repositories

注:当服务器上的扫描项配置有更新时,需要在 Preferences > Tools > SonarLint 点击 Update binding 按钮以同步最新的扫描规则。

作业流程

1.安装编辑器插件

安装 CheckStyle、SonarLint等插件并配置相关参数(参考工具安装与配置)。

2.在提交前代对代码进行检查

CheckStyle 检查

在要提交的文件编辑区域点击右键,点击 Check Current File

SonarLint 检查

在要提交的文件编辑区域点击右键,点击 Analyze with SonarLint

在项目上点击右键,点击 SonarLint > Analyze with SonarLint 扫描整个项目。

3.提交后等待发布