Javassist字节码增强示例
概述
- private static ClassLoader getLocaleClassLoader() throws Exception {
- List<URL> classPathURLs = new ArrayList<>();
- // 加载.class文件路径
- classPathURLs.add(classesPath.toURI().toURL());
- // 获取所有的jar文件
- File[] jarFiles = libPath.listFiles(new FilenameFilter() {
- @Override
- public boolean accept(File dir, String name) {
- return name.endsWith(".jar");
- }
- });
- Assert.assertFalse(ObjectHelper.isArrayNullOrEmpty(jarFiles));
- // 将jar文件路径写入集合
- for (File jarFile : jarFiles) {
- classPathURLs.add(jarFile.toURI().toURL());
- }
- // 实例化类加载器
- return new URLClassLoader(classPathURLs.toArray(new URL[classPathURLs.size()]));
- }
获取类型信息
- @Test
- public void test() throws NotFoundException {
- // 获取默认类型池对象
- ClassPool classPool = ClassPool.getDefault();
- // 获取指定的类型
- CtClass ctClass = classPool.get("java.lang.String");
- System.out.println(ctClass.getName()); // 获取类名
- System.out.println("\tpackage " + ctClass.getPackageName()); // 获取包名
- System.out.print("\t" + Modifier.toString(ctClass.getModifiers()) + " class " + ctClass.getSimpleName()); // 获取限定符和简要类名
- System.out.print(" extends " + ctClass.getSuperclass().getName()); // 获取超类
- // 获取接口
- if (ctClass.getInterfaces() != null) {
- System.out.print(" implements ");
- boolean first = true;
- for (CtClass c : ctClass.getInterfaces()) {
- if (first) {
- first = false;
- } else {
- System.out.print(", ");
- }
- System.out.print(c.getName());
- }
- }
- System.out.println();
- }
修改类方法
- @Test
- public void test() throws Exception {
- // 获取本地类加载器
- ClassLoader classLoader = getLocaleClassLoader();
- // 获取要修改的类
- Class<?> clazz = classLoader.loadClass("edu.alvin.reflect.TestLib");
- // 实例化类型池对象
- ClassPool classPool = ClassPool.getDefault();
- // 设置类搜索路径
- classPool.appendClassPath(new ClassClassPath(clazz));
- // 从类型池中读取指定类型
- CtClass ctClass = classPool.get(clazz.getName());
- // 获取String类型参数集合
- CtClass[] paramTypes = {classPool.get(String.class.getName())};
- // 获取指定方法名称
- CtMethod method = ctClass.getDeclaredMethod("show", paramTypes);
- // 赋值方法到新方法中
- CtMethod newMethod = CtNewMethod.copy(method, ctClass, null);
- // 修改源方法名称
- String oldName = method.getName() + "$Impl";
- method.setName(oldName);
- // 修改原方法
- newMethod.setBody("{System.out.println(\"执行前\");" + oldName + "($$);System.out.println(\"执行后\");}");
- // 将新方法添加到类中
- ctClass.addMethod(newMethod);
- // 加载重新编译的类
- clazz = ctClass.toClass(); // 注意,这一行会将类冻结,无法在对字节码进行编辑
- // 执行方法
- clazz.getMethod("show", String.class).invoke(clazz.newInstance(), "hello");
- ctClass.defrost(); // 解冻一个类,对应freeze方法
- }
动态创建类
- @Test
- public void test() throws Exception {
- ClassPool classPool = ClassPool.getDefault();
- // 创建一个类
- CtClass ctClass = classPool.makeClass("edu.alvin.reflect.DynamiClass");
- // 为类型设置接口
- //ctClass.setInterfaces(new CtClass[] {classPool.get(Runnable.class.getName())});
- // 为类型设置字段
- CtField field = new CtField(classPool.get(String.class.getName()), "value", ctClass);
- field.setModifiers(Modifier.PRIVATE);
- // 添加getter和setter方法
- ctClass.addMethod(CtNewMethod.setter("setValue", field));
- ctClass.addMethod(CtNewMethod.getter("getValue", field));
- ctClass.addField(field);
- // 为类设置构造器
- // 无参构造器
- CtConstructor constructor = new CtConstructor(null, ctClass);
- constructor.setModifiers(Modifier.PUBLIC);
- constructor.setBody("{}");
- ctClass.addConstructor(constructor);
- // 参数构造器
- constructor = new CtConstructor(new CtClass[] {classPool.get(String.class.getName())}, ctClass);
- constructor.setModifiers(Modifier.PUBLIC);
- constructor.setBody("{this.value=$1;}");
- ctClass.addConstructor(constructor);
- // 为类设置方法
- CtMethod method = new CtMethod(CtClass.voidType, "run", null, ctClass);
- method.setModifiers(Modifier.PUBLIC);
- method.setBody("{System.out.println(\"执行结果\" + this.value);}");
- ctClass.addMethod(method);
- // 加载和执行生成的类
- Class<?> clazz = ctClass.toClass();
- Object obj = clazz.newInstance();
- clazz.getMethod("setValue", String.class).invoke(obj, "hello");
- clazz.getMethod("run").invoke(obj);
- obj = clazz.getConstructor(String.class).newInstance("OK");
- clazz.getMethod("run").invoke(obj);
- }
创建代理类
- @Test
- public void test() throws Exception {
- // 实例化代理类工厂
- ProxyFactory factory = new ProxyFactory();
- //设置父类,ProxyFactory将会动态生成一个类,继承该父类
- factory.setSuperclass(TestProxy.class);
- //设置过滤器,判断哪些方法调用需要被拦截
- factory.setFilter(new MethodFilter() {
- @Override
- public boolean isHandled(Method m) {
- return m.getName().startsWith("get");
- }
- });
- Class<?> clazz = factory.createClass();
- TestProxy proxy = (TestProxy) clazz.newInstance();
- ((ProxyObject)proxy).setHandler(new MethodHandler() {
- @Override
- public Object invoke(Object self, Method thisMethod, Method proceed, Object[] args) throws Throwable {
- //拦截后前置处理,改写name属性的内容
- //实际情况可根据需求修改
- System.out.println(thisMethod.getName() + "被调用");
- try {
- Object ret = proceed.invoke(self, args);
- System.out.println("返回值: " + ret);
- return ret;
- } finally {
- System.out.println(thisMethod.getName() + "调用完毕");
- }
- }
- });
- proxy.setName("Alvin");
- proxy.setValue("1000");
- proxy.getName();
- proxy.getValue();
- }
其中,TestProxy类内容如下:
- public class TestProxy {
- private String name;
- private String value;
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public String getValue() {
- return value;
- }
- public void setValue(String value) {
- this.value = value;
- }
- }
获取方法名称
- @Test
- public void test() throws Exception {
- // 获取本地类加载器
- ClassLoader classLoader = getLocaleClassLoader();
- // 获取要修改的类
- Class<?> clazz = classLoader.loadClass("edu.alvin.reflect.TestLib");
- // 实例化类型池
- ClassPool classPool = ClassPool.getDefault();
- classPool.appendClassPath(new ClassClassPath(clazz));
- CtClass ctClass = classPool.get(clazz.getName());
- // 获取方法
- CtMethod method = ctClass.getDeclaredMethod("show", ObjectHelper.argumentsToArray(CtClass.class, classPool.get("java.lang.String")));
- // 判断是否为静态方法
- int staticIndex = Modifier.isStatic(method.getModifiers()) ? 0 : 1;
- // 获取方法的参数
- MethodInfo methodInfo = method.getMethodInfo();
- CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
- LocalVariableAttribute localVariableAttribute = (LocalVariableAttribute)codeAttribute.getAttribute(LocalVariableAttribute.tag);
- for (int i = 0; i < method.getParameterTypes().length; i++) {
- System.out.println("第" + (i + 1) + "个参数名称为: " + localVariableAttribute.variableName(staticIndex + i));
- }
- }
关于“获取方法名称”,其主要作用是:当Java虚拟机加载.class文件后,会将类方法“去名称化”,即丢弃掉方法形参的参数名,而是用形参的序列号来传递参数。如果要通过Java反射获取参数的参数名,则必须在编辑是指定“保留参数名称”。Javassist则不存在这个问题,对于任意方法,都能正确的获取其参数的参数名。
Javassist字节码增强示例的更多相关文章
- JVM插庄之一:JVM字节码增强技术介绍及入门示例
字节码增强技术:AOP技术其实就是字节码增强技术,JVM提供的动态代理追根究底也是字节码增强技术. 目的:在Java字节码生成之后,对其进行修改,增强其功能,这种方式相当于对应用程序的二进制文件进行修 ...
- 字节码增强技术-Byte Buddy
本文转载自字节码增强技术-Byte Buddy 为什么需要在运行时生成代码? Java 是一个强类型语言系统,要求变量和对象都有一个确定的类型,不兼容类型赋值都会造成转换异常,通常情况下这种错误都会被 ...
- 深入浅出Java探针技术1--基于java agent的字节码增强案例
Java agent又叫做Java 探针,本文将从以下四个问题出发来深入浅出了解下Java agent 一.什么是java agent? Java agent是在JDK1.5引入的,是一种可以动态修改 ...
- 从底层入手,解析字节码增强和Btrace应用
这篇文章聊下字节码和相关的应用. 1.机器码和字节码 机器码(machine code),学名机器语言指令,有时也被称为原生码(Native Code),是电脑的CPU可直接解读的数据. 通常意义上来 ...
- Java字节码增强技术
简单介绍下几种java字节码增强技术. ASM ASM是一个Java字节码操控框架,它能被用来动态生成类或者增强既有类的功能.ASM可以直接产生class文件,也可以在类被加载入Java虚拟机之前动态 ...
- 字节码增强-learnning
jvm加载java的过程主要是: 编写java文件->进行java文件的编译->生成.class字节码文件->jvm通过类加载器去加载生成的二进制文件 java编译器将源码文件编译称 ...
- Javassist 字节码 语法 MD
Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...
- JVM——字节码增强技术简介
Java字节码增强指的是在Java字节码生成之后,对其进行修改,增强其功能,这种方式相当于对应用程序的二进制文件进行修改.Java字节码增强主要是为了减少冗余代码,提高性能等. 实现字节码增强的主要步 ...
- SpringAOP之CGLIB字节码增强
SpringAOP的基础原理就是动态代理 有两种实现方式:1)jdk动态代理 2)cglib动态代理 jdk动态代理和cglib动态代理的区别在于: cglib没有接口(通过继承父类) 只有实现类. ...
随机推荐
- 实用Android 屏幕适配方案分享
转载地址:http://blog.csdn.net/gao_chun/article/details/45645051 真正可用,并且简单易行,可以在多个屏幕大小和屏幕密度上有良好表现的Android ...
- hibernate 动态多数据库
最近老师给了一个任务,需求是这样的 服务器A上有一张表,里面存放了若干个服务器的信息,表的字段包括: private int id; private String serverName; privat ...
- Linux内核通用队列的使用笔记(读linux内核设计与实现)
Linux内核通用队列实现 Kfifo位置:kernel/kififo.c 使用需要包含头文件#include <kernel/kififo> 1.创建队列(动态创建)int kfifo_ ...
- Android 几种网络请求的区别与联系
HttpUrlConnection 最开始学android的时候用的网络请求是HttpUrlConnection,当时很多东西还不知道,但是在android 2.2及以下版本中HttpUrlConne ...
- Erlang Rebar 使用指南之一:入门篇
Erlang Rebar 使用指南之一:入门篇 全文目录: https://github.com/rebar/rebar/wiki 本章原文: https://github.com/rebar/reb ...
- 图像的基本运算——scale, rotation, translation
图像是视觉信息的直接反应,图像呈现出各种各样的特效都是通过图像的基本运算来完成的.图像最基本的运算有三种,分别是scale,rotation 和translation,叫做尺度,旋转和平移.很多图像的 ...
- android离线缓存技术
离线缓存是指在有网络的状态下将从服务器获取的网络数据,如Json 数据缓存到本地,在断网的状态下启动APP时读取本地缓存数据显示在界面上,常用的APP(网易新闻.知乎等等)都是支持离线缓存的,这样带来 ...
- c#实例化继承类,必须对被继承类的程序集做引用
0x00 问题 类型“Model.NewModel”在未被引用的程序集中定义.必须添加对程序集“Model, Version=1.0.0.0, Culture=neutral, PublicKeyTo ...
- [ SSH框架 ] Hibernate框架学习之三
一.表关系的分析 Hibernate框架实现了ORM的思想,将关系数据库中表的数据映射成对象,使开发人员把对数据库的操作转化为对对象的操作,Hibernate的关联关系映射主要包括多表的映射配置.数据 ...
- Javac的实现过程
主要介绍Javac的实现过程及原理. 首先弄明白什么是Javac? Javac是一种编译器,将一种语言转换为另一种语言规范.编译器的作用就是将符合java语言规范的源代码转化为JVM虚拟机能够识别的字 ...