`
jetway
  • 浏览: 484099 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
社区版块
存档分类
最新评论

使用javassist动态注入代码

    博客分类:
  • java
阅读更多
使用javassist动态注入代码

    关于java字节码的处理,目前有很多工具,如bcel,asm。不过这些都需要直接跟虚拟机指令打交道。如果你不想了解虚拟机指令,可以采用javassist。javassist是jboss的一个子项目,其主要的优点,在于简单,而且快速。直接使用java编码的形式,而不需要了解虚拟机指令,就能动态改变类的结构,或者动态生成类。
    下面通过一个简单的例子,通过javassist来实现如何动态注入代码。
    假设,存在类A,如下:
public class A {
    public void method() {
        for (int i = 0; i < 1000000; i++) {
        }
        System.out.println("method1");
    }
}
测试类B如下:
public class B {
    public static void main(String[] args) {
        A a = new A();
        a.method();    
    }
}
现在想统计一下method的执行时间,
默认的实现是修改method:
 public void method() {
        long start = System.currentTimeMillis();
        for (int i = 0; i < 1000000; i++) {
        }
        System.out.println("method1");
        long end = System.currentTimeMillis();
        System.out.println(end - start);
    }
如果A的方法很多,统计方法的执行时间的代码就会相应的增加。为了减少工作量,通过动态注入代码的形式来实现。
修改B的main方法:
    public static void main(String[] args) throws Exception {
      //用于取得字节码类,必须在当前的classpath中,使用全称
        CtClass ctClass = ClassPool.getDefault().get("org.esoft.A");
         //需要修改的方法名称
        String mname = "method";        
        CtMethod mold = ctClass.getDeclaredMethod(mname);
         //修改原有的方法名称
        String nname = mname + "$impl";
        mold.setName(nname);
         //创建新的方法,复制原来的方法
        CtMethod mnew = CtNewMethod.copy(mold, mname, ctClass, null);
         //主要的注入代码
        StringBuffer body = new StringBuffer();
        body.append("{\nlong start = System.currentTimeMillis();\n");
        //调用原有代码,类似于method();($$)表示所有的参数
        body.append(nname + "($$);\n");
        body.append("System.out.println(\"Call to method "
                    + mname
                    + " took \" +\n (System.currentTimeMillis()-start) + "
                    + "\" ms.\");\n");
       
        body.append("}");
         //替换新方法
        mnew.setBody(body.toString());
         //增加新方法
        ctClass.addMethod(mnew);
        //类已经更改,注意不能使用A a=new A();,因为在同一个classloader中,不允许装载同一个类两次
        A a=(A)ctClass.toClass().newInstance();
        a.method();
    }
这只是简单的一个应用。javassist还提供了很多的功能,用于更改类结构。有兴趣的可以参考相关文档

最近重新再看<Inside JVM>,对JAVA编译成的字节码结构很感兴趣,希望找个工具能够对.class文件进行的解析和查看。没找到,倒发现javaassist可以对字节码进行操作和修改。此工具是JBOSS项目的一部分,JBOSS实现AOP的基础。呵呵,开眼界了,原来我们可以直接对字节码文件进行修改,哪怕不知道源文件(跟反编译完全不同)。一个简单例子:

import javassist.*;
class Hello {
    public void say() {
        System.out.println("Hello");
    }
}

public class Test {
    public static void main(String[] args) throws Exception {
        ClassPool cp = ClassPool.getDefault();
        CtClass cc = cp.get("Hello");
        CtMethod m = cc.getDeclaredMethod("say");
        m.setBody("{System.out.println(/"/");}");
        m.insertBefore("System.out.println(/"/");");
        Class c = cc.toClass();
        Hello h = (Hello)c.newInstance();
        h.say();
    }
}

编译运行此文件,输出:

 

 

我们在

 CtMethod m = cc.getDeclaredMethod("say");
  m.setBody("{System.out.println(/"/");}");

  m.insertBefore("System.out.println(/"/");");

修改了say()方法,改成了

System.out.println("");

System.out.println("");

这里的ClassPool是CtClass的容器,它读取class文件,并根据要求保存CtClass的结构以便日后使用,默认状态下是从当前的类装载器获得,当然你可以指定:

pool.insertClassPath("/usr/local/javalib");

当然,不仅仅是修改方法,你还可以新建一个class,利用makeClass()方法,如:

ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.makeClass("Point");

还可以新增方法,下面是sample里的一个例子,同样的:

package sample;

import javassist.*;
import java.lang.reflect.*;

/*
   A very simple sample program

   This program overwrites sample/Test.class (the class file of this
   class itself) for adding a method g().  If the method g() is not
   defined in class Test, then this program adds a copy of
   f() to the class Test with name g().  Otherwise, this program does
   not modify sample/Test.class at all.

   To see the modified class definition, execute:

   % javap sample.Test

   after running this program.
*/
public class Test {
    public int f(int i) {
     i++;
    return i;
    }

    public static void main(String[] args) throws Exception {
 ClassPool pool = ClassPool.getDefault();

 CtClass cc = pool.get("sample.Test");
 Test test=new Test();
 Class c=test.getClass();
 Method []method=c.getDeclaredMethods();
 for(int i=0;i<method.length;i++){
  System.out.println(method[i]);
 }
 try {
     cc.getDeclaredMethod("g");
     System.out.println("g() is already defined in sample.Test.");
 }
 catch (NotFoundException e) {
     /* getDeclaredMethod() throws an exception if g()
      * is not defined in sample.Test.
      */
     CtMethod fMethod = cc.getDeclaredMethod("f");
     CtMethod gMethod = CtNewMethod.copy(fMethod, "g", cc, null);
     cc.addMethod(gMethod);
     cc.writeFile(); // update the class file
     System.out.println("g() was added.");
 }
    }
}
第一次运行时,因为Test里并没有g()方法,所以执行

 CtMethod fMethod = cc.getDeclaredMethod("f");
     CtMethod gMethod = CtNewMethod.copy(fMethod, "g", cc, null);  //把f方法复制给g
     cc.addMethod(gMethod);
     cc.writeFile(); //更新class文件

     System.out.println("g() was added.");
打印:g() was added

第2次运行时,因为以上步骤已经在class文件中增加了一个g方法,所以

 System.out.println("g() is already defined in sample.Test.");
打印:g() is already defined in sample.Test

 

 

分享到:
评论

相关推荐

    Javassist代码注入

    在本文中,我们将深入探讨Javassist如何实现代码注入,并结合实例`HotPatchDemo`来展示其实现过程。 首先,理解代码注入的基本概念是必要的。代码注入是一种技术,允许在程序运行时向现有代码中插入新的行为或修改...

    javaagent+javassist

    例如,我们可以通过javaagent在程序启动时注入javassist生成的代码,以实现对目标类的动态增强。以下是一个简单的示例: 1. 创建一个`javaagent`,定义一个`premain`方法,该方法会在JVM启动时被调用: ```java ...

    agent+javassist例子

    通常,Java Agent用于AOP(面向切面编程)或者性能监控,它可以通过`Premain`方法在JVM启动时注入代码,也可以通过`Agentmain`方法在应用程序启动后注入代码。`java.lang.instrument.Instrumentation`接口提供了与...

    动态代码生成器动态代码生成器动态代码生成器

    因此,使用动态代码生成时需要谨慎,确保生成的代码是可信的。 9. **调试与测试**:由于动态生成的代码不易阅读和理解,调试和测试会变得复杂。开发者需要采取特殊手段,如记录生成的代码或使用专门的调试工具,以...

    javassistDemo.zip

    总结来说,"javassistDemo.zip"是一个实践教程,通过实例演示了如何使用Javaassist库来动态修改Java类,包括插入新的方法和改变方法的实现。这对于理解和掌握Java运行时代码修改技术,尤其是对于那些需要在运行时...

    javassistDemo

    在"javassistDemo"项目中,开发者可能通过以上技术展示了如何使用Javaassist进行类的动态修改和创建,同时也可能演示了如何结合Charles进行网络调试。理解这些知识点对于深入学习Java的动态性、网络调试和AOP编程...

    javassist 3.23.1版本

    2. **代码注入**:Javaassist 提供了API,使得在运行时向已加载的类中注入代码成为可能。这对于实现诸如事务管理、安全控制等跨切面关注点特别有效。开发者可以通过CtClass的`insertBefore()`、`insertAfter()`和`...

    javassist-3.7.ga.jar

    在Struts 2.2及以上版本中,由于框架自身的更新和改进,可能会遇到一些异常,这些问题通常与类加载、依赖注入或者动态方法调用等有关。javassist-3.7.ga.jar 文件是为了解决这些问题而提供的一个辅助工具,它为...

    java 动态编译打包 动态编译可以用于实现动态代码生成、动态加载、插件化等功能

    因此,在使用动态编译时,需要权衡效率和安全性,确保对生成的代码进行充分的验证和测试。 5. **工具和库支持**:Java标准库提供了`javax.tools.JavaCompiler`接口,可以直接在运行时编译Java源代码。除此之外,...

    S-MVP::fire::fire:优化版MVP,使用注解泛型简化代码编写,使用串行协议方便维护,APT过程使用注解解析器利用JavaPoet:full_moon_face:完成重复模块的编写,利用ASpect + GradlePlugin完成横向AOP编程+ Javassist动态字节码注入+ Tinker实现热修复+ Retrofit实现优雅网络操作+ RxJava轻松玩转数据处理

    如此如此,我们还可以编写测试用的视图,模拟用户的各种操作,从而实现对Presenter的测试-而不需要使用自动化的测试工具 MVP解压缩View时代:以前我们的V层是一个活动或框架实现,大多数情况下,我们的一个页面布局...

    @Unit注解处理实现代码

    在单元测试中,`javassist` 可能被用来动态生成测试类,模拟类的行为,或者在测试框架中注入预期的行为,以便更好地控制测试环境。 以下是对`@Unit`注解处理和`javassist`库使用的一些关键知识点的详细说明: 1. *...

    行业分类-设备装置-保护Java字节码代码免受恶意运行环境中的静态和动态攻击的系统和方法.zip

    - **动态代码保护**:例如,使用Owasp Java Enforcer或Javassist库,实时监测和阻止不安全的操作。 - **JIT编译器控制**:限制Just-In-Time (JIT) 编译器的行为,避免恶意代码被优化执行。 2. **内存溢出和缓冲区...

    行业分类-设备装置-一种向目标进程内注入Java字节码的方法及装置.zip

    1. **字节码注入的原理**:字节码注入通常通过动态代理、ASM、BCEL、Javassist等字节码操作库来实现。这些库允许程序在运行时动态生成或修改字节码,然后通过ClassLoader加载到JVM中,使得修改后的字节码影响目标...

    注入到Java世界

    标题"注入到Java世界"暗示我们将探讨如何利用Hook技术对Java应用进行动态修改和调试。在Java中,有几种常见的Hook技术: 1. **Java代理(Java Proxy)**:这是Java标准库提供的一种Hook机制,主要用于创建接口的...

    基于java_字节码的灰盒动态漏洞检测.pdf

    通过Javassist,可以对目标函数动态注入字节码代码,实现函数的备份和修改。 在实际应用中,例如在Tomcat服务器上,可以劫持如Request请求初始化和销毁函数、数据库查询函数以及页面输出函数等关键点。这样,可以...

    MCInject:一个修改库,允许将代码轻松注入其他类

    MCInject:一个修改库,允许将代码轻松注入其他类。 注:该项目已废弃! 我将来可能会回到它,但现在它没有使用。 MCInject 是一个 Minecraft 模组库,它允许任何使用自定义类加载器的模组轻松编辑其他类中的代码...

    Android热补丁动态修复实践,android0基础方法类(csdn)————程序.pdf

    为了实现代码注入,这里使用了`javassist`库。`javassist`是一个Java字节码操作库,允许我们在运行时修改类或生成新的类。创建一个名为`buildSrc`的模块,该模块基于Groovy开发。在`buildSrc`模块中,定义一个名为`...

    Java安全漫谈与java代码审计学习知识库

    学习如何分析和修改字节码,可以提升代码审计的能力,例如使用ASM、BCEL或Javassist等库。 6. **Java代码审计**:代码审计是检查代码以发现潜在安全漏洞的过程。这涉及到对源代码和字节码的深度分析,寻找可能的...

    提高Java程序动态性的一个新途径.zip

    9. **Bytecode操作库**:如ASM、BCEL和Javassist等,可以用于动态生成和修改字节码,实现运行时代码的动态生成和注入。 10. **Spring框架**:Spring的DI(依赖注入)和AOP特性使得Java应用更加灵活,易于扩展。它的...

    javassist-example

    - 注入代码:利用 `CtMethod.insertBefore()` 或 `CtMethod.insertBefore()` 在方法前后插入自定义的 Java 代码。 - 动态代理:实现接口并生成代理类,用于在调用实际方法前后执行特定逻辑。 - 处理异常:学习如何...

Global site tag (gtag.js) - Google Analytics