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. Docker镜像仓库Harbor安装

    export VERSION=18.06 && curl -fsSL http://rainbond-pkg.oss-cn-shanghai.aliyuncs.com/releases ...

  2. JVM(八)执行引擎相关内容

    一:两种解释器 JAVA字节码解释器: java字节码===>c++代码==>硬编码. 首先.java文件编译成字节码,遍历每行的字节码指令,因为每个字节码指令的含义都是固定的所以可以根据 ...

  3. 基于源码分析Vue的nextTick

    摘要:本文通过结合官方文档.源码和其他文章整理后,对Vue的nextTick做深入解析.理解本文最好有浏览器事件循环的基础,建议先阅读上文<事件循环Event loop到底是什么>. 一. ...

  4. STP 根桥、根端口、指定端口是如何选举的

    学习HCIA过程中,对交换机的根桥.跟端口以及指定端口选举有些迷糊,也度娘了一番,总觉得一部分人解释的不够全面精细.通过仔细研究最终有了自己的理解,分享给大家,如果纰漏,欢迎指正. STP收敛过程: ...

  5. Android字节码优化工具redex初探

    https://mp.weixin.qq.com/s/Og2TkGrZR490h9-KO23lmw 背景 apk瘦身和启动时间优化是移动端开发性能优化中经常被提到的两个问题.apk瘦身的常规做法有,s ...

  6. 一个支付宝小程序在一段时间内只能保留一个 WebSocket 连接

    一个支付宝小程序在一段时间内只能保留一个 WebSocket 连接 my.connectSocket - 支付宝开放平台 https://opendocs.alipay.com/mini/api/vx ...

  7. 关于js中each()使用return不能终止循环

    Jquery的each里面用return false代替break:return ture代替continue $(xx).each(function() { if(xx){ return false ...

  8. codevs1700 施工方案第二季

    题目描述 Description c国边防军在边境某处的阵地是由n个地堡组成的.工兵连受命来到阵地要进行两期施工. 第一期的任务是挖掘暗道让所有地堡互联互通.现已勘测设计了m条互不相交的暗道挖掘方案, ...

  9. Spark 应用监控告警-Graphite_exporter

    Spark 应用监控告警-Graphite_exporter Spark监控和工具 Web界面 事后查看 REST API 度量 高级工具 一.下载graphite_exporter 1.1 修改gr ...

  10. Web漏洞扫描-AWVS

    Web漏洞扫描-AWVS 一.AWVS概述 二.功能以及特点 三.AWVS界面 四.AWVS使用 相关优质博文: CSDN:帽子不够白:WEB渗透测试之三大漏扫神器 一.AWVS概述 Acunetix ...