代理模式--静态代理和动态代理

1.代理模式

定义:代理模式就是代替对象具备真实对象的功能,并代替真实对象完成相应的操作并且在不改变真实对象源代码的情况下扩展其功能,在某些情况下,⼀个对象不适合或者不能直接引⽤另⼀个对象,⽽代理对象可以在客户端和⽬标对象之间起到中介的作⽤

使用代理模式可以降低系统的耦合性,扩展性好,并且可以起到保护目标对象的作用
例如:我们平时租房的过程,租房中介就相当于代理类
代理模式分为静态代理和动态代理

2.静态代理

静态代理实现步骤:

  1. 定义⼀个接⼝及其实现类(目标类);
  2. 创建⼀个代理类同样实现这个接⼝(继承同一个接口的原因就是,代理类需要拥有和目标类同样的方法这样才能代理)
  3. 将⽬标对象注⼊进代理类,然后在代理类的对应⽅法调⽤⽬标类中的对应⽅法。
public interface IRentHouse {void rent();
}
public class RentHouse implements IRentHouse{@Overridepublic void rent() {System.out.println("租户租房子");}
}
public class IntermediaryProxy implements IRentHouse{private IRentHouse iRentHouse;public IntermediaryProxy(IRentHouse iRentHouse) {this.iRentHouse = iRentHouse;}@Overridepublic void rent() {System.out.println("交中介费");iRentHouse.rent();System.out.println("租房子后中介负责维护管理");}
}
/*** 测试类*/
public class Test {public static void main(String[] args) {// 定义租房IRentHouse iRentHouse = new RentHouse();// 定义中介IRentHouse proxy = new IntermediaryProxy(iRentHouse);// 租房proxy.rent();}
}

运行结果如下:
在这里插入图片描述
静态代理有很多缺点,实际应用场景非常少,几乎不用
对目标对象的每个方法的增强都是手动完成的,非常不灵活(比如接口中一旦新增方法,目标对象和代理对象都要修改),且麻烦(需要对每个目标类都单独写一个代理类)

3.动态代理

相比于静态代理来说,动态代理更加灵活,不需要针对每个目标类都单独创建一个代理类,也不需要我们必须实现接口
动态代理允许使用一种方法的单个类(代理类),为具有任意数量方法的任意类(目标类)的多个方法提供服务,看到这句话,是不是联想到动态代理的实现与Java反射机制密不可分

Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个属性和方法,这种动态获取的信息以及动态调用对象的方法的功能称之为Java语言的反射机制

从 JVM ⻆度来说,动态代理是在运⾏时动态⽣成类字节码,并加载到 JVM 中
的。说到动态代理,不得不提的是Spring AOP,它的实现依赖了动态代理

代理类的两个作用
1.添加增强方法,2.调用目标类

1.jdk动态代理(接口代理)

在 Java 动态代理机制中java.long.reflect包中的 InvocationHandler 接⼝和 Proxy 类是核⼼

实际上就是在内存中生产一个对象,该对象实现了指定的目标对象的所有接口,代理对象和目标对象是兄弟关系,
jdk自带动态代理技术,需要使用一个静态方法来创建代理对象,他需要目标对象必须实现接口,生产的代理对象和目标对象都实现同一个接口

JDK 动态代理类使⽤步骤:

  1. 定义⼀个接⼝及其实现类;
  2. ⾃定义 InvocationHandler 并重写invoke⽅法,在 invoke ⽅法中我们会调⽤ 原⽣⽅法(被代理类的⽅法)并⾃定义⼀些处理逻辑;
  3. 通过 Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) ⽅法创建代理对象;

1.定义JDK动态代理类

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;// JDK 动态代理类
public class JDKInvocationHandler implements InvocationHandler {//⽬标对象即就是被代理对象private Object target;public JDKInvocationHandler(Object target) {this.target = target;}/**** @param proxy 代理对象* @param method 代理方法* @param args 参数** @return* @throws Throwable*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 1234就是一些增强方法//1.安全检查System.out.println("安全检查");//2.记录⽇志System.out.println("记录⽇志");//3.时间统计开始System.out.println("记录开始时间");//通过反射调⽤被代理类的⽅法Object retVal = method.invoke(target, args);//4.时间统计结束System.out.println("记录结束时间");return retVal;}
}

2.创建⼀个代理对象并使用

public class Main {public static void main(String[] args) {// 代理对象PayService target= new AliPayService();// 静态的是已经写好了的// 动态的创建⼀个代理类:通过被代理类、被代理实现的接⼝、⽅法调⽤处理器来创建PayService proxy = (PayService) Proxy.newProxyInstance(// 通过目标类的getClassLoadertarget.getClass().getClassLoader(),// 被代理类实现的一些接口new Class[]{PayService.class},// 实现了InvocationHandler接口的对象new JDKInvocationHandler(target));proxy.pay();}}

Proxy 类中使⽤频率最⾼的⽅法是:newProxyInstance() ,这个⽅法主要⽤来⽣成⼀个代理对象

public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)throws IllegalArgumentException{....}

这个⽅法⼀共有 3 个参数:

  1. loader :类加载器,⽤于加载代理对象。
  2. interfaces : 被代理类实现的⼀些接⼝;
  3. h : 实现了 InvocationHandler 接⼝的对象;

运行main方法
在这里插入图片描述
JDK 动态代理有⼀个最致命的问题是其只能代理实现了接⼝的类,为了解决这个问题,我们可以⽤ CGLIB 动态代理机制来避免

CGLIB(Code GenerationLibrary)是⼀个基于ASM的字节码⽣成库,它允许我们在运⾏时对字节码进⾏修改和动态⽣成。CGLIB通过继承⽅式实现代理。很多知名的开源框架都使⽤到了CGLIB, 例如 Spring 中的 AOP 模块中:如果⽬标对象实现了接⼝,则默认采⽤JDK 动态代理,否则采⽤ CGLIB 动态代理。

在 CGLIB 动态代理机制中 MethodInterceptor 接⼝和 Enhancer 类是核⼼

2.CGLIB 动态代理类使⽤步骤

  1. 定义⼀个类;
  2. ⾃定义 MethodInterceptor 并重写 intercept ⽅法,intercept ⽤于拦截增强
    被代理类的⽅法,和 JDK 动态代理中的 invoke ⽅法类似;
  3. 通过 Enhancer 类的 create()创建代理类

1.添加依赖

和JDK 动态代理不同, CGLIB(Code Generation Library) 实际是属于⼀个开源项⽬,如果你要使⽤它的话,需要⼿动添加相关依赖

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

2.⾃定义 MethodInterceptor(⽅法拦截器)

public class CGLIBInterceptor implements MethodInterceptor {//被代理对象private Object target;public CGLIBInterceptor(Object target){this.target = target;}@Overridepublic Object intercept(Object o, Method method, Object[] args, Method
Proxy methodProxy) throws Throwable {//1.安全检查System.out.println("安全检查");//2.记录⽇志System.out.println("记录⽇志");//3.时间统计开始System.out.println("记录开始时间");//通过cglib的代理⽅法调⽤Object retVal = methodProxy.invoke(target, args);//4.时间统计结束System.out.println("记录结束时间");return retVal;}
}

3.创建代理类, 并使⽤

public static void main(String[] args) {PayService target= new AliPayService();PayService proxy= (PayService) Enhancer.create(target.getClass(),ne
w CGLIBInterceptor(target));proxy.pay();}

你需要⾃定义 MethodInterceptor 并重写 intercept ⽅法,intercept ⽤于拦截增强被代理类的⽅法

public interface MethodInterceptor
extends Callback{// 拦截被代理类中的⽅法public Object intercept(Object obj, java.lang.reflect.Method method, Ob
ject[] args,MethodProxy proxy) throws Throwable;
}
  • obj : 被代理的对象(需要增强的对象)
  • method : 被拦截的⽅法(需要增强的⽅法)
  • args : ⽅法⼊参
  • proxy : ⽤于调⽤原始⽅法

3.JDK 动态代理和 CGLIB 动态代理对⽐:

  • JDK 动态代理只能代理实现了接⼝的类或者直接代理接⼝,⽽ CGLIB 可以代理未实现任何接⼝的类
  • CGLIB动态代理是通过⽣成⼀个被代理类的⼦类来拦截被代理类的⽅法调⽤,因此不能代理声明为 final

性能: ⼤部分情况都是 JDK 动态代理更优秀,随着 JDK 版本的升级,这个优势更加明显

Spring代理选择

  1. proxyTargetClass 为false, ⽬标实现了接⼝, ⽤jdk代理
  2. proxyTargetClass 为false, ⽬标未实现接⼝, ⽤cglib代理
  3. proxyTargetClass 为true, ⽤cglib代理

下篇见~
在这里插入图片描述

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

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

相关文章

【前端知识】React 基础巩固(三十六)——RTK中的异步操作

React 基础巩固(三十六)——RTK中的异步操作 一、RTK中使用异步操作 引入RTK中的createAsyncThunk&#xff0c;在extraReducers中监听执行状态 import { createSlice, createAsyncThunk } from "reduxjs/toolkit"; import axios from "axios";export cons…

Spring事务传播机制、实现方式、失效场景即原理

贴一篇源码分析的好文章&#xff1a;https://blog.csdn.net/qq_30905661/article/details/114400417 本质&#xff1a; 一个事务对应一个数据库连接。 通过 this 来调用某个带有 Transactional 注解的方法时&#xff0c;这个注解是失效的&#xff0c;可以看做这个方法&#x…

安防视频汇聚平台EasyCVR视频广场面包屑侧边栏支持拖拽操作

智能视频监控平台EasyCVR能在复杂的网络环境中&#xff0c;将海量设备实现集中统一接入与汇聚管理&#xff0c;实现视频的处理与分发、录像与存储、按需调阅、平台级联等。 TSINGSEE青犀视频汇聚平台EasyCVR可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协…

2023年中职组“网络安全”赛项吉安市竞赛任务书

2023年中职组“网络安全”赛项 吉安市竞赛任务书 一、竞赛时间 总计&#xff1a;360分钟 竞赛阶段 竞赛阶段 任务阶段 竞赛任务 竞赛时间 分值 A模块 A-1 登录安全加固 180分钟 200分 A-2 本地安全策略配置 A-3 流量完整性保护 A-4 事件监控 A-5 服务加固…

MYSQL进阶-事务

什么是数据库事务&#xff1f; 事务是一个不可分割的数据库操作序列&#xff0c;也是数据库并发控制的基本单位&#xff0c;其执 行的结果必须使数据库从一种一致性状态变到另一种一致性状态。事务是逻辑上 的一组操作&#xff0c;要么都执行&#xff0c;要么都不执行。 事务最…

使用express搭建后端服务

目录 1 创建工程目录2 初始化3 安装express依赖4 启动服务5 访问服务总结 上一篇我们利用TDesign搭建了前端服务&#xff0c;现在的开发讲究一个前后端分离&#xff0c;后端的话需要单独搭建服务。后端服务的技术栈还挺多&#xff0c;有java、php、python、nodejs等。在众多的技…

2023-08-03 LeetCode每日一题(删除注释)

2023-08-03每日一题 一、题目编号 722. 删除注释二、题目链接 点击跳转到题目位置 三、题目描述 给一个 C 程序&#xff0c;删除程序中的注释。这个程序source是一个数组&#xff0c;其中source[i]表示第 i 行源码。 这表示每行源码由 ‘\n’ 分隔。 在 C 中有两种注释风…

【图论】强连通分量

一.定义 强连通分量&#xff08;Strongly Connected Components&#xff0c;简称SCC&#xff09;是图论中的一个概念&#xff0c;用于描述有向图中的一组顶点&#xff0c;其中任意两个顶点之间都存在一条有向路径。换句话说&#xff0c;对于图中的任意两个顶点u和v&#xff0c;…

数学学习——最优化问题引入、凸集、凸函数、凸优化、梯度、Jacobi矩阵、Hessian矩阵

文章目录 最优化问题引入凸集凸函数凸优化梯度Jacobi矩阵Hessian矩阵 最优化问题引入 例如&#xff1a;有一根绳子&#xff0c;长度一定的情况下&#xff0c;需要如何围成一个面积最大的图像&#xff1f;这就是一个最优化的问题。就是我们高中数学中最常见的最值问题。 最优化…

《Java-SE-第二十七章》之常见的锁策略

前言 在你立足处深挖下去,就会有泉水涌出!别管蒙昧者们叫嚷:“下边永远是地狱!” 博客主页&#xff1a;KC老衲爱尼姑的博客主页 博主的github&#xff0c;平常所写代码皆在于此 共勉&#xff1a;talk is cheap, show me the code 作者是爪哇岛的新手&#xff0c;水平很有限&…

Maven发布项目到Nexus私服

项目pom配置 在项目pom.xml中文件中的仓库配置&#xff0c;Nexus私服如何搭建在这里不介绍了可自行百度。 <distributionManagement><repository><id>releases</id><name>Nexus Release Repository</name><url>http://私服地址:34…

hive删除数据进行恢复

在实际开发或生产中&#xff0c;hive表如果被误删&#xff0c;如被truncate或是分区表的分区被误删了&#xff0c;只要在回收站的清空周期内&#xff0c;是可以恢复数据的&#xff0c;步骤如下&#xff1a; &#xff08;1&#xff09; 先找到被删除数据的存放目录&#xff0c;…

Linux NUMA架构(非统一内存访问)

NUMA架构 NUMA Architecture| Non Uniform Memory Access Policy/Model | Numa Node Configuration (CPU Affinity) NUMA架构产生的原因 cpu的高速处理功能和内存存储直接的速度会严重影响cpu的性能。传统的计算机单核架构,cpu通过内存总线(内存访问控制器)直接连接到一…

在CSDN学Golang云原生(Kubernetes声明式资源管理Kustomize)

一&#xff0c;生成资源 在 Kubernetes 中&#xff0c;我们可以通过 YAML 或 JSON 文件来定义和创建各种资源对象&#xff0c;例如 Pod、Service、Deployment 等。下面是一个简单的 YAML 文件示例&#xff0c;用于创建一个 Nginx Pod&#xff1a; apiVersion: v1 kind: Pod m…

使用Express部署Vue项目

使用Express部署Vue项目 目录 1. 背景 2. 配置Vue CLI 1.1 安装nodejs 1.2 创建vue-cli 1.3 创建vue项目 1.4 构建vue项目3. 配置Express 2.1 安装express 2.2 创建项目4. 使用express部署vue项目 1&#xff0c;背景 我们想要做一个前后端分离的课程项目&#xff0c;前端…

推荐两款github敏感信息搜集工具(gsil、gshark)

推荐两款github敏感信息搜集工具&#xff08;gsil、gshark&#xff09; - 云社区 - 腾讯云 (tencent.com) github敏感信息泄露是很多企业时常忽视的一个问题&#xff0c;国外有一份研究报告显示&#xff0c;在超过24,000份的GitHub公开数据中&#xff0c;发现有数千个文件中可能…

学习笔记——压力测试案例,监控平台

测试案例 # 最简单的部署方式直接单机启动 nohup java -jar lesson-one-0.0.1-SNAPSHOT.jar > ./server.log 2>&1 &然后配置执行计划&#xff1a; 新建一个执行计划 配置请求路径 配置断言配置响应持续时间断言 然后配置一些查看结果的统计报表或者图形 然后我…

数据中台系列2:rabbitMQ 安装使用之 window 篇

RabbitMQ 是一个开源的消息队列系统&#xff0c;是高级消息队列协议&#xff08;AMQP&#xff09;的标准实现&#xff0c;用 erlang 语言开发。 因此安装 RabbitMQ 之前要先安装好 erlang。 1、安装 erlang 到 这里 下载本机能运行的最新版 erlang 安装包。如果本机没有装过 …

双重for循环优化

项目中有段代码逻辑是个双重for循环&#xff0c;发现数据量大的时候&#xff0c;直接导致数据接口响应超时&#xff0c;这里记录下不断优化的过程&#xff0c;算是抛砖引玉吧~ Talk is cheap,show me your code&#xff01; 双重for循环优化 1、数据准备2、原始双重for循环3、…

Atcoder 做题记录

My OI Blog A R C 155 F \mathbb{ARC \ 155 \ F} ARC 155 F E, F 先咕着&#xff0c;做一些多项式题&#xff0c;这篇题解是我人工翻译的 [1] Double Counting 双重计数 考虑从叶子节点开始&#xff0c;用唯一的方式&#xff08;如果有的话&#xff09;来构造出一棵满足条件的树…