javaAgent和Java字节码增强技术的学习与实践

参考文章:

https://www.cnblogs.com/chiangchou/p/javassist.html

https://blog.csdn.net/u010039929/article/details/62881743

https://www.jianshu.com/p/0f64779cdcea

【本文代码下载】

  下载代码

【背景】

  最近在工作中进行程序的性能调优时,想起之前同事的介绍的阿里的Java在线诊断工具 —— arthas,决定试用一下。

  这玩意,是真的好用,能在对被检测程序 不做 任何改动 和 设置 的情况下,无侵入的对运行中的程序进行性能分析诊断,监控进入指定方法的请求并展示请求的参数,甚至在线热更新代码,

  通过查阅资料发现,arthas是基于javaAgent技术 和 Java字节码增强技术 实现的,所以接下来就开始介绍javaAgent 和 Java字节码 技术的学习及案例

【知识准备】

 什么是java agent?

  Java agent是在JDK1.5引入的,是一种可以动态修改Java字节码的技术。java类编译之后形成字节码被JVM执行,JVM在执行这些字节码之前获取这些字节码信息,并且对这些字节码进行修改,来完成一些额外的功能,这种就是java agent技术。

  我们可以使用agent技术构建一个独立于应用程序的代理程序(即为Agent),用来协助监测、运行甚至替换其他JVM上的程序。使用它可以实现虚拟机级别的AOP功能,并且比起 Spring的 AOP 更加的优美。

  Agent分为两种,一种是在主程序之前运行的Agent,一种是在主程序之后运行的Agent(前者的升级版,1.6以后提供),待会都会讲解

 什么是字节码增强技术?

  个人理解,是在Java字节码生成之后,运行期对其进行修改,增强其功能,这种方式相当于对应用程序的二进制文件进行修改。Java字节码增强主要是为了减少冗余代码,提高性能等。

  通常可以用 ASM 或 javassist 框架来修改字节码

  

 JVM Attach机制

  jvm attach机制上JVM提供的一种JVM进程间通信的功能,能让一个进程传命令给另一个进程,并进行一些内部的操作,比如进行线程dump,那么就需要执行jstack进行,然后把pid等参数传递给需要dump的线程来执行,这就是一种java attach。

 Class Transform的实现

  第一次类加载的时候要求被transform的场景,在加载类文件的时候发出ClassFileLoad事件,交给instrument agent来调用java agent里注册的ClassFileTransformer实现字节码的修改

【案例】

  在了解java agent 和 字节码增强技术 后,我们可以结合起来做一个小案例:

在指定类的指定方法执行前,先打印一串11111111

  正如上文所述,Agent分为两种,一种是在主程序之前运行的Agent,一种是在主程序之后运行的Agent,接下来两种案例都会给出,本次案例使用javassist 框架

=======================准备好被增强类的代码=======================

被增强类的pom.xml

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <project xmlns="http://maven.apache.org/POM/4.0.0"
 3          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 5     <parent>
 6         <artifactId>java_agent</artifactId>
 7         <groupId>xcy</groupId>
 8         <version>1.0-SNAPSHOT</version>
 9     </parent>
10     <modelVersion>4.0.0</modelVersion>
11
12     <artifactId>app</artifactId>
13
14     <dependencies>
15         <!-- 在被增强类的pom文中,需要加入jdk的tools工具,或者自己把该jar包放入到项目中引用,因为我发现在默认情况下,idea即便引用jdk,用的包居然都是jdk中的jre,jre中没有tools.jar这个包,就会导致被增强类启动后,没有attach服务,无法连接过来 -->
16         <dependency>
17             <groupId>jdk.tools</groupId>
18             <artifactId>jdk.tools</artifactId>
19             <version>jdk1.8.0_121</version>
20             <scope>system</scope>
21             <systemPath>F:/ProgramFiles/java/jdk1.8.0_121/lib/tools.jar</systemPath>
22         </dependency>
23     </dependencies>
24
25     <build>
26 <!--        <finalName>App</finalName>-->
27         <plugins>
28             <plugin>
29                 <groupId>org.apache.maven.plugins</groupId>
30                 <artifactId>maven-compiler-plugin</artifactId>
31                 <configuration>
32                     <source>1.8</source>
33                     <target>1.8</target>
34                     <encoding>utf-8</encoding>
35                 </configuration>
36             </plugin>
37             <plugin>
38                 <groupId>org.apache.maven.plugins</groupId>
39                 <artifactId>maven-shade-plugin</artifactId>
40                 <version>1.2.1</version>
41                 <executions>
42                     <execution>
43 <!--                        <phase>package</phase>-->
44                         <goals>
45                             <goal>shade</goal>
46                         </goals>
47                         <configuration>
48                             <transformers>
49                                 <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
50                                     <mainClass>com.app.App</mainClass>
51                                 </transformer>
52                             </transformers>
53                         </configuration>
54                     </execution>
55                 </executions>
56             </plugin>
57         </plugins>
58     </build>
59 </project>

被增强的类

  备注:代码在java_agent项目中的app模块下

  

 1 package com.app;
 2
 3 public class App {
 4     public static void main(String[] args) {
 5         hello();
 6     }
 7
 8     public static void hello() {
 9         System.out.println("hello");
10     }
11 }

=======================主程序之前运行的Agent方式=======================

  备注:代码在java_agent项目中的agent_non_attach模块下

pom.xml

  agent通过MANIFEST.MF 中指定的Premain-Class参数,获取代理程序入口,所以写好的代理类想要运行,在打 jar 包前,还需要要在 MANIFEST.MF 中指定代理程序入口。

  在pom文件中,配置好Premain-Class,就会封装到META-INF下的MANIFEST.MF中

  

  备注:与 agent 相关的参数

  • Premain-Class :JVM 启动时指定了代理,此属性指定代理类,即包含 premain 方法的类
  • Agent-Class :JVM动态加载代理,此属性指定代理类,即包含 agentmain 方法的类
  • Boot-Class-Path :设置引导类加载器搜索的路径列表,列表中的路径由一个或多个空格分开。
  • Can-Redefine-Classes :布尔值(true 或 false)。是否能重定义此代理所需的类
  • Can-Retransform-Classes :布尔值(true 或 false)。是否能重转换此代理所需的类
  • Can-Set-Native-Method-Prefix :布尔值(true 或 false)。是否能设置此代理所需的本机方法前缀
 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <project xmlns="http://maven.apache.org/POM/4.0.0"
 3          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 5     <parent>
 6         <artifactId>java_agent</artifactId>
 7         <groupId>xcy</groupId>
 8         <version>1.0-SNAPSHOT</version>
 9     </parent>
10     <modelVersion>4.0.0</modelVersion>
11
12     <artifactId>agent_non_attach</artifactId>
13
14     <dependencies>
15         <!-- https://mvnrepository.com/artifact/org.javassist/javassist -->
16         <dependency>
17             <groupId>org.javassist</groupId>
18             <artifactId>javassist</artifactId>
19             <version>3.26.0-GA</version>
20         </dependency>
21     </dependencies>
22
23     <build>
24         <plugins>
25             <plugin>
26                 <groupId>org.apache.maven.plugins</groupId>
27                 <artifactId>maven-compiler-plugin</artifactId>
28                 <configuration>
29                     <source>1.8</source>
30                     <target>1.8</target>
31                     <encoding>utf-8</encoding>
32                 </configuration>
33             </plugin>
34             <plugin>
35                 <groupId>org.apache.maven.plugins</groupId>
36                 <artifactId>maven-shade-plugin</artifactId>
37                 <version>3.0.0</version>
38                 <executions>
39                     <execution>
40                         <phase>package</phase>
41                         <goals>
42                             <goal>shade</goal>
43                         </goals>
44                         <configuration>
45                             <transformers>
46                                 <transformer
47                                         implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
48                                     <manifestEntries>
49                                         <Premain-Class>com.agent.non.attach.demo.Agent</Premain-Class>
50                                     </manifestEntries>
51                                 </transformer>
52                             </transformers>
53                         </configuration>
54                     </execution>
55                 </executions>
56             </plugin>
57         </plugins>
58     </build>
59 </project>

Agent代理类

  addTransformer:注册一个Transformer,从此之后的类加载都会被 transformer 拦截。

  VM启动后动态加载的 agent,Instrumentation 会通过 agentmain 方法传入代理程序,agentmain 在 main 函数开始运行后才被调用。

  对于VM启动时加载的 agent,Instrumentation 会通过 premain 方法传入代理程序,premain 方法会在程序 main 方法执行之前被调用。此时大部分Java类都没有被加载(“大部分”是因为,agent类本身和它依赖的类还是无法避免的会先加载的),是一个对类加载埋点做手脚(addTransformer)的好机会。但这种方式有很大的局限性,Instrumentation 仅限于 main 函数执行前,此时有很多类还没有被加载,如果想为其注入 Instrumentation 就无法办到。

 1 package com.agent.non.attach.demo;
 2
 3 import java.lang.instrument.Instrumentation;
 4
 5 public class Agent {
 6     /**
 7      *  agentArgs 是 premain 函数得到的程序参数,通过 -javaagent 传入。这个参数是个字符串,如果程序参数有多个,需要程序自行解析这个字符串。
 8      *  inst 是一个 java.lang.instrument.Instrumentation 的实例,由 JVM 自动传入。
 9      */
10     public static void premain(String agentOps, Instrumentation inst) {
11         System.out.println("=========premain方法执行========");
12         // 添加Transformer
13         inst.addTransformer(new MyTransformer("com.app.App", "hello"));
14     }
15
16     /**
17      *  带有 Instrumentation 参数的 premain 优先级高于不带此参数的 premain。
18      *  如果存在带 Instrumentation 参数的 premain,不带此参数的 premain 将被忽略。
19      * @param agentArgs
20      */
21     public static void premain(String agentArgs) {
22
23     }
24 }

MyTransformer类

  拦截指定方法,进行类增强

 1 package com.agent.non.attach.demo;
 2
 3 import javassist.ClassPool;
 4 import javassist.CtClass;
 5 import javassist.CtMethod;
 6
 7 import java.lang.instrument.ClassFileTransformer;
 8 import java.lang.instrument.IllegalClassFormatException;
 9 import java.security.ProtectionDomain;
10
11 public class MyTransformer implements ClassFileTransformer {
12     private String targetClassName;//被增强类的类名
13     private String targetMethodName;//被增强的方法名
14
15     public MyTransformer(String targetClassName, String targetMethodName) {
16         this.targetClassName = targetClassName;
17         this.targetMethodName = targetMethodName;
18     }
19
20     public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
21         className = className.replace("/", ".");
22         if (className.equals(targetClassName)) {
23             CtClass ctclass = null;
24             try {
25                 ctclass = ClassPool.getDefault().get(className);// 使用全称,用于取得字节码类<使用javassist>
26                 CtMethod ctmethod = ctclass.getDeclaredMethod(targetMethodName);// 得到这方法实例
27                 ctmethod.insertBefore("System.out.println(1111111);");
28                 return ctclass.toBytecode();
29             } catch (Exception e) {
30                 System.out.println(e.getMessage());
31                 e.printStackTrace();
32             }
33         }
34         return null;
35     }
36 }

运行

 运行有两种方式:

  1、直接运行,格式:java -javaagent:agent的jar路径 被增强jar的路径

   例如:java -jar -javaagent:agent_non_attach-1.0-SNAPSHOT.jar app-1.0-SNAPSHOT.jar

  2、在idea中运行

    在被增强类项目中新建一个启动类,在该类中添加main方法,在运行时,指定VM options参数:-javaagent: agent的jar包路径 被增强的jar包路径

    例如:-javaagent:F:\workspace\java_agent\agent_non_attach\target\agent_non_attach-1.0-SNAPSHOT.jar

=======================主程序之后运行的Agent方式=======================

  备注:代码在java_agent项目中的agent_attach模块下

被增强类的pom.xml

  在被增强类的pom文中,需要加入jdk的tools工具,或者自己把该jar包放入到项目中引用,因为我发现在默认情况下,idea即便引用jdk,用的包居然都是jdk中的jre,jre中没有tools.jar这个包,就会导致被增强类启动后,没有attach服务,无法连接过来

1 <dependency>
2   <groupId>jdk.tools</groupId>
3   <artifactId>jdk.tools</artifactId>
4    <version>jdk1.8.0_121</version>
5    <scope>system</scope>
6    <systemPath>F:/ProgramFiles/java/jdk1.8.0_121/lib/tools.jar</systemPath>
7 </dependency>

KeepRunMain类

  该agent需要在被增强类运行的时候执行,所以需要让被增强类一直运行

 1 package com.app;
 2
 3 public class KeepRunMain {
 4     public static void main(String[] args) throws InterruptedException {
 5         String[] params = {};
 6         while (true) {
 7             App.hello();
 8             Thread.sleep(1000L);
 9         }
10     }
11 }

agent的pom.xml

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <project xmlns="http://maven.apache.org/POM/4.0.0"
 3          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 5     <parent>
 6         <artifactId>java_agent</artifactId>
 7         <groupId>xcy</groupId>
 8         <version>1.0-SNAPSHOT</version>
 9     </parent>
10     <modelVersion>4.0.0</modelVersion>
11
12     <artifactId>agent_attach</artifactId>
13
14     <dependencies>
15         <!-- https://mvnrepository.com/artifact/org.javassist/javassist -->
16         <dependency>
17             <groupId>org.javassist</groupId>
18             <artifactId>javassist</artifactId>
19             <version>3.26.0-GA</version>
20         </dependency>
21
22         <dependency>
23             <groupId>jdk.tools</groupId>
24             <artifactId>jdk.tools</artifactId>
25             <version>jdk1.8.0_121</version>
26             <scope>system</scope>
27             <systemPath>F:/ProgramFiles/java/jdk1.8.0_121/lib/tools.jar</systemPath>
28         </dependency>
29     </dependencies>
30
31     <build>
32         <plugins>
33             <plugin>
34                 <groupId>org.apache.maven.plugins</groupId>
35                 <artifactId>maven-compiler-plugin</artifactId>
36                 <configuration>
37                     <source>1.8</source>
38                     <target>1.8</target>
39                     <encoding>utf-8</encoding>
40                 </configuration>
41             </plugin>
42             <plugin>
43                 <groupId>org.apache.maven.plugins</groupId>
44                 <artifactId>maven-shade-plugin</artifactId>
45                 <version>3.0.0</version>
46                 <executions>
47                     <execution>
48                         <phase>package</phase>
49                         <goals>
50                             <goal>shade</goal>
51                         </goals>
52                         <configuration>
53                             <transformers>
54                                 <transformer
55                                         implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
56                                     <manifestEntries>
57                                         <Agent-Class>com.agent.attach.demo.Agent</Agent-Class>
58                                         <Can-Retransform-Classes>true</Can-Retransform-Classes>
59                                     </manifestEntries>
60                                 </transformer>
61                             </transformers>
62                         </configuration>
63                     </execution>
64                 </executions>
65             </plugin>
66         </plugins>
67     </build>
68 </project>

Agent类

 1 package com.agent.attach.demo;
 2
 3 import java.lang.instrument.Instrumentation;
 4 import java.lang.instrument.UnmodifiableClassException;
 5
 6 public class Agent {
 7     public static String className="com.app.App";
 8     public static String methon="hello";
 9     static {
10         System.out.println("ddd");
11     }
12     public static void agentmain(String args, Instrumentation inst) throws UnmodifiableClassException {
13         Class[] allClass = inst.getAllLoadedClasses();
14         for (Class c : allClass) {
15             System.out.println(c.getName());
16             if(c.getName().equals(className)){
17                 System.out.println("agent loaded");
18                 inst.addTransformer(new MyTransformer(className, methon), true);
19                 inst.retransformClasses(c);
20             }
21         }
22     }
23 }

MyTransformer类

 1 package com.agent.attach.demo;
 2
 3 import javassist.ClassPool;
 4 import javassist.CtClass;
 5 import javassist.CtMethod;
 6
 7 import java.lang.instrument.ClassFileTransformer;
 8 import java.lang.instrument.IllegalClassFormatException;
 9 import java.security.ProtectionDomain;
10
11 public class MyTransformer implements ClassFileTransformer {
12     private String targetClassName;//被增强类的类名
13     private String targetMethodName;//被增强的方法名
14
15     public MyTransformer(String targetClassName, String targetMethodName) {
16         this.targetClassName = targetClassName;
17         this.targetMethodName = targetMethodName;
18     }
19
20     @Override
21     public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
22         try {
23             CtClass ctClass = ClassPool.getDefault().get(this.targetClassName);
24             CtMethod ctMethod=ctClass.getDeclaredMethod(this.targetMethodName);
25             System.out.println(ctMethod.getName());
26             ctMethod.insertBefore("System.out.println(\" 11111111111111111111111\");");
27             ctClass.writeFile();
28             return ctClass.toBytecode();
29         } catch (Exception e) {
30             System.out.println(e.getMessage());
31         }
32         return null;
33     }
34 }

AttachMain类

 1 package com.agent.attach.demo;
 2
 3 import com.sun.tools.attach.AttachNotSupportedException;
 4 import com.sun.tools.attach.VirtualMachine;
 5 import com.sun.tools.attach.VirtualMachineDescriptor;
 6
 7 import java.util.List;
 8
 9 public class AttachMain {
10     public static void main(String args[]) throws AttachNotSupportedException {
11         VirtualMachine vm;
12         List<VirtualMachineDescriptor> vmList= VirtualMachine.list();
13         if(vmList==null || vmList.isEmpty()){
14             System.out.println("当前没有java程序运行");
15             return;
16         }
17
18         //展示所有运行中的java程序
19         System.out.println("当前运行中的java程序:");
20         for(int i=0;i<vmList.size();i++){
21             System.out.println("["+i+"]  "+vmList.get(i).displayName()+" ,id:"+vmList.get(i).id()+" ,provider:"+vmList.get(i).provider());
22         }
23         System.out.println("请选择(输入序号):");
24
25         //选择其中一个java进程进行增强
26         try{
27             int num=System.in.read()-48;
28             if(num!=-1&&num<vmList.size()){
29                 vm= VirtualMachine.attach(vmList.get(num));
30                 vm.loadAgent("F:\\workspace\\java_agent\\agent_attach\\target\\agent_attach-1.0-SNAPSHOT.jar");
31                 System.in.read();
32             }
33         }catch(Exception e){
34             e.printStackTrace();
35         }
36     }
37 }

运行方式

  直接执行AttachMain类的main方法,选择要增强类的进程即可

原文地址:https://www.cnblogs.com/byzgss/p/11745327.html

时间: 2024-10-16 19:59:04

javaAgent和Java字节码增强技术的学习与实践的相关文章

字节码增强技术探索

1.字节码 1.1 什么是字节码? Java之所以可以“一次编译,到处运行”,一是因为JVM针对各种操作系统.平台都进行了定制,二是因为无论在什么平台,都可以编译生成固定格式的字节码(.class文件)供JVM使用.因此,也可以看出字节码对于Java生态的重要性.之所以被称之为字节码,是因为字节码文件由十六进制值组成,而JVM以两个十六进制值为一组,即以字节为单位进行读取.在Java中一般是用javac命令编译源代码为字节码文件,一个.java文件从编译到运行的示例如图1所示. 图1 Java运

JVM——字节码增强技术简介

Java字节码增强指的是在Java字节码生成之后,对其进行修改,增强其功能,这种方式相当于对应用程序的二进制文件进行修改.Java字节码增强主要是为了减少冗余代码,提高性能等. 实现字节码增强的主要步骤为: 1.修改字节码 在内存中获取到原来的字节码,然后通过一些工具(如 ASM,Javaasist)来修改它的byte[]数组,得到一个新的byte数组. 2.使修改后的字节码生效 有两种方法: 1) 自定义ClassLoader来加载修改后的字节码: 2)替换掉原来的字节码:在JVM加载用户的C

关于java字节码框架ASM的学习

一.什么是ASM ASM是一个java字节码操纵框架,它能被用来动态生成类或者增强既有类的功能.ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为.Java class 被存储在严格格式定义的 .class文件里,这些类文件拥有足够的元数据来解析类中的所有元素:类名称.方法.属性以及 Java 字节码(指令).ASM从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类.asm字节码增强技术主要是用来反射的时候提升性能的,

JVM字节码增强

JVM——字节码增强技术简介 Java字节码增强指的是在Java字节码生成之后,对其进行修改,增强其功能,这种方式相当于对应用程序的二进制文件进行修改.Java字节码增强主要是为了减少冗余代码,提高性能等. 实现字节码增强的主要步骤为: 1.修改字节码 在内存中获取到原来的字节码,然后通过一些工具(如 ASM,Javaasist)来修改它的byte[]数组,得到一个新的byte数组. 2.使修改后的字节码生效 有两种方法: 1) 自定义ClassLoader来加载修改后的字节码: 2)替换掉原来

字节码增强

之前看了美团技术团队推送的一篇文章,介绍了字节码增强技术,可的很好,自己也记录一下,增强一下记忆,也方便日后巩固学习,有兴趣的可以去搜索美团技术团队的原文 字节码是JVM的底层基础知识,如果能够掌握对于排查问题会有更深层次的理解 1.什么是字节码 首先我们看看什么是字节码,找到一个.class文件,看看长什么样子 Java之所以可以一次编译,到处运行,首先是因为JVM针对各种操作系统和平台都进行了定制,二是无论在什么平台,都可以通过javac命令将一个.java文件编译成固定格式的字节码(.cl

深入了解 Java 字节码

1.1 什么是字节码? Java 在刚刚诞生之时曾经提出过一个非常著名的口号: "一次编写,到处运行(write once,run anywhere)",这句话充分表达了软件开发人员对冲破平台界限的渴求."与平台无关"的理想最终实现在操作系统的运用层上: 虚拟机提供商开发了许多可以运行在不同平台上的虚拟机,这些虚拟机都可以载入和执行同一种平台无关的字节码,从而实现了程序的"一次编写到处运行". 各种不同平台的虚拟机与所有平台都统一使用的程序存储格

Java 字节码

Java作为业界应用最为广泛的语言之一,深得众多软件厂商和开发者的推崇,更是被包括Oracle在内的众多JCP成员积极地推动发展.但是对于Java语言的深度理解和运用,毕竟是很少会有人涉及的话题.InfoQ中文站特地邀请IBM高级工程师成富为大家撰写这个<Java深度历险>专栏,旨在就Java的一些深度和高级特性分享他的经验. 在一般的Java应用开发过程中,开发人员使用Java的方式比较简单.打开惯用的IDE,编写Java源代码,再利用IDE提供的功能直接运行Java 程序就可以了.这种开发

java字节码忍者禁术

Java语言本身是由Java语言规格说明(JLS)所定义的,而Java虚拟机的可执行字节码则是由一个完全独立的标准,即Java虚拟机规格说明(通常也被称为VMSpec)所定义的. JVM字节码是通过javac对Java源代码文件进行编译后生成的,生成的字节码与原本的Java语言存在着很大的不同.比方说,在Java语言中为人熟知的一些高级特性,在编译过程中会被移除,在字节码中完全不见踪影. 这方面最明显的一个例子莫过于Java中的各种循环关键字了(for.while等等),这些关键字在编译过程中会

修改原有的方法名称(字节码增强)

通常对一个方法增加日志记录,安全检查都会说采用AOP或CGLIB动态代理,但无论哪种方式都必需改变原有的调用方式:同时,大量的反射调用也必增加系统的开销.下面介绍一种不需要改变客户端调用方式而又能实现对指定方法增加缓存或日志的方式,那就是——字节码增强! 在实际项目中通常需要对一些频繁访问数据库的方法采用对象缓存,从而提高系统性能减少不必要的网络开销.这时候一般我们会去修改方法的源码,增加Cache的put,get调用,要么采用AspectJ或cglib进行方法执行前或执行后的拦截 但采用无论采