一、代理模式的原理

1.1、代理模式是如何实现的

代理模式是常用的设计模式之一,它一般分为三个角色:

  1. 抽象角色:指代理角色和真实角色对外提供的公共方法,一般为一个接口
  2. 真实角色:需要实现抽象角色接口,定义了真正所要实现的业务逻辑,以便代理角色调用。
  3. 代理角色:需要实现抽象角色接口,是真实角色的代理,通过真实角色的业务逻辑来实现抽象方法,并可以附加自己的操作,将统一的流程控制都放到代理角色中处理。

三个角色的关系图如下:

1.2、使用代理模式的目的

  1. 通过引入代理对象来间接访问目标对象,可对调用者和目标对象进行解耦,保护目标对象。
  2. 可用过代理对象对目标对象的实现进行扩展和增强。

二、代理模式分类

2.1、静态代理

简单的demo:

这里我们构建一个在京东(代理对象)买实体厂家(目标对象)空调的场景。

//抽象角色(空调厂家)
public interface ACFactory {void deliverGoods();}//真实角色
public class Geli implements ACFactory{@Overridepublic void deliverGoods() {System.out.println("格力空调....发货了");}
}//真实角色
public class Haier implements ACFactory{@Overridepublic void deliverGoods() {System.out.println("海尔空调....发货了");}
}//代理角色
public class JD implements ACFactory{private Factory factory;public JD(Factory factory) {this.factory = factory;}private void sailBefore(){System.out.println("售前服务");}private void sailAfter(){System.out.println("售后服务");}//对真实角色的行为(方法)进行增强@Overridepublic void deliverGoods() {sailBefore();factory.deliverGoods();sailAfter();}
}//调用者
public class ShoppingTest {public static void main(String[] args) {Factory haier = new Haier();JD jd = new JD(haier);jd.deliverGoods();}
}

如果此时我感觉格力和海尔的空调太贵了不适合我,我想买个奥克斯的(新需求),此时我们能快速的进行扩展。

public class Aux implements ACFactory{@Overridepublic void deliverGoods() {System.out.println("奥克斯空调....发货了");}
}//调用者
public class ShoppingTest {public static void main(String[] args) {Factory aux = new Aux();JD jd = new JD(aux);jd.deliverGoods();}
}

再说一个真实场景帮助大家理解,当我们进行Android开发时会用到网络请求框架,比如最早使用的是Volley框架,之后我想换成okhttp框架,如果之前是强关联(即在业务代码中直接引用Volley),想要做框架替换无疑是痛苦的,如果在设计之初,我们写了一个代理层则可以快速进行框架转换,哪怕将来要求替换为Retrofit也会毫不费力,代理模式可以增强我们代码的扩展性。

静态代理的缺点:

  1. 静态代理会导致类和接口泛滥,难以管理
  2. 如需对接口进行改动,那么所有实现类都要改动

正因静态代理的缺点,由此引出动态代理。

2.2、动态代理

首先需要说明的是静态代理和动态代理的思想和原理是一模一样的,只是实现代理类的方式不同。

静态代理:是手动创建代理类并实现接口(.java文件),这个文件是实实在在存储在磁盘上的文件,然后经过编译生成字节码文件(.class文件),最后通过类加载器加载到内存中。

动态代理:是在运行时动态生成字节流,其内容是和实现了接口的代理类的字节码文件是基本一致的,只不过它直接存储在内存中,帮我们省去了手动实现接口这一过程。

(可类比于Android中xml中的控件和java中直接new的控件的关系)。

下面先看一下动态代理的使用方式:

public class ShoppingTest {public static void main(String[] args) {
//静态代理的使用方式
//        Factory haier = new Haier();
//        JD jd = new JD(haier);
//        jd.deliverGoods();//动态代理的使用方式,与静态代理对应着看有助于理解//步骤一 创建目标对象Haier haier = new Haier();// o即静态代理中的JD代理类//步骤二,创建代理类对象Object o = Proxy.newProxyInstance(ShoppingTest.class.getClassLoader(),  //类加载器,用于加载生成的字节码(是一个字节数组)new Class[]{Factory.class},  //要实现的接口new InvocationHandler() {    //回调,会在生成的字节码中实现的接口方法中进行调用@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object invoke= null;if (method.getName().equals("deliverGoods")){sailBefore();//调用目标对象(真实角色)的方法//步骤四  调用目标对象的方法(真正的逻辑代码)invoke = method.invoke(haier, args);sailAfter();}return invoke;}});//相当于静态代理中jd.deliverGoods()//步骤三 调用代理类接口方法Factory factory = (Factory) o;factory.deliverGoods();}private static void sailBefore() {System.out.println("售前服务");}private static void sailAfter() {System.out.println("售后服务");}
}

看完动态代理的使用,我相信大家还是会有很多疑惑的,最大的疑惑肯定是代理类调用实现的接口方法到底是怎么回调到InvocationHandler的invoke()的呢?那接下来就来看下源码,相信所有的疑惑都会烟消云散。

Proxy.javapublic static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)throws IllegalArgumentException{   //检查h不为空Objects.requireNonNull(h);//拷贝接口类数组final Class<?>[] intfs = interfaces.clone();...// 标记1:寻找或生成代理类.Class<?> cl = getProxyClass0(loader, intfs);...try {...//获取代理类的构造方法final Constructor<?> cons = cl.getConstructor(constructorParams);final InvocationHandler ih = h;...//通过反射创建代理类对象,并将传入的InvocationHandler对象传递给代理类return cons.newInstance(new Object[]{h});} catch (IllegalAccessException|InstantiationException e) {throw new InternalError(e.toString(), e);} catch (InvocationTargetException e) {Throwable t = e.getCause();if (t instanceof RuntimeException) {throw (RuntimeException) t;} else {throw new InternalError(t.toString(), t);}} catch (NoSuchMethodException e) {throw new InternalError(e.toString(), e);}}

这个方法的源码很好理解,重点是标记一处的代理类是如何生成的,继续跟入最终来到Proxy.ProxyClassFactory的apply()方法:

public Class<?> apply(ClassLoader var1, Class<?>[] var2) {...//上面的逻辑就是做了一些字符上的拼接,这里得到的var22就是代理类的内容了byte[] var22 = ProxyGenerator.generateProxyClass(var23, var2, var17);try {//这里根据上面的字节数组创建一个class对象return Proxy.defineClass0(var1, var23, var22, 0, var22.length);} catch (ClassFormatError var14) {throw new IllegalArgumentException(var14.toString());}}}

那如果我们将byte[]输出到一个class文件,我们就能清楚生成的代理类到底是什么样子的了:

private static void  generateProxy(){String name = Factory.class.getName()+"$Proxy0";byte[] bytes = ProxyGenerator.generateProxyClass(name, new Class[]{Factory.class});try {FileOutputStream fos = new FileOutputStream(name+".class");fos.write(bytes);fos.close();} catch (Exception e) {e.printStackTrace();}}

运行此方法,我们就可以拿到生成的动态代理类了,内容如下:

//这里实现了Factory接口
public final class Factory$Proxy0 extends Proxy implements Factory {private static Method m1;private static Method m3;private static Method m2;private static Method m0;//这个InvocationHandler就是我们使用动态代理时传入的回调对象public Factory$Proxy0(InvocationHandler var1) throws  {super(var1);}//重写了Object类中的equals()方法。public final boolean equals(Object var1) throws  {try {return (Boolean)super.h.invoke(this, m1, new Object[]{var1});} catch (RuntimeException | Error var3) {throw var3;} catch (Throwable var4) {throw new UndeclaredThrowableException(var4);}}//重写了Factory接口中的方法public final void deliverGoods() throws  {try {//super.h就是上面构造函数中的InvocationHandler对象,这里就调用了invoke()方法进行回调super.h.invoke(this, m3, (Object[])null);} catch (RuntimeException | Error var2) {throw var2;} catch (Throwable var3) {throw new UndeclaredThrowableException(var3);}}//重写了Object类中的toString()方法。public final String toString() throws  {try {return (String)super.h.invoke(this, m2, (Object[])null);} catch (RuntimeException | Error var2) {throw var2;} catch (Throwable var3) {throw new UndeclaredThrowableException(var3);}}//重写了Object类中的hashCode()方法。public final int hashCode() throws  {try {return (Integer)super.h.invoke(this, m0, (Object[])null);} catch (RuntimeException | Error var2) {throw var2;} catch (Throwable var3) {throw new UndeclaredThrowableException(var3);}}//静态块初始化所有要重写的方法的对象static {try {m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));m3 = Class.forName("com.example.view.test4.Factory").getMethod("deliverGoods");m2 = Class.forName("java.lang.Object").getMethod("toString");m0 = Class.forName("java.lang.Object").getMethod("hashCode");} catch (NoSuchMethodException var2) {throw new NoSuchMethodError(var2.getMessage());} catch (ClassNotFoundException var3) {throw new NoClassDefFoundError(var3.getMessage());}}
}

同理如果我们传入多个接口,生成的代理类(只有一个类)会将他们全部实现,到此动态代理的原理就非常清晰了,很多优秀框架都用到了动态代理如Retrofit。

java基础-静态代理与动态代理相关推荐

  1. Java基础加强-(注解,动态代理,类加载器,servlet3.0新特性)

    1.   Annotation注解 1.1.  Annotation概述 Annotation是JDK 5.0以后提供对元数据的支持,可以在编译.加载和运行时被读取,并执行相应的处理.所谓Annota ...

  2. Java基础-静态代理以及动态代理

    动态代理: 在了解动态代理之前,先对代理有一个认识. 代理模式是Java常见的设计模式之一.所谓代理模式是指客户端并不直接调用实际的对象,而是通过调用代理,来间接的调用实际的对象. 打个比方:你买火车 ...

  3. Java、Android静态代理与动态代理

    代理 (1) 什么是代理? 大道理上讲代理是一种软件设计模式,目的地希望能做到代码重用.具体上讲,代理这种设计模式是通过不直接访问被代理对象的方式,而访问被代理对象的方法.这个就好比 商户----&g ...

  4. Java静态代理、动态代理与CGLib代理

    java的动态代理举足轻重,它同反射原理一直是许多框架的底层实现.今天唠一下. 一.代理模式 代理模式是一种设计模式,提供了对目标对象额外的访问方式,即通过代理对象访问目标对象,这样可以在不修改原目标 ...

  5. Java的代理模式之静态代理和动态代理

    文章目录 静态代理 动态代理 jdk生成代理对象 cglib代理 代理模式简介: 代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目 ...

  6. 浅谈Java和SAP ABAP的静态代理和动态代理,以及ABAP面向切面编程的尝试

    文章目录 Java的静态代理 静态代理的优缺点 ABAP的静态代理 Spring AOP的动态代理 JDK动态代理的优缺点 CGLIB动态代理的优缺点 ABAP CGLIB的模拟实现 ABAP Pre ...

  7. Java静态代理、动态代理以及CGLIB动态代理

    代理模式是java中最常用的设计模式之一,尤其是在spring框架中广泛应用.对于java的代理模式,一般可分为:静态代理.动态代理.以及CGLIB实现动态代理. 对于上述三种代理模式,分别进行说明. ...

  8. 第六周 Java语法总结_设计原则_工厂模式_单例模式_代理模式(静态代理_动态代理)_递归_IO流_网络编程(UDP_TCP)_反射_数据库

    文章目录 20.设计原则 1.工厂模式 2.单例模式 1)饿汉式 2)懒汉式 3.Runtime类 4.代理模式 1)静态代理 2)动态代理 动态代理模板 21.递归 22.IO流 1.File 2. ...

  9. 一篇文章让你搞懂Java中的静态代理和动态代理

    文章目录 什么是代理模式 代理的优缺点 静态代理 动态代理 InvocationHandler 接口 和 invoke 方法介绍 静态代理和动态代理的区别 什么是代理模式 代理模式是常用的java设计 ...

最新文章

  1. Java项目:校园二手市场系统(java+SSM+mysql+maven+tomcat)
  2. 三、单链表增删改查原理和代码实现
  3. 微信支付-接收返回通知结果
  4. 国内阿里Maven仓库镜像及自己收集镜像库
  5. Oracle学习:分组数据(group by)与笛卡尔积
  6. 数据结构与算法--分治算法-最大子序列和问题
  7. 进程比线程更多资源_为什么我们不应该使用比我们需要更多的线程
  8. dubbo 路由配置_Dubbo-go v1.5.1发布,Dubbo 的 Go实现
  9. 软件_搭建rtsp视频推送环境
  10. 这份中台与数据报表的干货我写了10小时,真不想告诉你
  11. Eclipse开发程序,取得新的工程后,启动Web服务出错原因总结
  12. 用 JNI 进行 Java 编程(1)
  13. css 水印_Inpaint,一款简洁干净的去水印软件
  14. 大疆水弹机器人_现身央视的大疆机甲大师 S1 备受瞩目的教育机器人圆了儿时机甲梦...
  15. 除了百度,这几个搜索引擎更好用
  16. 公众号管理系统 html,Java SpringBoot+Mybatis Layui+JQuery+html微信公众号后台管理系统...
  17. 图解侧方停车技巧2015高清版
  18. 计算机支架式教学案例,万紫千红总是春--支架式教学(网友来稿)
  19. apk反编译(6)用ProGuard 混淆、压缩代码,压缩资源。
  20. Python爬虫(第三周)

热门文章

  1. PYNQ例程一:1.3 PYNQ Overlays
  2. python第8周 作业
  3. web端自动化测试--淘宝
  4. 计算机规则英语作文,计算机信息技术(五笔及中英文打字测试试题).doc
  5. 第五章 C语言循环结构和选择结构_C语言break和continue用法详解(跳出循环)
  6. 2017.10.12 记者招待会
  7. linux下USB触摸屏开机不好使重新热插拔好使问题解决
  8. 《乔布斯传》英文原著重点词汇笔记(九)【 chapter seven】
  9. [世事无常,珍爱眼前人] 纪念科比,曼巴精神永在。
  10. flash加密解密的相关知识