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

静态代理和动态代理的区别以及实现过程

前言

代理模式是一种设计模式,能够使得在不修改源目标的前提下,额外扩展源目标的功能。即通过访问源目标的代理类,再由代理类去访问源目标。这样一来,要扩展功能,就无需修改源目标的代码了。只需要在代理类上增加就可以了。

在这里插入图片描述
其实代理模式的核心思想就是这么简单,在java中,代理又分静态代理动态代理2种,其中动态代理根据不同实现又区分基于接口的的动态代理和基于子类的动态代理。

其中静态代理由于比较简单,面试中也没啥问的,在代理模式一块,问的最多就是动态代理,而且动态代理也是spring aop的核心思想,spring其他很多功能也是通过动态代理来实现的,比如拦截器,事务控制等。

熟练掌握动态代理技术,能让你业务代码更加精简而优雅。如果你需要写一些中间件的话,那动态代理技术更是必不可少的技能包。

静态代理

静态代理,就是通过声明一个明确的代理类来访问源对象。

我们有1个接口,Person。这个个接口各有2个实现类,UML如下图:
在这里插入图片描述

实现

接口:person.java

package StaticProxy;/*** @author zyz* @version 1.0* @data 2023/2/15 13:29* @Description:*/
public interface Person {/*** 起床*/public void wakeup();/*** 睡觉*/public void sleep();
}

实现类:Student .java

package StaticProxy;/*** @author zyz* @version 1.0* @data 2023/2/15 13:32* @Description:*/
public class Student implements Person{private String name;public Student(){}public Student(String name){this.name = name;}@Overridepublic void wakeup() {System.out.println("学生:"+name+",起床了!!!");}@Overridepublic void sleep() {System.out.println("学生:"+name+",睡觉了!!!");}
}

假设我们现在要做一件事,就是在所有的实现类调用wakeup()前增加一行输出早安,调用sleep()前增加一行输出晚安。那我们只需要编写1个代理类PersonProxy

代理类:PersonProxy .java

package StaticProxy;/*** @author zyz* @version 1.0* @data 2023/2/15 13:35* @Description:*/
public class PersonProxy implements Person{private Person person;public PersonProxy(Person person){this.person = person;}@Overridepublic void wakeup() {System.out.println("早上好啊!!!");person.wakeup();}@Overridepublic void sleep() {System.out.println("晚上好啊!!!");person.sleep();}
}

测试类

package StaticProxy;/*** @author zyz* @version 1.0* @data 2023/2/15 13:37* @Description:*/
public class Test {public static void main(String[] args) {Person student1 = new Student("张三");PersonProxy studentProxy = new PersonProxy(student1);studentProxy.wakeup();studentProxy.sleep();}
}

结果

在这里插入图片描述

结论:

静态代理的代码相信已经不用多说了,代码非常简单易懂。这里用了1个代理类,代理了Person接口。

这种模式虽然好理解,但是缺点也很明显:

  • 会存在大量的冗余的代理类,这里演示了1个接口,如果有10个接口,就必须定义10个代理类。
  • 不易维护,一旦接口更改,代理类和目标类都需要更改。

动态代理

动态代理,通俗点说就是:无需声明式的创建java代理类,而是在运行过程中生成"虚拟"的代理类,被ClassLoader加载。从而避免了静态代理那样需要声明大量的代理类。

JDK从1.3版本就开始支持动态代理类的创建。主要核心类只有2个:java.lang.reflect.Proxyjava.lang.reflect.InvocationHandler

还是前面那个例子,用JDK动态代理类去实现的代码如下:

创建一个JdkProxy类,用于统一代理:

package DynamicProxy;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;/*** @author zyz* @version 1.0* @data 2023/2/15 13:28* @Description:*/
public class JdkProxy implements InvocationHandler {private Object bean;public JdkProxy(Object bean) {this.bean = bean;}/*** 其中proxy为代理过之后的对象(并不是原对象),method为被代理的方法,args为方法的参数。** 如果你不传原有的bean,直接用method.invoke(proxy, args)的话,那么就会陷入一个死循环。* @param proxy* @param method* @param args* @return* @throws Throwable*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {String methodName = method.getName();if (methodName.equals("wakeup")) {System.out.println("早安~~~");} else if (methodName.equals("sleep")) {System.out.println("晚安~~~");}return method.invoke(bean, args);}
}

测试

package DynamicProxy;import StaticProxy.Person;
import StaticProxy.Student;import java.lang.reflect.Proxy;/*** @author zyz* @version 1.0* @data 2023/2/15 13:46* @Description:*/
public class Test {public static void main(String[] args) {JdkProxy proxy = new JdkProxy(new Student("李四"));Person student = (Person) Proxy.newProxyInstance(proxy.getClass().getClassLoader(), new Class[]{Person.class}, proxy);student.wakeup();student.sleep();}
}

结果

在这里插入图片描述

可以看到,相对于静态代理类来说,无论有多少接口,这里只需要一个代理类。核心代码也很简单。唯一需要注意的点有以下2点:

JDK动态代理是需要声明接口的,创建一个动态代理类必须得给这个”虚拟“的类一个接口。可以看到,这时候经动态代理类创造之后的每个bean已经不是原来那个对象了。

这里JdkProxy最核心的方法就是

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable

其中proxy为代理过之后的对象(并不是原对象),method为被代理的方法,args为方法的参数。

如果你不传原有的bean,直接用method.invoke(proxy, args)的话,那么就会陷入一个死循环。

相关文章:

静态代理和动态代理的区别以及实现过程

前言 代理模式是一种设计模式,能够使得在不修改源目标的前提下,额外扩展源目标的功能。即通过访问源目标的代理类,再由代理类去访问源目标。这样一来,要扩展功能,就无需修改源目标的代码了。只需要在代理类上增加就可…...

Consul SpringCloudK8S

背景说起微服务,就需要用到SpringCloud,目前市面上主流的SpringCloud产品有这些:SpringCloudNeflix、Spring Cloud Alibaba、Spring Cloud for AWS、Spring Cloud Azure 和 Spring Cloud Kubernetes。其中SpringCloudNeflix已经不在更新&…...

anaconda3文件夹被移动之后,如何操作可以复用原有conda环境

anaconda3文件夹被移动A-调整conda PATH地址B-更改.conda/environments.txt中的地址C-修改conda内的变量和每个环境的pip目录A-调整conda PATH地址 B-更改.conda/environments.txt中的地址 a. 优先切换到用户根目录 b. 查看隐藏conda目录 c. 编辑 vi .conda/environments.txt…...

【Java】Stack(栈) Queue(单向队列) Deque(双向队列)

Stack (栈) Stack 是一个先进后出的栈,可以将其理解为一个只开了一个口子的管子,放进去的东西只能从这一个口进出。所以先放进去的元素在取出的时候只能到最后才能取出来。 Stack具备一下几个方法: boolean empty() …...

自定义spring拦截器

说明: 一些版本比较老的spring框架的,是通过继承HandlerInterceptorAdapter并重写preHandle()方法,和继承WebMvcConfigurerAdapter并重写 addInterceptors()方法来实现拦截器的,但是这两个类很久前就已经过时了,不推荐…...

今天正式上线!虹科汽车免拆诊断云展厅:感受精准修车魅力,畅享汽修领先技术

『虹科汽车免拆诊断云展厅』 2月15日正式上线! 在这里,您可以参观了解: 虹科Pico汽车示波器产品模型 全流程专业讲解的视频资料 产品功能和应用场景 全面详细的产品手册 还有虹科首席技术工程师在线连麦答疑!!&#xf…...

4.数据类型-字符串【Python】

文章目录字符串索引切片转义字符格式化符号f-string字符串操作判断&检测转换剪切&填充拼接统计格式转化练习字符串 ​ 字符串是 Python 中最常用的数据类型。可以使用单引号,双引号,3对双引号创建一个字符串。Python 中没有单独的字符类型 char…...

搞量化先搞数(上):A股股票列表免费抓取实战

到了这一步,我们学习了基础的爬虫请求库urllib和requests,尤其是后者,强大且易用,极其适合新手使用。那么今天我们就找一个相对简单的案例,来看一下如何在实战中应用爬虫技能。 相信很多朋友都对股票感兴趣,甚至有些朋友想要通过量化研究来获得超额收益。然而,想要进行…...

SpringCloud-负载均衡Ribbon

一、配置使用1、添加依赖&#xff08;该依赖包含在eureka-client依赖中&#xff09;<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-ribbon</artifactId></dependency>2、在RestTemp…...

Linux入门篇(二)

Linux前言链接文件符号链接&#xff08;软链接&#xff09;硬链接shellshell 的类型shell的父子关系理解外部命令和内建命令外部命令内建命令Linux环境变量PATH环境变量前言 在这一章&#xff0c;我对Linux中有关shell较为深入的理解和环境变量方面知识的一个记录。同时&#x…...

第四部分:特殊用途的句子——第三章:虚拟

虚拟语气 1、什么是虚拟&#xff1f; 虚拟就是非真实。换句话说&#xff0c;这事不是真的&#xff0c;这事不太可能成真&#xff0c;非真实&#xff0c;就是虚拟 2、怎么表示虚拟&#xff1f; 英语是一个典型的形式来补充内容的语言&#xff0c;若要表达虚拟&#xff0c;只…...

Java中如何获取泛型类型信息

文章目录声明侧泛型使用侧泛型获取泛型类型相关方法1. Class类的泛型方法2. Field类的泛型方法3. Method类的泛型方法4. ParameterizedType类获取声明侧的泛型类型信息获取使用侧的泛型类型信息匿名内部类实现获取使用侧的泛型类型根据使用泛型位置的不同可以分为&#xff1a;声…...

【云原生】centos7搭建安装k8s集群 v1.25版本详细教程实战

文章目录前言一. 实验环境二. k8s 的介绍三 . k8s的安装3.1 搭建实验环境3.1.1 硬件层面的要求3.1.2 软件层面环境配置3.2 docker的安装3.2.1 搭建docker3.2.2 部署 cri-dockerd3.3 部署k8s3.3.1 配置添加阿里云的yum源3.3.2 安装kubeadm kubelet kubectl3.3.3 k8s-master节点初…...

c语言指针

指针 指针是存放地址的变量&#xff0c;也可以说指针地址。 对于定义p&#xff08;这里的话&#xff0c;只是定义&#xff0c;说明p是指针&#xff09;&#xff0c;p作为一个指针去指向存放数据的位置&#xff0c;而p意思是取&#xff08;p指向的内存位置的数据&#xff09;&…...

5.33 综合案例2.0 -ESP32拍照上传阿里云OSS

综合案例2.0 - ESP32拍照上传阿里云OSS案例说明连线功能实现1.阿里云平台连接2.OSS对象存储服务3.ESP32-CAM开发环境4.代码ESP32-CAM开发板代码HaaS506开发板代码测试数据转图片方法案例说明 使用ESP32拍照,将照片数据上传阿里云OSS&#xff08;通过4G网络上传&#xff09;。 …...

java无重复字符的最长子串

给定一个字符串 s &#xff0c;请你找出其中不含有重复字符的 最长子串 的长度。 示例 1: 输入: s “abcabcbb” 输出: 3 解释: 因为无重复字符的最长子串是 “abc”&#xff0c;所以其长度为 3。 示例 2: 输入: s “bbbbb” 输出: 1 解释: 因为无重复字符的最长子串是 “…...

测试用例设计工作中的应用

1. 等价类划分 常见的软件测试面试题划分等价类: 等价类是指某个输入域的子集合.在该子集合中,各个输入数据对于揭露程序中的错误都是等效的.并合理地假定:测试某等价类的代表值就等于对这一类其它值的测试.因此,可以把全部输入数据合理划分为假设干等价类,在每一个等价类中取一…...

leetcode 困难 —— 数字 1 的个数(简单逻辑题)

&#xff08;害&#xff0c;做题是真的慢&#xff0c;这面试给我这题我估计就傻了&#xff09; 题目&#xff1a; 给定一个整数 n&#xff0c;计算所有小于等于 n 的非负整数中数字 1 出现的个数。 题解&#xff1a; 首先看看整数范围 0 < n < 10^9 不能遍历&#xff0…...

关于JSON

<!DOCTYPE html> <html> <head> <meta charset"utf-8"> <title></title> </head> <body> <script> /* 1、JSON的英文全称&#xff1a;Java…...

Apifox-接口调用、自动化测试工具

Apifox简介 Apifox 的定位是Postman Swagger Mock JMeter&#xff0c;具有API文档管理、API调试、API Mock、API 自动化测试等功能。可以通过一种工具解决之前使用多种工具的数据同步问题。高效、及时、准确&#xff01; 安装 Apifox的安装非常方便&#xff0c;直接下载安…...

iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘

美国西海岸的夏天&#xff0c;再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至&#xff0c;这不仅是开发者的盛宴&#xff0c;更是全球数亿苹果用户翘首以盼的科技春晚。今年&#xff0c;苹果依旧为我们带来了全家桶式的系统更新&#xff0c;包括 iOS 26、iPadOS 26…...

Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器

第一章 引言&#xff1a;语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域&#xff0c;文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量&#xff0c;支撑着搜索引擎、推荐系统、…...

【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分

一、项目背景回顾 前五弹完成了Json-Rpc协议解析、请求处理、客户端调用等基础模块搭建。 本弹重点聚焦于服务端的模块划分与架构设计&#xff0c;提升代码结构的可维护性与扩展性。 二、服务端模块设计目标 高内聚低耦合&#xff1a;各模块职责清晰&#xff0c;便于独立开发…...

C++八股 —— 单例模式

文章目录 1. 基本概念2. 设计要点3. 实现方式4. 详解懒汉模式 1. 基本概念 线程安全&#xff08;Thread Safety&#xff09; 线程安全是指在多线程环境下&#xff0c;某个函数、类或代码片段能够被多个线程同时调用时&#xff0c;仍能保证数据的一致性和逻辑的正确性&#xf…...

智能分布式爬虫的数据处理流水线优化:基于深度强化学习的数据质量控制

在数字化浪潮席卷全球的今天&#xff0c;数据已成为企业和研究机构的核心资产。智能分布式爬虫作为高效的数据采集工具&#xff0c;在大规模数据获取中发挥着关键作用。然而&#xff0c;传统的数据处理流水线在面对复杂多变的网络环境和海量异构数据时&#xff0c;常出现数据质…...

ip子接口配置及删除

配置永久生效的子接口&#xff0c;2个IP 都可以登录你这一台服务器。重启不失效。 永久的 [应用] vi /etc/sysconfig/network-scripts/ifcfg-eth0修改文件内内容 TYPE"Ethernet" BOOTPROTO"none" NAME"eth0" DEVICE"eth0" ONBOOT&q…...

Linux 内存管理实战精讲:核心原理与面试常考点全解析

Linux 内存管理实战精讲&#xff1a;核心原理与面试常考点全解析 Linux 内核内存管理是系统设计中最复杂但也最核心的模块之一。它不仅支撑着虚拟内存机制、物理内存分配、进程隔离与资源复用&#xff0c;还直接决定系统运行的性能与稳定性。无论你是嵌入式开发者、内核调试工…...

纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join

纯 Java 项目&#xff08;非 SpringBoot&#xff09;集成 Mybatis-Plus 和 Mybatis-Plus-Join 1、依赖1.1、依赖版本1.2、pom.xml 2、代码2.1、SqlSession 构造器2.2、MybatisPlus代码生成器2.3、获取 config.yml 配置2.3.1、config.yml2.3.2、项目配置类 2.4、ftl 模板2.4.1、…...

[ACTF2020 新生赛]Include 1(php://filter伪协议)

题目 做法 启动靶机&#xff0c;点进去 点进去 查看URL&#xff0c;有 ?fileflag.php说明存在文件包含&#xff0c;原理是php://filter 协议 当它与包含函数结合时&#xff0c;php://filter流会被当作php文件执行。 用php://filter加编码&#xff0c;能让PHP把文件内容…...

(一)单例模式

一、前言 单例模式属于六大创建型模式,即在软件设计过程中,主要关注创建对象的结果,并不关心创建对象的过程及细节。创建型设计模式将类对象的实例化过程进行抽象化接口设计,从而隐藏了类对象的实例是如何被创建的,封装了软件系统使用的具体对象类型。 六大创建型模式包括…...