因业务需要,公司将某项目改造,故需要将业务逻辑层service实现类中所有的私有private方法改为public,以便在项目打包成jar包后,供子类复用父类的私有方法。考虑到业务类比较多,手动改比较麻烦,现采用ASM字节码操作技术编写maven插件,利用插件在项目编译compile阶段进行修改。
maven 插件开发 新建插件项目1、将项目的pom文件打包方式配置为如下:
maven-plugin
2、新增maven依赖
3.5.2 2.2.1 9.0 5.8.0 3.5.2 org.apache.maven maven-plugin-api ${maven-plugin-api.version} org.apache.maven.plugin-tools maven-plugin-annotations ${maven-plugin-annotations.version} provided org.apache.maven maven-project ${maven-project.version} org.ow2.asm asm ${asm.version} cn.hutool hutool-all ${hutool-all.version}
3、打包plugin引入如下
org.apache.maven.plugins maven-plugin-plugin 3.5 org.apache.maven.plugins maven-compiler-plugin 3.8.1 1.8 1.8 UTF-8
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];
}
}
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 = "";
public MethodClassVisitor(ClassVisitor classVisitor) {
super(Opcodes.ASM9, classVisitor);
}
@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、在项目中引入已打包好的插件
org.apache.maven.plugins maven-compiler-plugin {输入你项目的插件版本} 8 8 utf8 com.xxxx.framework.plugin method-modify-plugin 1.0-SNAPSHOT modify compile
相关文章参考连接
ASM字节码插桩技术参考1
ASM字节码插桩技术参考2
maven 内置变量说明参考
maven插件开发参考



