Java基础:代理
这里写目录标题
- 什么是代理
- 1.静态代理(委托类、代理类):
- 使用步骤:
- 示例
- 优缺点
 
- 2.动态代理(委托类、中介类)
- 2.1 JDK动态代理
- 使用:
- 中介类:
- 示例1:
- 示例2:
 
- 2.2 CGLib动态代理
- 使用方法:
- 目标类(原始类)不能为final
- 示例1:
- 示例2:
 
 
 
 
什么是代理
代理模式是一种设计模式,简单说即是在不改变源码的情况下,实现对目标对象的功能扩展。
比如有个歌手对象叫Singer,这个对象有一个唱歌方法叫sing()
1 public class Singer{
2     public void sing(){
3         System.out.println("唱一首歌");
4     }  
5 }
假如你希望对目标对象Singer的sing方法进行功能扩展,例如在唱歌前后向观众问好和答谢,类似这样:
1 public void sing(){
2     System.out.println("向观众问好");
3     System.out.println("唱一首歌");
4     System.out.println("谢谢大家");
5 }  
但是又不能直接对源代码进行修改,甚至有可能你都不知道要对哪个目标对象进行扩展。这时就需要用到java的代理模式了。
1.静态代理(委托类、代理类):
静态代理要求原始类有实现某个接口。
 需要创建一个代理类,实现和原始类相同的接口,并在需要增强的方法里,调用原始类的该方法,调用前后加上我们需要添加的代码。
 使用的时候,直接创建一个代理类实例,调用目标方法即可。
使用步骤:
共同接口
public interface Action {public void doSomething();
}
原始类
public class RealObject implements Action{public void doSomething() {System.out.println("do something");}
}
代理类
public class Proxy implements Action {private Action realObject; public Proxy(Action realObject) {this.realObject = realObject;}public void doSomething() {System.out.println("proxy do");realObject.doSomething();}
}
使用
Proxy proxy = new Proxy(new RealObject());
proxy.doSomething();
示例
public interface ISinger {void sing();
}/***  目标对象实现了某一接口*/
public class Singer implements ISinger{public void sing(){System.out.println("唱一首歌");}  
}/***  代理对象和目标对象实现相同的接口*/
public class SingerProxy implements ISinger{// 接收目标对象,以便调用sing方法private ISinger target;public UserDaoProxy(ISinger target){this.target=target;}// 对目标对象的sing方法进行功能扩展public void sing() {System.out.println("向观众问好");target.sing();System.out.println("谢谢大家");}
}
测试类:
/*** 测试类*/
public class Test {public static void main(String[] args) {//目标对象ISinger target = new Singer();//代理对象ISinger proxy = new SingerProxy(target);//执行的是代理的方法proxy.sing();}
}
总结:这里做的事情无非就是,创建一个代理类SingerProxy,继承原始类的ISinger接口,实现其中的方法,并在实现中调用目标对象的方法。这里的关键是“调用目标对象方法”,如果直接重写就不叫代理了。
优缺点
优点:扩展原功能,不侵入原代码。
缺点:
 ①冗余。由于代理对象要实现与目标对象一致的接口,会产生过多的代理类。
 ②不易维护。代理对象必须提前写出,一旦接口发生了变化,代理对象的代码也要进行维护。
2.动态代理(委托类、中介类)
代理类在程序运行时运用反射机制创建的代理方式被成为动态代理。
 也就是说,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。
 相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类的函数。
2.1 JDK动态代理
同样要求原始类实现某个接口,但不用手动创建代理类,而是创建中介类。中介类实现InvocationHandler接口。
使用:
调用Proxy类中的newProxyInstance(ClassLoader loader,Class<?>[]
 interfaces,InvocationHandler h)方法以创建一个动态代理对象,其中第三个参数为我们创建的实现InvocationHandler接口的类(中介类),前两个参数可通过目标类.getclass().getxxx获取。
中介类:
需实现InvocationHandler接口,包含一个Object类型的对象,并利用其编写中介类的有参构造函数。重写的方法:public Object invoke(Object proxy, Method method, Object[] args) throws Throwable里,proxy表示代理类对象, method标识了我们具体调用的代理类的方法,args为这个方法的参数。
示例1:
public interface ISinger {void sing();
}/***  目标对象实现了某一接口*/
public class Singer implements ISinger{public void sing(){System.out.println("唱一首歌");}  
}-------------------------public class Test{public static void main(String[] args) {Singer target = new Singer();//这行要自己写ISinger proxy  = (ISinger) Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("向观众问好");//目标对象方法前后编写需要扩展的代码Object returnValue = method.invoke(target, args);System.out.println("谢谢大家");return returnValue;}});proxy.sing();}
}
示例2:
public static void main(String[] args) throws InterruptedException {EnHello enHello=new EnHello();Hello hello=(Hello)Proxy.newProxyInstance(enHello.getClass().getClassLoader(),enHello.getClass().getInterfaces(), new MyInvocationHandler(enHello));hello.sayHello("Tom");}interface Hello{String sayHello(String username);
}static class EnHello implements Hello{@Overridepublic String sayHello(String username) {System.out.println("Hello, "+username);return "finished";}
}static class MyInvocationHandler implements InvocationHandler{private Object object;public MyInvocationHandler(Object object){this.object=object;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object result=null;System.out.println("before say hello");if("sayHello".equals(method.getName())){result=method.invoke(object,args);}System.out.println("before say hello");return result;}
}
还可以只为指定方法动态代理,在invoke方法加上以下判断:
String methodName = method.getName();
if("eating".equals(methodName))method.invoke(obj,args);
优点一:可以隐藏委托类的实现;
 优点二:可以实现客户与委托类间的解耦,在不修改委托类代码的情况下能够做一些额外的处理
2.2 CGLib动态代理
JDK动态代理和cglib动态代理有什么区别?
使用JDK动态代理的对象必须实现一个或多个接口
 使用cglib代理的对象则无需实现接口。
cglib可以对任意类生成代理对象,它的原理是对目标对象进行继承代理,所以如果目标对象被final修饰,那么该类无法被cglib代理。
使用方法:
导包-创建MethodInterceptor实现类
使用cglib需要引入cglib的jar包,如果你已经有spring-core的jar包,则无需引入,因为spring中包含了cglib。
目标类(原始类)不能为final
目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法
示例1:
/*** 目标对象,没有实现任何接口*/
public class Singer{public void sing() {System.out.println("唱一首歌");}
}----------------------/*** Cglib子类代理工厂*/
public class ProxyFactory implements MethodInterceptor{// 维护目标对象private Object target;public ProxyFactory(Object target) {this.target = target;}// 给目标对象创建一个代理对象public Object getProxyInstance(){//1.工具类Enhancer en = new Enhancer();//2.设置父类en.setSuperclass(target.getClass());//3.设置回调函数en.setCallback(this);//4.创建子类(代理对象)return en.create();}/*
使用时只有intercept方法中,代码行 method.invoke前后的代码需要修改,其他的代码直接使用
*/@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("向观众问好");//执行目标对象的方法Object returnValue = method.invoke(target, args);System.out.println("谢谢大家");return returnValue;}
}-----------------------/*** 测试类*/
public class Test{public static void main(String[] args){//目标对象Singer target = new Singer();//代理对象Singer proxy = (Singer)new ProxyFactory(target).getProxyInstance();//执行代理对象的方法proxy.sing();}
}
示例2:
public class TestCglib implements MethodInterceptor {    Object target;   //动态生成一个新的类,使用父类的无参构造方法创建一个指定了特定回调的代理实例    public Object getProxyObject(Object object) {      this.target = object;       //增强器,动态代码生成器     Enhancer enhancer=new Enhancer();        //回调方法enhancer.setCallback(this); //设置生成类的父类类型        enhancer.setSuperclass(target.getClass());   //动态生成字节码并返回代理对象      return enhancer.create();   }public Object intercept(Object o, Method method, Object[] objects, MethodProxy 
methodProxy) throws Throwable {System.out.println("----------before");    // 调用方法      Object result = methodProxy.invoke(target, objects);   System.out.println("----------after");       return null;  }
}//使用
public static void main(String[] args) {Boss boss=(Boss) new TestCglib().getProxyObject(new Boss());boss.eating();boss.sleeping();
}
相关文章:
Java基础:代理
这里写目录标题 什么是代理1.静态代理(委托类、代理类):使用步骤:示例优缺点 2.动态代理(委托类、中介类)2.1 JDK动态代理使用:中介类:示例1:示例2: 2.2 CGLi…...
每日一学——防火墙2
防火墙是一种网络安全设备,用于保护计算机网络免受未经授权的访问、攻击和恶意行为的影响。以下是一些防火墙的基本概念: 防火墙规则:防火墙会根据预先设置的规则来决定允许或拒绝特定的网络流量。这些规则可以指定源 IP 地址、目标 IP 地址、…...
 
Web学习笔记-React(组合Components)
笔记内容转载自 AcWing 的 Web 应用课讲义,课程链接:AcWing Web 应用课。 CONTENTS 1. 创建父组件2. 从上往下传递数据3. 传递子节点4. 从下往上调用函数5. 兄弟组件间传递消息6. 无状态函数组件7. 组件的生命周期 本节内容是组件与组件之间的组合&#…...
 
【strstr函数的介绍和模拟实现——超详细版】
strstr函数的介绍和模拟实现 strstr函数的介绍 资源来源于cplusplus网站 strstr函数声明: char *strstr( const char *str1, const char *str2 ); 它的作用其实就是: 在字符串str1中查找是否含有字符串str2,如果存在,返回str2在…...
 
【Terraform】Terraform自动创建云服务器脚本
Terraform 是由 HashiCorp 创建的开源“基础架构即代码”工具 (IaC) 使用HCL(配置语言)描述云平台基础设施(这里教你使用低级基础设施:交换机、云服务器、VPC、带宽) Terraform提供者…...
 
TCP机制之确认应答及超时重传
TCP因为其可靠传输的特性被广泛使用,这篇博客将详细介绍一下TCP协议是如何保证它的可靠性的呢?这得主要依赖于其确认应答及超时重传机制,同时三次握手四次挥手也起到了少部分不作用,但是主要还是由确认应答和超时重传来决定的;注意:这里的可靠传输并不是说100%能把数据发送给接…...
Openharmony3.2 源码编译(ubuntu 22.04) 过程记录
OS: ubuntu 22.04 x64 1. 下载源码 1.1 安装码云repo工具 sudo apt install python3-pip git-lfsmkdir ~/bin curl https://gitee.com/oschina/repo/raw/fork_flow/repo-py3 -o ~/bin/repo chmod ax ~/bin/repo pip3 install -i https://repo.huaweicloud.com/repository/p…...
 
PostgreSQL 数据库使用 psql 导入 SQL
最近我们有一个 SQL 需要导入到 PostgreSQL ,但数据格式使用的是用: -- -- TOC entry 7877 (class 0 OID 21961) -- Dependencies: 904 -- Data for Name: upload_references; Type: TABLE DATA; Schema: public; Owner: - --COPY public.upload_refere…...
 
容器编排学习(三)端口映射与Harber镜像仓库介绍
一 对外发布服务(端口映射) 1 概述 新创建容器的IP 地址是随机的 容器在重启后每次 IP 都会发生变化 容器服务只有宿主机才能访问 如何才能使用容器对外提供稳定的服务? 容器端口可以与宿主机的端口进行映射绑定 从而把宿主机变成对应的服务&a…...
 
Day_13 > 指针进阶(2)
目录 1.函数指针数组 2.指向函数指针数组的指针 3.回调函数 qsort()函数 代码示例 void* 4.结束 今天我们在进阶指针的基础上,学习进阶指针的第二部分 1.函数指针数组 首先我们回顾一下指针数组 char* arr[5]://字符指针数组 - 数组 - 存放的是字符指针 in…...
 
对Transformer中的Attention(注意力机制)的一点点探索
摘要:本文试图对 Transformer 中的 Attention 机制进行一点点探索。并就 6 个问题深入展开。 ✅ NLP 研 1 选手的学习笔记 简介:小王,NPU,2023级,计算机技术 研究方向:文本生成、摘要生成 文章目录 一、为啥…...
车内信息安全技术-安全技术栈-软件安全
操作系统 1.隔离技术 信息安全中的隔离技术通常指的是将不同安全级别的信息或数据隔离开来,以保护敏感信息不受未授权的访问或泄露。在操作系统中,常见的隔离技术包括:虚拟化技术:通过虚拟化软件,将物理计算机分割成多个独立的虚拟计算机,每个虚拟计算机都可以运行独立的…...
 
Redis常见命令
命令可以查看的文档 http://doc.redisfans.com/ https://redis.io/commands/ 官方文档(英文) http://www.redis.cn/commands.html 中文 https://redis.com.cn/commands.html 个人推荐这个 https://try.redis.io/ redis命令在线测试工具 https://githubfa…...
 
Android Studio实现一笔画完小游戏
文章目录 一、项目概述二、开发环境三、详细设计3.1、数据库设计3.2、普通模式3.3、随机模式3.4、关卡列表 四、运行演示五、项目总结六、源码获取 一、项目概述 Android一笔画完是一种益智游戏,玩家需要从起点开始通过一条连续的线,将图形中所有的方块…...
【Python 程序设计】数据人员入门【02/8】
一、说明 介绍如何管理 Python 依赖项和一些虚拟环境最佳实践。 以下文章是有关 Python 数据工程系列文章的一部分,旨在帮助数据工程师、数据科学家、数据分析师、机器学习工程师或其他刚接触 Python 的人掌握基础知识。迄今为止,本初学者指南包括&#…...
 
学习笔记——树上哈希
普通子树哈希 树上的很多东西都是转化成链上问题的,比如树上哈希 树上哈希,主要是用于树的同构这个东西上的 什么是树的同构? 如图,不考虑节点编号,三棵树是同构的 将树转化成链,一般有两种方式…...
 
Opencv快速入门教程,Python计算机视觉基础
快速入门 OpenCV 是 Intel 开源计算机视觉库。它由一系列 C 函数和少量 C 类构成, 实现了图像处理和计算机视觉方面的很多通用算法。 OpenCV 拥有包括 300 多个 C 函数的跨平台的中、高层 API。它不依赖于其它的外部库——尽管也 可以使用某些外部库。 OpenCV 对非…...
laravel 报错误信息 Carbon\Exceptions\InvalidFormatException
Carbon\Exceptions\InvalidFormatException Unexpected data found. at vendor\nesbot\carbon\src\Carbon\Traits\Creator.php:687 683▕ return $instance; 684▕ } 685▕ 686▕ if (static::isStrictModeEnabled()) { ➜ 687…...
 
UI自动化之混合框架
什么是混合框架,混合框架就是将数据驱动与关键字驱动结合在一起,主要用来回归业务主流程,将核心流程串联起来。 上一篇我们写到了关键字驱动框架,关键字驱动框架是针对一个业务场景的单条测试用例的。 我们以163邮箱的登录到创建…...
SQL创建用户-非DM8.2环境(达梦数据库)
DM8:达梦数据库SQL创建用户-非DM8.2环境 环境介绍 环境介绍 在没有图形化界面,或者想快速创建用户,可以使用一下SQL语句;将其中的 CESHI 替换为要创建的用户名即可,默认创建了数据表空间,索引表空间,文件大…...
 
wordpress后台更新后 前端没变化的解决方法
使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…...
 
Chapter03-Authentication vulnerabilities
文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...
Python如何给视频添加音频和字幕
在Python中,给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加,包括必要的代码示例和详细解释。 环境准备 在开始之前,需要安装以下Python库:…...
 
网站指纹识别
网站指纹识别 网站的最基本组成:服务器(操作系统)、中间件(web容器)、脚本语言、数据厍 为什么要了解这些?举个例子:发现了一个文件读取漏洞,我们需要读/etc/passwd,如…...
 
三分算法与DeepSeek辅助证明是单峰函数
前置 单峰函数有唯一的最大值,最大值左侧的数值严格单调递增,最大值右侧的数值严格单调递减。 单谷函数有唯一的最小值,最小值左侧的数值严格单调递减,最小值右侧的数值严格单调递增。 三分的本质 三分和二分一样都是通过不断缩…...
【学习笔记】erase 删除顺序迭代器后迭代器失效的解决方案
目录 使用 erase 返回值继续迭代使用索引进行遍历 我们知道类似 vector 的顺序迭代器被删除后,迭代器会失效,因为顺序迭代器在内存中是连续存储的,元素删除后,后续元素会前移。 但一些场景中,我们又需要在执行删除操作…...
 
[论文阅读]TrustRAG: Enhancing Robustness and Trustworthiness in RAG
TrustRAG: Enhancing Robustness and Trustworthiness in RAG [2501.00879] TrustRAG: Enhancing Robustness and Trustworthiness in Retrieval-Augmented Generation 代码:HuichiZhou/TrustRAG: Code for "TrustRAG: Enhancing Robustness and Trustworthin…...
 
FFmpeg avformat_open_input函数分析
函数内部的总体流程如下: avformat_open_input 精简后的代码如下: int avformat_open_input(AVFormatContext **ps, const char *filename,ff_const59 AVInputFormat *fmt, AVDictionary **options) {AVFormatContext *s *ps;int i, ret 0;AVDictio…...
 
【UE5 C++】通过文件对话框获取选择文件的路径
目录 效果 步骤 源码 效果 步骤 1. 在“xxx.Build.cs”中添加需要使用的模块 ,这里主要使用“DesktopPlatform”模块 2. 添加后闭UE编辑器,右键点击 .uproject 文件,选择 "Generate Visual Studio project files",重…...
【HarmonyOS 5】鸿蒙中Stage模型与FA模型详解
一、前言 在HarmonyOS 5的应用开发模型中,featureAbility是旧版FA模型(Feature Ability)的用法,Stage模型已采用全新的应用架构,推荐使用组件化的上下文获取方式,而非依赖featureAbility。 FA大概是API7之…...
