java 字节码操作_Java 动态性(4) - 字节码操作

1.字节码操作

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

字节码操作

反射

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

动态生成新的类

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

优势

比反射开销小,性能高

JAVAasist性能高于反射,低于asm

2.常见的字节码操作类库

BCEL

Byte Code Engineering Library (BCEL), 这是Apache Software Foundation 的 Jakarta 项目的一部分.BCEL是Java classworking广泛使用的一种框,它可以让您深入JVM汇编语言进行类操作的细节.BCEL与Javassist有不同的处理字节码方法,BCEL在实际的JVM指令层次上进行操作(BCEI拥有丰富的JVM指令级支持)而Javassist所强调的是源代码级别的工作

ASM

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

CGLIB(Code Generation Library)

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

Javassist

是一个开源的分析、编辑和创建Jaw字节码的类库.性能较ASM差,跟cglib差不多,但是使用简单.很多开源框架都在使用它

3.JAVAssist库

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-level API allows the users to directly edit a class file as other editors.

Aspect Oriented Programming(AOP面向切面编程):Javassist can be a good tool for adding new methods into a class and for inserting before/after/around advice at the both caller and callee sides.

Reflection:Ones of applications of Javassist is runtime reflection;Javassist enables Java programs to use a metaobject that controls method calls on base-level objects.No specialized complier or virtual machine are needed.

4.JAVAssist库的API简介

javaassist的最外层的API和JAVA的反射包中的API颇为类似

它主要由CtClass,CtMethod,以及CtField几个类组成.用以执行和JDK反射API中java.lang.Class,java.lang.reflect.Method,java.lang.reflect.Method.Field相同的操作(Ct为Complie Time)

5.JAVAssist库的简单使用

创建一个全新的类

使用XJAD反编译工具,将生成的class文件反编译成JAVA文件

使用前先导入javassist的jar包

Demo:

/**

* 使用javassist生成一个新的类

* @author Matrix42

*

*/

public class Demo01 {

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

ClassPool pool = ClassPool.getDefault();

CtClass cc = pool.makeClass("com.lorinda.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);

CtMethod m3 = CtMethod.make("public String getEname(){return ename;}", cc);

CtMethod m4 = CtMethod.make("public void setEname(String empno){this.ename = ename;}", cc);

cc.addMethod(m3);

cc.addMethod(m4);

//添加构造器

CtConstructor constructor = new CtConstructor(new CtClass[]{CtClass.intType,pool.get("java.lang.String")}, cc);

constructor.setBody("{this.empno=$1;this.ename=$2;}");

cc.addConstructor(constructor);

//将上面构造好的类写入到d:/myjava

cc.writeFile("d:/myjava");

System.out.println("生成类,成功!");

}

}

创建完成后使用XJAD反编译就可以看到源码了

反编译源码:

package com.lorinda.bean;

public class Emp

{

private int empno;

private String ename;

public int getEmpno()

{

return empno;

}

public void setEmpno(int i)

{

empno = i;

}

public String getEname()

{

return ename;

}

public void setEname(String s)

{

ename = ename;

}

public Emp(int i, String s)

{

empno = i;

ename = s;

}

}

6.JAVAssist库的API详解

方法操作

修改已有方法的方法体(插入代码到已有方法体)

新增方法

删除方法

a

b

c

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

this and actual parameters

\$0 代表的是 this,\$1 代表方法参数的第一个参数,\$2 代表方法参数的第二个参数, 以此类推,$N 代表方法参数的第 N 个参数

\$args

An arrar of parameters

The type of $args is Object[], \$args[0] 对应的是 \$1 而不是 \$0

$$

所有方法参数的简写, 主要用在方法调用上

move(String a,String b) move(\$\$) 相当于 move(\$1,\$2)

fallthrough

path

在类路径, 源文件路径等中有不存在的路径警告

\$cflow

\$r

\$_

addCatch()

\$class

\$sig

属性操作

修改已有方法的方法体(插入代码到已有方法体)

新增方法

删除方法

Demo:

import java.awt.color.CMMException;

import java.io.IOException;

import java.lang.reflect.Method;

import java.util.Arrays;

public class Demo02 {

public static void test01() throws Exception{

ClassPool pool = ClassPool.getDefault();

CtClass cc = pool.get("javassist.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()); //获得接口

}

public static void test02()throws Exception{

ClassPool pool = ClassPool.getDefault();

CtClass cc = pool.get("javassist.Emp");

//CtMethod m = CtMethod.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(\"Ha Ha\");return $1+$2;}");

cc.addMethod(m);

//通过反射调用新生产的方法

Class> clazz = cc.toClass();

Object obj = clazz.newInstance();

Method method = clazz.getDeclaredMethod("add", int.class,int.class);

Object result = method.invoke(obj, 200,300);

System.out.println(result);

}

public static void test03()throws Exception{

ClassPool pool = ClassPool.getDefault();

CtClass cc = pool.get("javassist.Emp");

CtMethod cm = cc.getDeclaredMethod("sayHello", new CtClass[]{CtClass.intType});

cm.insertBefore("System.out.println($1);");

Class> clazz = cc.toClass();

Object obj = clazz.newInstance();

Method method = clazz.getDeclaredMethod("sayHello", int.class);

method.invoke(obj, 90);

}

public static void test04() throws Exception{

ClassPool pool = ClassPool.getDefault();

CtClass cc = pool.get("javassist.Emp");

//CtField f1 = CtField.make("private int empno", cc);

CtField f1 = new CtField(CtClass.intType,"salary",cc);

f1.setModifiers(Modifier.PRIVATE);

cc.addField(f1,"1000");//1000位默认值

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

cc.addMethod(CtNewMethod.getter("salary",f1));

cc.addMethod(CtNewMethod.setter("salary", f1));

}

public static void test05()throws Exception{

ClassPool pool = ClassPool.getDefault();

CtClass cc = pool.get("javassist.Emp");

CtConstructor[] cs = cc.getConstructors();

for (CtConstructor ctConstructor : cs) {

System.out.println(ctConstructor.getLongName());

}

}

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

//test01();

//test02();

// test03();

// test04();

test05();

}

}

构造方法操作

getConstructors()

注解操作

代码片段:

public @interface Author {

String name();

int year();

}

@Author(name="Chiba",year=2005)

public class Point{

int x,y;

}

CtClass cc = ClassPool.getDefault.get("Point");

Object[] all = cc.getAnnotations();

Author a = (Author)all[0];

String name = a.name();

int year = a.year();

System.out.println("name:"+name+",year:"+year);

当调用了writeFile(),toClass(),toBytecode(),Javassist会把那个CtClass对象冻结,如果想使用冻结的对象可以调用.defrose()方法

局限性

JDK5.0新语法不支持(包括泛型,枚举),不支持注解修改,单可以的通过底层javasist类来解决,具体参考:javassist.bytecode.annotation

不支持数组的初始化,如String[]{"1","2"},除非只有数组容量为1

不支持内部类盒匿名类

不支持continue盒break表达式

对于继承关系,有些语法不支持,如:

class A{}

class B extends A{}

class C extends B{}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值