Javassist 通用工具之 CodeInjector
Javassist 通用工具之CodeInjector
最近在做一个APM项目,要在运行时代码修改。目前常用修改的几种工具有:ASM、BCEL、Javassist。经过对比,项目中采用了Javassist。
看这篇文章,需要对Javassist有一定的了解,可以参考:Javassist: Quick Start
在使用Javassist过程中,最常用的方法有CtMethod(或者CtConstructor)的insertBefore,insertAfter,addCatch,另外还有一种是injectAround(这种是需要自己来完成的)。可以参考:Spring:Aop before after afterReturn afterThrowing around 的原理
在代码里引入了before,beforeAround,beforeAfter,beforeThrowing,beforeReturning的概念,是取材于Spring AOP配置中的叫法。
package org.fjn.frameworkex.javassist; import javassist.CtClass; import javassist.CtConstructor; import javassist.CtMethod; import javassist.CtNewMethod; /** * Code Inject Tool * * @author <a href="mailto:fs1194361820@163.com">fs1194361820@163.com</a> * */ public class CodeInjector { public static final String METHOD_RUTURN_VALUE_VAR = "__FJN__result"; public static final String METHOD_EXEC_EXCEPTION_VAR = "ex"; public static final String CONSTRUCTOR_DELEGATE_PREFIX = "__FJN__DOC__"; public static final String METHOD_DELEGATE_PREFIX = "__FJN__DOM__"; public static final String PROCEED = "$proceed"; public static final String CRLF = "\n"; public static final String CRLF_TAB = "\n\t"; public static final String CRLF_2TAB = "\n\t\t"; public static final String CRLF_3TAB = "\n\t\t\t"; public static final String CRLF_4TAB = "\n\t\t\t\t"; public static final String getDelegateMethodNameOfConstructor(String constructorName) { return CONSTRUCTOR_DELEGATE_PREFIX + constructorName; } public static final String getDelegateMethodNameOfMethod(String methodName) { return METHOD_DELEGATE_PREFIX + methodName; } /** * Inject around code to the specified method * * @see #injectInterceptor(CtClass, CtMethod, String, String, String, * String, String) */ public void injectAround(CtMethod method, String beforeAround, String afterAround, String afterThrowing, String afterReturning) throws Exception { CtClass clazz = method.getDeclaringClass(); injectAround(clazz, method, beforeAround, afterAround, afterThrowing, afterReturning); } /** * Inject around code to the specified method * * @see #injectInterceptor(CtClass, CtMethod, String, String, String, * String, String) */ public void injectAround(CtClass clazz, CtMethod method, String beforeAround, String afterAround, String afterThrowing, String afterReturning) throws Exception { injectInterceptor(clazz, method, null, beforeAround, afterAround, afterThrowing, afterReturning); } /** * Inject around code to the specified method * * <pre> * <code> * <span style="font-size:12px; color:green;">before block ... </span> * try{ * <span style="font-size:12px; color:green;">beforeAround block ... </span> * $procced($$); * <span style="font-size:12px; color:green;">afterAround block ... </span> * }catch (Throwable ex){ * <span style="font-size:12px; color:green;">afterThrowing block ... </span> * }finally{ * <span style="font-size:12px; color:green;">afterReturning block ... </span> * } * </code> * </pre> * */ public void injectInterceptor(CtClass clazz, CtMethod method, String before, String beforeAround, String afterAround, String afterThrowing, String afterReturning) throws Exception { clazz.defrost(); int modifiers = method.getModifiers(); CtClass returnType = method.getReturnType(); CtClass[] parameters = method.getParameterTypes(); CtClass[] exceptions = method.getExceptionTypes(); String methodName = method.getName(); String delegateMethod = getDelegateMethodNameOfMethod(methodName); method.setName(delegateMethod); StringBuilder buffer = new StringBuilder(256); boolean hasReturnValue = (CtClass.voidType == returnType); buffer.append("{" + CRLF_TAB); { if (hasReturnValue) { String returnClass = returnType.getName(); buffer.append(returnClass + " " + METHOD_RUTURN_VALUE_VAR + ";"); } if (before != null) { buffer.append(before); } buffer.append(CRLF_TAB); buffer.append("try {" + CRLF_2TAB); { if (beforeAround != null) { buffer.append(beforeAround); } buffer.append(CRLF_2TAB); if (hasReturnValue) { buffer.append(METHOD_RUTURN_VALUE_VAR + " = ($r)" + delegateMethod + "($$);"); } else { buffer.append(delegateMethod + "($$);"); } if (afterAround != null) { buffer.append(CRLF_2TAB); buffer.append(afterAround); } if (hasReturnValue) { buffer.append(CRLF_2TAB); buffer.append("return " + METHOD_RUTURN_VALUE_VAR); } } buffer.append(CRLF_TAB); buffer.append("} catch (Throwable ex) {"); { buffer.append(CRLF_2TAB); if (afterThrowing != null) { buffer.append(afterThrowing); } buffer.append(CRLF_2TAB); buffer.append("throw ex;"); } buffer.append(CRLF_TAB); buffer.append("}"); if (afterReturning != null) { buffer.append(CRLF_TAB); buffer.append("finally {"); { buffer.append(CRLF_2TAB); buffer.append(afterReturning); } buffer.append(CRLF_TAB); buffer.append("}"); } } buffer.append(CRLF); buffer.append("}"); System.out.println(methodName + " will be modified as :\n" + buffer.toString()); CtMethod newMethod = CtNewMethod.make(modifiers, returnType, methodName, parameters, exceptions, buffer.toString(), clazz); clazz.addMethod(newMethod); } /** * Inject around code to the specified constructor * * @see #injectAround(CtClass, CtConstructor, String, String, String, * String) */ public void injectAround(CtConstructor constructor, String beforeAround, String afterAround, String afterThrowing, String afterReturning) throws Exception { CtClass clazz = constructor.getDeclaringClass(); injectAround(clazz, constructor, beforeAround, afterAround, afterThrowing, afterReturning); } /** * Inject around code to the specified constructor * * <pre> * <code> * try{ * <span style="font-size:12px; color:green;">beforeAround block ... </span> * $procced($$); * <span style="font-size:12px; color:green;">afterAround block ... </span> * }catch (Throwable ex){ * <span style="font-size:12px; color:green;">afterThrowing block ... </span> * }finally{ * <span style="font-size:12px; color:green;">afterReturning block ... </span> * } * </code> * </pre> * */ public void injectAround(CtClass clazz, CtConstructor constructor, String beforeAround, String afterAround, String afterThrowing, String afterReturning) throws Exception { clazz.defrost(); String delegateMethodName = getDelegateMethodNameOfConstructor(constructor.getName()); CtMethod delegateMethod = constructor.toMethod(delegateMethodName, clazz); clazz.addMethod(delegateMethod); injectAround(clazz, delegateMethod, beforeAround, afterAround, afterThrowing, afterReturning); constructor.setBody("{" + PROCEED + "($$);", "this", delegateMethodName); } /** * Copy form the src method's body to a overrid method's body in target * class. * * @param targetClass * overrid the method in the target class * @param srcMethod * the overrid from will copy from the src method. If the target * class has not owner overrid method , you should specified the * srcMethod in the super class. * @param body * the body of the override method * @param delegateObject * the delegate object default value is "this". * @param delegateMethod * @throws Exception */ public void overrideMethod(CtClass targetClass, CtMethod srcMethod, String body, String delegateObject, String delegateMethod) throws Exception { targetClass.defrost(); System.out.println(body); if (delegateObject == null) { delegateObject = "this"; } // override method in a super class of the target class if (srcMethod.getDeclaringClass() != targetClass) { CtMethod destMethod = CtNewMethod.copy(srcMethod, targetClass, null); if (body != null && !body.isEmpty()) { if (delegateMethod != null && !delegateMethod.isEmpty()) { destMethod.setBody(body, delegateObject, delegateMethod); } else { destMethod.setBody(body); } } targetClass.addMethod(destMethod); } // override method in the target class else { if (delegateMethod != null && !delegateMethod.isEmpty()) { srcMethod.setBody(body, delegateObject, delegateMethod); } else { srcMethod.setBody(body); } } } }
injectInterceptor()的 实现原理:将原来的方法改名为一个delegateMethod,重新创建一个target method,方法体是织入代码,并调用delegateMethod。
injectAround(CtConstrouctor)的实现原理:先将构造体的内容提取到一个delegateMethod中,再对delegateMethod做织入,最后设置新的构建体。在新的构造体中调用delegateMethod。
Javassist 通用工具之 CodeInjector的更多相关文章
- 用 javassist 来修改 class 文件
import javassist.ClassPool; import javassist.CtClass; import javassist.CtMethod; public class Test { ...
- SSH整合时执行hibernate查询报错:java.lang.ClassCastException: com.ch.hibernate.Department_$$_javassist_0 cannot be cast to javassist.util.proxy
今天在整合ssh三个框架时,有一个功能,是查询所有员工信息,且员工表和部门表是多对一的映射关系,代码能正常运行到查询得到一个List集合,但在页面展示的时候,就报异常了, java.lang.Clas ...
- Java动态编程初探——Javassist
最近需要通过配置生成代码,减少重复编码和维护成本.用到了一些动态的特性,和大家分享下心得. 我们常用到的动态特性主要是反射,在运行时查找对象属性.方法,修改作用域,通过方法名称调用方法等.在线的应用不 ...
- Java javassist动态代理
package org.windwant.spring.core.proxy; import javassist.ClassPool; import javassist.CtClass; import ...
- Javassist 字节码操作
1.读写字节码 Javassist是用来处理java字节码的类库.字节码保存在二进制文件中称为类文件.每个类文件夹包括一个java类或接口. Javasssist.CtClass这个类是一个类文件的抽 ...
- 【hibernate 报错】No serializer found for class org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer 【get和load的区别】
报错: HTTP Status 500 - Could not write content: No serializer found for class org.hibernate.proxy.poj ...
- Android RecyclerView单击、长按事件:基于OnItemTouchListener +GestureDetector标准实现(二),封装抽取成通用工具类
Android RecyclerView单击.长按事件:基于OnItemTouchListener +GestureDetector标准实现(二),封装抽取成通用工具类 我写的附录文章2,介绍了 ...
- javassist AOP
对于AOP,这个概念,不用解释,主要用途很多,我这里主要是为了后续研究如何实现APM做准备.前面研究了动态代理实现AOP,考虑到性能的问题,改用javassist直接修改直接码实现! javassis ...
- org.codehaus.jackson.map.JsonMappingException: No serializer found for class org.hibernate.proxy.pojo.javassist.
2011-08-16 13:26:58,484 [http-8080-1] ERROR [core.web.ExceptionInterceptor] - org.codehaus.jackson.m ...
随机推荐
- Failed to stop iptables.service: Unit iptables.service not loaded.
redhat 7 [root@lk0 ~]# service iptables stop Redirecting to /bin/systemctl stop iptables.service Fai ...
- 【.net深呼吸】(WCF)OperationContextScope 的用途
一个WCF服务可以实现多个服务协定(服务协定实为接口),不过,每个终结点只能与一个服务协定关联,并指定调用的唯一地址.那么,binding是干吗的?binding是负责描述通信的协议,以及消息是否加密 ...
- Logstash时区、时间转换,message重组
适用场景 获取日志本身时间 日志时间转Unix时间 重组message 示例日志: hellow@,@world@,@2011-11-01 18:46:43 logstash 配置文件: input{ ...
- 构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(8)-MVC与EasyUI DataGrid 分页
系列目录 前言 为了符合后面更新后的重构系统,文章于2016-11-1日重写 EasyUI Datagrid在加载的时候会提交一些分页的信息到后台,我们需要根据这些信息来进行数据分页再次返回到前台 实 ...
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(34)-文章发布系统①-简要分析
系列目录 最新比较闲,为了学习下Android的开发构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(1)-前言与,虽然有点没有目的的学习,但还是了解了Andro ...
- TextView使用大全
最近打算写一个系列的android初级开发教程,预计40篇以上的文章,结合我实际工作中的经验,写一些工作中经常用到的技术,让初学者可以少走弯路,写一个系列的话,大家学习起来也有头有尾. 今天就从我们每 ...
- CSS3与页面布局学习总结(六)——CSS3新特性(阴影、动画、渐变、变形、伪元素等)
CSS3在CSS2.1的基础上新增加了许多属性,这里选择了较常用的一些功能与大家分享,帮助文档中有很详细的描述,可以在本文的示例中获得帮助文档. 一.阴影 1.1.文字阴影 text-shadow&l ...
- Android面试一天一题(1Day)
写在前面 该博客思路源于在简书看到goeasyway博主写的Android面试一天一题系列,无copy之意,仅为让自己总结知识点,成长一点点.先感谢各位大神的无私分享~! 关于题目,大部分则出自And ...
- Java对象序列化剖析
对象序列化的目的 1)希望将Java对象持久化在文件中 2)将Java对象用于网络传输 实现方式 如果希望一个类的对象可以被序列化/反序列化,那该类必须实现java.io.Serializable接口 ...
- Linux零起点之进程管理----c语言编程
进程 (Process)是指操作系统中被加载到内存中的.正在运行的应用程序实例.进程是系统资源分配的基本单元,在其生命周期内会使用系统中的各种资源.进程主要由程序.数据以及进程控制快(PCB)3个部分 ...