动态代理

一、代理模式与静态代理

代理模式

给某一个对象提供一个代理,并由代理对象控制对原对象的引用。

代理模式包含如下角色:
Subject:抽象主题角色
Proxy:代理主题角色
RealSubject:真实主题角色

静态代理

代理类在程序运行前就已经存在。通常情况下,静态代理中的代理类和委托类会实现同一接口或是派生自相同的父类。

静态代理例子:

public class ProxyDemo {
    
    public static void main(String[] args) {
        consumer(new TargetObject());
        consumer(new SimpleProxy(new TargetObject()));
    }

    public static void consumer(Interface inter) {
        inter.doSomething();
        inter.somethingElse("Tomy");
    }

    interface Interface { //抽象主题角色
        void doSomething();
        void somethingElse(String arg);
    }

    static class TargetObject implements Interface { //真实主题角色
        public void doSomething() {
            System.out.println("target doSomething");
        }
        public void somethingElse(String arg) {
            System.out.println("target somethingElse " + arg);
        }
    }

    static class SimpleProxy implements Interface { //代理主题角色
        private Interface target;
        public SimpleProxy(Interface target) {
            this.target = target;
        }
        public void doSomething() {
            System.out.println("SimpleProxy doSomething");
            target.doSomething();
        }
        public void somethingElse(String arg) {
            System.out.println("SimpleProxy somethingElse " + arg);
            target.somethingElse(arg);
        }
    }
}

//输出
target doSomething
target somethingElse Tomy
SimpleProxy doSomething
target doSomething
SimpleProxy somethingElse Tomy
target somethingElse Tomy

二、动态代理

动态代理概念

代理类在程序运行时创建的方式被成为动态代理。也就是说,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的"指示"动态生成的。相比于静态代理,动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类的函数。

动态代理相关类和接口

java.lang.reflect.Proxy:
Java动态代理机制的主类,提供了一组静态方法来为一组接口动态地生成代理类及其实例。

Proxy的静态方法:

//方法1: 该方法用于获取指定动态代理对象所关联的调用处理器
static InvocationHandler getInvocationHandler(Object proxy)
 
//方法2:该方法用于获取关联于指定类装载器和一组接口的动态代理对象
static Class getProxyClass(ClassLoader loader, Class[] interfaces)
 
//方法3:该方法用于判断指定类对象是否是一个动态代理类
static boolean isProxyClass(Class cl)
 
//方法4:该方法用于为指定类装载器、一组接口及调用处理器生成动态代理对象
static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)

java.lang.reflect.InvocationHandler:
调用处理器接口,它自定义了一个invoke方法,用于集中处理在动态代理对象上的方法调用,通常在该方法中实现对委托类的代理访问。每次生成动态代理对象时都需要指定一个实现了该接口的调用处理器对象。

InvocationHandler的核心方法:

//该方法负责集中处理动态代理类上的所有方法调用。
//第一个参数是代理对象,第二个参数是被调用的方法对象,第三个方法是调用参数。
//调用处理器根据这三个参数进行预处理或分派到委托类实例上反射执行。
Object invoke(Object proxy, Method method, Object[] args)

java.lang.ClassLoader:
类装载器,负责将类的字节码装载到Java虚拟机(JVM)中并为其定义类对象,然后该类才能被使用。Proxy静态方法生成代理类同样需要通过类装载器来进行装载才能使用,它与普通类的唯一区别就是其字节码是由JVM在运行时动态生成的而非预存在于任何一个.class文件中。每次生成动态代理对象时都需要指定一个类装载器对象。

动态代理实现方法

方法一:

  • 步骤1:通过实现InvocationHandler接口创建自己的调用处理器;
  • 步骤2:通过为Proxy类指定ClassLoader对象和一组interface来创建动态代理类;
  • 步骤3:通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;
  • 步骤4:通过构造函数创建动态代理对象,构造时调用处理器对象作为参数被传入。
//InvocationHandlerImpl实现了InvocationHandler接口,并能实现方法调用从代理类到委托类的分派转发
//其内部通常包含指向委托类实例的引用,用于真正执行分派转发过来的方法调用
InvocationHandler handler = new InvocationHandlerImpl(...); 
 
//通过Proxy为包括Interface接口在内的一组接口动态创建代理类的类对象
Class clazz = Proxy.getProxyClass(classLoader, new Class[] { Interface.class, ... }); 
 
//通过反射从生成的类对象获得构造函数对象
Constructor constructor = clazz.getConstructor(new Class[] { InvocationHandler.class }); 
 
//通过构造函数对象创建动态代理对象
Interface Proxy = (Interface)constructor.newInstance(new Object[] { handler });

方法二:

  • 还有更简便的动态代理实现方法,Proxy的静态方法newProxyInstance已经为我们封装了步骤2到步骤4的过程。
//InvocationHandlerImpl实现了InvocationHandler接口,并能实现方法调用从代理类到委托类的分派转发
InvocationHandler handler = new InvocationHandlerImpl(...); 
 
//通过Proxy直接创建动态代理对象
Interface proxy = (Interface)Proxy.newProxyInstance(classLoader, new Class[] { Interface.class }, handler );

动态代理例子:

//测试代码
public class DynamicProxy {
    
    public static void main(String[] args) {
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); //保存生成的动态代理.class文件
        
        TargetObject target = new TargetObject();
        consumer(target);
        //Insert a proxy and call again:
        Interface proxy = (Interface)Proxy.newProxyInstance(
                Interface.class.getClassLoader(),
                new Class[]{ Interface.class },
                new DynamicProxyHandler(target));
        consumer(proxy);
    }

    public static  void consumer(Interface iface) {
        iface.doSomething();
        iface.somethingElse("Tomy");
    }


    interface Interface { //抽象主题角色
        void doSomething();
        void somethingElse(String arg);
    }

    static class TargetObject implements Interface { //真实主题角色
        public void doSomething() {
            System.out.println("target doSomething");
        }
        public void somethingElse(String arg) {
            System.out.println("target somethingElse " + arg);
        }
    }

    static class DynamicProxyHandler implements InvocationHandler { //动态代理处理器
        private Object target;
        public DynamicProxyHandler(Object target) {
            this.target = target;
        }
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("InvocationHandler: " + proxy.getClass() +
                    ", method: " + method + ", args: " + args);
            if(args != null) {
                for(Object arg : args) {
                    System.out.println("InvocationHandler args: " + arg);
                }
            }
            return method.invoke(target, args);
        }
    }

}

//输出log
target doSomething
target somethingElse Tomy
InvocationHandler: class com.tomorrow.test.$Proxy0, method: public abstract void com.tomorrow.test.DynamicProxy$Interface.doSomething(), args: null
target doSomething
InvocationHandler: class com.tomorrow.test.$Proxy0, method: public abstract void com.tomorrow.test.DynamicProxy$Interface.somethingElse(java.lang.String), args: [Ljava.lang.Object;@16d3586
InvocationHandler args: Tomy
target somethingElse Tomy

生成的动态代理类:

package com.tomorrow.test;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

final class $Proxy0 extends Proxy
  implements DynamicProxy.Interface
{
  private static Method m1;
  private static Method m3;
  private static Method m2;
  private static Method m4;
  private static Method m0;

  public $Proxy0(InvocationHandler paramInvocationHandler)
    throws 
  {
    super(paramInvocationHandler);
  }

  public final boolean equals(Object paramObject)
    throws 
  {
    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 doSomething()
    throws 
  {
    try
    {
      this.h.invoke(this, m3, null);
      return;
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final String toString()
    throws 
  {
    try
    {
      return (String)this.h.invoke(this, m2, null);
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final void somethingElse(String paramString)
    throws 
  {
    try
    {
      this.h.invoke(this, m4, new Object[] { paramString });
      return;
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final int hashCode()
    throws 
  {
    try
    {
      return ((Integer)this.h.invoke(this, m0, null)).intValue();
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  static
  {
    try
    {
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
      m3 = Class.forName("com.tomorrow.test.DynamicProxy$Interface").getMethod("doSomething", new Class[0]);
      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
      m4 = Class.forName("com.tomorrow.test.DynamicProxy$Interface").getMethod("somethingElse", new Class[] { Class.forName("java.lang.String") });
      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
      return;
    }
    catch (NoSuchMethodException localNoSuchMethodException)
    {
      throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
    }
    catch (ClassNotFoundException localClassNotFoundException)
    {
      throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
    }
  }
}

动态代理类的继承关系

动态代理类的继承关系

动态代理对象的特点

  • 每个动态代理对象都会关联一个调用处理器对象,可以通过Proxy提供的静态方法getInvocationHandler获得动态代理对象关联的调用处理器对象。
  • 在动态代理对象上调用其代理的接口中所声明的方法时,这些方法最终都会执行调用处理器的invoke方法。
  • 动态代理类的根类java.lang.Object中有三个方法同样会被分派到调用处理器的invoke方法中执行,它们是hashCode、equals和toString。
    可能的原因有:
    (1)因为这些方法为public且非final类型,能够被代理类覆写;
    (2)因为这些方法往往呈现出一个类的某种特征属性,具有一定的区分度,所以为了保证代理类与委托类对外的一致性,这三个方法也应该被分派到委托类执行。

动态代理一组接口

当动态代理的一组接口有重复声明的方法且该方法被调用时,动态代理类总是从排在最前面的接口中获取方法对象并分派给调用处理器,而无论动态代理对象是否正在以该接口(或继承于该接口的某个子接口)的形式被外部引用,因为在动态代理类内部无法区分其当前的被引用类型。

例子:

//测试代码
public class DynamicProxy {
    
    public static void main(String[] args) {
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); //保存生成的动态代理.class文件
        
        try {
            TargetObject object = new TargetObject();
            InvocationHandler handler = new DynamicProxyHandler(object);
            Class<?> clazz = Proxy.getProxyClass(
                    TargetObject.class.getClassLoader(),
                    new Class[] {InterfaceB.class, InterfaceA.class}); //注意:这里先写接口B,然后写接口A
            Constructor<?> constructor = clazz.getConstructor(new Class[] {InvocationHandler.class});
            InterfaceA proxy_a = (InterfaceA) constructor.newInstance(new Object[] {handler});
            InterfaceB proxy_b = (InterfaceB) constructor.newInstance(new Object[] {handler});
            
            proxy_a.doSomething();
            proxy_a.doA();
            proxy_b.doSomething();
            proxy_b.doB();
        } catch(Exception e) {
            e.printStackTrace();
        }

    }

    public interface InterfaceA {
        void doSomething();
        void doA();
    }

    public interface InterfaceB {
        void doSomething();
        void doB();
    }

    static class TargetObject implements InterfaceA, InterfaceB {
        public void doSomething() {
            System.out.println("target doSomething");
        }
        public void doA() {
            System.out.println("target doA");
        }
        public void doB() {
            System.out.println("target doB");
        }
    }

    static class DynamicProxyHandler implements InvocationHandler { //动态代理处理器
        private Object target;
        public DynamicProxyHandler(Object target) {
            this.target = target;
        }
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("InvocationHandler: " + proxy.getClass() +
                    ", method: " + method + ", args: " + args);
            if(args != null) {
                for(Object arg : args) {
                    System.out.println("InvocationHandler args: " + arg);
                }
            }
            return method.invoke(target, args);
        }
    }

}

//输出log
InvocationHandler: class com.sun.proxy.$Proxy0, method: public abstract void com.tomorrow.test.DynamicProxy$InterfaceB.doSomething(), args: null
==> 虽然动态代理对象proxy_a正以接口InterfaceA的形式被外部引用,但获取的方法对象是在getProxyClass方法排前面的InterfaceB中的。
target doSomething
InvocationHandler: class com.sun.proxy.$Proxy0, method: public abstract void com.tomorrow.test.DynamicProxy$InterfaceA.doA(), args: null
target doA
InvocationHandler: class com.sun.proxy.$Proxy0, method: public abstract void com.tomorrow.test.DynamicProxy$InterfaceB.doSomething(), args: null
target doSomething
InvocationHandler: class com.sun.proxy.$Proxy0, method: public abstract void com.tomorrow.test.DynamicProxy$InterfaceB.doB(), args: null
target doB

生成的动态代理类:

package com.sun.proxy;

import com.tomorrow.test.DynamicProxy.InterfaceA;
import com.tomorrow.test.DynamicProxy.InterfaceB;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy
  implements DynamicProxy.InterfaceB, DynamicProxy.InterfaceA
{
  private static Method m1;
  private static Method m3;
  private static Method m5;
  private static Method m2;
  private static Method m4;
  private static Method m0;

  public $Proxy0(InvocationHandler paramInvocationHandler)
    throws 
  {
    super(paramInvocationHandler);
  }

  public final boolean equals(Object paramObject)
    throws 
  {
    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 doSomething()
    throws 
  {
    try
    {
      this.h.invoke(this, m3, null);
      return;
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final void doA()
    throws 
  {
    try
    {
      this.h.invoke(this, m5, null);
      return;
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final String toString()
    throws 
  {
    try
    {
      return (String)this.h.invoke(this, m2, null);
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final void doB()
    throws 
  {
    try
    {
      this.h.invoke(this, m4, null);
      return;
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final int hashCode()
    throws 
  {
    try
    {
      return ((Integer)this.h.invoke(this, m0, null)).intValue();
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  static
  {
    try
    {
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
      m3 = Class.forName("com.tomorrow.test.DynamicProxy$InterfaceB").getMethod("doSomething", new Class[0]); //注意:获取的是排在前面的InterfaceB的doSomething方法
      m5 = Class.forName("com.tomorrow.test.DynamicProxy$InterfaceA").getMethod("doA", new Class[0]);
      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
      m4 = Class.forName("com.tomorrow.test.DynamicProxy$InterfaceB").getMethod("doB", new Class[0]);
      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
      return;
    }
    catch (NoSuchMethodException localNoSuchMethodException)
    {
      throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
    }
    catch (ClassNotFoundException localClassNotFoundException)
    {
      throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
    }
  }
}

被动态代理的一组接口的特点

  • 要注意不能有重复的接口,以避免动态代理类代码生成时出现编译错误。
  • 这些接口对于类装载器必须可见,否则类装载器无法链接它们,将会导致动态代理类定义失败。
  • 需要被代理的所有非public的接口必须在同一个包中,否则动态代理类代码生成也会失败。
  • 接口的数目不能超过65535,这是JVM设定的限制。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,366评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,521评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,689评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,925评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,942评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,727评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,447评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,349评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,820评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,990评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,127评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,812评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,471评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,017评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,142评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,388评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,066评论 2 355

推荐阅读更多精彩内容

  • 1、代理概念 为某个对象提供一个代理,以控制对这个对象的访问。 代理类和委托类有共同的父类或父接口,这样在任何使用...
    孔垂云阅读 7,672评论 4 54
  • 一、代理概念 为某个对象提供一个代理,以控制对这个对象的访问。 代理类和委托类有共同的父类或父接口,这样在任何使用...
    wyatt_plus阅读 791评论 0 5
  • Java动态代理 引言 最近在看AOP代码,其中利用到了Java动态代理机制来实现AOP织入。所以好好地把Java...
    草捏子阅读 1,537评论 0 18
  • 一、基本概念 1.什么是代理? 在阐述JDK动态代理之前,我们很有必要先来弄明白代理的概念。代理这个词本身并不是计...
    小李弹花阅读 16,442评论 2 40
  • 11/21 徐芳香 温州【每日一结构】结构思考力21天思维改善训练营 G:【认知不协调对我们的很多决策与判断都具有...
    静静的叶子阅读 338评论 0 0