Java 动态生成二进制字节码

来源:互联网 发布:com.au域名注册 编辑:程序博客网 时间:2024/06/05 15:02

Java动态代理过程中,会动态生成二进制字节码(只在内存中),该生成过程究竟是怎样的,我来一探究竟。

class字节码文件是根据JVM虚拟机规范中规定的字节码组织规则生成的。

1、类加载器

a.定义一个 Programmer类:    public class Programmer {                public void code()          {              System.out.println("I'm a Programmer,Just Coding.....");          }      }  b.自定义一个类加载器:    public class MyClassLoader extends ClassLoader {                public Class<?> defineMyClass( byte[] b, int off, int len)           {              return super.defineClass(b, off, len);          }                }  c.然后编译成Programmer.class文件,在程序中读取字节码,然后转换成相应的class对象,再实例化:     public class MyTest {                public static void main(String[] args) throws IOException {              //读取本地的class文件内的字节码,转换成字节码数组              File file = new File(".");              InputStream  input = new FileInputStream(file.getCanonicalPath()+"\\bin\\samples\\Programmer.class");              byte[] result = new byte[1024];                            int count = input.read(result);              // 使用自定义的类加载器将 byte字节码数组转换为对应的class对象              MyClassLoader loader = new MyClassLoader();              Class clazz = loader.defineMyClass( result, 0, count);              //测试加载是否成功,打印class 对象的名称              System.out.println(clazz.getCanonicalName());                                           //实例化一个Programmer对象                     Object o= clazz.newInstance();  //new Programmer();                   try {                         //调用Programmer的code方法                          clazz.getMethod("code", null).invoke(o, null);                         } catch (IllegalArgumentException | InvocationTargetException                              | NoSuchMethodException | SecurityException e) {                           e.printStackTrace();                        }       }      }  以上代码演示了,通过字节码加载成class对象的能力。 

上面只是使用类加载器来手动加载class文件到内存中(class文件是存在的),那么class文件如何动态生成呢?

2、在运行期生成二进制字节码

由于JVM通过字节码的二进制信息加载类的,那么,如果我们在运行系统中,遵循Java编译系统组织.class文件的格式和结构,生成相应的二进制数据,然后再把这个二进制数据加载转换成对应的类,这样,就完成了在代码中,动态创建一个类的能力了。
在运行时期可以按照Java虚拟机规范对class文件的组织规则生成对应的二进制字节码。当前有很多开源框架可以完成这些功能,如ASM,Javassist。

1)Java字节码生成开源框架介绍:ASM
ASM 是一个 Java 字节码操控框架。它能够以二进制形式修改已有类或者动态生成类。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。ASM 从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。不过ASM在创建class字节码的过程中,操纵的级别是底层JVM的汇编指令级别,这要求ASM使用者要对class组织结构和JVM汇编指令有一定的了解

使用ASM框架提供了ClassWriter 接口,通过访问者模式进行动态创建Programmer.class字节码:

public class MyGenerator {        public static void main(String[] args) throws IOException {            System.out.println();          ClassWriter classWriter = new ClassWriter(0);         // 通过visit方法确定类的头部信息          classWriter.visit(Opcodes.V1_7,// java版本                  Opcodes.ACC_PUBLIC,// 类修饰符                  "Programmer", // 类的全限定名                  null, "java/lang/Object", null);                    //创建构造函数          MethodVisitor mv = classWriter.visitMethod(Opcodes.ACC_PUBLIC, "<init>",                                                                 "()V", null, null);          mv.visitCode();          mv.visitVarInsn(Opcodes.ALOAD, 0);          mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>","()V");          mv.visitInsn(Opcodes.RETURN);          mv.visitMaxs(1, 1);          mv.visitEnd();                    // 定义code方法          MethodVisitor methodVisitor = classWriter.visitMethod(Opcodes.ACC_PUBLIC,                                                          "code", "()V", null, null);          methodVisitor.visitCode();          methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out",                  "Ljava/io/PrintStream;");          methodVisitor.visitLdcInsn("I'm a Programmer,Just Coding.....");          methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println",                  "(Ljava/lang/String;)V");          methodVisitor.visitInsn(Opcodes.RETURN);          methodVisitor.visitMaxs(2, 2);          methodVisitor.visitEnd();          classWriter.visitEnd();           // 使classWriter类已经完成          // 将classWriter转换成字节数组写到文件里面去          byte[] data = classWriter.toByteArray();          File file = new File("D://Programmer.class"); //生成字节码文件         FileOutputStream fout = new FileOutputStream(file);          fout.write(data);          fout.close();      }  }  
2)Java字节码生成开源框架介绍Javassist
Javassist是一个开源的分析、编辑和创建Java字节码的类库。是由东京工业大学的数学和计算机科学系的 Shigeru Chiba (千叶滋)所创建的。它已加入了开放源代码JBoss应用服务器项目,通过使用Javassist对字节码操作为JBoss实现动态AOP框架。javassist是jboss的一个子项目,其主要的优点,在于简单,而且快速。直接使用java编码的形式,而不需要了解虚拟机指令,就能动态改变类的结构,或者动态生成类

生成Programmer.class字节码:

public class MyGenerator {        public static void main(String[] args) throws Exception {          ClassPool pool = ClassPool.getDefault();          //创建Programmer类               CtClass cc= pool.makeClass("com.samples.Programmer");          //定义code方法          CtMethod method = CtNewMethod.make("public void code(){}", cc);          //插入方法代码          method.insertBefore("System.out.println(\"I'm a Programmer,Just Coding.....\");");          cc.addMethod(method);          //保存生成的字节码          cc.writeFile("d://temp");      }  } 

参考:

Java动态代理机制详解(JDK 和CGLIB,Javassist,ASM) (清晰,浅显)

0 0
原创粉丝点击