相关代码参考: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工程编译jarinstall后,执行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方式时,遇到一个问题,在手动修改methodbody时,例如newMethod.setBody(bodyStr.toString());bodyStr为处理后的方法体,简单的语句,如:System.out.println("message")是可以的,但是复杂的逻辑不行,程序运行没有反映,控制台也没有异常打印。后来发现需要写对象的全路径,比如List需要写成java.util.List等。其中涉及javassist操作,参见http://blog.csdn.net/u011425751/article/details/51917895
7.遗留问题:
  SpringMVCorg.springframework.web.servlet.DispatcherServlet继承了抽象类FrameworkServletFrameworkServlet继承了抽象类HttpServletBeanHttpServletBean继承抽象类HttpServletHttpServlet继承了抽象类GenericServletGenericServlet实现了ServletServletConfig接口。

具体如下:  

     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

项目中拦截DispatcherServletdoService方法。

javaagent项目中使用的更多相关文章

  1. VS项目中使用Nuget还原包后编译生产还一直报错?

    Nuget官网下载Nuget项目包的命令地址:https://www.nuget.org/packages 今天就遇到一个比较奇葩的问题,折腾了很久终于搞定了: 问题是这样的:我的解决方案原本是好好的 ...

  2. ABP项目中使用Swagger生成动态WebAPI

    本文是根据角落的白板报的<使用ABP实现SwaggerUI,生成动态webapi>一文的学习总结,感谢原文作者角落的白板报. 1 安装Swashbuckle.core 1.1 选择WebA ...

  3. iOS 之项目中遇到的问题总结

    昨天去一家公司面试,面试官问了我在项目开发中遇到过哪些问题,是什么引起的,怎样解决的? 当时由于有点小紧张只说出了一两点,现在就来好好总结一下. 问题: 1.两表联动 所谓的两表联动就是有左右两个表格 ...

  4. My97DatePicker时间控件在项目中的应用

    一.下载My97DatePicker的压缩包My97DatePicker.rar,解压. 注:My97DatePicker最新版本有开发包,项目中使用时删掉,以便节省空间,提高程序的运行效率. 二.在 ...

  5. 在项目中同时使用Objective-C和Swift

    苹果发布的Swift语言可以和之前的Objective-C语言同时存在于一个项目中. 可能有人会认为是同一个类文件中既可以有Objective-C也可以有Swift,这是不对的.同一个类文件或同一个代 ...

  6. 在数据库访问项目中使用微软企业库Enterprise Library,实现多种数据库的支持

    在我们开发很多项目中,数据访问都是必不可少的,有的需要访问Oracle.SQLServer.Mysql这些常规的数据库,也有可能访问SQLite.Access,或者一些我们可能不常用的PostgreS ...

  7. 在基于MVC的Web项目中使用Web API和直接连接两种方式混合式接入

    在我之前介绍的混合式开发框架中,其界面是基于Winform的实现方式,后台使用Web API.WCF服务以及直接连接数据库的几种方式混合式接入,在Web项目中我们也可以采用这种方式实现混合式的接入方式 ...

  8. Web API项目中使用Area对业务进行分类管理

    在之前开发的很多Web API项目中,为了方便以及快速开发,往往把整个Web API的控制器放在基目录的Controllers目录中,但随着业务越来越复杂,这样Controllers目录中的文件就增加 ...

  9. 如何决解项目中hibernate中多对多关系中对象转换json死循环

    先写一下原因吧!我是写的SSH项目,在项目中我遇到的问题是把分页对象(也就是pageBean对象)转化为json数据,下面为代码: public class PageBean <T>{// ...

随机推荐

  1. (十六)C语言之函数

  2. bedtools 用法大全

    原文:https://cloud.tencent.com/developer/article/1078324 前言: bedtools等工具号称是可以代替普通的生物信息学数据处理工程师的!我这里用一个 ...

  3. Why are C# structs immutable?

    Compiler Error CS1612 Cannot modify the return value of 'expression' because it is not a variable cl ...

  4. Java-JVM 栈帧(Stack Frame)

    一.概述 栈帧位置 JVM 执行 Java 程序时需要装载各种数据到内存中,不同的数据存放在不同的内存区中(逻辑上),这些数据内存区称作运行时数据区(Run-Time Data Areas). 其中 ...

  5. ThreadPoolExecutor 优雅关闭线程池的原理.md

    经典关闭线程池代码 ExecutorService executorService = Executors.newFixedThreadPool(10); executorService.shutdo ...

  6. LC 807. Max Increase to Keep City Skyline

    In a 2 dimensional array grid, each value grid[i][j] represents the height of a building located the ...

  7. 阶段3 3.SpringMVC·_05.文件上传_5 文件上传之跨服务器上传分析和搭建环境

    使用这个jar包来跨服务器上传 搞两个tomcat.一个springmvc一个fileupload 选中tomcat server点击左边的加号 需要改端口和JMX pport这个端口 部署文件上传的 ...

  8. Block代码块中使用局部变量注意点

    第一次写代码遇到报这个错,实在是想不通为什么,按常理应该是不会有问题,报错的呀??纠结了一会之后只好仔细查看报错原因咯,原来是: 当我们在block代码块中使用局部变量时,就会很容易出现如图的错误. ...

  9. nginx创建www用户作用

    linux创建www用户组和用户 wdcp中的nginx服务启动需要依赖www用户,因此若没有此用户就可能会启动失败.创建这个用户的方法: [root@bogon local]# id www [ro ...

  10. 几种排序算法及Java实现排序的几种方式

    几种排序算法 下面的例子介绍了4种排序方法: 冒泡排序, 选择排序, 插入排序, 快速排序 package date201709.date20170915; public class SortUtil ...