JAVAssist字节码操作

Java动态性的两种常见实现方式

  • 字节码操作
  • 反射

运行时操作字节码可以让我们实现如下功能:

  • 动态生成新的类
  • 动态改变某个类的结构(添加/删除/修改  新的属性/方法)

优势:

  • 比反射开销小,性能高
  • JAVAasist性能高于反射,低于ASM

常见的字节码操作类库

BCEL

这是Apache Software Fundation的jakarta项目的一部分。BCEL是javaclassworking广泛使用的一种跨级啊,它可以让你深入JVM汇编语言进行类的操作的细节。BCEL与javassist所强调的是源码级别的工作。

ASM

是一个轻量及java字节码操作框架,直接涉及到JVM底层的操作和指令。

CGLIB

是一个强大的,高性能,高质量的Code生成类库,基于ASM实现。

JAVAssist

JAVAssist库的API

  • javassist最外层的API和Java的反射包中的API颇为类似、
  • 它主要有CtClass,CtMethod,以及CtField及各类组成。用以执行和JDK反射API中java.lang.Class,java.lang.reflect.Method,java.lang.reflect.Method.Field相同的操作。

官方介绍:

Javassist (Java Programming Assistant) makes Java bytecode manipulation simple. It is a class library for editing bytecodes in Java; it enables Java programs to define a new class at runtime and to modify a class file when the JVM loads it. Unlike other similar bytecode editors, Javassist provides two levels of API: source level and bytecode level. If the users use the source-level API, they can edit a class file without knowledge of the specifications of the Java bytecode. The whole API is designed with only the vocabulary of the Java language. You can even specify inserted bytecode in the form of source text; Javassist compiles it on the fly. On the other hand, the bytecode-level API allows the users to directly edit a class file as other editors.

javassist库的简单使用

  • 使用一个全新的类
  • 使用XJAD反编译工具,将生成的class二年间反编译成JAVA文件

例1:

public class Demo01 {
    public static void main(String[] args) throws Exception {
        ClassPool pool = ClassPool.getDefault();
        CtClass cc = pool.makeClass("com.bjsxt.bean.Emp");

        //创建属性
        CtField f1 = CtField.make("private int empno;", cc);
        CtField f2 = CtField.make("private String ename;", cc);
        cc.addField(f1);
        cc.addField(f2);

        //创建方法
        CtMethod m1 = CtMethod.make("public int getEmpno(){return empno;}", cc);
        CtMethod m2 = CtMethod.make("public void setEmpno(int empno){this.empno=empno;}", cc);
        cc.addMethod(m1);
        cc.addMethod(m2);

        //添加构造器
        CtConstructor constructor = new CtConstructor(new CtClass[]{CtClass.intType,pool.get("java.lang.String")}, cc);
        constructor.setBody("{this.empno=empno; this.ename=ename;}");
        cc.addConstructor(constructor);

        cc.writeFile("c:/myjava"); //将上面构造好的类写入到c:/myjava中
        System.out.println("生成类,成功!");
    }
}

javassist库的API

  • 方法操作
    • 修改已有方法的方法体(插入代码到已有方法体)
    • 新增方法
    • 删除方法

各种符号替代


$0, $1, $2, ...


this and actual parameters


$args


An array of parameters. The type of $args is Object[].


$$


All actual parameters.

For example, m($$) is equivalent to m($1,$2,...)


$cflow(...)


cflow variable


$r


The result type. It is used in a cast expression.


$w


The wrapper type. It is used in a cast expression.


$_


The resulting value


$sig


An array of java.lang.Class objects representing the formal parameter types.


$type


A java.lang.Class object representing the formal result type.


$class


A java.lang.Class object representing the class currently edited.

例2:

public class Demo02 {
    /**
     * 处理类的基本用法
     * @throws Exception
     */
    public static void test01() throws Exception{
        ClassPool pool = ClassPool.getDefault();
        CtClass cc = pool.get("com.sinosoft.test.Emp");

        byte[] bytes = cc.toBytecode();
        System.out.println(Arrays.toString(bytes));

        System.out.println(cc.getName()); //获取类名
        System.out.println(cc.getSimpleName()); //获取简要类名
        System.out.println(cc.getSuperclass()); //获得父类
        System.out.println(cc.getInterfaces()); //获得接口

    }

    /**
     * 测试产生新的方法
     * @throws Exception
     */
    public static void test02() throws Exception{
        ClassPool pool = ClassPool.getDefault();
        CtClass cc = pool.get("com.bjsxt.test.Emp");

//        CtMethod m = CtNewMethod.make("public int add(int a,int b){return a+b;}", cc);

        CtMethod m = new CtMethod(CtClass.intType,"add",
                new CtClass[]{CtClass.intType,CtClass.intType},cc);
        m.setModifiers(Modifier.PUBLIC);
        m.setBody("{System.out.println(\"Hello!!!\");return $1+$2;}");

        cc.addMethod(m);

        //通过反射调用新生成的方法
        Class clazz = cc.toClass();
        Object obj = clazz.newInstance();  //通过调用Emp无参构造器,创建新的Emp对象
        Method method = clazz.getDeclaredMethod("add", int.class,int.class);
        Object result = method.invoke(obj, 200,300);
        System.out.println(result);
    }

    /**
     * 修改已有的方法的信息,修改方法体的内容
     * @throws Exception
     */
    public static void test03() throws Exception{
        ClassPool pool = ClassPool.getDefault();
        CtClass cc = pool.get("com.sinosoft.test.Emp");

        CtMethod cm = cc.getDeclaredMethod("sayHello",new CtClass[]{CtClass.intType});
        //方法执行前
        cm.insertBefore("System.out.println($1);System.out.println(\"start!!!\");");
        cm.insertAt(9, "int b=3;System.out.println(\"b=\"+b);");
        //方法执行后
        cm.insertAfter("System.out.println(\"after!!!\");");

        //通过反射调用新生成的方法
        Class clazz = cc.toClass();
        Object obj = clazz.newInstance();  //通过调用Emp无参构造器,创建新的Emp对象
        Method method = clazz.getDeclaredMethod("sayHello", int.class);
        method.invoke(obj, 300);
    }

    /**
     * 属性的操作
     * @throws Exception
     */
    public static void test04() throws Exception{
        ClassPool pool = ClassPool.getDefault();
        CtClass cc = pool.get("com.sinosoft.test.Emp");

//        CtField f1 = CtField.make("private int empno;", cc);
        CtField f1 = new CtField(CtClass.intType,"salary",cc);
        f1.setModifiers(Modifier.PRIVATE);
        cc.addField(f1);

//        cc.getDeclaredField("ename");   //获取指定的属性

        //增加相应的set和get方法
        cc.addMethod(CtNewMethod.getter("getSalary", f1));;
        cc.addMethod(CtNewMethod.getter("setSalary", f1));;

    }

    /**
     * 构造方法的操作
     * @throws Exception
     */
    public static void test05() throws Exception {
        ClassPool pool = ClassPool.getDefault();
        CtClass cc = pool.get("com.sinosoft.test.Emp");

        CtConstructor[] cs = cc.getConstructors();
        for (CtConstructor c : cs) {
            System.out.println(c.getLongName());
        }
    }

    /**
     * 注解操作
     * @throws Exception
     */
    public static void test06() throws Exception{
         CtClass cc = ClassPool.getDefault().get("com.sinosoft.test.Emp");
         Object[] all = cc.getAnnotations();
         Author a = (Author)all[0];
         String name = a.name();
         int year = a.year();
         System.out.println("name: " + name + ", year: " + year);

    }

    public static void main(String[] args) throws Exception {
        test06();
    }
}

/**
 * 注解类
 *
*/
public @interface Author {
          String name();
           int year();
}

局限性:

  • JDK5.0行语法不支持(包括泛型、枚举),不支持注解修改,但可以通过底层的javassist类来解决,具体参考javassist.bytecode.annotation
  • 不支持数组的初始化,如String[]{“1”,“2”},除非只有数组的容量为1
  • 不支持内部类和匿名类
  • 不支持continue和btreak表达式
  • 对于继承关系,有些不支持,例如:
class A{}

class extends A{}

class C extends B{}
时间: 2024-10-05 03:40:35

JAVAssist字节码操作的相关文章

搭建agent服务+javassist字节码操作

简介 对于agent,是在vm启动,执行方法前,将字节码修改的服务代理. 对于javassist,是修改字节码具体实现. 这两个结合有什么用啊?写测试框架,不仅局限于此. 对于字节码: 字节码处理工具,bcel,asm与虚拟机指令打交道.Javassist采用java编码的方法处理字节码,性能相对较低,但方便. 官方:http://www.csg.ci.i.u-tokyo.ac.jp/~chiba/javassist/ 例子: 创建class http://blog.csdn.net/sadfi

[译]深入字节码操作:使用ASM和Javassist创建审核日志

深入字节码操作:使用ASM和Javassist创建审核日志 原文链接:https://blog.newrelic.com/2014/09/29/diving-bytecode-manipulation-creating-audit-log-asm-javassist/ 在堆栈中使用Spring和Hibernate,您的应用程序的字节码可能会在运行时被增强或处理. 字节码是Java虚拟机(JVM)的指令集,所有在JVM上运行的语言都必须最终编译为字节码. 操作字节码原因如下: 程序分析: 查找应用

Java字节码操作开源框架简介

avassist  Javassist是一个开源的分析.编辑和创建Java字节码的类库.是由东京技术学院的数学和计算机科学系的 Shigeru Chiba 所创建的.它已加入了开放源代码JBoss 应用服务器项目,通过使用Javassist对字节码操作为JBoss实现动态AOP框架. BCEL  Byte Code Engineering Library (BCEL),这是Apache Software Foundation 的Jakarta 项目的一部分.BCEL是 Java classwor

第五篇 java字节码操作

java动态性的两种常见的实现方式: ①字节码操作 ②反射 运行时操作字节码可以让我们实现如下功能: ①动态生成新的类 ②动态改变某个类的结构(添加.删除.修改  新的属性或方法) 优点: 比反射的开销小,性能高 Javassist性能高于反射,低于ASM BCEL(Byte Code Engineering Library): 是java classworking 广泛使用的一种框架,它可以让你深入JVM汇编语言进行类操作的细节.BCEL与Javassist有不同的处理字节码的方法,BCEL在

Dubbo源码分析(八):Javassist字节码技术生成代理

Java动态编程的作用:      通过配置生成代码,减少重复编码和维护成本 我们常用到的动态特性主要是反射,在运行时查找对象属性.方法,修改作用域,通过方法名称调用方法等.在线的应用不会频繁使用反射,因为反射的性能开销较大.其实还有一种和反射一样强大的特性,但是开销却很低,它就是Javassit. Javassit其实就是一个二方包,提供了运行时操作Java字节码的方法.大家都知道,Java代码编译完会生成.class文件,就是一堆字节码.JVM(准确说是JIT)会解释执行这些字节码(转换为机

Javassist字节码增强示例

概述 Javassist是一款字节码编辑工具,可以直接编辑和生成Java生成的字节码,以达到对.class文件进行动态修改的效果.熟练使用这套工具,可以让Java编程更接近与动态语言编程. 下面一个方法的目的是获取一个类加载器(ClassLoader),以加载指定的.jar或.class文件,在之后的代码中会使用到. [java] view plaincopy private static ClassLoader getLocaleClassLoader() throws Exception {

虚拟机字节码操作引擎-----基于栈的字节码解释引擎

虚拟机调用方法可以有解析和分派两种方式,那么虚拟机是如何执行方法中的字节码指令的? 1.解释执行   谈是解释执行还是翻译执行没有意义了,只有确定了某种具体的java实现版本和执行引擎运行模式时,谈解释执行还是编译执行才比较贴切. 如今,基于物理机.java虚拟机,或者非Java的其他高级语言虚拟机的语言,大多都会遵循这种基于现代经典编译原理的思路,在执行前先对程序源码进行词法分析和语法分析处理,把源码转化为抽象语法树 .对于一门具体语言的实现来说,词法分析.语法分析以致后面的优化器和目标代码生

5.Dubbo原理解析-代理之Javassist字节码技术生成代理 (转)

转载自  斩秋的专栏  http://blog.csdn.net/quhongwei_zhanqiu/article/details/41597219 JavassistProxyFactory:利用字节码技术来创建对象 public <T> T getProxy(Invoker<T> invoker,Class<?>[] interfaces) { return (T) Proxy.getProxy(interfaces).newInstance(new Invoke

JAVA 字节码操作利器javassist

引用:http://blog.csdn.net/hudashi/article/details/50884742 http://blog.csdn.net/haitaofeiyang/article/details/44264685