代理模式(Proxy Design Pattern)是为一个对象提供一个替身,以控制对这个对象的访问。即通过代理对象访问目标对象。被代理的对象可以是远程对象、创建开销大的对象或需要安全控制的对象。

一、代理模式介绍

在结束创建型模式的讲解后,从这一篇开始就进入到了结构型模式,结构型模式主要是总结一些类和或对象组合在一起的结构。代理模式在不改变原始代理类的情况下,通过引入代理类来给原始类附加功能。

代理模式的主要结构如下:

  1. Subject:抽象主题类,通过接口或抽象类声明主题和代理对象实现的业务方法

  2. RealSubject:真实主题类,实现Subject中的具体业务,是代理对象所代表的真实对象

  3. Proxy:代理类,其内部含有对真实主题的引用,它可以访问、控制或扩展RealSubject的功能

  4. Client:客户端,通过使用代理类来访问真实的主题类

按照上面的类图,可以实现如下代码:

//主题类接口
public interface Subject {void Request();
}//真实的主题类
public class RealSubject implements Subject{@Overridepublic void Request() {System.out.println("我是真实的主题类");}
}//代理类
public class Proxy implements Subject{private RealSubject realSubject;@Overridepublic void Request() {if (realSubject == null) {realSubject = new RealSubject();}realSubject.Request();}
}//客户端
public class Client {public static void main(String[] args) {Proxy proxy = new Proxy();proxy.Request();}
}

代理模式有比较广泛的使用,比如Spring AOP、RPC、缓存等。在 Java 中,根据代理的创建时期,可以将代理模式分为静态代理和动态代理,下面就来分别阐述。

二、代理模式实现

动态代理和静态代理的区分就是语言类型是在运行时检查还是在编译期检查。

2.1 静态代理

静态代理是指在编译期,也就是在JVM运行之前就已经获取到了代理类的字节码信息。即Java源码生成.class文件时期:

​由于在JVM运行前代理类和真实主题类已经是确定的,因此也被称为静态代理。

在实际使用中,通常需要定义一个公共接口及其方法,被代理对象(目标对象)与代理对象一起实现相同的接口或继承相同的父类。其实现代码就是第一节中的代码。

2.2 动态代理

动态代理,也就是在JVM运行时期动态构建对象和动态调用代理方法。

常用的实现方式是反射。反射机制是指程序在运行期间可以访问、检测和修改其本身状态或行为的一种能力,使用反射我们可以调用任意一个类对象,以及其中包含的属性及方法。比如JDK Proxy。

此外动态代理也可以通过ASM(Java 字节码操作框架)来实现。比如CGLib。

想学习C++工程化、高性能及分布式、深入浅出。性能调优、TCP,协程,Nginx源码分析Nginx,ZeroMQ,MySQL,Redis,MongoDB,ZK,Linux内核,P2P,K8S,Docker,TCP/IP,协程,DPDK学习资料视频获取点击:C++架构师学习资料

C++后台开发学习地址:

C/C++Linux服务器开发/C++后台开发架构师-学习视频https://ke.qq.com/course/417774?flowToken=1013189

2.2.1 JDK Proxy

这种方式是JDK自身提供的一种方式,它的实现不需要引用第三方类,只需要实现InvocationHandler接口,重写invoke()方法即可。代码实现如下所示:

public class ProxyExample {static interface Car {void running();}static class Bus implements Car {@Overridepublic void running() {System.out.println("bus is running");}}static class Taxi implements Car {@Overridepublic void running() {System.out.println("taxi is runnig");}}//核心部分 JDK Proxy 代理类static class JDKProxy implements InvocationHandler {private Object target;public Object getInstance(Object target) {this.target = target;//获得代理对象return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object result = method.invoke(target, args);return result;}}public static void main(String[] args) {JDKProxy jdkProxy = new JDKProxy();Car instance = (Car) jdkProxy.getInstance(new Taxi());instance.running();}
}

动态代理的核心是实现Invocation接口,我们再看看Invocation接口的源码:

public interface InvocationHandler {public Object invoke(Object proxy, Method method, Object[] args)throws Throwable;
}

实际上是通过invoke()方法来触发代理的执行方法。最终使得实现Invocation接口的类具有动态代理的能力。

动态代理的好处在于不需要和静态代理一样提前写好公共的代理接口,只需要实现Invocation接口就可拥有动态代理能力。下面我们再来看看 CGLib 是如何实现的

2.2.2 CGLib

CGLib 动态代理采取的是创建目标类的子类的方式,通过子类化,我们可以达到近似使用被调用者本身的效果。其实现代码如下所示:

public class CGLibExample {static class car {public void running() {System.out.println("car is running");}}static class CGLibProxy implements MethodInterceptor {private Object target;public Object getInstance(Object target) {this.target = target;Enhancer enhancer = new Enhancer();//设置父类为实例类enhancer.setSuperclass(this.target.getClass());//回调方法enhancer.setCallback(this);//创建代理对象return enhancer.create();}@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {Object result = methodProxy.invokeSuper(o, objects);return result;}}public static void main(String[] args) {CGLibProxy cgLibProxy = new CGLibProxy();car instance = (car) cgLibProxy.getInstance(new car());instance.running();}
}

从代码可以看出CGLib 也是通过实现代理器的接口,然后再调用某个方法完成动态代理,不同的是CGLib在初始化被代理类时,是通过Enhancer对象把代理对象设置为被代理类的子类来实现动态代理:

Enhancer enhancer = new Enhancer();
//设置父类为实例类
enhancer.setSuperclass(this.target.getClass());
//回调方法
enhancer.setCallback(this);
//创建代理对象
return enhancer.create();

2.2.3 JDK Proxy 和 CGLib 的区别

  1. 来源:JDK Proxy 是JDK 自带的功能,CGLib 是第三方提供的工具

  2. 实现:JDK Proxy 通过拦截器加反射的方式实现;CGLib 基于ASM实现,性能比较高

  3. 接口:JDK Proxy 只能代理继承接口的类,CGLib 不需要通过接口来实现,它是通过实现子类的方式来完成调用

三、代理模式的应用场景

3.1 MapperProxyFactory

在MyBatis 中,也存在着代理模式的使用,比如MapperProxyFactory。其中的newInstance()方法就是生成一个具体的代理来实现功能,代码如下:

public class MapperProxyFactory<T> {private final Class<T> mapperInterface;private final Map<Method, MapperMethodInvoker> methodCache = new ConcurrentHashMap<>();public MapperProxyFactory(Class<T> mapperInterface) {this.mapperInterface = mapperInterface;}public Class<T> getMapperInterface() {return mapperInterface;}public Map<Method, MapperMethodInvoker> getMethodCache() {return methodCache;}// 创建代理类@SuppressWarnings("unchecked")protected T newInstance(MapperProxy<T> mapperProxy) {return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);}public T newInstance(SqlSession sqlSession) {final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);return newInstance(mapperProxy);}
}

3.2 Spring AOP

代理模式最常使用的一个应用场景就是在业务系统中开发一些非功能性需求,比如监控、统计、鉴权、限流、事务、日志等。将这些附加功能与业务功能解耦,放在代理类中统一处理,让程序员只需要关注业务方面的开发。而Spring AOP 的切面实现原理就是基于动态代理

​Spring AOP 的底层通过上面提到的 JDK Proxy 和 CGLib动态代理机制,为目标对象执行横向织入。当Bean实现了接口时, Spring就会使用JDK Proxy,在没有实现接口时就会使用 CGLib。也可以在配置中强制使用 CGLib:

<aop:aspectj-autoproxy proxy-target-class="true"/> 

3.3 RPC 框架的封装

RPC 框架的实现可以看作成是一种代理模式,通过远程代理、将网络同步、数据编解码等细节隐藏起来,让客户端在使用 RPC 服务时,不必考虑这些细节。

设计模式—代理模式以及动态代理的实现相关推荐

  1. 【设计模式】--- 装饰器模式、静态代理模式和动态代理模式

    文章目录 1 引子 2 业务场景介绍 3 静态代理模式 4 装饰器模式 5 动态代理模式 5.1 Proxy --- 具体的代理对象生成组件 5.2 InvocationHandler --- 封装被 ...

  2. 代理模式和动态代理模式_代理模式介绍

    代理模式和动态代理模式 代表:被选中或当选为他人投票或代理的人– Merriam-Webster . 委托模式:在软件工程中,委托模式是面向对象编程中的一种设计模式,其中,一个对象而不是执行其陈述的任 ...

  3. Java拾遗:007 - 代理模式与动态代理

    2019独角兽企业重金招聘Python工程师标准>>> 代理模式 在日常开发中我们可以会接手一些老的项目,有时连源码都没有,或者有时候我会需要对业务逻辑做一定增强(功能扩展,如:日志 ...

  4. Java篇 - 代理模式和动态代理实现原理

    设计模式中有一种模式叫代理模式,Spring框架离不开动态代理技术,Android hook技术用到了反射 + 动态代理,Framework中我们也经常看到各种proxy,如ApplicationTh ...

  5. 代理模式、动态代理和面向方面

    代理的意思很好理解,它借鉴了我们日常所用的代理的意思:就是本来该自己亲自去做的某件事,由于某种原因不能直接做,而只能请人代替你做,这个被你请来做事的人就是代理.比如过春节要回家,由于你要上班,没时间去 ...

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

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

  7. 20171030_chr_proxy 代理模式(动态代理)

    代理模式(动态代理) /20171030_chr_proxy/src/nuc/sw/dynamic/proxy/Dog.java package nuc.sw.dynamic.proxy;public ...

  8. 23种设计模式之代理模式(动态代理)

    代理模式 代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问.在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用. 代理模式的组成 抽 ...

  9. java动态代理_Java代理模式及动态代理详解

    Java的动态代理在实践中有着广泛的使用场景,比如最场景的Spring AOP.Java注解的获取.日志.用户鉴权等.本篇文章带大家了解一下代理模式.静态代理以及基于JDK原生动态代理. 代理模式 无 ...

最新文章

  1. 学习软件测试发展前景怎么样?有前途吗?
  2. 没解决这个7次方程问题,为何这三个数学家却很开心
  3. java matlab 矩阵_如何在MATLAB中将函数应用于矩阵的每一行/列?
  4. 2020\Simulation_1\5.数位递增的数
  5. 前端性能分析工具利器
  6. JS_typeof()函数返回类型总结
  7. Mac 下如何用命令行解压和压缩 rar 文件
  8. python爬取qq音乐歌词风变编程_爬取QQ音乐歌词
  9. 使用subs和evals函数对sympy中的符号进行赋值并且设置数值位数
  10. 【TF-Slim使用】
  11. 通过PSAM读CPU卡号流程小结
  12. char可以存储汉字吗?为什么
  13. 如何去除ie的select下拉框箭头图标
  14. 北京精雕自定义机床模型仿真
  15. @EnableConfigurationProperties注解
  16. 2021-09-02 集合基础知识
  17. MBA-day11数学-浓度问题-练习题
  18. 【实战2】爬取豆瓣Top250电影的海报
  19. 简要说明linux系统FHS结构;用自己的理解总结 文件管理,用户管理,组用户,权限管理相关的命令
  20. SI14T触摸按键芯片兼容替代TMS12

热门文章

  1. java在捕获异常并弹窗_Java异常的捕获与处理
  2. 【答学员问】面试谈的很好,为什么最后都没下offer
  3. 一张图看懂半导体产业链
  4. 温湿度传感器SHTC3驱动开发(一)小白也能轻松理解
  5. matlab初始化很久,【原创】Matlab初始化initializing极慢解决方法
  6. 李岳恒:2019,媒体的大变局
  7. GrapeCity Demo示例展示:如何用Spread.Sheets来创建应用|附演示文件下载
  8. c语言毕业程序设计论文,C语言程序设计论文
  9. Android使用MPAndroidChart画折线图
  10. 网络工具端口使用大全