【java设计模式】——代理设计模式,两种举例说明

代理设计模式

1.介绍

Spring 框架中AOP底层使用动态代理设计模式。通过学习动态代理设计模式可以很好的理解Spring框架AOP底层

代理模式(Proxy)是GoF23种设计模式之一。所谓代理模式是指客户端并不直接调用实际的对象,而是通过调用代理,来间接的调用实际的对象。

image-20211224090242901

代理设计模式包括:静态代理和动态代理。

静态代理:代理对象由程序员自己编写,里面提供硬编码方式来访问调用者

动态代理:

  • JDK动态代理:一种基于接口的代理实现
  • Cglib动态代理:一种基于继承的代理实现

代理设计模式优点:

  • 保护代理对象。客户不能直接访问真实对象。只能通过代理对象进行访问
  • 可扩展性。复合开闭原则,可以实现在不修改真实代理对象情况下,进行扩展。
  • 对于一些访问困难的真实对象,可以使用代理对象,更轻松的访问。

代理设计模式缺点:

  • 系统复杂性增加。类的数目增加,代码相对更复杂一点。
  • 性能降低。每次访问真实对象,只能通过代理对象。

在这里插入图片描述

2.静态代理示例

以房屋买卖为例子(简易流程),客户想要买房,找中介,由中介带看房东的房子。

客户看好了,想要买,房东也同意买了,中介收取客户的中介费。

daili

创建房东(真实对象)

新建com.spring.proxy.staticproxy.Fangdong

public class Fangdong {public void bigHouse(){System.out.println("房东的大房子");}
}

创建中介类(代理对象)

新建com.spring.proxy.staticproxy.Zhongjie

public class Zhongjie {public void zhongjie(){System.out.println("约谈客户,进行带看");// 扩展功能Fangdong fangdong = new Fangdong();fangdong.bigHouse();System.out.println("交易成功,收取中介费"); // 扩展功能}
}

创建客户类(调用者)

com.spring.proxy.staticproxy.Kehu

public class Kehu {public static void main(String[] args) {Zhongjie zhongjie = new Zhongjie();zhongjie.zhongjie();}
}

3.动态代理示例

3.1JDK动态代理

JDK动态代理是基于接口来实现的,底层是基于Java 反射技术实现的。

创建接口

创建接口com.spring.proxy.jdkproxy.House

因为JDK动态代理要求调用者必须实现接口。所以先建立接口

public interface bigHouse {void bighose();
}

创建真实对象类

创建类com.spring.proxy.jdkproxy.Fangdong

/*** JDK动态代理强制要求:真实的被调用的类必须实现接口*/
public class FangDong implements bigHouse{@Overridepublic void bighose() {System.out.println("大房子");}
}

创建代理类

创建com.spring.proxy.jdkproxy.Zhongjie

/*** 在JDK动态代理底层是基于java的反射机制实现的。本质上就是方法的调用** 负责调用真实对象方法的类,必须实现InvocationHandler接口*/
public class ZhongJie implements InvocationHandler {private FangDong fangDong;public ZhongJie(FangDong fangDong) {this.fangDong = fangDong;}/**** @param proxy 产生的代理对象* @param method 真实对象中被调用的方法对象* @param args 被调用方法的参数* @return 被调用方法的返回值,都当做invoke方法的返回值* @throws Throwable*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("带看");//坑:千万不要用invoke方法的第一个参数proxy,他是代理对象,而不是真实对象Object result = method.invoke(fangDong, args);System.out.println("中介费");return result;}
}

创建客户类

创建com.spring.proxy.jdkproxy.Kehu

public class KeHu {public static void main(String[] args) {/*** 参数1:ClassLoader   在当前程序中,所有类的类加载器都是同一个对象*       作用:让生成的代理对象立即生效,(invoke中的proxy对象)* 参数2:Class[],放接口,告诉编译器,调用的是哪个方法*       作用:数组中放哪个接口,接口中的方法对象就会传递给invoke中的method方法对象* 参数3:InvocationHandler对象,负责调用真实对象的处理类,就是ZhongJie*///动态代理:动态:不是就调用某个对象的方法,而是想调哪个对象都可以InvocationHandler zhongJie = new ZhongJie(new FangDong());bigHouse bigHouse = (bigHouse) Proxy.newProxyInstance(Kehu.class.getClassLoader(), new Class[]{bigHouse.class}, 																					zhongJie);bigHouse.bighose();}
}
第二例子
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;// 定义接口
interface Hello {void sayHello();
}// 实现接口的类
class HelloImpl implements Hello {@Overridepublic void sayHello() {System.out.println("Hello, World!");}
}// 创建InvocationHandler实例,用于处理代理逻辑
class HelloProxy implements InvocationHandler {private Object target;public HelloProxy(Object target) {this.target = target; // 保存目标对象的引用}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("Before invoking"); // 在调用目标方法前执行一些逻辑Object result = method.invoke(target, args); // 调用目标对象的方法System.out.println("After invoking"); // 在调用目标方法后执行一些逻辑return result; // 返回结果}
}public class Main {public static void main(String[] args) {// 创建目标对象Hello hello = new HelloImpl();// 创建代理对象Hello proxy = (Hello) Proxy.newProxyInstance(hello.getClass().getClassLoader(), // 使用目标对象的类加载器hello.getClass().getInterfaces(), // 获取目标对象实现的接口列表new HelloProxy(hello) // 指定InvocationHandler实例);// 调用代理方法proxy.sayHello();}
}

3.2Cglib动态代理

Cglig是基于继承的,是第三方提供的技术,需要导入jar包,并且。

    <dependencies><dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.3.0</version></dependency></dependencies>

创建房东类

/*** 真实被调用对象*/
public class FangDong {public void bigHouse(){System.out.println("大房子");}
}

创建中介类

/*** 作用:调用真实对象的方法*/
public class ZhongJie implements MethodInterceptor {/**** @param o 目标对象:即FangDong对象* @param method 目标方法* @param objects 目标方法参数* @param methodProxy 生成的代理对象(即FangDong的子类)* @return 目标方法返回值* @throws Throwable*/@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {System.out.println("带看");Object result = methodProxy.invokeSuper(o, objects);System.out.println("中介费");return  result;}
}

创建客户类

public class KeHu {public static void main(String[] args) {//所有方法都在Enhancer中Enhancer enhancer = new Enhancer();//1.setSuperClass(真实是哪个类)  真实是哪个类,根据这个类创建代理对象,即FangDong的子类enhancer.setSuperclass(FangDong.class);//2.setCallBack(对象)    设置由哪个类作为增强实现类enhancer.setCallback(new ZhongJie());//3.create()   创建代理对象,返回值为object类型,本质为代理对象FangDong fangDong = (FangDong) enhancer.create();fangDong.bigHouse();}}

测试结果,发现出现异常,这是因为Java 17版本中的Java Platform Module System(java 9就开始有了)引起的,特别是强封装的实现。

Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make protected final java.lang.Class java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain) throws java.lang.ClassFormatError accessible: module java.base does not "opens java.lang" to unnamed module @1376c05c

把代码原封不动的放在Java 11的环境中就可以使用。

如果必须要在Java17中使用添加JVM参数,表示允许使用未命名模块。

--add-opens java.base/java.lang=ALL-UNNAMED

使用CGLib实现动态代理需要引入相关的依赖库。在示例中,我们将使用cglib-nodep库来创建代理对象。下面是一个使用CGLib实现动态代理的示例代码:

首先,确保已添加CGLib的依赖。对于Maven项目,可以在pom.xml文件中添加以下依赖:

<dependencies><dependency><groupId>cglib</groupId><artifactId>cglib-nodep</artifactId><version>3.3.0</version></dependency>
</dependencies>

然后,编写以下示例代码:

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;import java.lang.reflect.Method;// 目标类
class Hello {public void sayHello() {System.out.println("Hello, World!");}
}// 创建MethodInterceptor实例,用于处理代理逻辑
class HelloInterceptor implements MethodInterceptor {@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("Before invoking"); // 在调用目标方法前执行一些逻辑Object result = proxy.invokeSuper(obj, args); // 调用目标方法System.out.println("After invoking"); // 在调用目标方法后执行一些逻辑return result; // 返回结果}
}public class Main {public static void main(String[] args) {// 创建Enhancer对象Enhancer enhancer = new Enhancer();enhancer.setSuperclass(Hello.class); // 设置父类为目标类enhancer.setCallback(new HelloInterceptor()); // 设置回调对象// 创建代理对象Hello proxy = (Hello) enhancer.create();// 调用代理方法proxy.sayHello();}
}

在上述示例中,我们首先定义了一个Hello类作为目标类。然后,创建了一个HelloInterceptor类实现MethodInterceptor接口,用于在代理对象的方法执行前后进行处理。接下来,使用Enhancer类创建代理对象,并设置目标类和回调对象。最后,通过调用代理对象的方法来触发代理逻辑。

运行以上代码,输出结果将会是:

Before invoking
Hello, World!
After invoking

这就是一个使用CGLib实现的动态代理的例子。注意,在使用CGLib时,需要引入相应的依赖并设置目标类和回调对象。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/205325.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于ssm家庭理财系统源码和论文

基于ssm家庭理财系统源码和论文743 idea 数据库mysql5.7 数据库链接工具&#xff1a;navcat,小海豚等 环境&#xff1a; jdk8 tomcat8.5 开发技术 ssm 摘要 随着Internet的发展&#xff0c;人们的日常生活已经离不开网络。未来人们的生活与工作将变得越来越数字化&#xff…

【文件上传系列】No.0 利用 FormData 实现文件上传、监控网路速度和上传进度(原生前端,Koa 后端)

利用 FormData 实现文件上传 基础功能&#xff1a;上传文件 演示如下&#xff1a; 概括流程&#xff1a; 前端&#xff1a;把文件数据获取并 append 到 FormData 对象中后端&#xff1a;通过 ctx.request.files 对象拿到二进制数据&#xff0c;获得 node 暂存的文件路径 前端…

PDF编辑器免费版有哪些?哪个好用?

日常工作和学习中,普通的PDF的阅读器通常无法进行编辑,更别说逆向导出成为Word、PPT或者Excel了。而且。市场上很多PDF编辑器还要收费。那么问题来了,PDF编辑器免费版有哪些呢?这就给大家安利几款,强烈建议收藏! PDF编辑器免费版一:PDF24 这款编辑器的功能有主要有PDF的合并、…

LiDAR 城市模型的 3D Python 工作流

1.引言 LiDAR&#xff08;Light Detection and Ranging&#xff09;是一种通过测量激光束从发射到返回的时间来测量距离&#xff0c;从而生成物体的三维表面模型的技术。这种技术在城市建模中具有广泛的应用&#xff0c;可以用于城市规划、环境监测、交通仿真等领域。而使用Pyt…

Linux查看命令的绝对路径

linux查看命令的绝对路径 在Linux中&#xff0c;可以使用以下命令来查看命令的绝对路径&#xff1a; 1、which 命令名 例如&#xff0c;要查看chronyc命令的绝对路径&#xff0c;可以运行&#xff1a; which chronyc 2、whereis 命令名 例如&#xff0c;要查看chronyc命令…

HMMER学习——(待完善)

HMMER profile HMM files A HMMER profile file looks like this, with …’s marking elisions made for clarity and space: 1.全基因组基因家族的分析系列之HMMER3.1使用 2. hmmalign - align sequences to an HMM profile

springboot集成邮箱验证功能

准备工作 开启SMTP服务 前往你的邮箱网站&#xff0c;以网易邮箱为例&#xff0c;打开网易邮箱地址&#xff0c;登录你的邮箱&#xff0c;进入邮箱管理后台界面。点击“设置”》》“POP3/SMTP/IMAP”后&#xff0c;点击开启SMTP服务即可。 技术实现 Spring Boot 发送邮件验证…

linux学习之详解文件

目录 1.先认识文件 2.c语言中常用文件接口 fopen&#xff08;打开文件&#xff09; 3.系统接口操作文件 open write 文件的返回值以及打开文件的本质 理解struct_file内核对象 了解文件描述符&#xff08;fd&#xff09;分配规则 重定向 dup接口 标准错误流 文件缓冲…

YITH WooCommerce Product Bundles Premium电商商城产品捆绑销售高级版

点击阅读YITH WooCommerce Product Bundles Premium电商商城产品捆绑销售高级版原文 YITH WooCommerce Product Bundles Premium电商商城产品捆绑销售高级版的作用是在您的商店中创建特别优惠&#xff0c;将产品捆绑在一起提供折扣和特价。 您如何从中受益&#xff1a; 您将…

Swift 中 User Defaults 的读取和写入

文章目录 前言介绍 User Defaults共享 User DefaultsUser Defaults 存储数据类型响应更改监控 User Defaults 更改覆盖User Defaults 设置考虑的替代方案Keychain 用于安全性用于跨平台的 CloudKit 结论 前言 User Defaults 是 Swift 应用程序存储在应用启动之间保持的首选项的…

Nginx实战教程二

一.介绍 本文介绍SPRINGBOOTVUE项目配置API服务器的两种情况 NGINX 配置VUE项目 二.vue项目和后端api接口不在同一台服务器 如果打包好的vue项目应用(dist) 和后端 api 接口没有运行在同一个主机上 此时需要在开发环境下将 API 请求代理到 API 所在服务器。通过配置 vue.confi…

基于ssm实验室课程管理系统源码和论文

idea 数据库mysql5.7 数据库链接工具&#xff1a;navcat,小海豚等 环境&#xff1a; jdk8 tomcat8.5 摘 要 随着科学实验规模的不断扩大&#xff0c;实验室课程数量的急剧增加&#xff0c;有关实验室课程的各种信息量也在不断成倍增长。面对庞大的信息量&#xff0c;就需要有…

HBase 使用JDK21

HBase 使用JDK21 启动zookeeper和hadoop 创建软件目录 mkdir -p /opt/soft cd /opt/soft下载软件 wget https://dlcdn.apache.org/hbase/2.5.6/hbase-2.5.6-hadoop3-bin.tar.gz解压 hbase tar -zxvf hbase-2.5.6-hadoop3-bin.tar.gz修改 hbase 目录名称 mv hbase-2.5.6-had…

一则广告,一个故事,这就我选择学习计算机专业的两个原因

还记得当初自己为什么选择计算机&#xff1f; 现在回想起来&#xff0c;当初驱使自己选择学习计算机专业的原因&#xff0c;一共有两个&#xff1a; 一、一则长城电脑的广告。 上个世纪80年代&#xff0c;我还在读小学&#xff0c;当时在中央电视台上经常播放着的长城电脑的一则…

Abaqus基础教程--胶合失效仿真

胶合是电子行业中常见的连接方式&#xff0c;abaqus中常用cohesive单元或者cohesive接触两种方法进行胶合失效仿真&#xff0c;这两种方式操作方法有所差别&#xff0c;但结果一般大同小异。 本例模型比较简单&#xff0c;建模过程从略&#xff0c;使用静态分析&#xff0c;使…

【GAMES101】三维变换

games101的第四节课讲了三维变换和观察变换&#xff0c;我们这里先记录一下三维变换的知识&#xff0c;后面再讲观察变换 齐次坐标下的三维变换 类似于解决之前二维变换平移的问题&#xff0c;三维变换下用齐次坐标通过增加一个维度来表示&#xff0c;第四个维度为1表示这是个…

无敌是多么的寂寞!一本书讲透Java多线程!吊打多线程从原理到实践!

摘要 互联网的每一个角落&#xff0c;无论是大型电商平台的秒杀活动&#xff0c;社交平台的实时消息推送&#xff0c;还是在线视频平台的流量洪峰&#xff0c;背后都离不开多线程技术的支持。在数字化转型的过程中&#xff0c;高并发、高性能是衡量系统性能的核心指标&#xff…

vue2+electron桌面端一体机应用

vue2+electron项目 前言:公司有一个项目需要用Vue转成exe,首先我使用vue-cli脚手架搭建vue2项目,然后安装electron 安装electron 这一步骤可以省略,安装electron-builder时会自动安装electron npm i electron 安装electron-builder vue add electron-builder 项目中多出…

(NeRF学习)3D Gaussian Splatting Instant-NGP

学习参考&#xff1a; 3D Gaussian Splatting入门指南【五分钟学会渲染自己的NeRF模型&#xff0c;有手就行&#xff01;】 三维重建instant-ngp环境部署与colmap、ffmpeg的脚本参数使用 一、3D Gaussian Splatting &#xff08;一&#xff09;3D Gaussian Splatting环境配置…

JVM 类的加载器的基本特征和作用

Java全能学习面试指南&#xff1a;https://javaxiaobear.cn 1、作用 类加载器是 JVM 执行类加载机制的前提 ClassLoader的作用&#xff1a; ClassLoader是Java的核心组件&#xff0c;所有的Class都是由ClassLoader进行加载的&#xff0c;ClassLoader负责通过各种方式将Class信…