• 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的包。

     1     import java.lang.instrument.ClassFileTransformer;
     2     import java.lang.instrument.IllegalClassFormatException;
     3     import java.security.ProtectionDomain;
     4     import java.util.ArrayList;
     5     import java.util.HashMap;
     6     import java.util.List;
     7     import java.util.Map;
     8     import javassist.ClassPool;
     9     import javassist.CtClass;
    10     import javassist.CtMethod;
    11     import javassist.CtNewMethod;
    12     
    13     public class MyTransformer implements ClassFileTransformer {
    14 
    15     final static String prefix = "
    long startTime = System.currentTimeMillis();
    ";
    16     final static String postfix = "
    long endTime = System.currentTimeMillis();
    ";
    17 
    18     // 被处理的方法列表
    19     final static Map<String, List<String>> methodMap = new HashMap<String, List<String>>();
    20 
    21     public MyTransformer() {
    22         add("com.shanhy.demo.TimeTest.sayHello");
    23         add("com.shanhy.demo.TimeTest.sayHello2");
    24     }
    25 
    26     private void add(String methodString) {
    27         String className = methodString.substring(0, methodString.lastIndexOf("."));
    28         String methodName = methodString.substring(methodString.lastIndexOf(".") + 1);
    29         List<String> list = methodMap.get(className);
    30         if (list == null) {
    31             list = new ArrayList<String>();
    32             methodMap.put(className, list);
    33         }
    34         list.add(methodName);
    35     }
    36 
    37     @Override
    38     public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
    39             ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
    40         className = className.replace("/", ".");
    41         if (methodMap.containsKey(className)) {// 判断加载的class的包路径是不是需要监控的类
    42             CtClass ctclass = null;
    43             try {
    44                 ctclass = ClassPool.getDefault().get(className);// 使用全称,用于取得字节码类<使用javassist>
    45                 for (String methodName : methodMap.get(className)) {
    46                     String outputStr = "
    System.out.println("this method " + methodName
    47                             + " cost:" +(endTime - startTime) +"ms.");";
    48                     CtMethod ctmethod = ctclass.getDeclaredMethod(methodName);// 得到这方法实例
    49                     String newMethodName = methodName + "$old";// 新定义一个方法叫做比如sayHello$old
    50                     ctmethod.setName(newMethodName);// 将原来的方法名字修改
    51                     // 创建新的方法,复制原来的方法,名字为原来的名字
    52                     CtMethod newMethod = CtNewMethod.copy(ctmethod, methodName, ctclass, null);
    53                     // 构建新的方法体
    54                     StringBuilder bodyStr = new StringBuilder();
    55                     bodyStr.append("{");
    56                     bodyStr.append(prefix);
    57                     bodyStr.append(newMethodName + "($$);
    ");// 调用原有代码,类似于method();($$)表示所有的参数
    58                     bodyStr.append(postfix);
    59                     bodyStr.append(outputStr);
    60                     bodyStr.append("}");
    61 
    62                     newMethod.setBody(bodyStr.toString());// 替换新方法
    63                     ctclass.addMethod(newMethod);// 增加新方法
    64                 }
    65                 return ctclass.toBytecode();
    66             } catch (Exception e) {
    67                 System.out.println(e.getMessage());
    68                 e.printStackTrace();
    69             }
    70         }
    71         return null;
    72     }
    73     }
    View Code

    2.然后声明MyAgent类,实现方法:

    1      public static void premain(String args, Instrumentation inst){
    2         inst.addTransformer(new MyTransformer());
    3     }
    View Code

    3.将工程打成jar包,例如Myagent.jar,需要修改MANIFEST.MF内容,添加

     1 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接口。

    具体如下:  

    1     public class DispatcherServlet extends FrameworkServlet
    2     public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware
    3     public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware
    4     public abstract class HttpServlet extends GenericServlet
    5     public abstract class GenericServlet implements Servlet, ServletConfig, java.io.Serializable
    6     public interface Servlet

    项目中拦截DispatcherServletdoService方法。

  • 相关阅读:
    深度学习笔记(21)- 深度看图
    Mysql数据库主键,外键,索引概述
    软件架构的演进:单体、垂直、SOA、微服务
    2018年阿里巴巴关于Java重要开源项目汇总
    全面了解HTTP和HTTPS
    创业互联网公司如何搭建自己的技术架构
    Dubbo与Spring Cloud的比较
    Java工程师技能点梳理
    使用SpringCloud将单体迁移至微服务
    Springmvc与Struts区别?
  • 原文地址:https://www.cnblogs.com/Java-Script/p/11089314.html
Copyright © 2020-2023  润新知