• Javassist实现动态代理


    介绍

    Javassist 也是一个字节码框架,和其他字节码框架不同的是,它提供了两种层级的API,源层级和字节码层级,源层级不需要对字节码规则了解太多就可以操作。Hibernate的懒加载就使用到了Javassist。官网

    使用Javassist提供的动态代理接口实现

    maven依赖

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

    代理接口

    /**
     * 可以唱歌的
     */
    public interface Singable {
      /**
       * 唱歌
       */
      void sing();
    }
    

    被代理类

    /**
     * 歌手
     */
    public class Singer implements Singable {
      @Override
      public void sing() {
        System.out.println("I am singing...");
      }
    }
    

    创建动态代理

    public class Client {
    
      public static void main(String[] args) throws Exception {
        Singable proxy = createJavassistDynamicProxy();
        proxy.sing();
      }
    
      private static Singable createJavassistDynamicProxy()
          throws Exception {
        ProxyFactory proxyFactory = new ProxyFactory();
    // 设置实现的接口
        proxyFactory.setInterfaces(new Class[]{Singable.class});
        Class<?> proxyClass = proxyFactory.createClass();
        Singable javassistProxy = (Singable) proxyClass.getDeclaredConstructor().newInstance();
        ((ProxyObject) javassistProxy).setHandler(new JavassistInterceptor(new Singer()));
        return javassistProxy;
      }
    
      private static class JavassistInterceptor implements MethodHandler {
    
        // 被代理对象
        private Object delegate;
    
        private JavassistInterceptor(Object delegate) {
          this.delegate = delegate;
        }
    
        /**
         * @param self 创建的代理对象
         * @param m 被代理方法
         * @param proceed 如果代理接口,此参数为null,如果代理类,此参数为父类的方法
         * @param args 方法参数
         */
        public Object invoke(Object self, Method m, Method proceed,
            Object[] args) throws Throwable {
          System.out.println("javassist proxy before sing");
          Object ret = m.invoke(delegate, args);
          System.out.println("javassist proxy after sing");
          return ret;
        }
      }
    }
    

    和JDK的动态代理创建方式类似,但Javassist也可以代理类。

    public class Client {
    
      public static void main(String[] args) throws Exception {
        Singable proxy = createJavassistDynamicProxy();
        proxy.sing();
      }
    
      private static Singable createJavassistDynamicProxy()
          throws Exception {
        ProxyFactory proxyFactory = new ProxyFactory();
    // 设置父类
        proxyFactory.setSuperclass(Singer.class);
        Class<?> proxyClass = proxyFactory.createClass();
        Singable javassistProxy = (Singable) proxyClass.getDeclaredConstructor().newInstance();
        ((ProxyObject) javassistProxy).setHandler(new JavassistInterceptor());
        return javassistProxy;
      }
    
      private static class JavassistInterceptor implements MethodHandler {
    
        /**
         * @param self 创建的代理对象
         * @param m 被代理方法
         * @param proceed 如果代理接口,此参数为null,如果代理类,此参数为父类的方法
         * @param args 方法参数
         */
        public Object invoke(Object self, Method m, Method proceed,
            Object[] args) throws Throwable {
          System.out.println("javassist proxy before sing");
    // 调用父类的sing方法
          Object ret = proceed.invoke(self, args);
          System.out.println("javassist proxy after sing");
          return ret;
        }
      }
    }
    

    Javassist创建的代理类反编译之后为

    public class Singer_$$_jvst3e4_0 extends Singer
        implements ProxyObject
    {
    
        public final Object _d0clone()
            throws CloneNotSupportedException
        {
            return super.clone();
        }
    
        protected final Object clone()
            throws CloneNotSupportedException
        {
            Method amethod[] = _methods_;
            return (Object)handler.invoke(this, amethod[0], amethod[1], new Object[0]);
        }
    
        public final boolean _d1equals(Object obj)
        {
            return super.equals(obj);
        }
    
        public final boolean equals(Object obj)
        {
            Method amethod[] = _methods_;
            return ((Boolean)handler.invoke(this, amethod[2], amethod[3], new Object[] {
                obj
            })).booleanValue();
        }
    
        public final void _d2finalize()
            throws Throwable
        {
            super.finalize();
        }
    
        protected final void finalize()
            throws Throwable
        {
            Method amethod[] = _methods_;
            handler.invoke(this, amethod[4], amethod[5], new Object[0]);
        }
    
        public final int _d4hashCode()
        {
            return super.hashCode();
        }
    
        public final int hashCode()
        {
            Method amethod[] = _methods_;
            return ((Integer)handler.invoke(this, amethod[8], amethod[9], new Object[0])).intValue();
        }
    
    // 调用父类方法
        public final void _d7sing()
        {
            super.sing();
        }
    
        public final void sing()
        {
            Method amethod[] = _methods_;
    // amethod[14]为sing方法,amethod[15]为_d7sing方法,可以看静态代码块的初始化
            handler.invoke(this, amethod[14], amethod[15], new Object[0]);
        }
    
        public final String _d8toString()
        {
            return super.toString();
        }
    
        public final String toString()
        {
            Method amethod[] = _methods_;
            return (String)handler.invoke(this, amethod[16], amethod[17], new Object[0]);
        }
    
        public void setHandler(MethodHandler methodhandler)
        {
            handler = methodhandler;
        }
    
        public MethodHandler getHandler()
        {
            return handler;
        }
    
        Object writeReplace()
            throws ObjectStreamException
        {
            return RuntimeSupport.makeSerializedProxy(this);
        }
    
        private MethodHandler handler;
        public static byte _filter_signature[];
        public static final long serialVersionUID = -1L;
        private static Method _methods_[];
    
        static 
            throws ClassNotFoundException
        {
            Method amethod[] = new Method[24];
            Class class1 = Class.forName("com.imooc.sourcecode.java.dynamicproxy.javassist.test3.Singer_$$_jvst3e4_0");
            RuntimeSupport.find2Methods(class1, "clone", "_d0clone", 0, "()Ljava/lang/Object;", amethod);
            RuntimeSupport.find2Methods(class1, "equals", "_d1equals", 2, "(Ljava/lang/Object;)Z", amethod);
            RuntimeSupport.find2Methods(class1, "finalize", "_d2finalize", 4, "()V", amethod);
            RuntimeSupport.find2Methods(class1, "hashCode", "_d4hashCode", 8, "()I", amethod);
    // 下标14赋值为sing方法,15赋值为_d7sing方法
            RuntimeSupport.find2Methods(class1, "sing", "_d7sing", 14, "()V", amethod);
            RuntimeSupport.find2Methods(class1, "toString", "_d8toString", 16, "()Ljava/lang/String;", amethod);
            _methods_ = amethod;
        }
    
        public Singer_$$_jvst3e4_0()
        {
            handler = RuntimeSupport.default_interceptor;
            super();
        }
    }
    

    看一下RuntimeSupport的find2Methods方法

    /**
         * Finds two methods specified by the parameters and stores them
         * into the given array.
         *
         * @throws RuntimeException     if the methods are not found.
         * @see javassist.util.proxy.ProxyFactory
         */
        public static void find2Methods(Class<?> clazz, String superMethod,
                                        String thisMethod, int index,
                                        String desc, java.lang.reflect.Method[] methods)
        {
            methods[index + 1] = thisMethod == null ? null
                                                    : findMethod(clazz, thisMethod, desc);
            methods[index] = findSuperClassMethod(clazz, superMethod, desc);
        }
    

    使用Javassist提供的字节码API实现

    代理接口和被代理类同上

    public class Client {
    
      public static void main(String[] args) throws Exception {
        Singable proxy = createJavassistBytecodeDynamicProxy(new Singer());
        proxy.sing();
      }
    
      private static Singable createJavassistBytecodeDynamicProxy(Singable delegate) throws Exception {
        ClassPool mPool = new ClassPool(true);
        CtClass mCtc = mPool.makeClass(Singable.class.getName() + "JavaassistProxy");
        mCtc.addInterface(mPool.get(Singable.class.getName()));
        mCtc.addConstructor(CtNewConstructor.defaultConstructor(mCtc));
        mCtc.addField(CtField.make("public " + Singable.class.getName() + " delegate;", mCtc));
        String src = "public void sing() { "
            + "System.out.println("javassist bytecode proxy before sing");"
            + "delegate.sing();"
            + "System.out.println("javassist bytecode proxy after sing"); "
            + "}";
        mCtc.addMethod(CtNewMethod.make(src, mCtc));
        Class<?> pc = mCtc.toClass();
        Singable bytecodeProxy = (Singable) pc.getDeclaredConstructor().newInstance();
        Field filed = bytecodeProxy.getClass().getField("delegate");
        filed.set(bytecodeProxy, delegate);
        return bytecodeProxy;
      }
    
    }
    
    

    Javassist可以直接拼接java源码生成字节码,这是比ASM易用的地方,但也会造成一定的性能损失。

    生成的代理类反编译为

    public class SingableJavaassistProxy
        implements Singable
    {
    
        public SingableJavaassistProxy()
        {
        }
    
        public void sing()
        {
            System.out.println("javassist bytecode proxy before sing");
            _flddelegate.sing();
            System.out.println("javassist bytecode proxy after sing");
        }
    
        public Singable _flddelegate;
    }
    

    可以看到使用字节码生成的类相比代理工厂(JDK,CGLIB,Javassist的ProxyFactory)生成的类要小很多,所以速度也会更快。

  • 相关阅读:
    Dubbo (开源分布式服务框架)
    Springboot+Jpa的使用
    springboot(十二):springboot如何测试打包部署
    Java NIO:IO与NIO的区别
    eclipse下搭建shell脚本编辑器--安装开发shell的eclipse插件shelled
    spring boot
    【微服务架构】SpringCloud组件和概念介绍
    java正则表达式
    java的反射机制原理
    nginx代理服务器
  • 原文地址:https://www.cnblogs.com/strongmore/p/13548235.html
Copyright © 2020-2023  润新知