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 ...
随机推荐
- 创建DbContext
返回总目录<一步一步使用ABP框架搭建正式项目系列教程> 上一篇介绍了<创建实体>,这一篇我们顺其自然地介绍<创建DbContext>. 温故: 提到DbConte ...
- ABP源码分析十八:UI Inputs
以下图中描述的接口和类都在Abp项目的Runtime/Validation, UI/Inputs目录下的.在当前版本的ABP(0.83)中这些接口和类并没有实际使用到.阅读代码时可以忽略,无需浪费时间 ...
- 自定义tabBar
★★★★自定义tabBar★★★★★★★ Demo下载地址:https://github.com/marlonxlj/tabBarCustom.git 前言: 有的时候需求要对tabBar进行自定义的 ...
- PagerSlidingTabStrip介绍及使用,让ViewPager更绚丽
转载请注明出处http://blog.csdn.net/harryweasley/article/details/42290595,谢谢. 以前一直想着,ViewPager中间的那个横线怎么跟着屏幕的 ...
- Hyper-V1:创建和管理虚拟机
Hyper-V是微软的管理虚拟机(Virtual Machine)的服务,在安装Hyper-V功能之后,系统自动安装可视化的虚拟机管理工具:Hyper-V Manager.在同一台物理机上,能够使用H ...
- React.js入门必须知道的那些事
首先,React.js是facebook在2013年5月开源的一个前端框架,React不是一个MVC框架,它是构建易于可重复调用的web组件,侧重于UI, 也就是view层, React为了更高超的性 ...
- 【基于WPF+OneNote+Oracle的中文图片识别系统阶段总结】之篇二:基于OneNote难点突破和批量识别
篇一:WPF常用知识以及本项目设计总结:http://www.cnblogs.com/baiboy/p/wpf.html 篇二:基于OneNote难点突破和批量识别:http://www.cnblog ...
- JavaScript知识点总结(命名规范,变量的作用域)
命名规范 有人说JavaScript的宽容性是这个语言最糟糕的方面之一.比如说想把2个数字加在一起,JavaScript会把其中一个数字解析成字符串,那么就会得到一个奇怪的字符串,而不是2个数字的和. ...
- SELECT TOP 1 比不加TOP 1 慢的原因分析以及SELECT TOP 1语句执行计划预估原理
本文出处:http://www.cnblogs.com/wy123/p/6082338.html 现实中遇到过到这么一种情况: 在某些特殊场景下:进行查询的时候,加了TOP 1比不加TOP 1要慢(而 ...
- 你真的会玩SQL吗?表表达式,排名函数
你真的会玩SQL吗?系列目录 你真的会玩SQL吗?之逻辑查询处理阶段 你真的会玩SQL吗?和平大使 内连接.外连接 你真的会玩SQL吗?三范式.数据完整性 你真的会玩SQL吗?查询指定节点及其所有父节 ...