• Java JDK动态代理


    java中动态代理主要有JDK和CGLIB两种方式。

    区别主要是jdk是代理接口,而cglib是代理类。

    jdk的动态代理调用了Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) 方法。

    通过该方法生成字节码,动态的创建了一个代理类,interfaces参数是该动态类所继承的所有接口,而继承InvocationHandler 接口的类则是实现在调用代理接口方法前后的具体逻辑,下边是具体的实现:

    public class Test {
      static interface Subject{
        void sayHi();
        void sayHello();
      }
       
      static class SubjectImpl implements Subject{
     
        @Override
        public void sayHi() {
          System.out.println("hi");
        }
     
        @Override
        public void sayHello() {
          System.out.println("hello");
        }
      }
       
      static class ProxyInvocationHandler implements InvocationHandler{
        private Subject target;
        public ProxyInvocationHandler(Subject target) {
          this.target=target;
        }
     
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
          System.out.print("say:");
          return method.invoke(target, args);
        }
         
      }
       
      public static void main(String[] args) {
        Subject subject=new SubjectImpl();
        Subject subjectProxy=(Subject) Proxy.newProxyInstance(subject.getClass().getClassLoader(), subject.getClass().getInterfaces(), new ProxyInvocationHandler(subject));
        subjectProxy.sayHi();
        subjectProxy.sayHello();
         
      }
    }
    /**   
     *    
     * JDK动态代理类   
     *    
     *   
     */    
    public class JDKProxy implements InvocationHandler {    
        
        private Object targetObject;//需要代理的目标对象    
        
        public Object newProxy(Object targetObject) {//将目标对象传入进行代理    
            this.targetObject = targetObject;     
            return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),    
                    targetObject.getClass().getInterfaces(), this);//返回代理对象    
        }    
        
        public Object invoke(Object proxy, Method method, Object[] args)//invoke方法    
                throws Throwable {    
            before();
            Object ret = null;      // 设置方法的返回值    
            ret  = method.invoke(targetObject, args);       //invoke调用需要代理的方法
            after();
            return ret;    
        }    
        
        private void before() {//方法执行前   
            System.out.println("方法执行前 !");    
        }    
        private void after() {//方法执行后    
            System.out.println("方法执行后");    
        }    
    }  

    newProxyInstance方法执行了以下几种操作。

    1.生成一个实现了参数interfaces里所有接口且继承了Proxy的代理类的字节码,然后用参数里的classLoader加载这个代理类。

    2.使用代理类父类的构造函数 Proxy(InvocationHandler h)来创造一个代理类的实例,将我们自定义的InvocationHandler的子类传入。

    3.返回这个代理类实例。

    在main方法中加入System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true"),这样就会把生成的代理类Class文件保存在本地磁盘上,然后再反编译可以得到代理类的源码:

    public final class $Proxy0 extends Proxy
     implements Test.Subject
    {
     private static Method m4;
     private static Method m1;
     private static Method m3;
     private static Method m0;
     private static Method m2;
      
     static
     {
       try {
         m4 = Class.forName("Test$Subject").getMethod("sayHello", new Class[0]);
         m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
         m3 = Class.forName("Test$Subject").getMethod("sayHi", new Class[0]);
         m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
         m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
      } catch (Exception e) {
        throw new RuntimeException(e);
      }
     }
     
     public $Proxy0(InvocationHandler paramInvocationHandler)
     {
      super(paramInvocationHandler);
     }
     
     public final void sayHello()
     {
      try
      {
       this.h.invoke(this, m4, null);
       return;
      }
      catch (RuntimeException localRuntimeException)
      {
       throw localRuntimeException;
      }
      catch (Throwable localThrowable)
      {
        throw new UndeclaredThrowableException(localThrowable);
      }
     }
     
     public final boolean equals(Object paramObject)
     {
      try
      {
       return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
      }
      catch (RuntimeException localRuntimeException)
      {
       throw localRuntimeException;
      }
      catch (Throwable localThrowable)
      {
        throw new UndeclaredThrowableException(localThrowable);
      }
     }
     
     public final void sayHi()
     {
      try
      {
       this.h.invoke(this, m3, null);
       return;
      }
      catch (RuntimeException localRuntimeException)
      {
       throw localRuntimeException;
      }
      catch (Throwable localThrowable)
      {
        throw new UndeclaredThrowableException(localThrowable);
      }
     }
     
     public final int hashCode()
     {
      try
      {
       return ((Integer)this.h.invoke(this, m0, null)).intValue();
      }
      catch (RuntimeException localRuntimeException)
      {
       throw localRuntimeException;
      }
      catch (Throwable localThrowable)
      {
        throw new UndeclaredThrowableException(localThrowable);
      }
     }
     
     public final String toString()
     {
      try
      {
       return (String)this.h.invoke(this, m2, null);
      }
      catch (RuntimeException localRuntimeException)
      {
       throw localRuntimeException;
      }
      catch (Throwable localThrowable)
      {
        throw new UndeclaredThrowableException(localThrowable);
      }
     }
    }

    我们可以看到代理类内部实现比较简单,在调用每个代理类每个方法的时候,都用反射去调newProxyInstanceh方法中传来的h的invoke方法(也就是我们自定义的InvocationHandler的子类中重写的invoke方法),用参数传递了代理类实例、接口方法、调用参数列表,这样我们在重写的invoke方法中就可以实现对所有方法的统一包装了。

  • 相关阅读:
    TypeScript & JSDoc All In One
    k8s & Docker All In One
    How to custom your own Node.js Docker Image All In One
    rollup & TypeScript & tslib All In One
    Linux file system All In One
    how to use npm delete one history version package All In One
    How to use Web Components in React or Vue All In One
    看了这篇使用 dist 发布 npm 包的文章,我整个人都栓Q 了
    yarn 1.x & yarn 2.x All In One
    python中删除字符串中的指定字符
  • 原文地址:https://www.cnblogs.com/akaneblog/p/6720513.html
Copyright © 2020-2023  润新知