SpringBoot Maven 插件开发-ASM字节码插桩技术修改代码

需求背景

因业务需要,公司将某项目改造,故需要将业务逻辑层service实现类中所有的私有private方法改为public,以便在项目打包成jar包后,供子类复用父类的私有方法。考虑到业务类比较多,手动改比较麻烦,现采用ASM字节码操作技术编写maven插件,利用插件在项目编译compile阶段进行修改。

maven 插件开发

新建插件项目

1、将项目的pom文件打包方式配置为如下:

<packaging>maven-plugin</packaging>

2、新增maven依赖

	<properties>
        <maven-plugin-annotations.version>3.5.2</maven-plugin-annotations.version>
        <maven-project.version>2.2.1</maven-project.version>
        <asm.version>9.0</asm.version>
        <hutool-all.version>5.8.0</hutool-all.version>
        <maven-plugin-api.version>3.5.2</maven-plugin-api.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.apache.maven</groupId>
            <artifactId>maven-plugin-api</artifactId>
            <version>${maven-plugin-api.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.maven.plugin-tools</groupId>
            <artifactId>maven-plugin-annotations</artifactId>
            <version>${maven-plugin-annotations.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.maven</groupId>
            <artifactId>maven-project</artifactId>
            <version>${maven-project.version}</version>
        </dependency>
        <dependency>
            <groupId>org.ow2.asm</groupId>
            <artifactId>asm</artifactId>
            <version>${asm.version}</version>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>${hutool-all.version}</version>
        </dependency>
    </dependencies>

3、打包plugin引入如下

	<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.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
        </plugins>
    </build>

4、插件代码开发

import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;


@Mojo(name = "modify", defaultPhase = LifecyclePhase.COMPILE)
public class PluginAware extends AbstractMojo {

   // 入参输出目录,${project.build.directory} 表示采用maven的默认配置,为项目下的target目录
   @Parameter(name = "output", defaultValue = "${project.build.directory}")
   private File output;

   public void execute() {
       File f = output;
       if (!f.exists()) {
           f.mkdirs();
       }
       try {
           insertPile(f);
       } catch (Exception e) {
           e.printStackTrace();
       }
   }

   private void insertPile(File root) throws IOException {
       if (root.isDirectory()) {
           for (File file : root.listFiles()) {
               insertPile(file);
           }
       }
       String className = root.getName();
       if (filter(className)) {
           FileOutputStream fos = null;
           try {
               final byte[] data = doInsertPile(root);
               fos = new FileOutputStream(root);
               fos.write(data);
               fos.flush();
           } catch (Exception e) {
               e.printStackTrace();
           } finally {
               if (fos != null) {
                   fos.close();
               }
           }
       }
   }

   private byte[] doInsertPile(File file) {
       try {
           ClassReader cr = new ClassReader(new FileInputStream(file));
           ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
           cr.accept(new MethodClassVisitor(cw), ClassReader.SKIP_DEBUG);
           return cw.toByteArray();
       } catch (Exception e) {
           e.printStackTrace();
           return new byte[0];
       }
   }

   /**
    * 只处理业务逻辑层的的类
    * @param className 类名
    * @return  true or false
    */
   private boolean filter(String className) {
       String suffix = "ServiceImpl";
       return className.contains(suffix) && className.length() > suffix.length();
   }
}

import cn.hutool.core.util.StrUtil;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;

public class MethodClassVisitor extends ClassVisitor {

    /**
     * 构造器
     */
    private static final String CONSTRUCTOR = "<init>";


    public MethodClassVisitor(ClassVisitor classVisitor) {
        super(Opcodes.ASM9, classVisitor);
    }

    /**
     * 除了构造方法外,将所有带有private修饰符的方法改为public修饰符
     */
    @Override
    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
        // 过滤掉构造器方法
        if (StrUtil.equals(name, CONSTRUCTOR)) {
            return super.visitMethod(access, name, descriptor, signature, exceptions);
        }
        if (access == ACC_PRIVATE) {
            access = ACC_PUBLIC;
        }
        return super.visitMethod(access, name, descriptor, signature, exceptions);
    }
}

使用插件

1、将插件打包安装到本地
切换到项目根目录,执行

mvn clean install

2、在项目中引入已打包好的插件

	<build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>{输入你项目的插件版本}</version>
                <configuration>
                    <source>8</source>
                    <target>8</target>
                    <encoding>utf8</encoding>
                </configuration>
            </plugin>
            <!-- 这是你编写的插件 -->
            <plugin>
                <groupId>com.xxxx.framework.plugin</groupId>
                <artifactId>method-modify-plugin</artifactId>
                <version>1.0-SNAPSHOT</version>
                <executions>
                    <execution>
                        <goals>
                            <!-- 执行目标 需要与 PluginAware 类上name参数保持一致 -->
                            <goal>modify</goal>
                        </goals>
                        <!-- 执行这个目标所在的生命周期 -->
                        <phase>compile</phase>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

相关文章参考连接
ASM字节码插桩技术参考1
ASM字节码插桩技术参考2
maven 内置变量说明参考
maven插件开发参考

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值