相关代码参考: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. BZOJ3331压力

    码量略大. 题意就是求路径必经点. tarjan缩点,所有的非割点只有是起点终点时才必经,直接开个ans数组就OK了. 至于割点,因为缩完点之后的图是vDcc和割点共同组成的,而且题目说连通,那就是棵 ...

  2. VLC-Qt 入门指南

      关于 VLC-Qt VLC-Qt:一个结合了 Qt 应用程序和 libVLC 的免费开源库.它包含了用于媒体播放的核心类,以及用于更快速地进行媒体播放器开发的一些 GUI 类. 官网地址:http ...

  3. ssh不输入密码

    要通过跳转机器远程其他的机器 不方便使用秘钥 每次都要输入密码也很烦 使用sshpass可以复制一行命令就直接登录了 我的跳板机是Centos7安装sshpass很简单 直接如下搞定 yum inst ...

  4. ssh 远程登录错误

    错误信息: @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ WARNING: REMOTE HOST IDENTIFICATI ...

  5. PHP 封装POD 类

    使用POD的过程 //1.造DSN:驱动名:dbname=数据库名;host=服务器地址 $dsn = "mysql:dbname=mydb;host=localhost"; // ...

  6. 四、SpringBoot出现报错:java.lang.NoSuchMethodError: org.springframework.http.MediaType.equalsTypeAndSubtype(Lorg/springframework/util/MimeType;)Z

    idea启动SpringBoot项目后,出现如下错误: 2019-11-19 15:24:44.344 ERROR 39168 --- [nio-8443-exec-1] o.a.c.c.C.[.[. ...

  7. linux vim的全目录搜索 和 hostname的设置?

    vim下的搜索命令是: vimgrep, 简写就是vim. 关于这个全局搜索的用法根grep的差不多, 但是, 如果你直接使用 grep的话 就会在vim的外部执行, 根vim内部就没有什么关系了, ...

  8. 算法 - 插入排序交换次数 - Binary Indexed Tree

    场景:快速得到一段数组元素的和 题目:Insertion Sort Advanced Analysis | HackerRank 算法:binary-indexed-tree :: HackerRan ...

  9. java集合(List,Set,Map)详细总结

    一,集合的由来: 数组是长度是固定的,当添加的元素超过数组的长度时需要对数组重新定义,太麻烦了,java内部给我们提供了集合类,能存储任意对象,长度是可以改变的,随着元素的增加而增加,随着元素的减少而 ...

  10. stegsolve---图片隐写查看器

    今天做CTF隐写术的题偶然发现一隐写图片查看的神器------stegsolve,分享给大家 stegsolve下载地址:http://www.caesum.com/handbook/Stegsolv ...