• 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);
        }
    }
    【当你用心写完每一篇博客之后,你会发现它比你用代码实现功能更有成就感!】
  • 相关阅读:
    接口(interface)的使用于注意事项
    构造方法
    MySQL数据库软件
    final与static的作用
    区分like和in
    list,set,map各有什么异同?
    简述 Overload 与 Override
    标识符的基础知识
    类的继承关系
    MySQL常见索引失效
  • 原文地址:https://www.cnblogs.com/qlqwjy/p/15216085.html
Copyright © 2020-2023  润新知