字节码技术 Javassist的学习使用(动态加载接口实现类,AOP原理)


  • 官网

  • github

  • 作用

  • 简单使用案例

    • 依赖

    • 动态生成接口的实现

    • 对方法作代理增强


官网

https://www.javassist.org/

github

https://github.com/jboss-javassist/javassist

作用

简单来说Javassist主要用于操作Java字节码,让我们操作字节码变得简单,可以在JVM加载类文件的时候去修改它。与其他字节码编辑器不同的是Javassist提供了两个级别的API,源代码级和字节码级。如果用户使用源级 API,他们可以在不了解 Java 字节码规范的情况下编辑类文件。目前自己应用到的场景有如下:

  1. 动态加载实现接口的实现类
  2. 动态增强原有类的方法

简单使用案例

依赖

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

动态生成接口的实现

首先定义一个接口

public interface IHello {
    void sayHello(String name, Integer age);
}

动态生成接口实现类并调用

public class Proxy<T{

    /**
     * 接口
     */

    private Class<T> t;

    public Proxy(Class<T> t){
        this.t = t;
    }


    /**
     * 生成的代理对象名称前缀
     */

    private static final String PROXYFREFIX = "$Proxy";
    /**
     * 生成的代理对象名称前缀
     */

    private static final String PROXYSUFFIX = "Impl";


    /*对接口--IHello进行增强,添加方法体*/
    public static void main(String[] args) throws Exception{

        ClassPool pool = ClassPool.getDefault();
        //接口实现类的名字
        Proxy<IHello> proxy = new Proxy<>(IHello.class);
        String clazzName = proxy.getPackageName() + proxy.getProxyObjectName();
        System.out.println("clazzName: " + clazzName);

        // 创建实现类 = public class com.test.cider.javassist$ProxyIHelloImpl {}
        CtClass targetClass = pool.makeClass(clazzName);

        //获得接口的CtClass  IHello.class.getName() = proxy.t.getClass().getName()
        CtClass interf = pool.getCtClass(IHello.class.getName());
        CtClass[] interfs = new CtClass[]{interf};
        // 这行代码等 = public class ProxyIHelloImpl implements IHello{}
        targetClass.setInterfaces(interfs);
        // 获取接口 sayHello 方法
        CtMethod method = interf.getDeclaredMethod("sayHello");
        // 创建方法
        CtMethod m = new CtMethod(method.getReturnType(),method.getName(),method.getParameterTypes(),targetClass);
        //设置方法的实现类
//        System.out.println("姓名: " + $1 + " 年龄: " + $2);
        m.setBody("        System.out.println("姓名: " + $1 + " 年龄: " + $2);n");
        targetClass.addMethod(m);
        //生成Class文件
//        targetClass.writeFile("C:\Users\java\src\main\resources");
        //生成Class载入内存
        Class<?> target = targetClass.toClass();
        //实例化
        IHello o = (IHello) target.getDeclaredConstructor().newInstance();
        o.sayHello("weihubeats"18);
    }

    //获取包名
    public String getPackageName(){
        Package aPackage = t.getPackage();
        return aPackage.getName();
    }

    //获取代理对象的名称
    public String getProxyObjectName(){
        return PROXYFREFIX+t.getSimpleName()+PROXYSUFFIX;
    }


}

实际效果

字节码技术 Javassist的学习使用(动态加载接口实现类,AOP原理)
image-20211021110036019

对方法作代理增强

首先写一个需要增强的方法

public class JavassistTest {

    public  String sayHello(String a) {
        System.out.println("原来方法也执行了,参数: " + a);
        return "1223";
    }
}

然后对原有方法进行增强

public class Test {
    public static void  sayHello(String aa) {
        System.out.println("增强" + aa);

    }
    public static void main(String[] args) throws Exception{
        CtClass cc;
        ClassPool pool = ClassPool.getDefault();
        cc = pool.get("com.test.cider.javassist.JavassistTest");
        CtMethod ctMethod = cc.getDeclaredMethod("sayHello");
        ctMethod.insertBefore("com.test.cider.javassist.Test.sayHello($1);");
        cc.toClass();
        JavassistTest javassistTest = new JavassistTest();
        javassistTest.sayHello("haha");
    }

}

执行结果字节码技术 Javassist的学习使用(动态加载接口实现类,AOP原理)


原文始发于微信公众号(小奏技术):字节码技术 Javassist的学习使用(动态加载接口实现类,AOP原理)

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/30251.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!