javaagent项目中使用
相关代码参考:http://blog.csdn.net/catoop/article/details/51034778
近期项目中需要对SpringMVC
中的Controller
方法进行拦截做预处理,才接触到javaagent
,仅作记录。
思路:
1.声明MyTransformer
类,实现ClassFileTransformer
接口,该接口只有一个方法:byte[] transform(ClassLoader loader,String className,Class<?> classBeingRedefined,ProtectionDomain protectionDomain,byte[] classfileBuffer) throws IllegalClassFormatException;
在该方法中获取指定类的指定方法,修改其字节码,达到拦截的目的;如果需要修改方法字节码,则需要引入javassist-*.*.*-GA.jar
的包。
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.CtNewMethod; public class MyTransformer implements ClassFileTransformer { final static String prefix = "\nlong startTime = System.currentTimeMillis();\n";
final static String postfix = "\nlong endTime = System.currentTimeMillis();\n"; // 被处理的方法列表
final static Map<String, List<String>> methodMap = new HashMap<String, List<String>>(); public MyTransformer() {
add("com.shanhy.demo.TimeTest.sayHello");
add("com.shanhy.demo.TimeTest.sayHello2");
} private void add(String methodString) {
String className = methodString.substring(0, methodString.lastIndexOf("."));
String methodName = methodString.substring(methodString.lastIndexOf(".") + 1);
List<String> list = methodMap.get(className);
if (list == null) {
list = new ArrayList<String>();
methodMap.put(className, list);
}
list.add(methodName);
} @Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
className = className.replace("/", ".");
if (methodMap.containsKey(className)) {// 判断加载的class的包路径是不是需要监控的类
CtClass ctclass = null;
try {
ctclass = ClassPool.getDefault().get(className);// 使用全称,用于取得字节码类<使用javassist>
for (String methodName : methodMap.get(className)) {
String outputStr = "\nSystem.out.println(\"this method " + methodName
+ " cost:\" +(endTime - startTime) +\"ms.\");";
CtMethod ctmethod = ctclass.getDeclaredMethod(methodName);// 得到这方法实例
String newMethodName = methodName + "$old";// 新定义一个方法叫做比如sayHello$old
ctmethod.setName(newMethodName);// 将原来的方法名字修改
// 创建新的方法,复制原来的方法,名字为原来的名字
CtMethod newMethod = CtNewMethod.copy(ctmethod, methodName, ctclass, null);
// 构建新的方法体
StringBuilder bodyStr = new StringBuilder();
bodyStr.append("{");
bodyStr.append(prefix);
bodyStr.append(newMethodName + "($$);\n");// 调用原有代码,类似于method();($$)表示所有的参数
bodyStr.append(postfix);
bodyStr.append(outputStr);
bodyStr.append("}"); newMethod.setBody(bodyStr.toString());// 替换新方法
ctclass.addMethod(newMethod);// 增加新方法
}
return ctclass.toBytecode();
} catch (Exception e) {
System.out.println(e.getMessage());
e.printStackTrace();
}
}
return null;
}
}
2.然后声明MyAgent
类,实现方法:
public static void premain(String args, Instrumentation inst){
inst.addTransformer(new MyTransformer());
}
3.将工程打成jar
包,例如Myagent.jar
,需要修改MANIFEST.MF
内容,添加
Premain-Class: com.test.demo.agent.MyAgent
4.使用时只需要java -javaagent:D:/Myagent.jar -jar MyWebApp.jar
就可拦截transform
逻辑中想要的方法。-javaagent:D:/*.jar
可以使用多个,放到-jar *.jar
前面即可。
5.实际项目中,在使用maven
工程编译jar
包install
后,执行java -javaagent:D:/Myagent.jar -jar MyWebApp.jar
中文字符乱码,导致启动异常.
解决办法
:在启动命令中添加-Dfile.encoding=utf-8
,如下: java -Dfile.encoding=utf-8 -javaagent:D:/Myagent.jar -jar MyWebApp.jar
程序正常运行。
6.实现transform
方式时,遇到一个问题,在手动修改method
的body
时,例如newMethod.setBody(bodyStr.toString());
,bodyStr
为处理后的方法体,简单的语句,如:System.out.println("message")
是可以的,但是复杂的逻辑不行,程序运行没有反映,控制台也没有异常打印。后来发现需要写对象的全路径,比如List
需要写成java.util.List
等。其中涉及javassist
操作,参见http://blog.csdn.net/u011425751/article/details/51917895
7.遗留问题: SpringMVC
的org.springframework.web.servlet.DispatcherServlet
继承了抽象类FrameworkServlet
,FrameworkServlet
继承了抽象类HttpServletBean
,HttpServletBean
继承抽象类HttpServlet
,HttpServlet
继承了抽象类GenericServlet
,GenericServlet
实现了Servlet
和ServletConfig
接口。
具体如下:
public class DispatcherServlet extends FrameworkServlet
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware
public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware
public abstract class HttpServlet extends GenericServlet
public abstract class GenericServlet implements Servlet, ServletConfig, java.io.Serializable
public interface Servlet
项目中拦截DispatcherServlet
的doService
方法。
最新文章
- 关于 redis、memcache、mongoDB 的对比
- Android中矢量动画
- iOS推送原理
- 常用python机器学习库总结
- LightOJ Beginners Problems 部分题解
- 【转载】Fiddler进行模拟Post提交json数据,总为null解决方式
- 浅谈RSA加密算法
- Android屏幕适应详解(二)
- Qt的gzip模块实现
- jquery实现asp.net 网页鼠标所在位置
- Thrift原理与使用实例
- Ecmall系统自带的分页功能
- BA Practice Lead Handbook 1 - Why Is Business Analysis Taking The World By Storm?
- Python 2.7 学习笔记 列表的使用
- WinForm 控件(下)
- js实现深拷贝和浅拷贝
- JavaScript栈和队列
- android sqlite批量插入数据速度解决方案
- Mac 安装 Jenkins
- 20145212 罗天晨 WEB登陆发贴及会话管理功能的实现
热门文章
- 7 vi 编辑器
- Error in render: ";TypeError: Cannot read property &#39;url_img&#39; of undefined";
- golang 要去学习的文档记录
- 在visual studio工程设置中增加宏定义的方法
- ubuntu下如何检查nvidia显卡驱动是否安装OK?
- OpenStack 对接 Ceph 环境可以创建卷但不能挂载卷的问题
- 2019.11.07【每天学点SAP小知识】Day2 - ABAP 7.40新语法 - 内表
- 系统启动热键(Boot Hotkey)
- HTML <;canvas>; 学习笔记
- 使用shell脚本常见的一些问题