当前位置: 首页 > news >正文

Java设计模式之代理模式(一)

什么是代理?可以理解为其他对象提供一种代理以控制对这个对象的访问。

举个例子,生活中的外卖平台,店铺是制作外卖的,然后放到平台上售卖。这里的店铺就是真实角色,为了能够让店铺不用担心销售等问题,从而能够更加专注做外卖,所以店铺的外卖都会放到平台上面。这里平台就是代理。平台代店家出售,而店家只需要做外卖就好了。

代理模式和装饰器模式的区别:装饰器模式为了增强目标对象的功能,而代理模式是为了代理或增强目标对象的功能,本质上两者都可以为目标对象添加一些功能。

代理模式可以分为静态代理和动态代理。

一、静态代理

静态代理:程序在运行之前需要手动创建代理类,代理类与目标类实现同一个接口。代理类为目标类增加一些额外的功能。

1、创建接口:

package com.statics.proxy.interfaces;/*** @Author: 倚天照海* @Description:*/
public interface HotelInterface {public void cook();}

2、创建实现类

package com.statics.proxy.impl;import com.statics.proxy.interfaces.HotelInterface;/*** @Author: 倚天照海* @Description:*/
public class HuangMenJi implements HotelInterface {@Overridepublic void cook() {System.out.println("黄焖鸡米饭很好吃!");}}

3、创建代理类

package com.statics.proxy.impl;import com.statics.proxy.interfaces.HotelInterface;/*** @Author: 倚天照海* @Description:*/
public class MeiTuan implements HotelInterface {private HuangMenJi huangMenJi;public MeiTuan(HuangMenJi huangMenJi) {this.huangMenJi = huangMenJi;}@Overridepublic void cook() {discount();huangMenJi.cook();pay();}private void discount(){System.out.println("5折优惠,量大实惠,欢迎来购");}private void pay(){System.out.println("快捷付款,一键支付");}}

4、创建测试类

package com.statics.proxy;import com.statics.proxy.impl.HuangMenJi;
import com.statics.proxy.impl.MeiTuan;/*** @Author: 倚天照海* @Description:*/
public class ProxyTest {public static void main(String[] args) {HuangMenJi huangMenJi = new HuangMenJi();MeiTuan meiTuan = new MeiTuan(huangMenJi);meiTuan.cook();}}

输出结果:

二、JDK动态代理

动态代理是一种在运行时动态地创建代理对象并调用代理方法的机制。动态代理不需要定义代理类的.java源文件,在程序运行时由JVM根据反射机制动态的创建代理类对象。动态代理其实就是jdk运行期间,动态创建class字节码并加载到JVM。

动态代理的优点是提高了代码的灵活性和扩展性,具有解耦的意义。缺点是生成代理对象和调用代理方法需要耗费时间。

动态代理主要有两种实现方式:JDK动态代理和CGLIB动态代理

JDK动态代理其实是通过反射机制实现的。

使用JDK动态代理主要有以下几个步骤:

1.编写一个被代理的接口和接口实现类;

2.自定义一个调用处理器,并实现InvocationHandler接口,重写invoke方法;

3.在invoke方法中通过反射调用被代理方法(即上面接口实现类中的方法);

4.创建接口实现类的实例,并将该实例作为参数来创建自定义调用处理器的实例;

5.通过Proxy类的静态方法newProxyInstance获取代理对象(实现接口),并通过代理对象调用接口中的目标方法(实际是代理对象中重写后的目标方法)。

InvocationHandler是proxy代理实例调用处理程序实现的一个接口,每一个proxy代理实例都与一个调用处理器进行关联,在代理实例调用接口方法时,方法调用会被转发到调用处理器的invoke方法,即代理对象调用接口方法时,在内部会调用InvocationHandler的invoke方法。

Proxy类就是用来创建一个代理对象的类,其中最常用的方法是newProxyInstance静态方法。这个方法的作用就是创建一个代理类对象,它接收三个参数,三个参数的含义如下所示:

loader:一个classloader对象,定义了由哪个classloader对象对生成的代理类进行加载。

interfaces:一个interface对象数组,表示给代理对象提供一组接口对象,也就是声明了代理类实现了这些接口,代理类就可以调用接口中声明的所有方法。

h:一个InvocationHandler对象,表示的是当动态代理对象调用接口方法时,会关联到指定的InvocationHandler对象上,并调用指定的InvocationHandler的invoke方法。

示例代码:

被代理的接口及实现类:

package com.tx.study.others.proxy.jdkProxy;/*** @Author: 倚天照海*/
public interface Manager {public void modify();}package com.tx.study.others.proxy.jdkProxy;/*** @Author: 倚天照海*/
public class ManagerImpl implements Manager {@Overridepublic void modify() {System.out.println("*******实现类的modify()方法被调用*********");}}

自定义的调用处理器

package com.tx.study.others.proxy.jdkProxy;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;/*** @Author: 倚天照海*/
public class ManagerInvocationHandler implements InvocationHandler {private Object object = null;public ManagerInvocationHandler(Object object) {this.object = object;}/*** 重写了InvocationHandler接口中的invoke方法(该方法在何处被调用?)* @param proxy 代理对象* @param method 被代理类中的被代理方法的Method对象* @param args 被代理方法的参数* @return 被代理方法的返回值,如果被代理方法是被void修饰的,则返回null* @throws Throwable 可能会抛出的异常*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//在真实的对象执行之前可以添加自己的操作System.out.println("do something before method");//通过反射调用被代理方法,即接口实现类中的方法Object ret = method.invoke(this.object, args);//在真实的对象执行之后可以添加自己的操作System.out.println("do something after method");return ret;}}

获取代理对象并调用方法

package com.tx.study.others.proxy.jdkProxy;import java.lang.reflect.Proxy;/*** @Author: 倚天照海*/
public class JdkProxyTest {public static void main(String[] args) {//原对象(被代理对象)Manager managerImpl = new ManagerImpl();//业务代理类ManagerInvocationHandler businessHandler = new ManagerInvocationHandler(managerImpl);//获得代理类($Proxy0 extends Proxy implements Manager)的实例Manager managerProxy = (Manager) Proxy.newProxyInstance(managerImpl.getClass().getClassLoader(),managerImpl.getClass().getInterfaces(),businessHandler);//通过代理对象调用代理类中重写的接口方法(重写的invoke方法在此处被调用)managerProxy.modify();}}

执行结果:

do something before method
*******实现类的modify()方法被调用*********
do something after method

拓展:生成jdk动态代理类的源码:

package com.tx.study.others.proxy.jdkProxy;import sun.misc.ProxyGenerator;import java.io.*;/*** @Author: 倚天照海* @Description: 生成jdk动态代理类的源码*/
public class JdkProxySourceCodeUtil {public static void main(String[] args) {//指定生成的代理类名称String proxyClassName = "$proxy0";Class<Manager> interfaceClass = Manager.class;//指定保存代理类的路径String path = "D:\\ProgramFiles\\workspace\\myself\\data-query\\tx-study\\src\\main\\java\\com\\tx\\study\\others\\proxy\\jdkProxy\\";path = path.concat(proxyClassName).concat(".class");writeClassToFile(proxyClassName,interfaceClass,path);}private static <T> void writeClassToFile(String proxyClassName, Class<T> interfaceClass, String path){byte[] proxyClass = ProxyGenerator.generateProxyClass(proxyClassName, new Class[]{interfaceClass});OutputStream os = null;try {os = new FileOutputStream(new File(path));os.write(proxyClass);os.flush();} catch (IOException e) {e.printStackTrace();} finally {try {if (os != null){os.close();}} catch (IOException e) {e.printStackTrace();}}}}

由上可知,Proxy类中的newProxyInstance静态方法被调用,该方法返回一个代理对象,然后向上转型为其对应的接口。接下来简单看一下Proxy类中的部分源码(如下),主要看一下静态方法newProxyInstance。在该方法中调用getProxyClass0(loader,intfs)方法,根据类加载器和接口数组获取动态代理类的字节码文件对象,进而获取构造器对象,最后执行cons.newInstance(new Object[]{h})(根据指定的调用处理器对InvocationHandler进行初始化),通过反射调用有参构造器创建代理对象,并返回该代理对象。

public class Proxy implements java.io.Serializable {private static final long serialVersionUID = -2222568056686623797L;/** parameter types of a proxy class constructor */private static final Class<?>[] constructorParams ={ InvocationHandler.class };/** a cache of proxy classes */private static final WeakCache<ClassLoader, Class<?>[], Class<?>>proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());/** the invocation handler for this proxy instance. */protected InvocationHandler h;private Proxy() {}//有参构造器创建代理对象(为InvocationHandler初始化)protected Proxy(InvocationHandler h) {Objects.requireNonNull(h);this.h = h;}@CallerSensitivepublic static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException {Objects.requireNonNull(h);final Class<?>[] intfs = interfaces.clone();final SecurityManager sm = System.getSecurityManager();if (sm != null) {checkProxyAccess(Reflection.getCallerClass(), loader, intfs);}/* Look up or generate the designated proxy class. *///根据类加载器和接口数组获取动态代理类的字节码文件对象Class<?> cl = getProxyClass0(loader, intfs);/* Invoke its constructor with the designated invocation handler. */try {if (sm != null) {checkNewProxyPermission(Reflection.getCallerClass(), cl);}//通过字节码文件对象获取构造器对象final Constructor<?> cons = cl.getConstructor(constructorParams);final InvocationHandler ih = h;if (!Modifier.isPublic(cl.getModifiers())) {AccessController.doPrivileged(new PrivilegedAction<Void>() {public Void run() {cons.setAccessible(true);return null;}});}//获取代理对象(通过反射调用有参构造器创建对象)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);}}//根据类加载器和接口数组获取动态代理类的字节码文件对象private static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) {if (interfaces.length > 65535) {throw new IllegalArgumentException("interface limit exceeded");}// If the proxy class defined by the given loader implementing the given interfaces exists, // this will simply return the cached copy; otherwise, it will create the proxy class// via the ProxyClassFactoryreturn proxyClassCache.get(loader, interfaces);}//省略了Proxy类中的其他内部类和方法
}

在自定义调用处理器中重写了InvocationHandler接口中的invoke方法,但是,该方法在何处被调用?接下来看一下本例的$Proxy0类的源码。

下面是本例的代理类$Proxy0的源码:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0 extends Proxy implements Manager {private static Method m1;private static Method m0;private static Method m3;private static Method m2;static {try {m1 = Class.forName("java.lang.Object").getMethod("equals",new Class[] { Class.forName("java.lang.Object") });m0 = Class.forName("java.lang.Object").getMethod("hashCode",new Class[0]);m3 = Class.forName("com.tx.study.others.proxy.jdkProxy.Manager").getMethod("modify",new Class[0]);m2 = Class.forName("java.lang.Object").getMethod("toString",new Class[0]);} catch (NoSuchMethodException nosuchmethodexception) {throw new NoSuchMethodError(nosuchmethodexception.getMessage());} catch (ClassNotFoundException classnotfoundexception) {throw new NoClassDefFoundError(classnotfoundexception.getMessage());}} //静态代码块结束//在代理类$Proxy0的构造方法中调用父类Proxy的构造方法,初始化InvocationHandlerpublic $Proxy0(InvocationHandler invocationhandler) {super(invocationhandler);}@Overridepublic final boolean equals(Object obj) {try {return ((Boolean) super.h.invoke(this, m1, new Object[] { obj })) .booleanValue();} catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);}}@Overridepublic final int hashCode() {try {return ((Integer) super.h.invoke(this, m0, null)).intValue();} catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);}}//自定义的InvocationHandler中重写的invoke方法就是在此处被调用的public final void modify() {try {super.h.invoke(this, m3, null);return;} catch (Error e) {} catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);}}@Overridepublic final String toString() {try {return (String) super.h.invoke(this, m2, null);} catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);}}
}

本例的代理类$Proxy0继承了Proxy类,并实现了Manager接口(上面编写的需要被代理的接口),所以,在$Proxy0类中也实现了Manager接口中的modify()方法。在$Proxy0类中通过反射获取到了实现的modify()方法(由于此例中Manager接口中只有modify一个方法,所以就获取到了modify这一个方法,如果Manager接口中有多个方法,那么在$Proxy0类中通过反射将获取所有的实现方法),即Method m3方法。在$Proxy0类的modify方法中调用父类Proxy中h的invoke()方法,即InvocationHandler.invoke()方法。由于自定义调用处理器实现了InvocationHandler接口,并重写了invoke方法,由多态性可知,实际上调用的是重写后的invoke方法。所以,在上面的例子中,当创建了代理对象managerProxy,并通过代理对象调用managerProxy.modify()方法时,实际上是调用代理类$Proxy0中重写的modify方法,并在modify方法中调用了自定义调用处理器中重写的invoke方法。因此,自定义调用处理器中重写的invoke方法是在执行managerProxy.modify()方法时被调用的


http://www.mrgr.cn/news/59910.html

相关文章:

  • 对传输质量评价体系的评价
  • 实现mysql和es的数据同步以及es的集群
  • word表格跨页后自动生成的顶部横线【去除方法】
  • 华为ensp静态路由,浮动路由,缺省路由讲解及配置
  • MySQL——test4(综合练习)
  • python psutil 模块概述
  • 《模型部署》—— 客户端与服务端之间的交互实现模型的输出结果
  • 第十一部分 Java 数据结构及集合
  • 动态规划 —— 斐波那契数列模型-解码方法
  • HarmonyOS NEXT 应用开发实战(八、知乎日报List列表下拉刷新及上滑加载更多分页的实现)
  • 【笔记】Diffusion Model 扩散过程(熵增过程:从有序变为无序):在原始分布上逐步的加高斯噪声,加到最后这个分布就变成一个各项独立的高斯分布
  • 常用 Web 框架
  • 我的电脑问题
  • 使用openssl验证https配置的ssl证书是否可以正常访问
  • Mybatis-plus-扩展功能
  • linux中级(NFS服务器)
  • Linux TCP CC状态机
  • Puppeteer 与浏览器版本兼容性:自动化测试的最佳实践
  • uniapp实现与webview之间的相互通讯
  • Vue项目GET请求正常,POST请求却失效?揭秘Mock服务背后的故事
  • 创建WBS项目管理过程
  • 不小心drop column了一个列,真的凉凉了吗?
  • linux驱动-引入pinctrl子系统
  • 离散化步骤
  • 群控系统服务端开发模式-应用开发-业务架构逻辑开发BaseAPI
  • 企业信息化与数字化 - 信息化是基础,数字化是未来