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没有接口(通过继承父类) 只有实现类. ...
随机推荐
- Java进阶(五十一)必须记住的Myeclipse快捷键
Java进阶(五十一)必须记住的Myeclipse快捷键 在调试程序的时候,我们经常需要注释一些代码,在用Myeclipse编程时,就可以用 Ctrl+/ 为选中的一段代码加上以 // 打头的注释:当 ...
- 《java入门第一季》之面向对象(构造方法)
/* 构造方法: 给对象的数据进行初始化 格式: A:方法名与类名相同 B:没有返回值类型,连void都没有 C:没有具体的返回值 */ class Student { private String ...
- mysql大小写敏感(默认为1,不敏感)
在 MySQL 中,数据库和表其实就是数据目录下的目录和文件,因而,操作系统的敏感性决定数据库和表命名的大小写敏感.这就意味着数据库和表名在 Windows 中是大小写不敏感的,而在大多数类型的 Un ...
- 如何调整DOS窗口的宽高
运行->cmd->dos窗口上沿点右键->默认值所选字体字体: 新宋体大小: 13窗口大小宽度: 100高度: 40屏幕缓冲区大小宽度: 100高度: 300 在这里还可以设置依他常 ...
- android Material Design详解
原文地址:http://blog.csdn.net/jdsjlzx/article/details/41441083/ 前言 我们都知道Marterial Design是Google推出的全新UI设计 ...
- PHP单元测试使用
单元测试(unit testing),是指对软件中的最小可测试单元进行检查和验证.对于单元测试中单元的含义,一般来说,要根据实际情况去判定其具体含义,如C语言中单元指一个函数,Java里单元指一个类, ...
- sql中的case when then else end
hive中的case when的用法举例 select * from (select id, count(distinct ] in ("Virus","Worm&quo ...
- angularjs作用域之transclude
transclude是一个可选的参数.如果设置了,其值必须为true,它的默认值是false.嵌入有时被认为是一个高级主题,但某些情况下它与我们刚刚学习过的作用域之间会有非常好的配合.使用嵌入也会很好 ...
- oracle 随机数(转载)
http://blog.sina.com.cn/s/blog_6a01140c0100wimi.html 1.从表中随机取记录 select * from (select * from staff o ...
- 关于MVC的特性(AuthorizeAttribute)的一些理解
许多 Web 应用程序要求在用户登录之后才授予其对受限制内容的访问权限. 在某些应用程序中,即使是登录的用户,也会限制他们可以查看的内容或可以编辑的字段. 要限制对 ASP.NET MVC 视图的访问 ...