前言
最近在搞代码质量方面的项目,主要是针对Java语言,其他语言实际上也可以执行检查,核心原理是一样的,都是静态代码扫描,如果需要进行动态代码运行验证则可以通过单元测试的方式。以其中一个示例,实现自定义Java sonar规则。
准备
需要安装sonarqube或者sonarlint插件,自行实现
github:GitHub - SonarSource/sonar-java: :coffee: SonarSource Static Analyzer for Java Code Quality and Security
这里对于sonar来说,还有sonarqube的兼容性问题,这个xml定义了兼容的sonarqube版本,这里是8.9,笔者在使用最新版7.30.1.34514就出现签名错误的问题,这个版本支持9.9
关键是如下2个jar,在7.30相近的版本会出问题:An exception occurred while running the Java custom rule - Writing rules - Sonar Community
Caused by: java.lang.SecurityException: class "org.eclipse.jdt.core.dom.ASTUtils"'s signer information does not match signer information of other classes in the same package at java.base/java.lang.ClassLoader.checkCerts(ClassLoader.java:1150)
可通过自定义版本依赖规避
编写规则
规则分为3部分:
1. 规则扫描部分
2. 示例代码部分
3. 规则描述文件部分
示例代码主要用来测试,即使代码编译不过,也可以执行扫描,因为是源码扫描
可以直接使用单元测试来验证规则,因为依赖了
java-checks-testkit
查看示例代码
@Rule(key = "SpringControllerRequestMappingEntity")
public class SpringControllerRequestMappingEntityRule extends BaseTreeVisitor implements JavaFileScanner {private JavaFileScannerContext context;// 扫描自定义,一般不需要@Overridepublic void scanFile(JavaFileScannerContext context) {this.context = context;scan(context.getTree());}/*** Overriding the visitor method to implement the logic of the rule.* @param tree AST of the visited method.*/@Overridepublic void visitMethod(MethodTree tree) {Symbol.MethodSymbol methodSymbol = tree.symbol();// 类注解扫描SymbolMetadata parentClassOwner = methodSymbol.owner().metadata();boolean isControllerContext = parentClassOwner.isAnnotatedWith("org.springframework.stereotype.Controller");// 方法注解扫描if (isControllerContext && methodSymbol.metadata().isAnnotatedWith("org.springframework.web.bind.annotation.RequestMapping")) {// 方法的参数识别for (VariableTree param : tree.parameters()) {TypeTree typeOfParam = param.type();if (typeOfParam.symbolType().symbol().metadata().isAnnotatedWith("javax.persistence.Entity")) {// 识别参数的注解,然后报告问题,用于生成扫描问题报告// 在示例代码就会触发规则,如果我们把示例代码注解注释,就会扫描通过context.reportIssue(this, typeOfParam, String.format("Don't use %s here because it's an @Entity", typeOfParam.symbolType().name()));}}}super.visitMethod(tree);}}
如果需要写其他的规则,那么sonar原生的很多已经实现的规则可以参考,修改即可,逻辑就是获取代码特征,然后根据自己的业务具体要求,实现代码逻辑即可,有点Java编译为class的味道。
测试
使用测试用例
如果我们去掉参数的注解,执行单元测试,因为这个时候就符合规则扫描要求了,sonar的单元测试是有规则触发的问题时才是正常过测试用例的
总结
实际上这个很简单,如果需要考虑,那么需要考虑扫描算法怎么写,性能是否OK,毕竟一般执行扫描都是在代码编译打包的过程,本地很少单独执行扫描。sonar的规则写好后不需要使用sonarqube也可以验证,如果需要把规则给sonarqube,那么需要执行Maven插件的打包,放在sonarqube的plugins里面,sonar插件执行的shade fat jar打包,不需要额外的文件配置