Java代理模式与 * 之间的关系以及概念
作者:绿仔牛奶_ 发布时间:2022-03-15 18:05:34
什么是代理模式
代理模式是开发中常见的一种设计模式,使用代理模式可以很好的对程序进行横向扩展。代理,顾名思义就是一个真实对象会存在一个代理对象,并且代理对象可以替真实对象完成相应操作,外部通过代理对象来访问真实对象并且还可以在代理对象中进行额外操作的扩展。
代理模式的特征是拥有接口、代理类、被代理类。并且代理类与被代理类同时实现该接口。代理类与被代理类之间通常存在一定关联,设计时会在代理类中注册一个被代理类的对象用于调用代理类的方法。这也印证了代理对象依然是执行的真实对象的方法
代理模式又分为静态代理和 *
静态代理
静态代理,关键字静态是指在程序运行之前编译时就已经确定了代理类、被代理类、接口。
下面列举两个示例展示静态代理:
学生通过班长交班费,班长作为学生的代理,学生是被代理,具有同一行为就是交班费,这个行为我们用接口进行约束
程序:
// 接口
public interface Person {void giveMoney();}
// 学生类--> 被代理类
public class Student implements Person{
private String name;
public Student(String name) {this.name = name;}
public void giveMoney() {
System.out.println(name+"同学上交50元班费");
}
}
// 学生代理类
public class StuProxy implements Person{
private Student stu;
public StuProxy(Student stu) {
if (stu.getClass() == Student.class) {
this.stu = stu;
}
}
public void giveMoney() {stu.giveMoney();}
}
测试:
public static void main(String[] args) {
// 获取学生实例
Student stu = new Student("张三");
// 学生找交钱给班长 学生找到代理
StuProxy stuProxy = new StuProxy(stu);
// 班长交给老师 代理交钱(交的是学生的钱)
stuProxy.giveMoney();
}
房屋租赁,租客通过中介找房源,租客为被代理类,中介为代理类,接口声明租房方法
// 接口
public interface Rent {void doRent();}
// 被代理类
public class Renter implements Rent{
public void doRent() {
System.out.println("租客租房了");
}
}
// 代理类
public class RentProxy implements Rent{
private Renter renter;
public RentProxy(Renter renter) {
if (renter.getClass()==Renter.class)
this.renter = renter;
}
public void doRent() {renter.doRent();}
}
测试:
public static void main(String[] args) {
// 创建租客
Renter renter = new Renter();
// 租客找到中介
RentProxy rentProxy = new RentProxy(renter);
// 租客租房
rentProxy.doRent();
}
上述两个示例,向代理类中注入被代理对象的方式都是通过构造器注入,当然也可以通过set方法注入
下面演示如果需要在已经编写好的代理类的输出中添加其他操作时的操作比如打印一个日志,在不修改源代码的情况下扩展
// 下面是最经典的Service层的写法
public interface UserService {
void del();
void select();
}
public class UserServiceImpl implements UserService{
public void del() {
System.out.println("删除操作");
}
public void select() {
System.out.println("查询操作");
}
}
假设现在我们需要在每一次执行操作前后打印一次执行日志,并且不能修改源代码那么就可以用到代理模式,将UserServiceImpl作为被代理类,扩展代理类如下:
public class UserServiceImplProxy implements UserService{
private UserServiceImpl userService;
// set注入
public void setUserService(UserServiceImpl userService) {
this.userService = userService;
}
public void del() {
printLog();
userService.del();
}
public void select() {
printLog();
userService.select();
}
private void printLog(){
System.out.println("执行时间="+new Date());
}
}
*
* :代理类在程序运行时被创建的代理方式。
关键在于动态,程序具有了动态特性,可以在运行期间根据不同的目标对象生成 * 对象,并且可以通过 * 对象对目标对象(真实对象)进行功能性补强。大白话来讲就是,可以在程序运行期间额外的对真实对象功能进行扩展。
此处的 * 对象不是通过预先编写好的程序生成的,而是运行期间由于用户需求或者说是代码的指示生成的
* 分为两种:一类是基于接口实现的 * ,另一类是基于类的 *
基于接口的 * –JDK * 通过反射完成,基于类实现的–>cglib
JDK * 核心:Proxy类、InvocationHandler接口、要参与代理的目标类必须实现对应接口,比如上述的Student必须实现Person接口。
继续以上述学生交班费为例,更改为 * 模式:
JDK * 中间类:该类需要实现InvocationHandler接口
// JDK * 中间类
public class ProxyInvocationHandler implements InvocationHandler {
// 被代理的对象
private Object target;
// ser方法注入参数
public void setTarget(Object target) {this.target = target;}
// 生成 * 类
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
target.getClass().getInterfaces(),this);
}
// 处理代理实例,并返回结果
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(target,args);
return result;
}
}
测试:
public static void main(String[] args) {
// 获取真实角色
Student stu = new Student("张三三");
// * 中间类对象
InvocationHandlerProxyStudent proxyStudent = new InvocationHandlerProxyStudent();
proxyStudent.setStu(stu);
// 代理类实例
Person proxy = (Person) proxyStudent.getProxy();
// 代理类实例调用方法
proxy.giveMoney();
}
上述程序,我们利用Proxy来生成代理类:
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
stu.getClass().getInterfaces(),this);
}
Proxy提供了静态的获取Proxy代理类的方法newProxyInstance
,三个参数分别是1.类加载器 2.代理对象实现的接口 3. 调用处理程序(InvocationHandler)
在InvocationHandler官方文档就已经指出,每一个代理实例都关联有一个InvocationHandler调用处理程序,所以这里填this即可
在使用代理类的时候首先我们创建需要被代理的真实对象和 * 中间类的对象,用set方法将真实对象交给中间类中的代理对象。在调用上述getProxy方法获取代理类。 * 相对于静态代理有些难以理解,这是因为静态代理的代理类可以在程序中显式的被看到,而 * 中的代理类文件是缓存在Java虚拟机,类名叫$Proxy0。
在虚拟机中生成的代理类中就会将我们所调用的方法内置,我们在执行proxy.giveMoney()
的同时,实际上是将giveMoney()方法作为参数传进invoke()的参数中去,而此时我们就可以将其他的操作交给invoke,由于所有代理对象在执行时最终都会走invoke方法,所以也为我们的开发节省大量代码
来源:https://blog.csdn.net/yuqu1028/article/details/129014269
猜你喜欢
- this总要有个事物来代表类的当前对象,就像C++中的this指针一样,Java中的this关键字就是代表当前对象的引用。它有三个主要的作用
- 大致流程客户端根据远程服务的地址,客户端发送请求至服务端,服务端解析信息并找到对应的实现类,进行方法调用,之后将调用结果原路返回,客户端解析
- 命令模式定义:将请求封装成对象,这可以让你使用不同的请求、队列、或者日志来参数化其他对象。何时使用命令模式?当需要将发出请求的对象和执行请求
- Spring相关的依赖导入进去,即可使用spring的定时任务!<!-- spring核心包 -->
- 二叉堆是一种特殊的堆,二叉堆是完全二元树(二叉树)或者是近似完全二元树(二叉树)。二叉堆有两种:最大堆和最小堆。最大堆:父结点的键值总是大于
- springBoot yml文件的list读取问题折腾了很久,记录下。配置如下# 自定义数据上报信息xx: # 机组信息 &nb
- 这篇文章主要介绍了JAVA如何定义构造函数过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可
- 目录断言对象、数组、集合ObjectUtilsStringUtilsCollectionUtils文件、资源、IO 流FileCopyUti
- Spring的出现是为了简化 Java 程序开发,而 SpringBoot 的出现是为了简化 Spring 程序开发.SpringBoot
- poi导入纯数字等问题用poi导出excel时候,如果单元格设置纯数字,输入的数据一旦过大就是自动显示成科学记数法,导致导入后的数据出错,解
- redis redisson 集合操作相关类及接口Rlist:链表public interface RList<V> exten
- 此篇文章是我一个小白对委托的理解和总结,请高手多多评判指教。委托就是一种后期绑定机制,说的直白点就是在调用的时候才去传递业务逻辑的一种算法。
- SpringBoot 项目启动之后执行自定义方法的两种方式在测试配置中心的配置时,想在项目启动成功之后打印配置项,然后需要执行自定义的类一般
- spring Boot源码编译1. git上下拉最新版的spring Boot下载:git clone git@github.com:spr
- 目录概述ClassPoolCtClassCtMthodCtFieldCtConstructorClassPathClassLoader示例创
- Unity 跑马灯抽奖效果实现代码,供大家参考,具体内容如下这边用到插件是NGUI+Dotween,思路简单说下:先排版,通过移动图片蒙版来
- 在项目开发中,经常会碰到日期处理。比如查询中,可能会经常遇到按时间段查询,有时会默认取出一个月的数据。当我们提交数据时,会需要记录当前日期,
- 本文实例为大家分享了java实现幸运抽奖功能的具体代码,供大家参考,具体内容如下本系统较为简单,未使用是什么多的算法,也未添加保存文件读取文
- 一、项目简述本系统主要实现的功能有: 学生以及老师的注册登录,在线考试,错题查询,学生管理,问题管理,错题管理,错题查询,分数查询,试卷管
- 本文实例为大家分享了java生成字母验证码的具体代码,供大家参考,具体内容如下import java.awt.BasicStroke;imp