相关代码参考: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. Nginx事件管理之定时器事件

    1. 缓存时间 1.1 管理 Nginx 中的每个进程都会单独地管理当前时间.ngx_time_t 结构体是缓存时间变量的类型: typedef struct { /* 格林威治时间1970年1月1日 ...

  2. Git客户端TortoiseGit下载、安装及汉化

    本篇经验将和大家介绍Git客户端TortoiseGit下载.安装及汉化的方法,希望对大家的工作和学习有所帮助! TortoiseGit下载和安装   1 TortoiseGit是Windows下最好用 ...

  3. 打开新窗口(window.open)关闭窗口(window.close)

    打开新窗口(window.open) open() 方法可以查找一个已经存在或者新建的浏览器窗口. 语法: window.open([URL], [窗口名称], [参数字符串]) 参数说明: URL: ...

  4. zipkin-client:brave核心代码思路整理

    Zipkin是分布式跟踪系统. 简单地理解,可以将Zipkin分为两部分. 一部分为Zipkin Server,其负责接受存储应用程序处理耗时数据,以及UI展示. 另一部分为Zipkin Client ...

  5. 网络通信框架之retrofit

    主页: [https://github.com/square/retrofit](https://github.com/square/retrofit) 注意: 使用Retrofit的前提是**服务器 ...

  6. Windows 2012 英文版系统安装中文语言包及时间格式设置

    1.安装中文语言包:在运行窗口中输入"LPKSetup.exe",选择中文语言包安装.--------------------------------------------- 2 ...

  7. DevOps - Scrum

    1 - DevOps与敏捷开发 在采用敏捷开发的情况下,所有成员都对服务和产品负责,理解彼此的业务,符合DevOps的组织和文化. 以商业需求为核心,在较短期间内确定开发方针,并持续进行改善,从而逐步 ...

  8. Day01:文件操作(File、RandomAccessFile)

    文件操作 JAVA中的 File 类是文件和目录路径名的抽象形式.使用 File 类可以获取文件本身的一些信息,例如文件所在的目录.文件长度.文件读写权限等. 在 Java 中,File 类是 jav ...

  9. C#与Unity 数据存储

    使用Json存储数据 1.创建Json文件,直接创建txt文件,将后缀名改为xx.json 2.Json文件的数据格式,中括号表示列表,即中括号下的每个逗号隔开的内容都是列表的每个元素,花括号表示对象 ...

  10. jQuery I

    jQuery 两大特点: 链式编程:比如.show()和.html()可以连写成.show().html(). 隐式迭代:隐式对应的是显式.隐式迭代的意思是:在方法的内部进行循环遍历,而不用我们自己再 ...