zoukankan      html  css  js  c++  java
  • Java JDK动态代理

    jdk 动态代理的主要三个部分

    1. Proxy 类.

    2. ClassLoader 

    3.InvocationHandler 

    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方法中就可以实现对所有方法的统一包装了。

  • 相关阅读:
    Ajax实现文件下载
    jquery easyui 插件开发
    Chrome谷歌浏览器首页被改为Hao123导航怎么办|附各类解决方法【转】
    查看mysql版本的四种方法
    IntelliJ IDEA 快捷键大全
    Java中判断字符串是否为数字的五种方法
    比数据分析更要命的是:数据质量
    Python绘制六种可视化图表详解,三维图最炫酷!你觉得呢?
    大数据需要好设计
    Python模块学习filecmp文件比较
  • 原文地址:https://www.cnblogs.com/zeenzhou/p/11103960.html
Copyright © 2011-2022 走看看