一、javassist

  javassist让我们操作字节码更加简单,它是一个类库,允许我们修改字节码。它允许java程序动态的创建、修改类。

  javassist提供了两个层次的API,基于源码级别的和字节码级别的。

  

二、javassist创建类

  1.获取类池

  2.在类池中添加要创建的类

  3.添加变量,方法,构造器

  4.将创建好的类写出 

import java.io.IOException;
import java.lang.String; import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtField;
import javassist.CtMethod;
import javassist.NotFoundException; public class TestSsist {
public static void main(String[] args) throws CannotCompileException, NotFoundException {
//获取默认类池
ClassPool pool = ClassPool.getDefault();
//在类池中创建一个类
CtClass cc = pool.makeClass("com.TestSsist.TestUser");//包名(com.TestSsist)+类名(TestUser)
//创建属性
CtField name = CtField.make("private String name;", cc);//(代码,类)
CtField age = CtField.make("private int age;", cc);
//添加属性
cc.addField(name);
cc.addField(age); //创建方法
CtMethod setAll = CtMethod.make("public void setName(String name,int age){"
+ "this.name = name;"
+ "this.age = age;"
+ "}", cc);
CtMethod getName = CtMethod.make("public String getName(){"
+ "return this.name;"
+ "}", cc);
//添加方法
cc.addMethod(setAll);
cc.addMethod(getName); //创建构造器
CtConstructor constructor = new CtConstructor(new CtClass[]{pool.get("java.lang.String"),CtClass.intType},cc);
//设置构造器体内容,即{}部分内容
constructor.setBody("{this.name = name;"
+ "this.age = age;}");
//添加构造器
cc.addConstructor(constructor);
try {
cc.writeFile("F:\\TestJava");//将创建的类写出到指定路径
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("创建成功!");
}
}
运行结果:
创建成功

我们可以看指定目录下有创建好的class文件。

字节码文件我们是无法直接查看的,我们可以通过工具(XJad)将其反编译进行查看我们创建好的类。

三、获取类基本信息(该类已存在)

测试类(TestUser)内容如下:(该类已存在)

import java.io.Serializable;

public class TestUser implements Serializable{
private String naem;
private int age; public TestUser() {} public TestUser(String naem, int age) {
super();
this.naem = naem;
this.age = age;
} public String getNaem() {
return naem;
}
public void setNaem(String naem) {
this.naem = naem;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
} public void testSsist(int a){
System.out.println("ssist:"+a);
}
}

  public String getName();//获取类名

  public CtClass getSuperclass();//获取父类

  public CtClass[] getInterfaces();//获取接口

  

  1.获取类池

  2.获取指定类

  3.获取类相关信息

  

import java.io.IOException;
import java.lang.String; import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtField;
import javassist.CtMethod;
import javassist.NotFoundException; public class TestSsist {
public static void main(String[] args) throws CannotCompileException, NotFoundException {
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("TestSsist.TestUser");
System.out.println("类名:" + cc.getName());
System.out.println("父类:"+cc.getSuperclass().getName());
for(CtClass temp:cc.getInterfaces())
System.out.println("接口:" + temp.getName());
}
}
运行结果:
类名:TestSsist.TestUser
父类:java.lang.Object
接口:java.io.Serializable

四、动态添加方法

  1.获取类池

  2.获取对应类(TestUser)

  3.动态添加方法

  4.通过反射调用动态添加的方法  

import java.io.IOException;
import java.lang.String;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier; import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtField;
import javassist.CtMethod;
import javassist.NotFoundException; public class TestSsist {
public static void main(String[] args) throws CannotCompileException, NotFoundException, NoSuchMethodException,
                               SecurityException, InstantiationException, IllegalAccessException {
ClassPool pool = ClassPool.getDefault();//获取类池
CtClass cc = pool.get("TestSsist.TestUser");//获取指定类
//public CtMethod(CtClass returnType, String mname,CtClass[] parameters, CtClass declaring)
//动态添加方法
CtMethod mAdd = new CtMethod(CtClass.intType,"add",
new CtClass[]{CtClass.intType,CtClass.intType},cc);
mAdd.setModifiers(Modifier.PUBLIC);//设置方法访问权限
//因为我们在前面添加方法时只指定了形参类型,并没有指定形参名,所以要用如下方法设置。
//设置方法体,$1+$2,第一个参数+第二个参数:例如int add(int a,int b){...} retutn $1+$2就等于return a+b;
mAdd.setBody("{System.out.println($1 +\"+\"+ $2);return $1+$2;}");
cc.addMethod(mAdd); //通过反射调用动态生成的方法
Class cl = cc.toClass();
Object u1 = cl.newInstance();
//获取指定方法
Method addm = cl.getDeclaredMethod("add",int.class,int.class);
Object result = null;
try { result = addm.invoke(u1,50,60);//调用方法
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(result);
}
}
运行结果:
50+60
110

添加的方法只是运行时存在,运行结束就会消失,并不会将其真正写入TestUser类中,如果想保存的话,就通过writeFile写出。

五、动态修改方法

  1.获取类池

  2.获取指定类(TestUser)

   3.获取指定类中指定方法

   4.调用insertBefore,insertAfter、insertAt插入新语句,即修改方法。

   5.通过反射调用动态修改后的方法

   

import java.io.IOException;
import java.lang.String;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier; import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtField;
import javassist.CtMethod;
import javassist.NotFoundException; public class TestSsist {
public static void main(String[] args) throws CannotCompileException, NotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException {
ClassPool pool = ClassPool.getDefault();//获取类池
CtClass cc = pool.get("TestSsist.TestUser");//获取指定类
//获取指定方法,(方法名,方法类型(CtClass[]类型))
CtMethod m1 = cc.getDeclaredMethod("testSsist",new CtClass[]{CtClass.intType});//获取类中指定方法
//在32行插入语句
m1.insertAt(32, "System.out.println(\"32\");");
//在方法体开头插入语句
m1.insertBefore("System.out.println(\"start\");");
//在方法体末尾插入语句
m1.insertAfter("System.out.println(\"end\");");
//通过反射调用动态修改后的方法
Class cl = cc.toClass();
Object u1 = cl.newInstance();
//获取指定方法
Method addm = cl.getDeclaredMethod("testSsist",int.class);
Object result = null;
try { result = addm.invoke(u1,50);//调用方法
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
运行结果:
start
ssist:50
32
end
先执行方法体开头插入的语句
System.out.println(\"start\");
然后执行方法中自带的语句System.out.println("ssist:"+a);
然后执行插入到32行的语句Syste.out.println(32);
最后执行方法体末尾插入的语句System.out.println("end");

六、动态创建修改属性

  1.创建属性

   2.为对应属性创建set方法

   3.通过反射调用set方法为其赋值

   4.通过反射获取属性值

import java.io.IOException;
import java.lang.String;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier; import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtField;
import javassist.CtMethod;
import javassist.CtNewMethod;
import javassist.NotFoundException; public class TestSsist {
public static void main(String[] args) throws CannotCompileException, NotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, ClassNotFoundException {
ClassPool pool = ClassPool.getDefault();//获取类池
CtClass cc = pool.get("TestSsist.TestUser");//获取指定类
//动态创建password属性
CtField nameF = CtField.make("private String password;", cc);
cc.addField(nameF);
//动态创建set方法
CtMethod set = CtMethod.make("public void setPassword(String password){"
+ "this.password = password;"
+ "}", cc);
cc.addMethod(set);
Class clz = cc.toClass();
Object test = clz.newInstance();
try {
//通过反射调用set方法,为password设置值
Method setV = clz.getDeclaredMethod("setPassword",new Class[]{String.class});
setV.invoke(test, "123456");
//获取其属性值
Field pw = clz.getDeclaredField("password");
pw.setAccessible(true);//password是私有属性,设置不做访问检查
System.out.println(pw.get(test));
} catch (NoSuchFieldException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} }
}
运行结果:
123456

本例所用javassist下载地址及版本:https://github.com/jboss-javassist/javassist/releasesJavassist 3.21.0-GA

8.5(java学习笔记)8.5 字节码操作(javassist)的更多相关文章

  1. JVM学习笔记——类加载和字节码技术篇

    JVM学习笔记--类加载和字节码技术篇 在本系列内容中我们会对JVM做一个系统的学习,本片将会介绍JVM的类加载和字节码技术部分 我们会分为以下几部分进行介绍: 类文件结构 字节码指令 编译期处理 类 ...

  2. 字节码操作JAVAssist

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

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

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

  4. java学习笔记IO之字节输入输出流

    IO字节输入输出流 OutputStream:字节输出流 该抽象类是所有字节输出流的超类: 定义了一些共性的成员方法: 1.写入一个字节 void write(int b);//b表示字节 2.写入字 ...

  5. Java 学习笔记 IO流与File操作

    可能你只想简单的使用,暂时不想了解太多的知识,那么请看这里,了解一下如何读文件,写文件 读文件示例代码 File file = new File("D:\\test\\t.txt" ...

  6. 0032 Java学习笔记-类加载机制-初步

    JVM虚拟机 Java虚拟机有自己完善的硬件架构(处理器.堆栈.寄存器等)和指令系统 Java虚拟机是一种能运行Java bytecode的虚拟机 JVM并非专属于Java语言,只要生成的编译文件能匹 ...

  7. 《Java学习笔记(第8版)》学习指导

    <Java学习笔记(第8版)>学习指导 目录 图书简况 学习指导 第一章 Java平台概论 第二章 从JDK到IDE 第三章 基础语法 第四章 认识对象 第五章 对象封装 第六章 继承与多 ...

  8. 20145330第六周《Java学习笔记》

    20145330第六周<Java学习笔记> . 这周算是很忙碌的一周.因为第六周陆续很多实验都开始进行,开始要准备和预习的科目日渐增多,对Java分配的时间不知不觉就减少了,然而第十和十一 ...

  9. Java学习笔记4

    Java学习笔记4 1. JDK.JRE和JVM分别是什么,区别是什么? 答: ①.JDK 是整个Java的核心,包括了Java运行环境.Java工具和Java基础类库. ②.JRE(Java Run ...

  10. java学习笔记01--数据类型

    java学习笔记01--数据类型 java数据类型划分 分为两大类型: 1)基本数据类型:类似于普通的值. 2)引用数据类型:传递的是内存的地址. 浮点类型实际上就是表示小数. java基本数据类型 ...

随机推荐

  1. NodeJS概述

    NodeJS中文API 一.概述 Node.js 是一种建立在Google Chrome’s v8 engine上的 non-blocking (非阻塞), event-driven (基于事件的) ...

  2. 修改nginx对http请求数据大小限制

    1. 问题发现 在公司搭建了一个基于mindoc的wiki知识库,用nginx做的反向代理服务器,同事在使用过程中上传某个文件一直失败,于是看着看下mindoc自己的日志文件,发现都是类似于fastd ...

  3. js错误处理

    导致程序无法继续执行的异常状态称为错误. js中一旦发生错误,就会自动创建一个Error类型对象 js中有6中错误类型: SyntaxError 语法错误 ReferenceError 引用错误,找不 ...

  4. [bzoj3524==bzoj2223][Poi2014]Couriers/[Coci 2009]PATULJCI——主席树+权值线段树

    题目大意 给定一个大小为n,每个数的大小均在[1,c]之间的数列,你需要回答m个询问,其中第i个询问形如\((l_i, r_i)\),你需要回答是否存在一个数使得它在区间\([l_i,r_i]\)中出 ...

  5. 关于k Line Chart (k线图)

    K Line Chart python实现k线图的代码,之前找过matplotlib中文文档但是画k线图的finance方法已经弃用了.所以自己在网上搜寻一下加上改编,很好的实现出k线图, 代码如下: ...

  6. web前端 html/css总结点

    1.html块级.内联<img src="" alt="图片未加载提示" title="鼠标悬浮提示"><a href=& ...

  7. 转:布局【ViewGroup】

    转: http://www.cnblogs.com/leehyuan/p/3389527.html 像素单位的变化:是用dip,而不是px,主要用于宽高的设置 在Android中支持的描述大小区域的类 ...

  8. 如何开启SUSE Linux的SSH

    1. 使用命令行停止防火墙 /etc/init.d/SuSEfirewall2_init stop /etc/init.d/SuSEfirewall2_setup stop 2. 修改 vi /etc ...

  9. JMeter 定时器(Synchronizing Timer)之集合点应用

    性能测试中我们经常提到一个概念就是“并发”,其实在实际真实的性能测试中是不存在真正的并发的.为了更真实的模拟对一个请求的并发测试场景,我们通常设置一个集合点,JMeter中提供了这样的一个功能设置. ...

  10. 该配置节不能包含 CDATA 或文本元素

    当执行程序时报“该配置节不能包含 CDATA 或文本元素” ,有可能是你的web.config中有异常的文本节点,比如