1.代码实例
1.1 主类
package asm.bytecode;
import asm.create.bean.MyClassLoader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import static org.objectweb.asm.Opcodes.*;
/**
* public class ForAsm {
*
* public ForAsm() {
* super();
* }
* int sum = 0;
* for(int i=0; i<=100; i++) {
* sum += i;
* }
* }
* return sum;
* @date 2018/12/29 23:58
*/
public class ForAsmMain {
public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
String fullName = ClassUtil.buildFullName("ForAsm");
String fullNameType = fullName.replace(".","/");
/**
* ClassWriter参数:
* 0:自己计算操作数栈和局部变量栈
*/
ClassWriter cw = new ClassWriter(0);
ByteCodeUtil byteCodeUtil = new ByteCodeUtil(cw,fullNameType);
byteCodeUtil.createClass(null);
byteCodeUtil.generatorConstructor(null);
/**
* public int spain() {
* int sum = 0;
* for(int i=0; i<=100; i++) {
* sum += i;
* }
* return sum;
* }
*/
MethodVisitor spainMethod = cw.visitMethod(ACC_PUBLIC, "spain", "()I", null, null);
spainMethod.visitCode();
//下面两条指令: int sum = 0
spainMethod.visitInsn(ICONST_0);
spainMethod.visitVarInsn(ISTORE,1);
//for循环的开始标记
Label forLabel = new Label();
//for循环的结束标记
Label endLabel = new Label();
//变量索引0的位置存放了this
//int i = 0;
spainMethod.visitInsn(ICONST_0);
spainMethod.visitVarInsn(ISTORE,2);
//做了一个用于跳转的标记
spainMethod.visitLabel(forLabel);
//从索引为1的位置取出变量
spainMethod.visitVarInsn(ILOAD,2);
//将常量100放入操作数栈中
spainMethod.visitVarInsn(BIPUSH,100);
//当i<=100时,执行
spainMethod.visitJumpInsn(IF_ICMPGT,endLabel);
//做一些逻辑操作
spainMethod.visitVarInsn(ILOAD,1);
spainMethod.visitVarInsn(ILOAD,2);
spainMethod.visitInsn(IADD);
spainMethod.visitVarInsn(ISTORE,1);
//将局部变量表中索引为1的数值加1,做i++操作
spainMethod.visitIincInsn(2,1);
spainMethod.visitJumpInsn(GOTO,forLabel);
spainMethod.visitLabel(endLabel);
//要返回一个数据,只需要将该数据放入到操作数栈栈顶
spainMethod.visitVarInsn(ILOAD,1);
spainMethod.visitInsn(IRETURN);
//计算操作数栈和局部变量表的大小,对于操作数栈,任何读取操作数的指令,都会将数据POP(弹出栈)
spainMethod.visitMaxs(2,3);
spainMethod.visitEnd();
byte[] bytes = byteCodeUtil.writeToFile(null);
MyClassLoader classLoader = new MyClassLoader();
System.out.println(fullNameType.replace("/","."));
Class<?> cls = classLoader.defineClassPublic(fullNameType.replace("/","."),bytes,0,bytes.length);
Object o = cls.newInstance();
Method spain = cls.getMethod("spain");
Object ret = spain.invoke(o);
System.out.println(ret);
}
}
1.2 帮助生成字节码的工具类
package asm.bytecode;
import org.apache.commons.lang3.StringUtils;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Map;
import static org.objectweb.asm.Opcodes.*;
/**
* 字节码相关的操作工具类
* @date 2018/12/30 0:12
*/
public class ByteCodeUtil {
private ClassWriter cw;
private String classNameType;
public ByteCodeUtil(ClassWriter cw,String classNameType) {
this.cw = cw;
this.classNameType = classNameType;
}
/**
* 创建类
* public class Demo {
*
* private String deveceName;
* private String deviceType;
* private Integer deviceSize;
* ....
* }
* @param typeNameMap
*/
public void createClass(Map<String,Class<?>> typeNameMap) {
cw.visit(V1_7, ACC_PUBLIC, classNameType, null, "java/lang/Object", null);
if (typeNameMap != null && typeNameMap.size() != 0) {
for (Map.Entry<String, Class<?>> entry : typeNameMap.entrySet()) {
cw.visitField(ACC_PRIVATE, entry.getKey(), Type.getDescriptor(entry.getValue()), null, null).visitEnd();
}
}
}
/**
* 生成构造函数
* nameValueMap == null:
* public Demo() {
* super();
* }
* 不空:
* public Demo(String deveceName,String deviceType,Integer deviceSize...) {
* super.Object();
* this.deveceName = deveceName;
* this.deviceType = deviceType;
* this.deviceSize = deviceSize;
* ......
* }
* @param nameValueMap key:成员属性的名称,value:初始化的值
*/
public void generatorConstructor(Map<String,Object> nameValueMap){
MethodVisitor constructorMethod = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
constructorMethod.visitCode();
constructorMethod.visitIntInsn(ALOAD,0);
constructorMethod.visitMethodInsn(INVOKESPECIAL,"java/lang/Object","<init>","()V",false);
//带有参数的构造函数
boolean hasParam = nameValueMap != null && nameValueMap.size() != 0;
if(hasParam) {
// todo 设置参数
}
constructorMethod.visitInsn(RETURN);
//手动维护栈大小和局部变量表的大小
constructorMethod.visitMaxs(1,1);
if(hasParam) {
// todo 重新计算栈大小和局部变量表的大小
}
constructorMethod.visitEnd();
}
/**
* 将字节码数据写入到文件
* @param pathName 要写入的文件路径,目前为空
* @return
*/
public byte[] writeToFile(String pathName) {
colseClassWrite();
byte[] bytes = null;
if(StringUtils.isEmpty(pathName)) {
File file = new File("F:\\out.class");
try (
FileOutputStream fos = new FileOutputStream(file);
) {
bytes = cw.toByteArray();
fos.write(bytes);
fos.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
return bytes;
}
public void colseClassWrite() {
cw.visitEnd();
}
}
1.3 类操作相关的工具类
package asm.bytecode;
/**
* 类操作工具类
* @date 2018/12/30 20:44
*/
public class ClassUtil {
/**
* 构建全限定名,即包名+类名:asm.bytecode.Demo
* @param className
* @return
*/
public static String buildFullName(String className) {
String packagePath = ClassUtil.class.getName();
return packagePath + "." + className;
}
/**
* 构建类的路径名,即asm/bytecode/Demo
*/
public static String buildFullNameType(String className) {
return buildFullName(className).replace(".","/");
}
}
2.运行结果
3.出现的问题
4. 解决方案
java8添加该参数: -noverify
java7添加该参数:-XX:-UseSplitVerifier
源码github地址:https://github.com/wjyGithub/ASMLearn