文章目录

  • 专栏导航
  • 前言 代理 静态代理 RPC
  • 静态代理 VS 装饰器 VS AOP
  • 动态代理 VS 静态代理
  • RPC调用的例子
  • Why not Http?
  • 动态代理实现AOP
  • JDK动态代理 VS CGLib动态代理 字节码增强
  • 后记 思维 VS 实现
    • RPC概念

专栏导航

优雅のJava(零)—— 面向问题的学习

前言 代理 静态代理 RPC

文如其名 代理模式 就是代理 屏蔽细节 客户用起来感觉是一样的
类似之前的 客户只负责指定标准 接口 输入参数 你怎么返回我不管 我用起来感觉一样就行辣

静态代理的实现本身没啥技术 你直接把要代理的实例对象引用过来 调用它的方法就行啦

比如 本来是远程调用 别人服务器的Hello方法 然后回显数据 客户可不想看到 那些远程连接的一大堆代码 于是我们需要实现客户只是这么调用Stub.hello(); 就能看到回显数据了 那怎么办?Stub就是我们的代理类 这个名字是约定俗成的

这种远程调用也被称为RPC Remote Procedure Call 远程过程(过程:一段程序就叫过程)调用 那其实平常我们调用函数就是“本地过程调用(我就叫他LPC)”咯

客户就想 RPC底层我不想看到 我想使用起来好像本地调用一个函数一样简单 这就是RPC的代理

静态代理 VS 装饰器 VS AOP

其实实现的套路接近 都是拉别人的实例对象进来 加东西(不加也行 就套个壳 那或许变成适配器模式了。。)

只不过装饰器侧重点在装饰 所以是基于原来的做增强 或者说添加特性 这样符合“合成”的思想 而不是继承的思想

但是代理 则是尽量和被代理对象同规范 因为客户指定的规范 客户需要你这样和他对接 所以侧重点是代理

现在代理 往往都是往动态代理 转而往AOP增强的思路前进 那自然必定也有增强 但不是象装饰器那种 装饰一层 添加一个特性 而是只是为了代理 而增强功能而已

虽然两种都能达到一样的效果可能 但是思维不同

动态代理 VS 静态代理

扯了这么多动态代理 那为啥需要动态代理 原来那个不够嘛?拉进来被代理实例 前后做些操作就完事了啊

试想 假设 对面服务器上几百个组件类及其方法调用 你得创造多少代理类啊。。。还有他们的方法 等 毕竟要遵守同一个规范来着 这不折磨

更加折磨的是 如果有个组件的某个方法改了名字 那代码不符合开闭原则 你将会在一堆代码里边修修补补最后迷失自己

其实我们可以发现静态代理很多重复的东西 如果我们能够动态的生成类 和被代理的类同一个规范 那不是完事了吗?这就是所谓动态的代理

RPC调用的例子

我们看个简单的RPC调用的例子
首先 我们的客户端 使用起来还是很快乐 我们远程的服务 规范是UserService接口 然后举例子使用的方法是findUserById

public class Client{public static main(String[] args) throws exception {IUserService service = Stub.getStub();sout(service.findUserById(123));}
}

那我们这个stub应该怎么动态代理法?我们先使用jdk原生的动态代理 也就是熟悉的invoke Proxy.newProxyInstance那一套

public class stub{public static UserService getStub(){// 匿名内部类写好Handler 配置好 要生成的动态类的信息InvocationHandler h = new InvocationHandler() {@Override public Object invoke(Object proxy, Method method, Object[] args) throws ThrowableException{Socket s = new Socket("127.0.0.1", 8888);ObjectOutputStream oos = new ObjectOutputStream(s.getOutputStream());oos.writeUTF(method.getName());oos.writeObject(method.getParameterTypes());oos.writeObject(args);oos.flush();}};Object o = Proxy.newProxyInstance(UserService.class.getClassLoader(), ... )return (UserService) o;}
}

这里 invoke里边就是远程通信的具体细节 然后再使用newProxyInstance的方法获取代理生成的对象然后返回給客户使用
这里 这个本地的 getStub返回的对象 代理了远程服务器那个提供服务的对象(你可以这么理解)

当然了 服务器那边其实就需要解析了 解析出来反射一个对象 执行 然后返回结果即可

public class Skeleton{private static void process() throws Exception{Socket s = new Socket("127.0.0.1", 8888);InputStream in = s.getInputStream();OutputStream out = s.getOutputStram();ObjectInputStream oos = new ObjectInputStream(in);DataOutputStream dos = new DataOutputStream(out);// 开始解包 解出来name 接口参数等等String methodName = oos.readUTF();Class[] parameterTypes = (Class[])oos.readObject();Object[] args = (Object[])oos.readObject();UserService service = new UserServiceImpl();// 反射 获取方法Method method = service.getClass().getMethod(methodName, parameterTypes);// 调用 获取结果User user = (User)method.invoke(service, args);// ... 利用结果做什么 返回给client 比如说dos.writeInt(user.getId());dos.writeInt(user.getName());dos.flush();}
}

Why not Http?

这个问题其实很简单 按理来说 我们实际上把需要的对象 对象的方法 以及执行需要的参数 传过去 然后对面返回一个JSON结果就好了啊 没啥东西 就是普通的Http请求 然后controller回复就完事儿。。。

所以那个时候客户端可以这么调用:

 public class Client{public static main(String[] args) throws exception {IUserService service = Stub.getStub();sout(service.RPC("findUserById", 123)); // 举例 当然参数的封装自然用些复杂数据结构 更具有兼容性}}

确实! 而且这个和动态代理无关其实 我们invoke方法也可以这样来 只不过是接口规范改变了

注意 代理的关键是同一个规范 那自然 原来那种就好像调用本地对象 本地方法的思路 更符合代理模式的精髓

动态代理实现AOP

实际上 技术层面而言 动态代理比较关键的是能够实现AOP增强的代理!

之前RPC的那个远程服务的代理没有增强 只是屏蔽了底层细节

 public Object creatProxy() {// ...InvocationHandler invocationHandler = new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {beforeAdvice.before(); // 前置增强Object result = method.invoke(targetObject, args);//调用目标对象的目标方法afterAdvice.after(); // 后置增强return result;}};Object proxyObject = Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);return proxyObject; // 返回增前的代理对象}

这里 我们在原有的被代理对象前后加了东西 这才是前置后置增强 用AOP的思想来说就是 织入

而这么做的好处是 比如实现所有方法的前后打印日志 你动态代理所有的对象 給每个组件对象来个AOP增强 不就成了?

JDK动态代理 VS CGLib动态代理 字节码增强

首先 各位不要小看了的动态代理 以为这玩意也就是 类似静态代理一样 前置 引用执行结果 后置 完事

关键一点在于 静态代理只是代理一段过程 首先被代理的对象存在 然后静态代理用个所谓代理对象的方法 调用被代理的对象 完成套壳的操作 但是动态代理 被代理的对象压根就没出来过!

它是通过织入字节码的方式 我们任何类 方法 本质都是字节码 我往里边改改不就完事了吗:)改完 再用对应的classLoader 类加载器 就能实现运行的时候 产生新的类 而不是只能程序一启动那个时候加载类

这种技术统称 字节码增强技术 只是JDK和CGLib的思路有所不同

JDK动态代理专注于同一种规范 所以要求提供被代理类的

  • 接口
  • 类加载器

如果被代理类没有接口 那根本操作不了 如果有 那我可以用反射 套出来这个类里边有啥方法 方法参数 属性等等 然后增强 等同于造一个新的类 一个符合同一规范的类 然后用类加载器加载出来 你可以发现代理类往往叫“$proxy-序号” 序号只是为了造出来的类不重名罢了

CGLib动态代理 使用的是子类继承父类 从而获取到被代理(父类)的信息 再继承 增强其功能的 所以出来的类就是被代理类的子类哈哈

实际中两种方法并列 Spring源码里边 有以下片段

可以发现使用哪个 有三个条件

  • 第一 配置 config写啥就是啥

  • 第二 通过这个注解(标记) 这个属性配置的

    在 createAopProxy方法里边 根据这个条件选择使用哪个

  • 第三个条件就是 hasNoUserSuppliedProxyInterfaces
    即 bean组件如果有接口 使用JDK 字节码增强技术 $proxy
    没接口使用CGLib

所以看情况使用 但是核心不变 代理增强 动态的造类运动

后记 思维 VS 实现

这篇其实技术讲的尽量少了 关键是思维

其实你说技术上 静态代理和装饰器区别有吗 没有啊 关键不是实现的技术 而是思路 为什么要代理 什么是所谓代理 而什么又是“装饰” 装饰可以叠加 可以像前端那种mixin 代理只能是代理原有的

RPC概念

同样的道理 RPC框架的使用上 有人提问 为啥不使用HTTP?问的人可能太专注与技术本身而没有管思维 RPC调用是种思维 不是技术具体实现的方式 他只是接口 就类似代理模式这个概念一样。

RPC当然可以使用HTTP技术来实现 你制定好访问的接口 传递参数的数据格式 有什么不可以的呢?你甚至可以使用TCP UDP实现。。。之所以使用各种各样的 所谓RPC框架 那是因为他们框架包含更好的 序列化技术实现 信息传输的技术实现

RPC框架的实现依赖两步 序列化和网络信息传输

  • 序列化 用java自带的serializable自然是效率底下的 替代方案JSON传输 速度一般 不适用于分布式 高效的数据传输 而Thrift | Hessian | ProtoBuf 这些就符合要求
  • 传输层面 HTTP1.1问题很大 使用HTTP2.0就很舒服

我们著名的gRPC 其实是结合了HTTP2.0 和 Protobuf 所以gRPC的优点也主要是后面两个大哥提供的

所以 思维是关键 怎么实现又是另外一回事 不能混为一谈

专栏
优雅のJava(零)—— 面向问题的学习

优雅のJava(四)—— 优雅的理解代理模式相关推荐

  1. Java设计模式(10)代理模式(Proxy模式)

    理解并使用设计模式,能够培养我们良好的面向对象编程习惯,同时在实际应用中,可以如鱼得水,享受游刃有余的乐趣. Proxy是比较有用途的一种模式,而且变种较多,应用场合覆盖从小结构到整个系统的大结构,P ...

  2. Java 的工厂方法及代理模式

    Java 的工厂方法及代理模式 工厂方法(FactoryMethod) 概述:定义一个用于创建对象的接口,让子类决定实例化哪一个类.FactoryMethod使一个类的实例化延迟到其子类. 适用性: ...

  3. 第四章 Caché 设计模式 代理模式

    文章目录 第四章 Caché 设计模式 代理模式 定义 类型 使用场景 优点 缺点 结构图 完整示例 抽象主题类 真实主题类 代理类 对象类 调用 思考 第四章 Caché 设计模式 代理模式 定义 ...

  4. Java中的设计模式:“代理模式”的理解

    代理模式定义: 为其他对象提供一种代理以控制对这个对象的访问.在面向对象中,有时候直接访问一些对象比较麻烦,所以代理模式就是在这个对象上加上一个访问该对象的访问层.类似于很多明星的事务实际都是交给经纪 ...

  5. Java内功修炼系列:代理模式及动态代理

    目录 一 代理模式 1.1 简介 1.2 代理模式角色定义 二 静态代理 2.1 介绍和实例 2.2 静态代理的缺点 三 动态代理 3.1 基于JDK原生动态代理实现 四 小结 一 代理模式 1.1 ...

  6. 详解 Java 中的三种代理模式

    代理模式 代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能. 这里使用 ...

  7. Java设计模式(13)----------代理模式

    原文:https://www.imooc.com/article/24850 默课道可工程师学习地址:https://www.imooc.com/article/24850 介绍 代理模式中,在客户端 ...

  8. Java描述设计模式(16):代理模式

    本文源码:GitHub·点这里 || GitEE·点这里 一.生活场景 1.场景描述 在电商高速发展的今天,快递的数量十分庞大,甚至出现了快递代理行业,简单的说就是快递的主人没有时间收快递,会指定一个 ...

  9. Java中的三种代理模式解释

    本文转自:Java技术栈 www.javastack.cn 代理模式 代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的 ...

最新文章

  1. jar - 操作jar包的工具
  2. efficientransac_【泡泡图灵智库】基于图割优化的RANSAC算法(CVPR)
  3. 运行时错误76未找到路径怎么解决_自动化测试解决竞争问题?等待一下就行了~...
  4. 将时间戳转为年月日时分秒格式
  5. 气流与路易吉,阿戈,MLFlow,KubeFlow
  6. TCP系列05—连接管理—4、TCP连接的ISN、连接建立超时及TCP的长短连接
  7. 【Spring第九篇】AOP
  8. java join字符串_Java字符串join()
  9. 重磅!2020 年算法工程师技术路线图
  10. SSIM公式:结构相似性计算原理,基于SSIM的图像质量评价
  11. win7 升级IE11
  12. TX2--Building OpenCV for Tegra with CUDA
  13. 组网雷达融合处理组件化设计与仿真
  14. 摩尔定律、安迪-比尔定律、反摩尔定律
  15. MBR膜是什么?起到哪些用途?-世来福
  16. 计算机网络期末复习知识点
  17. 【小组作业】电影院管理系统
  18. 孙正义:未来30年的人工智能和物联网
  19. 怎样取得sql服务器时间?
  20. [轉]运用项目管理WBS方法成功创建网店

热门文章

  1. 学习rtklib(一)
  2. Aspnet core迁移 wwwroot静态文件文件
  3. 【Linux】【踩坑专栏】centOS 7 桌面版使用VMWare Workstation Player 16安装时踩坑
  4. 深入理解依赖倒置原则(Dependence Inversion Principle)
  5. 高德置地苏萌:杭州高德置地广场UTOPA HUB请你喝一杯Art Coffee
  6. 2019网络钓鱼邮件翻倍,如何预防鱼叉式钓鱼邮件攻击?
  7. mt4 python_一个使用Python自动化交易外汇MT4脚本实现
  8. 51book机票总结
  9. 终极单词index 排序 W-Y-Z
  10. Burden of Proof