【Java】代理模式(Proxy模式)详解
目录
1.代理模式
2.静态代理
2.1 通过继承实现静态代理
2.2 通过组合实现静态代理
3.动态代理
3.1 JDK动态代理
3.2 cglib动态代理
3.3 SpringAOP使用以及原理
4.原理理解
1.代理模式
代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。简单的说就是,我们在访问实际对象时,是通过代理对象来访问的,代理模式就是在访问实际对象时引入一定程度的间接性,因为这种间接性,可以附加多种用途。
这段话引用至该文章
2. 静态代理
简单来说代理模式就是将被代理类包装起来然后重新实现相同的方法,并且调用原来方法的同时可以在方法前后添加一个新的处理。而这种包装可以使用**继承**或者**组合**来使用。当我们调用的时候需要使用的是代理类的对象来调用而不是原来的被代理对象。
静态代理有两种实现方式:
通过继承实现
通过组合实现
2.1通过继承实现静态代理
通过继承被代理对象,重写被代理方法,可以对其进行代理。
优点:被代理类无需实现接口
缺点:只能代理这个类,要想代理其他类,要想代理其他类需要写新的代理方法。
cglib动态代理就是采用这种方式对类进行代理。不过类是由cglib
帮我们在内存中动态生成的。
public class Tank{public void move() {System.out.println("Tank moving cla....");}public static void main(String[] args) {new ProxyTank().move();}
}
class ProxyTank extends Tank{@Overridepublic void move() {System.out.println("方法执行前...");super.move();System.out.println("方法执行后...");}
}
2.2 通过组合实现静态代理
定义一个 Movable
接口被代理类需要和代理类都需要实现该接口。(接口在这里的目的就是起一个规范作用保证被代理类和代理类都实现了move()
方法)。代理类需要将该接口作为属性,实例化时需要传入该接口的对象,这样该代理类就可以实现代理所有实现Movable
的类了。
优点:可以代理所有实现接口的类。
缺点:被代理的类必须实现接口。
JDK动态代理就是采用的这种方式实现的。同样的代理类是由JDK自动帮我们在内存生成的。
public class Tank implements Movable{@Overridepublic void move() {System.out.println("Tank moving cla....");}public static void main(String[] args) {Tank tank = new Tank();new LogProxy(tank).move();}
}class LogProxy implements Movable{private Movable movable;public LogProxy(Movable movable) {this.movable = movable;}@Overridepublic void move() {System.out.println("方法执行前....");movable.move();System.out.println("方法执行后....");}
}
interface Movable {void move();
}
3.动态代理
动态代理其实本质还是 将被代理类包装一层,生成一个具有新的相同功能的代理类。
但是与静态代理不同的是,这个代理类我们自己定义的。而动态代理这个代理类是根据我们的提示动态生成的。
相比于静态代理,动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。
实现动态代理有几种方案:
- JDK动态代理
- CGLIB动态代理
- SpringAop
3.1 JDK动态代理
通过java提供的Proxy
类帮我们创建代理对象。
优点:可以生成所有实现接口的代理对象
缺点:JDK反射生成代理必须面向接口, 这是由Proxy的内部实现决定的。生成代理的方法中你必须指定实现类的接口,它根据这个接口来实现代理类生成的所实现的接口。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/*** 使用jdk的动态代理*/
public class Tank implements Movable{@Overridepublic void move() {System.out.println("Tank moving cla....");}public static void main(String[] args) {Tank tank = new Tank();// reflection 反射 通过二进制字节码分析类的属性和方法//newProxyInstance: 创建代理对象// 参数一: 被代理类对象// 参数二:接口类对象 被代理对象所实现的接口// 参数三:调用处理器。 被调用对象的那个方法被调用后该如何处理Movable o = (Movable)Proxy.newProxyInstance(Tank.class.getClassLoader(),new Class[]{Movable.class},new LogProxy(tank));o.move();}
}class LogProxy implements InvocationHandler {private Movable movable;public LogProxy(Movable movable) {this.movable = movable;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("方法:"+method.getName()+"()执行前");Object invoke = method.invoke(movable, args); // 此处相当于 movable.move()System.out.println("方法:"+method.getName()+"()执行后");return invoke;}
}interface Movable {void move();
}
3.2 cglib动态代理
CGLib(Code Generate Library) 与JDK动态代理不同的是,cglib生成代理是被代理对象的子类。因此它拥有继承方法实现静态代理的优点:不需要被代理对象实现某个接口。
缺点:不能给final
类生成代理,因为final
类无法拥有子类。
使用cglib
生成代理类也很简单,只要指定父类和回调方法即可
首先需要引入依赖
<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.2.12</version></dependency>
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;public class Main {public static void main(String[] args) {Enhancer enhancer = new Enhancer(); // 增强者enhancer.setSuperclass(Tank.class); // 指定父类enhancer.setCallback(new TimeMethodInterceptor()); // 当被代理对象的方法调用的时候会调用 该对象的interceptTank tank = (Tank)enhancer.create(); // 动态代理的生成tank.move(); // 生成之后会调用}
}class TimeMethodInterceptor implements MethodInterceptor{@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {System.out.println("生成的类名"+o.getClass().getName());System.out.println("生成的类的父类"+o.getClass().getSuperclass().getName());System.out.println("方法执行前,被代理的方法"+method.getName());Object result = null;result = methodProxy.invokeSuper(o, objects);System.out.println("方法执行后,被代理的方法"+method.getName());return result;}
}
class Tank{public void move(){System.out.println("Tank moving clacla....");}
}
SpringAOP使用以及原理
关于SpringAOP的使用以及原理可以查看我的这篇文章
SpringAOP使用以及原理
4. 原理理解
jdk动态代理Proxy原理。
Proxy.newProxyInstance 根据用户传进来的参数调用 asm
来生成Proxy$0
这个代理类。并返回一个实现了接口的代理类,用户实际调用时是调用的代理类的move()
方法。
扩展:asm
提供了一套API,java可以通过它来直接操控内存中的字节码文件即.class文件
。有人说因为有了反射java成了动态语言可以操控class文件,但其实java是因为有了ASM才可以真正算得上动态语言!因为反射只能拿到类的内部信息和执行,但是ASM可以直接在内存中修改class文件。
为了看清Proxy代理到底帮我们生成了什么,可以执行下面这段代码
package com.zxh.proxy;/*** Created by zxh on 2022/1/20*/
import sun.misc.ProxyGenerator;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Random;
public class Tank implements Movable{@Overridepublic void move() {System.out.println("Tank moving cla....");try {Thread.sleep(new Random().nextInt(9000));} catch (InterruptedException e) {e.printStackTrace();}}public static void main(String[] args) {Tank tank = new Tank();// 1.8以上版本执行这段话//System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles","true"); // 1.8及1.8以下执行这段话 具体的可以查看ProxyGenerator下的saveGeneratedFiles属性System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); Movable o = (Movable)Proxy.newProxyInstance(Tank.class.getClassLoader(), new Class[]{Movable.class},new LogHandler(tank) );o.move();}
}
class LogHandler implements InvocationHandler{Movable movable ;public LogHandler(Movable movable) {this.movable = movable;}// proxy@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("method:"+method.getName()+" start.....");Object invoke = method.invoke(movable, args); // 调用被代理对象 相当于tank.move()System.out.println("method:"+method.getName()+" end!");return null;}
}interface Movable {void move();
}
执行成功之后会自动生成一个这个文件
代码没有仔细研究,不过还是可以很明显的看到生成的这个类实现了Movable
方法
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//package com.zxh.proxy;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;final class $Proxy0 extends Proxy implements Movable {private static Method m1;private static Method m3;private static Method m2;private static Method m0;public $Proxy0(InvocationHandler var1) throws {super(var1);}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);}}public final void move() throws {try {super.h.invoke(this, m3, (Object[])null);} catch (RuntimeException | Error var2) {throw var2;} catch (Throwable var3) {throw new UndeclaredThrowableException(var3);}}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);}}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.zxh.proxy.Movable").getMethod("move");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());}}
}
【Java】代理模式(Proxy模式)详解相关推荐
- java 工厂模式详解_java 工厂模式的实例详解
java 工厂模式的实例详解 工厂方法中的"工厂"和我们平常理解的一样:用于生产产品. 而客户是要和产品打交道,所以工厂方法模式的意义在于把客户和产品分开,达到解耦和更灵活的目的. ...
- java新建一个女朋友_详解java创建一个女朋友类(对象啥的new一个就是)==建造者模式,一键重写...
创建一个女朋友,她有很多的属性,比如:性别,年龄,身高,体重,类型等等,虽然每个女朋友都有这些属性,但是每个人找女朋友的要求都是不一样的,有的人喜欢男的,有的人喜欢女的,有的喜欢胖的,不同的人可以根据 ...
- LVS-NAT和LVS-DR模式的实现详解
LVS-NAT和LVS-DR模式的实现详解 2011-09-10 10:51:08 我来说两句 收藏我要投稿 linux下LVS的实现 在2.4.23之前的linux内核想要使用LVS需要重新 ...
- RabbitMQ--交换器类型/队列模式--使用/教程/详解
原文网址:RabbitMQ--交换器类型/队列模式--使用/教程/详解_IT利刃出鞘的博客-CSDN博客 简介 本文介绍RabbitMQ的交换器类型和队列模式. 本内容也是Java后端面试常见的问题. ...
- android strictmode有什么作用,Android严苛模式StrictMode使用详解
StrictMode类是Android 2.3 (API 9)引入的一个工具类,可以用来帮助开发者发现代码中的一些不规范的问题,以达到提升应用响应能力的目的.举个例子来说,如果开发者在UI线程中进行了 ...
- android mvp模式例子_Android中mvp模式使用实例详解
MVP 是从经典的模式MVC演变而来,它们的基本思想有相通的地方:Controller/Presenter负责逻辑的处理,Model提供数据,View负 责显示.作为一种新的模式,MVP与MVC有着一 ...
- 【5G RLC】AM模式的数据传输详解
博主未授权任何人或组织机构转载博主任何原创文章,感谢各位对原创的支持! 博主链接 本人就职于国际知名终端厂商,负责modem芯片研发. 在5G早期负责终端数据业务层.核心网相关的开发工作,目前牵头6G ...
- STM32寄存器操作端口模式CRL/CRH详解
STM32寄存器操作端口模式CRL/CRH详解 首先,在开始讲解前,大家请先看如下一段代码: #define SDA_IN_24c02(){GPIOB->CRH&=0XFFF0FFFF; ...
- 【虚幻引擎UE】UE5 三种模式调用API详解(案例基于免费Varest插件)
[虚幻引擎UE]UE5 三种模式调用API详解(案例基于免费Varest插件) 想通过UE5 调用API实现GET和POST, 可以通过自己编写C++方法, 或基于相关HTTP请求插件, 如Vares ...
- 如何将3dmax软件的界面设置成经典模式?-图文详解
据3dmax软件的版本不同界面也会有所改变,很多朋友还是习惯于经典模式.那你知道如何将3dmax软件的界面设置成经典模式吗?本文小编将和大家分享将3dmax软件的界面设置成经典模式的方法与步骤,感兴趣 ...
最新文章
- sdutoj-4209-移动小球
- 如何书写高质量的jQuery代码
- NeurIPS 2019:计算机视觉论文回顾
- [BZOJ 1076][SCOI2008]奖励关(期望+状压Dp)
- JAVA的网络编程【转】
- _Linux进程信号详解
- 手把手带你学习如何在小程序、网页前端部署AI模型
- 落实业务服务管理从基础设施管理做起
- 计算机视觉论文-2021-09-14
- 三国群雄传ol服务器 修改,三国群英传OL DATA.PAK相关修改
- 开源微信小程序源码新版及教程
- TCP的短链接和长连接
- 平面设计内容包括什么,平面设计具体包括哪些内容
- ugui 转轮_Unity3D的FingerGesture插件
- 注册邮箱账号十大品牌分析
- 对数正态分布(Log-Normal Distribution)
- 数据库的数据存储文件
- 精妙绝伦的脑洞acm题
- 机器学习4——推荐系统
- 石英晶体谐振器的应用指南