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{}

JAVAssist字节码操作的更多相关文章

  1. Javassist 字节码操作

    1.读写字节码 Javassist是用来处理java字节码的类库.字节码保存在二进制文件中称为类文件.每个类文件夹包括一个java类或接口. Javasssist.CtClass这个类是一个类文件的抽 ...

  2. 字节码操作JAVAssist

    字节码操作Javassist 字节码:字节码是设计被用来将代码高效的传送给多种软件平台.硬件平台,字节码的设计也实现了Java的平台无关性,字节码比机器码更抽象,它通常被认为是包含了一个可执行文件的二 ...

  3. Javassist 字节码 简介 案例 MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  4. Javassist 字节码 语法 MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  5. 尚学堂 216 java中的字节码操作

    所谓的字节码操作就是操作我们已经加载的字节码 接下来我们重点来讲解javaassist类库 使用需要下载jar包,把jar包添加到对应的工程之后 package com.bjsxt.test; pub ...

  6. Java动态性 字节码操作

    Java动态性的两种常见方式:-字节码操作 - 反射;字节码操作比反射开销小,性能高,JAVAasist性能高于反射,低于ASM 运行时操作字节码可是实现  : 动态生成新的类:动态的改变某个类的结构 ...

  7. [19/04/20-星期六] Java的动态性_字节码操作(Javassist类库(jar包),assist:帮助、援助)

    一.概念 [基本] /** * */ package cn.sxt.jvm; import javassist.ClassPool; import javassist.CtClass; import ...

  8. 8.5(java学习笔记)8.5 字节码操作(javassist)

    一.javassist javassist让我们操作字节码更加简单,它是一个类库,允许我们修改字节码.它允许java程序动态的创建.修改类. javassist提供了两个层次的API,基于源码级别的和 ...

  9. 字节码操作、javassist使用

    一.功能 1.动态生成新的类 2.动态改变某个类的结构(添加.删除.修改   新的属性.方法) 二.优势 1.比反射开销小,性能高 2.JAVAasist性能高于反射,低于ASM 使用javassis ...

随机推荐

  1. MT【91】空间余弦定理

    评:空间余弦定理:空间四边形$ABCD$中$cos<AC,BD>=\frac{|(|AB|^2+|CD|^2)-(|BC|^2+|AD|^2)}{2|AC||BD|}$,证明用向量.

  2. 【HDU1710】树的遍历

    题目大意:给定一棵 N 个节点的二叉树的前序遍历和中序遍历,求其后序遍历. 题解:递归操作,每次只需知道先序遍历和中序遍历的开始点,左子树大小即可,根据前序遍历的开始位置可知子树根节点的坐标,再在中序 ...

  3. Python3:if __name__ == '__main__' 详解

    一般在风格比较好的代码中会有一行if __name__ == '__main__' :代码,这里说明一下这句代码的用处,先上两个代码test1.py和test2.py: # test1.py prin ...

  4. sqoop的安装与配置

    最近需要将MySQL的数据导出到HDFS,所以搜到了sqoop2.跟sqoop1相比,sqoop2的好处是直接使用程序连接到集群上的sqoop,远程操作.流程是需要先创建link也可以理解成要操作的对 ...

  5. OpenStack 网络服务 Neutron 私有网络构建(十九)

    本章内容基于之前提供者网络构建的基础上进行改动,之前文章参考如下: Openstack 网络服务 Neutron介绍和控制节点部署 (九) Openstack 网络服务 Neutron计算节点部署(十 ...

  6. windows下非管理员权限安装mysql

    windows下,mysql有两种安装方式: 1.msi安装 2.zip安装 无论是哪种安装方式,都因为需要将mysql安装为一个服务,所以必须要以管理员权限安装. 因为公司的换了虚拟机,无法取得管理 ...

  7. 支付宝APP支付,提示代码 ALIN10070

    ALIN10070 此代码时ALI64代码拆分后的细分代码: 代表签名验证失败等相关问题: 如果近期修改过或者续签 过签约协议,也需要更新公私钥.

  8. H5 以及 CSS3

    <!DOCTYPE html> <html> <head> <style> *{ padding:0; margin:0; } header{ disp ...

  9. 【整理】HTML5游戏开发学习笔记(5)- 猜谜游戏

    距上次学习笔记已有一个多月过去了,期间由于新项目赶进度,以致该学习计划给打断,十分惭愧.书本中的第六章的例子相对比较简单.所以很快就完成. 1.预备知识html5中video标签的熟悉 2.实现思路对 ...

  10. 使用data:uri上传图片

    上传图片的方式有两种,一种是使用传统的html控件的方式,设置form属性为multipart/form-data.这种方式兼容ie6,ie7.另一种方式是使用data:uri,将base64编码从浏 ...