javassist用法

  在看dubbo源码和mybatis源码的时候发现代理用的是javassist, 简单研究下。可以动态的修改一个类,也可以动态的创建类,也可以实现代理(可以基于继承和接口两种)。

pom如下;

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

1. 基本用法

1. 实现动态的创建类和增加字段和方法

package org.example.javassit;

import javassist.*;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URISyntaxException;

public class Test2 {

    public static void main(String[] args) throws Exception {
        test2();
    }

    // 动态的添加字段信息
    private static void test2() throws Exception {
        //创建类,这是一个单例对象
        ClassPool cp = ClassPool.getDefault();
        //我们需要构建的类
        CtClass ctClass = cp.get("cn.qz.Person");

        //创建字段,指定了字段类型、字段名称、字段所属的类
        CtField field = new CtField(cp.get("java.lang.Integer"), "age", ctClass);
        //指定该字段使用private修饰
        field.setModifiers(Modifier.PRIVATE);
        //设置age字段的getter/setter方法
        ctClass.addMethod(CtNewMethod.setter("getAge", field));
        ctClass.addMethod(CtNewMethod.getter("setAge", field));

        //当前工程的target目录
        final String targetClassPath = Thread.currentThread().getContextClassLoader().getResource("").toURI().getPath();
        //生成.class文件
        ctClass.writeFile(targetClassPath);
    }

    // 创建类信息
    private static void test1() throws CannotCompileException, NotFoundException, URISyntaxException, IOException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        //创建类,这是一个单例对象
        ClassPool pool = ClassPool.getDefault();

        //我们需要构建的类
        CtClass ctClass = pool.makeClass("cn.qz.Person");
        //新增字段
        CtField field$name = new CtField(pool.get("java.lang.String"), "name", ctClass);
        //设置访问级别
        field$name.setModifiers(Modifier.PRIVATE);
        //也可以给个初始值
        ctClass.addField(field$name, CtField.Initializer.constant("qz-default"));
        //生成get/set方法
        ctClass.addMethod(CtNewMethod.setter("setName", field$name));
        ctClass.addMethod(CtNewMethod.getter("getName", field$name));

        //新增构造函数
        //无参构造函数
        CtConstructor cons$noParams = new CtConstructor(new CtClass[]{}, ctClass);
        cons$noParams.setBody("{name = "qz";}");
        ctClass.addConstructor(cons$noParams);
        //有参构造函数
        CtConstructor cons$oneParams = new CtConstructor(new CtClass[]{pool.get("java.lang.String")}, ctClass);
        // $0=this  $1,$2,$3... 代表方法参数
        cons$oneParams.setBody("{$0.name = $1;}");
        ctClass.addConstructor(cons$oneParams);

        // 创建一个名为 print 的方法,无参数,无返回值,输出name值
        CtMethod ctMethod = new CtMethod(CtClass.voidType, "print", new CtClass[]{}, ctClass);
        ctMethod.setModifiers(Modifier.PUBLIC);
        ctMethod.setBody("{System.out.println(name);}");
        ctClass.addMethod(ctMethod);

        //当前工程的target目录
        final String targetClassPath = Thread.currentThread().getContextClassLoader().getResource("").toURI().getPath();
        //生成.class文件
        ctClass.writeFile(targetClassPath);

        // 获取Class 对象的两种方式
        // 1. 直接转
        Class aClass = ctClass.toClass();
        // 2. 调用类加载获取 class 信息
//        Class<?> aClass = ClassLoader.getSystemClassLoader().loadClass("cn.qz.Person");
        Object o = aClass.newInstance();
        Method method = o.getClass().getMethod("print");
        method.invoke(o);
    }

}

2. 通过创建代理类实现增强:

(1) 接口:

package org.example.javassit.proxy;

public interface IHelloService {

    String sayHello(String name);
}

(2) 代理接口

package org.example.javassit.proxy;

public interface IProxy {

    void setProxy(Object t);
}

(3) 测试类:

package org.example.javassit.proxy;

import javassist.*;

import java.util.Arrays;

/**
 * @author 乔利强
 * @date 2021/8/31 10:45
 * @description
 */
public class ProxyTest {

    public static void main(String[] args) throws Exception {
        //创建类,这是一个单例对象
        ClassPool pool = ClassPool.getDefault();
        pool.appendClassPath(Thread.currentThread().getContextClassLoader().getResource("").toURI().getPath());
        //我们需要构建的类
        CtClass ctClass = pool.makeClass("org.example.javassit.proxy.HelloServiceJavassistProxy");
        //这个类实现了哪些接口
        ctClass.setInterfaces(new CtClass[]{
                pool.getCtClass("org.example.javassit.proxy.IHelloService"),
                pool.getCtClass("org.example.javassit.proxy.IProxy")});

        //新增字段
        CtField field$name = new CtField(pool.get("org.example.javassit.proxy.IHelloService"), "helloService", ctClass);
        //设置访问级别
        field$name.setModifiers(Modifier.PRIVATE);
        ctClass.addField(field$name);

        //新增构造函数
        //无参构造函数
        CtConstructor cons$noParams = new CtConstructor(new CtClass[]{}, ctClass);
        cons$noParams.setBody("{}");
        ctClass.addConstructor(cons$noParams);

        //重写sayHello方方法,可以通过构造字符串的形式
        CtMethod m = CtNewMethod.make(buildSayHello(), ctClass);
        ctClass.addMethod(m);

        // 创建一个名为 setProxy 的方法
        CtMethod ctMethod = new CtMethod(CtClass.voidType, "setProxy",
                new CtClass[]{pool.getCtClass("java.lang.Object")}, ctClass);
        ctMethod.setModifiers(Modifier.PUBLIC);
        // // $0=this  $1,$2,$3... 代表方法参数
        ctMethod.setBody("{$0.helloService =   $1;}");
        ctClass.addMethod(ctMethod);

        // 写到本地
        ctClass.writeFile(Thread.currentThread().getContextClassLoader().getResource("").toURI().getPath());

        //获取实例对象
        final Object instance = ctClass.toClass().newInstance();

        System.out.println(Arrays.toString(instance.getClass().getDeclaredMethods()));
        //设置目标方法
        if (instance instanceof IProxy) {
            IProxy proxy = (IProxy) instance;
            proxy.setProxy(new IHelloService() {
                @Override
                public String sayHello(String name) {
                    System.out.println("目标接口实现:name=" + name);
                    return "name:" + name;
                }
            });
        }

        if (instance instanceof IHelloService) {
            IHelloService service = (IHelloService) instance;
            service.sayHello("qz");
        }
    }

    private static String buildSayHello() {
        String methodString = "   public String sayHello(String name) {
"
                + "        System.out.println("静态代理前 ..");
"
                + "        helloService.sayHello(name);
"
                + "        System.out.println("静态代理后 ..");
"
                + "        return name;
"
                + "    }";
        return methodString;
    }

}

结果:

[public java.lang.String org.example.javassit.proxy.HelloServiceJavassistProxy.sayHello(java.lang.String), public void org.example.javassit.proxy.HelloServiceJavassistProxy.setProxy(java.lang.Object)]
静态代理前 ..
目标接口实现:name=qz
静态代理后 ..

(4) 查看生成的类:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.example.javassit.proxy;

public class HelloServiceJavassistProxy implements IHelloService, IProxy {
    private IHelloService helloService;

    public HelloServiceJavassistProxy() {
    }

    public String sayHello(String var1) {
        System.out.println("静态代理前 ..");
        this.helloService.sayHello(var1);
        System.out.println("静态代理后 ..");
        return var1;
    }

    public void setProxy(Object var1) {
        this.helloService = (IHelloService)var1;
    }
}

2. 实现代理

1. 基于继承实现

1. 需要增强的类:

package org.example.javassit.proxy2;

public class UserDao {

    public void saveUser() {
        System.out.println("saveUser ======-");
    }
}

2. ProxyFactory 实现增强(基于继承实现增强)

package org.example.javassit.proxy2;

import javassist.util.proxy.MethodFilter;
import javassist.util.proxy.MethodHandler;
import javassist.util.proxy.ProxyFactory;

import java.lang.reflect.Method;

public class ProxyTest {

    public static void main(String[] args) throws Exception {
        ProxyFactory factory = new ProxyFactory();
        // 设置写出的目录会导出到具体的目录
//        factory.writeDirectory = "D:/proxy";
        // 指定父类,ProxyFactory会动态生成继承该父类的子类
        factory.setSuperclass(UserDao.class);
        // 设定接口,接口可以继承多个,所以用数组
//        factory.setInterfaces(new Class[]{});

        //设置过滤器,判断哪些方法调用需要被拦截
        factory.setFilter(new MethodFilter() {
            @Override
            public boolean isHandled(Method method) {
                if (method.getName().equals("saveUser")) {
                    return true;
                }
                return false;
            }
        });
        //设置拦截处理
        factory.setHandler(new MethodHandler() {
            @Override
            public Object invoke(Object self, Method thisMethod, Method proceed, Object[] args) throws Throwable {
                System.out.println("前置处理");
                Object result = proceed.invoke(self, args);
                System.out.println("执行结果:" + result);
                System.out.println("后置处理");
                return result;
            }
        });

        // 创建 UserDao 代理类,并创建代理对象
        Class<?> c = factory.createClass();
        UserDao javassistTest = (UserDao) c.newInstance();
        // saveUser方法,会被拦截
        javassistTest.saveUser();
        System.out.println(javassistTest.toString());
    }
}

结果:

前置处理
saveUser ======-
执行结果:null
后置处理
org.example.javassit.proxy2.UserDao_$$_jvst840_0@1593948d

3. 反编译查看类

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.example.javassit.proxy2;

import java.io.ObjectStreamException;
import java.lang.reflect.Method;
import javassist.util.proxy.MethodHandler;
import javassist.util.proxy.ProxyObject;
import javassist.util.proxy.RuntimeSupport;

public class UserDao_$$_jvst840_0 extends UserDao implements ProxyObject {
    public static MethodHandler default_interceptor;
    private MethodHandler handler;
    public static byte[] _filter_signature;
    public static final long serialVersionUID;
    private static Method[] _methods_;

    public UserDao_$$_jvst840_0() {
        this.handler = default_interceptor;
        if (default_interceptor == null) {
            this.handler = RuntimeSupport.default_interceptor;
        }

        super();
    }

    public final void _d7saveUser() {
        super.saveUser();
    }

    public final void saveUser() {
        Method[] var1 = _methods_;
        this.handler.invoke(this, var1[14], var1[15], new Object[0]);
    }

    static {
        Method[] var0 = new Method[24];
        Class var1 = Class.forName("org.example.javassit.proxy2.UserDao_$$_jvst840_0");
        RuntimeSupport.find2Methods(var1, "saveUser", "_d7saveUser", 14, "()V", var0);
        _methods_ = var0;
        serialVersionUID = -1L;
    }

    public void setHandler(MethodHandler var1) {
        this.handler = var1;
    }

    public MethodHandler getHandler() {
        return this.handler;
    }

    Object writeReplace() throws ObjectStreamException {
        return RuntimeSupport.makeSerializedProxy(this);
    }
}

2. 基于接口

(1) UserDao 接口

package org.example.javassit.proxy2;

public interface UserDao {

    void saveUser();
}

(2) 测试类:

package org.example.javassit.proxy2;

import javassist.util.proxy.MethodFilter;
import javassist.util.proxy.MethodHandler;
import javassist.util.proxy.ProxyFactory;

import java.lang.reflect.Method;
import java.util.Arrays;

public class ProxyTest {

    public static void main(String[] args) throws Exception {
        ProxyFactory factory = new ProxyFactory();
        // 设置写出的目录会导出到具体的目录
        factory.writeDirectory = "D:/proxy";
        // 设定接口,接口可以继承多个,所以用数组
        factory.setInterfaces(new Class[]{UserDao.class});

        //设置过滤器,判断哪些方法调用需要被拦截
        factory.setFilter(new MethodFilter() {
            @Override
            public boolean isHandled(Method method) {
                if (method.getName().equals("saveUser")) {
                    return true;
                }
                return false;
            }
        });
        //设置拦截处理
        factory.setHandler(new MethodHandler() {
            @Override
            public Object invoke(Object self, Method thisMethod, Method proceed, Object[] args) throws Throwable {
                System.out.println("前置处理");
                System.out.println("self: " + self + "	thisMethod: " + thisMethod + "	proceed: " + proceed + "	args: " + Arrays.toString(args));
                System.out.println("后置处理");
                return "";
            }
        });

        // 创建 UserDao 代理类,并创建代理对象
        Class<?> c = factory.createClass();
        UserDao javassistTest = (UserDao) c.newInstance();
        // saveUser方法,会被拦截
        javassistTest.saveUser();
        System.out.println(javassistTest.toString());
    }
}

(3) 结果;

前置处理
self: org.example.javassit.proxy2.UserDao_$$_jvst840_0@1b604f19    thisMethod: public abstract void org.example.javassit.proxy2.UserDao.saveUser()    proceed: null    args: []
后置处理
org.example.javassit.proxy2.UserDao_$$_jvst840_0@1b604f19

(4) 反编译查看类

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.example.javassit.proxy2;

import java.io.ObjectStreamException;
import java.lang.reflect.Method;
import javassist.util.proxy.MethodHandler;
import javassist.util.proxy.ProxyObject;
import javassist.util.proxy.RuntimeSupport;

public class UserDao_$$_jvst840_0 implements UserDao, ProxyObject {
    public static MethodHandler default_interceptor;
    private MethodHandler handler;
    public static byte[] _filter_signature;
    public static final long serialVersionUID;
    private static Method[] _methods_;

    public UserDao_$$_jvst840_0() {
        this.handler = default_interceptor;
        if (default_interceptor == null) {
            this.handler = RuntimeSupport.default_interceptor;
        }

        super();
    }

    public final void saveUser() {
        Method[] var1 = _methods_;
        this.handler.invoke(this, var1[14], var1[15], new Object[0]);
    }

    static {
        Method[] var0 = new Method[24];
        Class var1 = Class.forName("org.example.javassit.proxy2.UserDao_$$_jvst840_0");
        RuntimeSupport.find2Methods(var1, "saveUser", (String)null, 14, "()V", var0);
        _methods_ = var0;
        serialVersionUID = -1L;
    }

    public void setHandler(MethodHandler var1) {
        this.handler = var1;
    }

    public MethodHandler getHandler() {
        return this.handler;
    }

    Object writeReplace() throws ObjectStreamException {
        return RuntimeSupport.makeSerializedProxy(this);
    }
}
【当你用心写完每一篇博客之后,你会发现它比你用代码实现功能更有成就感!】
原文地址:https://www.cnblogs.com/qlqwjy/p/15216085.html