一、基本概念

代理模式是对象的结构模式。

代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用(接口的引用)

二、静态代理

静态代理是指,代理类在程序运行前就已经定义好,其与**目标类(被代理类)**的关系在程序运行前就已经确立。

静态代理类似于企业与企业的法律顾问间的关系。法律顾问与企业的代理关系,并不是在“官司“发生后才建立的,而是之前就确立好的一种关系

而动态代理就是外面打官司一样,是官司发生了之后临时请的律师。

代理可以看做就是在被代理对象外面包裹一层(和装饰者类似但又不同):

案例: 比如我们有一个可以移动的坦克,它的主要方法是move(),但是我们需要记录它移动的时间,以及在它移动前后做日志,其静态代理的实现模式就类似下面的图:

两个代理类以及结构关系:

代码:

public interface Movable {void move();
}
public class Tank implements Movable {@Overridepublic void move() {// 坦克移动System.out.println("Tank Moving......");try {Thread.sleep(new Random().nextInt(5000)); // 随机产生 1~5秒, 模拟坦克在移动 } catch (InterruptedException e) {e.printStackTrace();}}
}复制代码

两个代理类: TankTimeProxyTankLogProxy:

public class TankTimeProxy implements Movable {private Movable tank;public TankTimeProxy(Movable tank) {this.tank = tank;}@Overridepublic void move() {// 在前面做一些事情: 记录开始时间long start = System.currentTimeMillis();System.out.println("start time : " + start);tank.move();// 在后面做一些事情: 记录结束时间,并计算move()运行时间long end = System.currentTimeMillis();System.out.println("end time : " + end);System.out.println("spend all time : " + (end - start)/1000 + "s.");}
}
public class TankLogProxy implements Movable {private Movable tank;public TankLogProxy(Movable tank) {this.tank = tank;}@Overridepublic void move() {// tank 移动前记录日志System.out.println("Tank Log start.......");tank.move();// tank 移动后记录日志System.out.println("Tank Log end.......");}
}复制代码

测试:

public class Client {public static void main(String[] args){Movable target = new TankLogProxy(new TankTimeProxy(new Tank()));    //先记录时间,再记录日志
//        Movable target = new TankTimeProxy(new TankLogProxy(new Tank())); //先记录日志,再记录时间target.move();}
}复制代码

输出:

Tank Log start.......
start time : 1551271511619
Tank Moving......
end time : 1551271514522
spend all time : 2s.
Tank Log end.......复制代码

这其中有两个很重要的点,那就是:

  • 两个代理对象内部都有着被代理对象(target)实现的接口的引用
  • 且两个代理对象都实现了被代理对象(target)实现的接口

三、基本动态代理

上面静态代理的缺点在哪?

现在单看做时间这个代理,如果我们现在多了一个飞机,飞机里面的方法是fly(),现在要给飞机做代理,那么我们不能用之前写的TankTimeProxy,我们需要额外的写一个PlaneTimeProxy,这明显是冗余代码,所以这就是静态代理最大的缺点,这可以用动态代理解决

动态代理是指,程序在整个运行过程中根本就不存在目标类的代理类(在JDK内部叫$Proxy0,我们看不到),目标对象的代理对象只是由代理生成工具(如代理工厂类) 在程序运行时由 JVM 根据反射等机制动态生成的。代理对象与目标对象的代理关系在程序运行时才确立。

对比静态代理,静态代理是指在程序运行前就已经定义好了目标类的代理类。代理类与目标类的代理关系在程序运行之前就确立了。

首先看动态代理的一些特点:

  • 动态代理不需要写出代理类的名字,你要的代理对象我直接给你产生,是使用的时候生成的;
  • 只需要调用Proxy.newProxyInstance()就可以给你产生代理类;

JDK动态代理相关API:

下面看使用动态代理解决上面的问题(可以用TimeProxy代理一切对象):

public interface Movable {void move();
}
public class Tank implements Movable {@Overridepublic void move() {// 坦克移动System.out.println("Tank Moving......");try {Thread.sleep(new Random().nextInt(5000)); // 随机产生 1~5秒, 模拟坦克在移动 } catch (InterruptedException e) {e.printStackTrace();}}
}复制代码

新增的飞机:

public interface Flyable {void fly();
}
public class Plane implements Flyable{@Overridepublic void fly() {System.out.println("Plane Flying......");try {Thread.sleep(new Random().nextInt(5000)); // 随机产生 1~5秒, 飞机在飞行 } catch (InterruptedException e) {e.printStackTrace();}}
}复制代码

我们的关键处理,即编写MyTimeProxyInvocationHandler:

// 静态代理做不到既为飞机做时间代理,又为坦克做时间代理,但是动态代理可以为所有对象做代理
public class MyTimeProxyInvocationHandler implements InvocationHandler {private Object target;//注意这里是 Object ,不是Movable或者Flyablepublic MyTimeProxyInvocationHandler(Object target) {this.target = target;}// proxy  : 代理对象  可以是一切对象 (Object)// method : 目标方法// args   : 目标方法的参数@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 在前面做一些事情: 记录开始时间long start = System.currentTimeMillis();System.out.println("start time : " + start);method.invoke(target, args); // 调用目标方法  invoke是调用的意思, 可以有返回值的方法(我们这里move和fly都没有返回值)// 在后面做一些事情: 记录结束时间,并计算move()运行时间long end = System.currentTimeMillis();System.out.println("end time : " + end);System.out.println("spend all time : " + (end - start)/1000 + "s.");return null;}
}复制代码

最后测试类:

public class Client {public static void main(String[] args){Movable tank = new Tank();//可以为所有对象产生时间代理的 InvocationHandlerMyTimeProxyInvocationHandler myInvocationHandler = new MyTimeProxyInvocationHandler(tank);Movable tankProxy = (Movable) Proxy.newProxyInstance(tank.getClass().getClassLoader(),tank.getClass().getInterfaces(),myInvocationHandler);tankProxy.move();System.out.println("--------------------");Flyable plane = new Plane();myInvocationHandler = new MyTimeProxyInvocationHandler(plane);// 为飞机产生代理, 为..产生代理,这样可以为很多东西产生代理,静态代理做不到Flyable planeProxy = (Flyable) Proxy.newProxyInstance(plane.getClass().getClassLoader(),plane.getClass().getInterfaces(),myInvocationHandler);planeProxy.fly();}
}复制代码

输出(同时为TankPlane做了代理):

start time : 1551275526486
Tank Moving......
end time : 1551275531193
spend all time : 4s.
--------------------
start time : 1551275531195
Plane Flying......
end time : 1551275532996
spend all time : 1s.复制代码

我们分析一下这个代理过程:

调用过程(重要):

  • JDK内部的Proxy类在内部创建了一个$Proxy0的代理对象(它实现了目标对象所在接口Movable
  • $Proxy0内部有InvocationHandler接口的引用,然后在$Proxy中调用了接口的invoke()方法;
  • 而我们将InvocationHandler接口的实现类传入了Proxy,所以我们在实现类中加入的前后逻辑就会得到执行;

如果这里还不够理解,可以看代理模式(二),会模拟实现JDK的底层实现。

四、CGLIB动态代理

问题: 使用 JDK 的 Proxy 实现代理,要求目标类与代理类实现相同的接口。若目标类不存在接口,则无法使用该方式实现

可以用 CGLIB 来解决上面的问题。

CGLIB 代理的生成原理是生成目标类的子类,而子类是增强过的,这个子类对象就是代理对象

所以,使用CGLIB 生成动态代理,要求目标类必须能够被继承,即不能是 final 的类

基本结构:

代码:

Tank类(没有接口)

// 没有实现接口
public class Tank   {public void move() {// 坦克移动System.out.println("Tank Moving......");try {Thread.sleep(new Random().nextInt(5000)); // 随机产生 1~5秒, 模拟坦克在移动 } catch (InterruptedException e) {e.printStackTrace();}}
}复制代码

MyCglibFactory类:

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;//需要实现MethodInterceptor, 当前这个类的对象就是一个回调对象
// MyCglibFactory 是 类A,它调用了Enhancer(类B)的方法: setCallback(this),而且将类A对象传给了类B
// 而类A 的 方法intercept会被类B的 setCallback调用,这就是回调设计模式
public class MyCglibFactory implements MethodInterceptor {  //public interface MethodInterceptor extends Callbackprivate Tank target;public MyCglibFactory(Tank target) {this.target = target;}public Tank myCglibCreator() {Enhancer enhancer = new Enhancer();// 设置需要代理的对象 :  目标类(target) , 也是父类enhancer.setSuperclass(Tank.class);// 设置代理对象, 这是回调设计模式:  设置回调接口对象 :enhancer.setCallback(this); // this代表当前类的对象,因为当前类实现了Callbackreturn (Tank) enhancer.create();}// 这个就是回调方法(类A的方法)@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {// 在前面做一些事情: 记录开始时间long start = System.currentTimeMillis();System.out.println("start time : " + start);method.invoke(target, args);// 在后面做一些事情: 记录结束时间,并计算move()运行时间long end = System.currentTimeMillis();System.out.println("end time : " + end);System.out.println("spend all time : " + (end - start)/1000 + "s.");return null;}
}复制代码

测试:

public class Client {public static void main(String[] args){Tank proxyTank = new MyCglibFactory(new Tank()).myCglibCreator();proxyTank.move();}
}复制代码

输出(进行了时间代理TimeProxy):

start time : 1551327522964
Tank Moving......
end time : 1551327526214
spend all time : 3s.复制代码

上面的设计模式用到了回调设计模式: 在 Java 中,类 A 调用类 B 中的某个方法 b(),然后类 B 又在某个时候反过来调用类 A中的某个方法 a(),对于 A来说,这个 a() 方法便叫做回调方法。
Java 的接口提供了一种很好的方式来实现方法回调。这个方式就是定义一个简单的接口,在接口之中定义一个我们希望回调的方法。这个接口称为回调接口。(Callback) 在前面的例子中,我们定义的 MyCglibFactory 类就相当于前面所说的 A类,而 Enhancer 类则是 B 类。A 类中调用了 Enhancer 类的 setCallback(this)方法,并将回调对象 this 作为实参传递给了Enhancer 类。Enhancer 类在后续执行过程中,会调用A类中的intercept()方法,这个 intercept()方法就是回调方法

转载于:https://juejin.im/post/5cffa9266fb9a07efe2db3c7

通俗易懂的讲解一下Java的代理模式相关推荐

  1. java设计模式代理模式_Java中的代理设计模式

    java设计模式代理模式 代理对象或代理对象为另一个对象提供占位符,以控制对该对象的访问. 代理充当原始对象的轻量级版本或简化版本. 它支持与原始对象相同的操作,但可以将那些请求委托给原始对象以实现它 ...

  2. 黑马程序员——Java的代理模式

    ------- android培训.java培训.期待与您交流! ---------- Java中代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问.在某些情况下,一个客户不想或者不能直接 ...

  3. 通俗易懂说java设计模式-代理模式

    基本概念 代理模式,和我们在生活中所理解的"代理"思想并没有太大的区别.代理模式的实现需要我们设计一个代理类,在不直接访问被代理对象的前提下,通过代理类实现对被代理对象的间接访问, ...

  4. JAVA设计模式--代理模式(静态)

    目录 一.什么是代理模式 二.静态代理模式的结构 三.静态代理模式应用举例 应用一 应用二 四.代理模式的应用场景 五.静态代理模式的特点 一.什么是代理模式 代理(Proxy)模式为其他对象提供一种 ...

  5. Java设计模式(代理模式-模板方法模式-命令模式)

    Java设计模式Ⅴ 1.代理模式 1.1 代理模式概述 1.2 静态代理 1.2.1 静态代理概述 1.2.2 代码理解 1.3 动态代理之JDK代理 1.3.1 动态代理之JDK代理概述 1.3.2 ...

  6. Java设计模式—代理模式

    1. 什么是代理模式? 代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问. 2. 代理模式有什么好处? 在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标 ...

  7. java设计模式-代理模式初探

    根据圣思园的视频,整理的java代理模式. 代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问. 在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间 ...

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

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

  9. JAVA设计模式 - 代理模式

    在对象的一个业务方法完成之后, 有时候我们可能需要去添加一些新的功能(前置校验等). 但我们又不想更改原来的代码 , 代理模式就为我们提供了一种解决方案 . 1 . 代理模式的定义 代理模式就是在不改 ...

最新文章

  1. 深入理解pytorch中计算图的inplace操作
  2. C# 集合类(三):Stack
  3. 音视频技术开发周刊 | 146
  4. Angular里ngModel双向绑定的一个使用例子
  5. 判断是否为二叉排序树的递归算法_左神直通BAT算法笔记(基础篇)
  6. Hadoop官方文档翻译——MapReduce Tutorial
  7. Java 8 Friday Goodies:SQL ResultSet流
  8. 概率论与数理统计思维导图知识框架_考研概率论与数理统计 综合题型秘籍思维导图① 随机变量1~3章 [21考研上岸之旅]...
  9. python安装失败未指定_windows 7 32bit安装 python3.5.0 安装错误 0x80240017 -未指定错误...
  10. python文件中执行py文件
  11. linux/windows对应的软件
  12. 为何国内“程序员”是秃头代名词?来看看国外程序员的一天
  13. Science揭秘:为什么“好记性不如烂笔头”?
  14. vue滑杆_Vue无限滑杆组件
  15. 到另外一个城市工作时,五险一金怎么办?
  16. 基于Quartz.net 的任务调度平台Weiz.TaskManager
  17. 十年前的AlexNet,今天的NeurIPS 2022时间检验奖
  18. 五种提高 SQL 性能的方法 [来源:MSDN Johnny Papa]
  19. linux环境下常用的打包、压缩、解压命令(tar、gzip、bzip2、zip)
  20. 中国天气网天气预报API接口城市代码(XML格式,信息全)

热门文章

  1. java 根据预览图片上传_JavaSctit 利用FileReader和滤镜上传图片预览功能
  2. java 抛出403_java.io.IOException:服务器返回HTTP响应代码:403 [URL]
  3. python 廖雪峰_廖雪峰的Python系列教程(20)——高级特性之生成器
  4. python 3.7.5下载_you-get简介:功能强大的命令行工具,随心所欲下载视频
  5. php smarty 分页类,Smarty分页类Smartypaginate
  6. tomcat安装_基于CentOS 一键安装tomcat脚本
  7. java线程代码实现_Java 多线程代码实现讲解
  8. 矩阵化为行最简形矩阵计算器_[内附完整源码和文档] 基于C++的小型特殊计算器...
  9. 如何步入深度学习刷榜第一重境界
  10. 全球与中国文件夹架市场研究与商业模式创新分析报告2022-2028年