SpringBoot自定義maven-plugin插件整合asm代碼插樁

背景

公司開發框架增加瞭web系統license授權證書校驗模塊,實行一臺機器一個授權證書,初步方案是增加攔截器針對全局請求進行攔截校驗,評估後認為校驗方式單一,應該增加重要工具類,業務service實現中每個方法的進行校驗,因為涉及代碼量較大硬編碼工作困難,故選擇通過自定義maven插件在編譯期間進行動態代碼插樁操作

項目配置

新建maven項目設置打包方式

<packaging>maven-plugin</packaging>

增加依賴項

         <!--使用doc的方式-->
        <dependency>
            <groupId>org.apache.maven</groupId>
            <artifactId>maven-plugin-api</artifactId>
            <version>3.5.2</version>
        </dependency>
        <!--使用註解的方式-->
        <dependency>
            <groupId>org.apache.maven.plugin-tools</groupId>
            <artifactId>maven-plugin-annotations</artifactId>
            <version>3.5.2</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.maven</groupId>
            <artifactId>maven-project</artifactId>
            <version>2.2.1</version>
        </dependency>
        <dependency>
            <groupId>org.ow2.asm</groupId>
            <artifactId>asm</artifactId>
            <version>9.0</version>
        </dependency>
 

build內容配置

     <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-plugin-plugin</artifactId>
                <version>3.5</version>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.6.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>

編譯攔截

創建編譯操作類FramePlugin,繼承AbstractMojo並使用Mojo註解標註,output參數是class文件編譯後路徑

@Mojo(name = "deepcompile", defaultPhase = LifecyclePhase.COMPILE)
public class FramePlugin extends AbstractMojo {
    @Parameter(name = "output", defaultValue = "${project.build.directory}")
    private File output;
    public void execute() throws MojoExecutionException {
        File f = ;
        if (!f.exists()) {
            f.mkdirs();
        }
        try {
             insertPile(f);
        } catch (Exception e) {
            exceptioncount++;
            e.printStackTrace();
        }
    }

 ASM插樁

新建ClassVisitor重寫visitMethod方法來過濾訪問需要插樁的方法,需要排除自帶的init方法

public class MethodCoverageClassVisitor extends ClassVisitor {
    public MethodCoverageClassVisitor(ClassVisitor classVisitor) {
        super(Opcodes.ASM9, classVisitor);
    }

    @Override
    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature,
                                     String[] exceptions) {
        final MethodVisitor methodVisitor = super.visitMethod(access, name, descriptor, signature, exceptions);
        if (name.equals("<init>")) {
            return methodVisitor;
        }
        return new MethodCoverageMethodVisitor(Opcodes.ASM9, methodVisitor);
    }
}

新建MethodVisitor重寫visitCode方法針對方法內部字節碼進行自定義操作,這裡是使用框架內部封裝好的一個靜態方法來校驗license證書

public class MethodCoverageMethodVisitor extends MethodVisitor {
    public MethodCoverageMethodVisitor(int api, MethodVisitor methodVisitor) {
        super(api, methodVisitor);
    }

    @Override
    public void visitCode() {
        mv.visitFieldInsn(Opcodes.INVOKESTATIC, "com/xxxx/frame/common/utils/ComplieSDK", "checkLicense", "()V");
    }
}

最後在execute中進行文件遞歸查找調用,就是將已經編譯的class文件讀取/自定義操作後保存

 private void insertPile(File root) throws IOException {
        if (root.isDirectory()) {
            for (File file : root.listFiles()) {
                insertPile(file);
            }
        }
        String className = root.getName().replace(".class", "");
        if (root.getName().endsWith(".class")) {
            //class篩選
            boolean flag = false;
             //自定義的class文件篩選條件代碼
            if (flag) {
                System.out.println("【insertPile】:" + className);
                FileOutputStream fos = null;
                try {
                    final byte[] instrumentBytes = doInsertPile(root);
                    fos = new FileOutputStream(root);
                    fos.write(instrumentBytes);
                    fos.flush();
                } catch (MojoExecutionException e) {
                    System.out.println("【insertPile-exception】:" + className);
                    e.printStackTrace();
                } finally {
                    if (fos != null) {
                        fos.close();
                    }
                }
            }
        }
    }

項目使用

maven-plugin項目執行mvn install安裝到本地倉庫

框架項目配置自定義maven插件進行打包,配置執行的聲明周期為complie(編譯),這裡goal自定義命令名稱需要和mojo註解標註類中指定的name名稱一致

          <plugin>
                <groupId>com.xxxxx</groupId>
                <artifactId>frame-maven-plugin</artifactId>
                <version>1.2.5</version>
                <executions>
                    <execution>
                        <goals>
                            <!-- 執行目標 -->
                            <goal>deepcompile</goal>
                        </goals>
                        <!-- 執行這個目標所在的生命周期 -->
                        <phase>compile</phase>
                    </execution>
                </executions>
            </plugin>

到此這篇關於SpringBoot自定義maven-plugin插件整合asm代碼插樁的文章就介紹到這瞭,更多相關maven-plugin asm代碼插樁內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: