代理模式(静态、动态、retrofit实例)


某些情况下,我们不希望或是不能直接访问对象 A,而是通过访问一个中介对象 B,由 B 去访问 A 达成目的,这种方式我们就称为代理。
对象 A 所属类称为委托类 / 被代理类,对象 B 所属类称为代理类。

代理是一种软件设计模式。

代理的特点:

面向切面编程(AOP),我们能在一个切点之前执行一些操作,在一个切点之后执行一些操作,这个切点就是一个个方法。

  • 代理类与委托类实现了同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。
  • 每个代理类都与一个委托类相关联,但是代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。
    在这里插入图片描述

代理的优点:

  • 隐藏委托类的实现

  • 解耦,不改变委托类代码情况下做一些额外处理,比如添加初始判断及其他公共操作

根据程序运行前代理类是否已经存在,可以将代理分为静态代理和动态代理。

1、静态代理

由程序员创建或特定工具自动生成源代码,也就是在编译时就已经将接口,被代理类(委托类),代理类等确定下来。在程序运行之前,代理类的.class文件就已经生成。

在这里插入图片描述
公共接口(Subject),具体实现类(被代理类 / 委托类)(RealSubject),代理类(ProxySubject),代理类持有委托类类的实例,代为执行具体类实例方法。

//公共接口
public interface Subject {
    void doSomthing(String sth);
}

//委托类,实现公共接口,是实际干活的
public class RealSubject implements Subject {
    @Override
    public void doSomthing(String sth) {
        System.out.println("委托类doSomthing : " + sth);
    }
}

//代理类,实现公共接口,持有委托类的对象引用,间接调用委托类的相关方法
public class ProxySubject implements Subject {
    private RealSubject realSubject;

    public ProxySubject(RealSubject realSubject) {
        System.out.println("代理类实例化,代理类持有委托类的对象引用");
        this.realSubject = realSubject;
    }

    @Override
    public void doSomthing(String sth) {
    	//代理类可以做一些额外的操作
        System.out.println("代理类间接调用委托类的相关方法");
        realSubject.doSomthing(sth);
    }
}

//测试类
public class StaticProxyTest {

    public static void main(String[] args) {
        //创建委托类,真正干活的
        RealSubject realSubject = new RealSubject();
        //创建代理类,代理类持有委托类的对象引用
        ProxySubject proxySubject = new ProxySubject(realSubject);
        //操作代理类,间接调用委托类的相关方法
        proxySubject.doSomthing("洗衣服");
    }
}
2、动态代理

代理类在程序运行时创建的代理方式被称为动态代理。

在java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过这个类和这个接口可以生成JDK动态代理类和动态代理对象。

  • 动态代理要求委托类必须实现某个接口(Retrofit没有委托类,只有接口,因为它不需要执行方法)

  • 可以指定代理实现多个接口 (new Class[] { interface1, interface2 })

  • 可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。因为所有被代理执行的方法,都是通过在InvocationHandler中的invoke方法调用的,所以我们只要在invoke方法中统一处理,就可以对所有被代理的方法进行相同的操作了。

  • 如果需要对某个方法进行自定义逻辑处理,可以根据method的特征信息进行判断分别处理。

运行实例

//公共接口
public interface Operate {
    void doSomething(String sth);
}

//委托类,实现公共接口,是实际干活的
public class RealOperate implements Operate {
    @Override
    public void doSomething(String sth) {
        System.out.println(this.getClass().getSimpleName() + " doSomething " + sth);
    }
}

//负责连接代理类和委托类的中间类
public class OperateInvocation implements InvocationHandler {

    private Object operate;
    
    public OperateInvocation(Object operate) {
        this.operate = operate;
    }

	/**
     * 调用代理对象的每个函数实际最终都会调用InvocationHandler的invoke函数,自定义代理逻辑处理
     * @param proxy 通过 Proxy.newProxyInstance() 动态生成的代理类对象
     * @param method 被代理对象调用的函数
     * @param args 被代理对象调用的函数的参数
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("动态代理执行前夕");
        Object obj = method.invoke(operate, args);
        System.out.println("动态代理执行完成");
        return obj;
    }
}

//测试类
//代理类是不是同一个实例,是与传入OperateInvocation构造函数中的委托类相关的
public class DynamicTest {
    public static void main(String[] args) {
    //创建代理类proxyOperate 
        Operate proxyOperate = (Operate) Proxy.newProxyInstance(Operate.class.getClassLoader(), 
                new Class[] {Operate.class}, new OperateInvocation(new RealOperate2()));
        System.out.println("代理类proxyOperate:" + proxyOperate.getClass()); //打印出代理类名
        proxyOperate.doSomething("做饭");
    }
}

jdk proxy原理解析

Proxy.newProxyInstance()得到代理类的实例
生成的代理类:class com.sun.proxy.$Proxy0
只保留关键代码

 //动态生成的代理类,继承自Proxy,实现了公共接口
public final class $Proxy0 extends Proxy implements Operate
{
  private static Method m1;
  private static Method m2;
  private static Method m3;
  private static Method m0;
  
  //动态生成的代理类持有InvocationHandler 的引用
  public $Proxy0(InvocationHandler paramInvocationHandler) throws  {
    super(paramInvocationHandler);  //调用父类的构造方法
  }
  
  //通过反射得到method实例
   static {
    try{
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
      m3 = Class.forName("com.zhen.proxy.Operate").getMethod("doSomething", Class.forName("java.lang.String"));
      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
      return;
    } //省略catch抛异常的部分
  }
 
  public final void doSomething(String paramString) throws  {
    try  {
    //代理类持有InvocationHandler 的引用,回调invoke(proxy, method, args)方法
    //也验证了前面说的三个入参分别是:代理类对象、代理对象调用的方法、代理对象调用的方法的参数
      this.h.invoke(this, m3, paramString); 
      return;
    } //省略catch抛异常的部分
  }

  //省去toString,hashCode、equals方法的内容。
}
public interface InvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}

Proxy源码

public class Proxy implements java.io.Serializable {

    private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
        proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

    protected InvocationHandler h;
	//持有InvocationHandler的引用
 	protected Proxy(InvocationHandler h) {
        this.h = h;
    }
    
/**
* loader 类加载器
* interfaces 公共接口,可以是单个接口或者接口数组
* 自定义invocationHandler,一般持有委托类的引用
*/
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
        throws IllegalArgumentException {

        final Class<?>[] intfs = interfaces.clone();
        //设置权限access
		//查找或者生成动态代理类
        Class<?> cl = getProxyClass0(loader, intfs);
        try {
        	//得到代理类构造方法
      		 final Constructor<?> cons = cl.getConstructor(constructorParams);
      		 //通过代理类的构造方法和参数,得到代理类实例
            return cons.newInstance(new Object[]{h});
        } 
    }
  • 代理类的构造方法调用super(invocationHandler),代理类持有invocationHandler的引用。

  • 代理类调用doSomething,实际this.h.invoke(this, m3, paramString)。

  • invocationHandler可以对每个代理的方法进行拦截、过滤等处理。

  • invocationHandler 持有委托类的引用,最终是通过反射调用委托类的相关方法。

  • $Proxy0 extends Proxy implements Person,代理类继承自Proxy类,所以java动态代理只能对接口进行代理(Java单继承机制)。

  • jdk生成的这个$Proxy0类是在内存中的,Proxy.newProxyInstance()创建代理对象时,通过反射获得这个类的构造方法,然后创建的代理实例(里面做了缓存,没有就创建代理类,有就从缓存里取)。

  • 代理类$Proxy0持有InvocationHandler的实例,在调用方法时执行InvocationHandler.invoke(),InvocationHandler持有委托类的实例,method.invoke(target, args)。即代理类调用接口方法时,通过自身持有的中介类对象(InvocationHandler)来调用委托类(被代理对象)的方法。也就是说,动态代理通过中介类实现了具体的代理功能。

  • 动态代理实现实际是双层的静态代理,开发者提供了委托类 B,程序动态生成了代理类 A(B和A具有相同的接口行为)。开发者还需要提供一个实现了InvocationHandler的子类 C,子类 C 连接代理类 A 和委托类 B,它是代理类 A 的委托类,委托类 B 的代理类。用户直接调用代理类 A 的对象,A 将调用转发给委托类 C,委托类 C 再将调用转发给它的委托类 B。

关于Retrofit的动态代理解析:

 1. 创建一个Retrofit实例(构造者模式)
 2. 创建一个接口,并通过动态代理得到接口的实例
 3. 请求网络接口请求网络接口
retrofit = Retrofit.Builder().baseUrl("url").build() //创建retrofit实例
apiService = retrofit.create(ApiService.class);  //得到动态代理apiService(动态代理实现了接口)
apiService.login(userName, password);  //通过动态代理进行网络请求
  • retrofit.create(ApiService.class) 返回一个实现了ApiService的动态代理类
//Retrofit.java
public <T> T create(final Class<T> service) {
    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
        new InvocationHandler() { //匿名内部类,没有用到proxy,所以没有委托类(被代理对象,真正干活的对象)
          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            return loadServiceMethod(method).invoke(args);
          }
        });
  }
  • 动态代理相当于一个拦截器,接口中的所有方法都会走InvocationHandler中的invoke()
    (生成的动态代理类持有InvocationHandler的引用,在每个方法中调用h.invoke(this, method, args))
  • 动态获取接口的注解、入参、返回值(封装网络请求,获取解析返回值)
  • 没有委托类,不执行接口方法

在这里插入图片描述

参考:
10分钟看懂动态代理设计模式
23种设计模式----------代理模式(三) 之 动态代理模式
java动态代理实现与原理详细分析
秒懂Java代理与动态代理模式
公共技术点之 Java 动态代理

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值