0x01、javassist介绍

什么是javassist,这个词一听起来感觉就很懵,对吧~

public void DynGenerateClass() {
ClassPool pool = ClassPool.getDefault();
CtClass ct = pool.makeClass("com.ideaGenerateClass");//创建类
ct.setInterfaces(new CtClass[]{pool.makeInterface("java.lang.Cloneable")});//让类实现Cloneable接口
try {
CtField f= new CtField(CtClass.intType,"id",ct);//获得一个类型为int,名称为id的字段
f.setModifiers(AccessFlag.PUBLIC);//将字段设置为public
ct.addField(f);//将字段设置到类上
//添加构造函数
CtConstructor constructor=CtNewConstructor.make("public GeneratedClass(int pId){this.id=pId;}",ct);
ct.addConstructor(constructor);
//添加方法
CtMethod helloM=CtNewMethod.make("public void hello(String des){ System.out.println(des);}",ct);
ct.addMethod(helloM); ct.writeFile();//将生成的.class文件保存到磁盘 //下面的代码为验证代码
Field[] fields = ct.toClass().getFields();
System.out.println("属性名称:" + fields[0].getName() + " 属性类型:" + fields[0].getType());
} catch (CannotCompileException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (NotFoundException e) {
e.printStackTrace();
}
}

参考该篇文章java编程-javassist

0x02 Javassist 使用

这里主要讲一下主要的几个类:

1、ClassPool

ClassPool是CtClass对象的容器,它按需读取类文件来构造CtClass对象,并且保存CtClass对象以便以后使用。

从实现的角度来看,ClassPool 是一个存储 CtClass 的 Hash 表,类的名称作为 Hash 表的 key。ClassPool 的 get() 函数用于从 Hash 表中查找 key 对应的 CtClass 对象。如果没有找到,get() 函数会创建并返回一个新的 CtClass 对象,这个新对象会保存在 Hash 表中。

需要注意的是ClassPool会在内存中维护所有被它创建过的CtClass,当CtClass数量过多时,会占用大量的内存,API中给出的解决方案是重新创建ClassPool 或 有意识的调用CtClass的detach()方法以释放内存。

常用方法:

//返回默认的ClassPool,一般通过该方法创建我们的ClassPool;
static ClassPool getDefault() //在搜索路径的开头插入目录或jar(或zip)文件。
ClassPath insertClassPath(java.lang.String pathname) //ClassPath在搜索路径的开头插入一个对象。
ClassPath insertClassPath(ClassPath cp) //获取类加载器toClass(),getAnnotations()在 CtClass等
java.lang.ClassLoader getClassLoader() //从源中读取类文件,并返回对CtClass 表示该类文件的对象的引用。
CtClass get(java.lang.String classname) //将ClassPath对象附加到搜索路径的末尾。
ClassPath appendClassPath(ClassPath cp) //创建一个新的public类
CtClass makeClass(java.lang.String classname)

2、CtClass

CtClass类表示一个class文件,每个CtClass对象都必须从ClassPool中获取,CtClass需要关注的方法:

常用方法:

//更改超类,除非此对象表示接口。
void setSuperclass(CtClass clazz) //将此类转换为java.lang.Class对象。
java.lang.Class<?> toClass(java.lang.invoke.MethodHandles.Lookup lookup) //将该类转换为类文件。
byte[] toBytecode() //将由此CtClass 对象表示的类文件写入当前目录。
void writeFile() //将由此CtClass 对象表示的类文件写入本地磁盘。
void writeFile(java.lang.String directoryName) //在当前类中创建了一个静态代码块
CtConstructor makeClassInitializer()
freeze:冻结一个类,使其不可修改;
isFrozen:判断一个类是否已被冻结;
defrost:解冻一个类,使其可以被修改;
prune:删除类不必要的属性,以减少内存占用。调用该方法后,许多方法无法将无法正常使用,慎用;
detach:将该class从ClassPool中删除;
writeFile:根据CtClass生成.class文件;
toClass:通过类加载器加载该CtClass;
addField,removeField:添加/移除一个CtField;
addMethod,removeMethod:添加/移除一个CtMethod;
addConstructor,removeConstructor:添加/移除一个CtConstructor。

3、CtMethod

CtMethod:表示类中的方法。

insertBefore:在方法的起始位置插入代码;
insterAfter: 在方法的所有 return 语句前插入代码以确保语句能够被执行,除非遇到exception;
insertAt: 在指定的位置插入代码;
setBody: 将方法的内容设置为要写入的代码,当方法被abstract修饰时,该修饰符被移除;
make: 创建一个新的方法。

4、CtConstructor

CtConstructor的实例表示一个构造函数。它可能代表一个静态构造函数(类初始化器)。可以通过CtConstructor.make方法创建

常用方法

//设置构造函数主体。
void setBody(java.lang.String src) //从另一个构造函数复制一个构造函数主体。
void setBody(CtConstructor src, ClassMap map) //复制此构造函数并将其转换为方法。
CtMethod toMethod(java.lang.String name, CtClass declaring)

5、ClassPath

该类作用是用于通过 getResourceAsStream() 在 java.lang.Class 中获取类文件的搜索路径。

构造方法:

//创建一个搜索路径。
ClassClassPath(java.lang.Class<?> c)

常见方法:

//获取指定类文件的URL。
java.net.URL find (java.lang.String classname) //通过获取类文getResourceAsStream()。
java.io.InputStream openClassfile(java.lang.String classname)

代码实例:

ClassPool pool = ClassPool.getDefault();

在默认系统搜索路径获取ClassPool对象。

如果需要修改类搜索的路径需要使用insertClassPath方法进行修改。

pool.insertClassPath(new ClassClassPath(this.getClass()));

将本类所在的路径插入到搜索路径中

6、oBytecode

package com.demo;

import javassist.*;

import java.io.IOException;
import java.util.Arrays; public class testssit {
public static void main(String[] args) throws NotFoundException, CannotCompileException, IOException {
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(new ClassClassPath(demo.class.getClass()));
CtClass ctClass = pool.get("com.demo.test");
ctClass.setSuperclass(pool.get("com.demo.test"));
// System.out.println(ctClass);
byte[] bytes = ctClass.toBytecode();
String s = Arrays.toString(bytes);
System.out.println(s);
} }

7、toClass

toClass:将修改后的CtClass加载至当前线程的上下文类加载器中,CtClass的toClass方法是通过调用本方法实现。

Hello类:
public class Hello {
public void say() {
System.out.println("Hello");
}
}
Test 类
public class Test {
public static void main(String[] args) throws Exception {
ClassPool cp = ClassPool.getDefault();//在默认系统搜索路径获取ClassPool对象。
CtClass cc = cp.get("com.demo.Hello"); //获取hello类的
CtMethod m = cc.getDeclaredMethod("say"); //获取hello类的say方法
m.insertBefore("{ System.out.println(\"Hello.say():\"); }");//在正文的开头插入字节码
Class c = cc.toClass();//将此类转换为java.lang.Class对象
Hello h = (Hello)c.newInstance(); //反射创建对象并进行强转
h.say();调用方法say
}
}

0x03、创建Class文件

public class App {
public static void main(String[] args) {
try {
createPerson();
} catch (Exception e) {
e.printStackTrace();
}
} private static void createPerson() throws Exception {
ClassPool pool = ClassPool.getDefault(); // 1. 创建一个空类
CtClass cc = pool.makeClass("com.hearing.demo.Person"); // 2. 新增一个字段 private String name = "hearing";
CtField param = new CtField(pool.get("java.lang.String"), "name", cc);
param.setModifiers(Modifier.PRIVATE);
cc.addField(param, CtField.Initializer.constant("hearing")); // 3. 生成 getter、setter 方法
cc.addMethod(CtNewMethod.setter("setName", param));
cc.addMethod(CtNewMethod.getter("getName", param)); // 4. 添加无参的构造函数
CtConstructor cons = new CtConstructor(new CtClass[]{}, cc);
cons.setBody("{name = \"hearing\";}");
cc.addConstructor(cons); // 5. 添加有参的构造函数
cons = new CtConstructor(new CtClass[]{pool.get("java.lang.String")}, cc);
// $0=this / $1,$2,$3... 代表方法参数
cons.setBody("{$0.name = $1;}");
cc.addConstructor(cons); // 6. 创建一个名为printName方法,无参数,无返回值,输出name值
CtMethod ctMethod = new CtMethod(CtClass.voidType, "printName", new CtClass[]{}, cc);
ctMethod.setModifiers(Modifier.PUBLIC);
ctMethod.setBody("{System.out.println(name);}");
cc.addMethod(ctMethod); //这里会将这个创建的类对象编译为.class文件
cc.writeFile("../");
}
}

创建的class文件如下

public class Person {
private String name = "hearing"; public void setName(String var1) {
this.name = var1;
} public String getName() {
return this.name;
} public Person() {
this.name = "hearing";
} public Person(String var1) {
this.name = var1;
} public void printName() {
System.out.println(this.name);
}
}

0x04、调用生成的类对象

1.通过反射的方式调用:

Object person = cc.toClass().newInstance();
Method setName = person.getClass().getMethod("setName", String.class);
setName.invoke(person, "hearing1");
Method execute = person.getClass().getMethod("printName");
execute.invoke(person);

2.通过读取class文件的方式调用:

ClassPool pool = ClassPool.getDefault();
// 设置类路径
pool.appendClassPath("../");
CtClass ctClass = pool.get("com.hearing.demo.Person");
Object person = ctClass.toClass().newInstance();
// 下面和通过反射的方式一样去使用

java之 javassist简单使用的更多相关文章

  1. Java 异步处理简单实践

    Java 异步处理简单实践 http://www.cnblogs.com/fangfan/p/4047932.html 同步与异步 通常同步意味着一个任务的某个处理过程会对多个线程在用串行化处理,而异 ...

  2. 使用Java编写一个简单的Web的监控系统cpu利用率,cpu温度,总内存大小

    原文:http://www.jb51.net/article/75002.htm 这篇文章主要介绍了使用Java编写一个简单的Web的监控系统的例子,并且将重要信息转为XML通过网页前端显示,非常之实 ...

  3. Java中的简单工厂模式

    举两个例子以快速明白Java中的简单 工厂模式: 女娲抟土造人话说:“天地开辟,未有人民,女娲抟土为人.”女娲需要用土造出一个个的人,但在女娲造出人之前,人的概念只存在于女娲的思想里面.女娲造人,这就 ...

  4. Java之英格玛简单实现以及加密验证码的应用

    最近看了一部电影<模仿游戏>,<模仿游戏>中艾伦·图灵破译英格玛让我对英格玛产生了好奇,于是就开始翻阅资料对其进行研究,但是毕竟智慧有限,所以我这里用Java实现一个简单的英格 ...

  5. 【JAVA零基础入门系列】Day12 Java类的简单应用

    俗话说的好,实践出真知,所以除了理论知识掌握扎实以外,更重要的是要多加操练,这样才能掌握核心科技. 今天我们就用刚学会的类来实践一下,目标便是完成上一篇中的剁手任务. 我们的商品类已经准备好了,代码重 ...

  6. java Socket实现简单在线聊天(二)

    接<java Socket实现简单在线聊天(一)>,在单客户端连接的基础上,这里第二步需要实现多客户端的连接,也就需要使用到线程.每当有一个新的客户端连接上来,服务端便需要新启动一个线程进 ...

  7. Java消息系统简单设计与实现

    前言:由于导师在我的毕设项目里加了消息系统(本来想水水就过的..),没办法...来稍微研究研究吧..简单简单... 需求分析 我的毕设是一个博客系统,类似于简书这样的,所以消息系统也类似,在用户的消息 ...

  8. Java操作符真的简单到易如反掌?

    之前我写了一篇<吃人的那些Java名词:对象.引用.堆.栈和堆栈>,本以为凭借自己8年的Java编程经验足够把这些“吃人”的Java名词解释清楚了,但有网友不以为然,在文章底部评论说:“老 ...

  9. Java中的简单工厂模式(转)

    Java中的简单工厂模式 举两个例子以快速明白Java中的简单工厂模式: 女娲抟土造人话说:“天地开辟,未有人民,女娲抟土为人.”女娲需要用土造出一个个的人,但在女娲造出人之前,人的概念只存在于女娲的 ...

随机推荐

  1. CodeMonkey少儿编程第3章 times循环

    目标 了解程序由哪三种基本的结构组成 了解循环的概念 掌握times的结构与用法 三种基本结构 计算机程序由三种最基本的结构组成,它们分别是: 顺序结构 循环结构 选择结构 千万不要被这些陌生的术语给 ...

  2. java 日期与时间操作

    我们先来了解一下基本的概念 日期 2020-11-21 2020-11-22 时间 15:36:43 2020-11-21 15:36:43 时区 北京时间 2020-11-21 15:36:43 东 ...

  3. Linux Ubuntu系统版本通过Crontab设置定时任务的执行

    Linux Ubuntu系统版本通过Crontab设置定时任务的执行 本文由本人收集网络信息总结而来 特别鸣谢:https://linux.zone/2258 1 crontab 简单介绍以及语法使用 ...

  4. Java流程控制与Scanner类的使用

    Java流程控制与Scanner类的使用 Scanner类 Scanner类可以使程序接受键盘输入,实现人机交互 一个完整的Scanner的使用例子: //创建一个扫描器对象,用于接收键盘数据 Sca ...

  5. Kubernetes集群管理工具kubectl命令技巧大全

    一. kubectl概述 Kubectl是用于控制Kubernetes集群的命令行工具,通过kubectl能够对集群本身进行管理,并能够在集群上进行容器化应用的安装部署. kubectl命令的语法如下 ...

  6. Windows server 2008常用优化设置

    1. 如何取消开机按 CTRL+ALT+DEL登录? 控制面板→管理工具→本地安全策略→本地策略→安全选项→交互式登录:无须按CTRL+ALT+DEL→启用. 2. 如何取消关机时出现的关机理由选择项 ...

  7. SELECT ... FOR UPDATE or SELECT ... FOR SHARE Locking Reads session

    小结: 1.注意使用限制 Locking reads are only possible when autocommit is disabled (either by beginning transa ...

  8. OpenSSL 常见对称加密算法特性分析

    在选择加密算法,面对一大长串的选项时,大家都有这样的疑问,究竟哪种加密方式是最好的呢? 对于加密方式.算法来说,一般安全性与性能呈负相关,越是安全的,对性能要求则更高. 现在主流的加密协议的安全性均能 ...

  9. mysql本地中127.0.0.1连接不上数据库怎么办

    首先在本地使用Navicat for MySQL建立一个bai数据库.在dreamweaver中建立一个PHP格式的网页,方便链接测试.测试发du现,如果zhi无法使用localhost链接mysql ...

  10. Python中单引号,双引号,三引号的区别

    Python中的字符串一般用单引号('A'),双引号("A")和三引号('''A''')或者("""A""") 1.单引 ...