编程语言
首页 > 编程语言> > APM - Javassist 入门 生成一个简单类

APM - Javassist 入门 生成一个简单类

作者:互联网



在这里插入图片描述


概述

Javassist是一个开源的分析、编辑和创建Java字节码的类库,可以直接编辑和生成Java生成的字节码。

相对于bcel, asm等这些工具,开发者不需要了解虚拟机指令,就能动态改变类的结构,或者动态生成类。

Javassist简单易用, 快速。


Javassist作用
常用API
说明
ClassPoolJavassist的类池,使用ClassPool 类可以跟踪和控制所操作的类, 与 JVM ClassLoader相似
CtClassCtClass提供了类的操作,如在类中动态添加新字段、方法和构造函数、以及改变类、父类和接口的方法。
CtMethod类中的方法,通过它可以给类创建新的方法,还可以修改返回类型,访问修饰符等, 甚至还可以修改方法体内容代码
CtConstructor构造函数
CtField类的属性,通过它可以给类创建新的属性,还可以修改已有的属性的类型,访问修饰符等

Javassist 语法
项目Value
$0, $1, $2, …this and actual parameters
$argsAn array of parameters. The type of $args is Object[].
$$All actual parameters.For example, m($$) is equivalent to m($1,$2,…)
$cflow(…)cflow variable
$rThe result type. It is used in a cast expression.
$wThe wrapper type. It is used in a cast expression.
$_The resulting value
$sigAn array of java.lang.Class objects representing the formal parameter types
$typeA java.lang.Class object representing the formal result type.
$classA java.lang.Class object representing the class currently edited.

在这里插入图片描述


Javassist使用流程

在这里插入图片描述


Demo

依赖

 	 <dependency>
            <groupId>org.javassist</groupId>
            <artifactId>javassist</artifactId>
            <version>3.18.1-GA</version>
        </dependency>

import javassist.*;/**
 * 使用Javassist 构建 一个新的类 并执行
 */public class FirstJavasisit {
    public static void main(String[] args) throws CannotCompileException,
            NotFoundException, InstantiationException, IllegalAccessException {
        ClassPool pool = new ClassPool(true);
        // 插入类路径,通过类路径去搜索我们要的类
        pool.insertClassPath(new LoaderClassPath(FirstJavasisit.class.getClassLoader()));


        // 构建一个新的CtClass对象
        CtClass targetClass = pool.makeClass("com.artisan.Hello");
        // 实现一个接口
        targetClass.addInterface(pool.get(IHello.class.getName()));
        // 获取返回类型
        CtClass returnType = pool.get(void.class.getName());

        // 方法名称
        String mname = "sayHello";
        // 方法参数
        CtClass[] parameters = new CtClass[]{pool.get(String.class.getName())};
        // 实例化方法
        CtMethod method = new CtMethod(returnType, mname, parameters, targetClass);

        // 方法中的源码
        String src = "{"
                + "System.out.println($1);"
                + "}";
        // 设置src到方法中
        method.setBody(src);
        // 添加方法
        targetClass.addMethod(method);

        // 装在到当前的ClassLoader中
        Class cla = targetClass.toClass();
        // 实例化
        IHello hello = (IHello) cla.newInstance();
        // 方法调用
        hello.sayHello("artisan");
    }

    /**
     * 接口不是必须的,只是为了方便演示,少写点反射代码
     */
    public interface IHello {
        void sayHello(String name);
    }}

在这里插入图片描述


Demo2

让我们对UserService类 插装一下

package com.artisan.agent;public class UserService {

    /**
     * 无参方法
     * @throws InterruptedException
     */
    public void sayHello() throws InterruptedException {
        Thread.sleep(100);
        System.out.println("hello 小工匠");
    }

    /**
     * 无返回值的
     * @param name
     * @param age
     * @param other
     * @throws InterruptedException
     */
    public void say2Void(String name,int age,Object other) throws InterruptedException {
        Thread.sleep(100);
        System.out.println("hello 小工匠2");
    }


    /**
     * 带有返回值
     * @param name
     * @param age
     * @param other
     * @return
     * @throws InterruptedException
     */
    public String say2(String name,int age,Object other) throws InterruptedException {
        Thread.sleep(100);
        System.out.println("hello 小工匠2 with return ");
        return "ttttt";
    }}


  @Test
    public void test3() throws NotFoundException, CannotCompileException, InterruptedException {

        // 类加载器
        ClassPool classPool = new ClassPool();
        // 追加系统ClassLoader
        classPool.appendSystemPath();
        // 获取要操作的类
        CtClass ctClass = classPool.get("com.artisan.agent.UserService");
        // 获取方法
        CtMethod originMethod = ctClass.getDeclaredMethod("say2Void");
        // copy 一个新的方法
        CtMethod newMethod = CtNewMethod.copy(originMethod,ctClass,null);
        // 设置新名字
        originMethod.setName(originMethod.getName()+ "$agent");
        // 对原方法进行包装,比如加计算方法耗时
        newMethod.setBody("{ long begin = System.currentTimeMillis();\n" +
                "        say2Void$agent($$);\n" +
                "        long end = System.currentTimeMillis();\n" +
                "        System.out.println(end - begin);" +
                "}");
        // 将新方法添加到单签类中
        ctClass.addMethod(newMethod);
        //把修改后的class装载到JVM
        ctClass.toClass();

        new com.artisan.agent.UserService().say2Void("art2",18,"xxxx");
    }

    @Test
    public void test4() throws NotFoundException, CannotCompileException, InterruptedException {

        // 类加载器
        ClassPool classPool = new ClassPool();
        // 追加系统ClassLoader
        classPool.appendSystemPath();
        // 获取要操作的类
        CtClass ctClass = classPool.get("com.artisan.agent.UserService");
        // 获取方法
        CtMethod originMethod = ctClass.getDeclaredMethod("say2");
        // copy 一个新的方法
        CtMethod newMethod = CtNewMethod.copy(originMethod,ctClass,null);
        // 设置新名字
        originMethod.setName(originMethod.getName()+ "$agent");
        // 对原方法进行包装,比如加计算方法耗时   带有返回值的的 $r
        newMethod.setBody("{ long begin = System.currentTimeMillis();\n" +
                "        say2$agent($$);\n" +
                "        long end = System.currentTimeMillis();\n" +
                "        System.out.println(end - begin);" +
                " Object s = \"test\" ;" +
                " return ($r)s ;" +
                "}");
        // 将新方法添加到单签类中
        ctClass.addMethod(newMethod);
        //把修改后的class装载到JVM
        ctClass.toClass();

        System.out.println((new com.artisan.agent.UserService().say2("art2", 18, "xxxx")));
    }

在这里插入图片描述

在这里插入图片描述


注意事项

标签:入门,System,agent,throws,ctClass,APM,方法,public,Javassist
来源: https://blog.51cto.com/u_15239532/2838638