创建Idea静态代码扫描工具

背景

近期公司框架升级,代码和配置的变动较大。为了保证升级的质量,开发了一个静态代码扫描工具,供所有开发者使用。此工具专注于检查异步方法中线程变量(例如myThreadlocal)的使用情况。

项目设置

版本

  • JDK 1.8
  • IntelliJ IDEA 2022
  • 基于 Gradle 构建插件

项目创建步骤

1. 新建项目

使用 IntelliJ IDEA 创建一个新的 Gradle 项目,并配置基本的项目结构。

2. 创建异步方法扫描类

新建一个 Java 类 AsyncInspection 来定义扫描逻辑。

package com.dxz.demo;

import com.intellij.codeInspection.AbstractBaseJavaLocalInspectionTool;
import com.intellij.codeInspection.ProblemsHolder;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.psi.*;
import org.jetbrains.annotations.NotNull;

public class AsyncInspection extends AbstractBaseJavaLocalInspectionTool {

    private static final Logger LOG = Logger.getInstance(AsyncInspection.class);

    @Override
    @NotNull
    public String getShortName() {
        return "AsyncInspection";
    }

    @Override
    public String getGroupDisplayName() {
        return "WalletInspection";
    }

    @Override
    public @NotNull PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
        return new JavaElementVisitor() {
            @Override
            public void visitMethodCallExpression(PsiMethodCallExpression expression) {
                super.visitMethodCallExpression(expression);
                if (isAsyncMethodCall(expression)) {
                    LOG.info("AsyncInspection " + expression.getText());
                    checkTntCodeUsage(expression, holder);
                }
            }

            @Override
            public void visitAnnotation(PsiAnnotation annotation) {
                super.visitAnnotation(annotation);
                if (isAsyncAnnotation(annotation)) {
                    PsiElement parent = annotation.getParent().getParent();
                    if (parent instanceof PsiMethod) {
                        PsiMethod method = (PsiMethod) parent;
                        checkTntCodeUsage(method.getBody(), holder);
                    }
                }
            }
        };
    }

    private boolean isAsyncMethodCall(PsiMethodCallExpression expression) {
        String methodName = expression.getMethodExpression().getReferenceName();
        return "runAsync".equals(methodName) || "supplyAsync".equals(methodName);
    }

    private boolean isAsyncAnnotation(PsiAnnotation annotation) {
        String qualifiedName = annotation.getQualifiedName();
        return "org.springframework.scheduling.annotation.Async".equals(qualifiedName);
    }

    private void checkTntCodeUsage(PsiElement element, ProblemsHolder holder) {
        if (element != null && !containsTntCode(element)) {
            holder.registerProblem(element, "Possible missing myThreadlocal usage in async method.");
        }
    }

    private boolean containsTntCode(PsiElement element) {
        if (element instanceof PsiReferenceExpression) {
            String text = element.getText();
            if (text.contains("MyThreadlocal") || text.contains("myThreadlocal")) {
                return true;
            }
        }

        for (PsiElement child : element.getChildren()) {
            if (containsTntCode(child)) {
                return true;
            }
        }

        return false;
    }
}

 

3. 配置 Gradle 构建脚本

build.gradle 中添加以下内容:

plugins {
    id 'java'
    id 'org.jetbrains.intellij' version '1.8.0'
}

group = 'com.dxz'
version = '1.0.2.2021.3'

repositories {
    mavenCentral()
}

intellij {
    version.set('2021.3.3')
    type.set('IC')
    plugins.set(['com.intellij.java'])
}

dependencies {
    implementation 'org.jetbrains:annotations:21.0.1'
}

tasks.withType(JavaCompile) {
    sourceCompatibility = '11'
    targetCompatibility = '11'
}

patchPluginXml {
    sinceBuild.set('213')
    untilBuild.set('223.*')
}

signPlugin {
    certificateChain.set(System.getenv('CERTIFICATE_CHAIN'))
    privateKey.set(System.getenv('PRIVATE_KEY'))
    password.set(System.getenv('PRIVATE_KEY_PASSWORD'))
}

publishPlugin {
    token.set(System.getenv('PUBLISH_TOKEN'))
}

 

4. 配置 plugin.xml

创建 plugin.xml 文件以配置插件元数据:

<idea-plugin>
    <id>com.dxz.demo</id>
    <name>demo-inspection</name>
    <vendor email="duanxz@xx.com" url="https://www.yourcompany.com">duanxz</vendor>
    <description><![CDATA[
        我的第一个扫描工具,异步方法中的线程变量处理检测。
    ]]></description>
    <depends>com.intellij.modules.platform</depends>
    <extensions defaultExtensionNs="com.intellij">
        <localInspection language="JAVA"
                         shortName="AsyncInspection"
                         displayName="Absence of myThreadlocal Inspection"
                         groupDisplayName="Wallet Inspections"
                         implementationClass="com.dxz.AsyncInspection"/>
    </extensions>
</idea-plugin>

 

5. 打包构建

在 IntelliJ IDEA 中,使用 gradle buildPlugin 命令进行构建。成功后,在 build/distributions 目录下会生成相应的 zip 文件。

6. 插件安装与使用

在 IDEA 中安装生成的插件,然后从菜单栏选择 Code -> Inspect Code 运行静态代码扫描。

总结

通过这个静态扫描工具,可以有效地检查异步方法中的线程变量使用情况,确保代码质量,为框架升级提供保障。

posted on 2024-07-26 13:46  duanxz  阅读(149)  评论(0编辑  收藏  举报