【Spring篇】JDK动态代理

目录

什么是代理?

代理模式

动态代理

Java中常用的代理模式

问题来了,如何动态生成代理类?

动态代理底层实现


什么是代理?

顾名思义,代替某个对象去处理一些问题,谓之代理,那么何为动态?即让JVM虚拟机去完成而非程序员去完成(与静态对比),连起来就是让虚拟机去动态的创建一个对象去代替另一个对象完成某些业务需求

呢么其中就涉及到了两个对象,代理类目标类;代理类又被前辈归纳成代理模式,下面看代理模式;

代理模式

代理模式是GoF23种设计模式之一。属于结构型设计模式。

代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个客户不想或者不能直接引用一个对象,此时可以通过一个称之为“代理”的第三者来实现间接引用。代理对象可以在客户端和目标对象之间起到中介的作用,并且可以通过代理对象去掉客户不应该看到的内容和服务或者添加客户需要的额外服务。 通过引入一个新的对象来实现对真实对象的操作或者将新的对象作为真实对象的一个替身,这种实现机制即为代理模式,通过引入代理对象来间接访问一个对象,这就是代理模式的模式动机。

代理模式中的角色:

  • 代理类(代理主题)
  • 目标类(真实主题)
  • 代理类和目标类的公共接口(抽象主题):客户端在使用代理类时就像在使用目标类,不被客户端所察觉,所以代理类和目标类要有共同的行为,也就是实现共同的接口。

为什么要有个公共接口呢?

呢你代理类要代替目标类完成某些操作,你是不是就需要拥有目标类该有的功能,如何拥有这个类拥有的功能呢?一、代理类继承目标类(但是继承太死板,耦合度太高,而且当需求量上去后,你是不是要写n个子类,不明智!!!)二、通过实现共同接口,实现必要方法,nice!!!,完美解决;

 知道基本概念后,我们完善一下,用更加官方的话来表示。。。

动态代理

在程序运行阶段,在内存中动态生成代理类,被称为动态代理,目的是为了减少代理类的数量。解决代码复用的问题。

Java中常用的代理模式

  • jdk动态代理;
  • cglib静态代理;
  • Javassist动态代理技术;

 我们主要分析JDK代理技术

JDK动态代理(理解):使用java反射包中的类和接口实现动态代理的功能,反射包java.lang.reflect,里面有三个类:InvocationHandler,Method,Proxy


 |                                        下面我们通过一个实际业务需求来分析动态代理                                   |


假设某项目已上线,并且运行正常,只是客户反馈系统有一些地方运行较慢,要求项目组对系统进行优化。于是项目负责人就下达了这个需求。首先需要搞清楚是哪些业务方法耗时较长,于是让我们统计每个业务方法所耗费的时长。你坑定不能直接在已经上线并且运行很好的项目的源代码上操作啊,于是乎我们采用静态代理方法,代码如下:

// 目标接口
public interface OrderService {void add();void update();void delete();
}
// 目标类
public class OrderServiceImpl implements OrderService {@Overridepublic void add() {System.out.println("添加用户");}@Overridepublic void update() {System.out.println("修改用户");}@Overridepublic void delete() {System.out.println("删除用户");}
}
// 代理类
public class OrderServiceProxy implements OrderService {@Overridepublic void add() {long begin = System.currentTimeMillis();OrderService.add();long end = System.currentTimeMillis();System.out.println("耗时"+(end - begin)+"毫秒");}@Overridepublic void update() {long begin = System.currentTimeMillis();OrderService.update();long end = System.currentTimeMillis();System.out.println("耗时"+(end - begin)+"毫秒");}@Overridepublic void delete() {long begin = System.currentTimeMillis();OrderService.delete();long end = System.currentTimeMillis();System.out.println("耗时"+(end - begin)+"毫秒");}
}

在上面的静态代理方法中,代理类和目标类都实现同一个接口,在代理类中维护目标类对象,并完成对目标类对象方法的增强,这种方式虽然遵循开闭原则,但是代理类和目标类至少是“一对一”的绑定关系,如果需要被代理的目标类个数越多,代理类就会越多,会产生大量重复的代码,也不利于后期的维护。

于是乎我们使用动态代理技术:目前代码是这样的,需要我们动态生成一个代理类:

// 目标接口
public interfaceOrderService {void add();void update();void delete();
}
// 目标类
public class OrderServiceImpl implements OrderService {@Overridepublic void add() {System.out.println("添加用户");}@Overridepublic void update() {System.out.println("修改用户");}@Overridepublic void delete() {System.out.println("删除用户");}
}

问题来了,如何动态生成代理类?

回归到文章中心题目,JDK动态代理技术

在JDK中,有一个Proxy类,Proxy类是专门完成代理的操作类,可以通过此类为一个或多个接口动态的生成实现类。这个类有一个静态方法:newProxyInstance()方法。这个方法的目的就是给我们的目标对象返回一个代理对象。

我们可以看到其中newProxyInstance()方法有三个参数,下面分析三个参数:

  • 第一个参数:类加载器。在内存中生成了字节码,要想执行这个字节码,也是需要先把这个字节码加载到内存当中的。所以要指定使用哪个类加载器加载。
  • 第二个参数:接口类型。代理类和目标类实现相同的接口,所以要通过这个参数告诉JDK动态代理生成的类要实现哪些接口。
  • 第三个参数:调用处理器。这是一个JDK动态代理规定的接口,接口全名:java.lang.reflect.InvocationHandler。显然这是一个回调接口,也就是说调用这个接口中方法的程序已经写好了,就差这个接口的实现类了。

所以接下来我们要写一下java.lang.reflect.InvocationHandler接口的实现类,并且实现接口中的方法,代码如下:

public class TimerInvocationHandler implements InvocationHandler {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {return null;}
}

InvocationHandler接口中有一个方法invoke,这个invoke方法上有三个参数:

  • 第一个参数:Object proxy。代理对象。设计这个参数只是为了后期的方便,如果想在invoke方法中使用代理对象的话,尽管通过这个参数来使用。
  • 第二个参数:Method method。目标方法。
  • 第三个参数:Object[] args。目标方法调用时要传的参数。

我们将来肯定是要调用“目标方法”的,但要调用目标方法的话,需要“目标对象”的存在,“目标对象”从哪儿来呢?我们可以给TimerInvocationHandler提供一个构造方法,可以通过这个构造方法传过来“目标对象”,代码如下:

public class TimerInvocationHandler implements InvocationHandler {// 目标对象private Object target;// 通过构造方法来传目标对象public TimerInvocationHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 目标执行之前增强。long begin = System.currentTimeMillis();// 调用目标对象的目标方法Object retValue = method.invoke(target, args);// 目标执行之后增强。long end = System.currentTimeMillis();System.out.println("耗时"+(end - begin)+"毫秒");// 一定要记得返回哦。return retValue;}
}

接下来我们开始调用:

 // 创建目标对象OrderService target = new OrderServiceImpl();// 创建代理对象OrderService orderServiceProxy = (OrderService) ProxyUtil.newProxyInstance(target);// 调用代理对象的代理方法orderServiceProxy.add();orderServiceProxy.update();orderServiceProxy.delete();

 成功;

动态代理底层实现

现在我们思考一个问题:在使用动态代理之前,我们有一个目标类、一个公共接口;我们如何通过二者和proxy类返回一个代理类对象呢?

我们知道创建一个对象就先要有这个类才能new对象,那现在代理类都还不存在,该怎么去构造代理对象呢?

JDK动态代理的目的就是通过接口来生成代理类以及代理类的对象,我们知道接口是不能直接通过new关键字创建对象的。那么JDK动态代理是怎么创建出代理类以及代理类对象的呢?
 

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

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

相关文章

Zabbix-Liunx服务器内存使用率测试

要在Python 2.7中运行内存消耗脚本并安装psutil,您需要先安装pip。以下是完整的步骤,包括如何在Python 2.7环境中安装pip,然后安装psutil,以及最后如何运行内存消耗脚本。 步骤1: 安装pip 在Python 2.7中安装pip: 首先…

积分球吸收光谱测量的领域有哪些?

积分球吸收光谱测量是一种常用的吸收光谱测量方法,它通过将样品放置在积分球的入口处,球内的光线经过多次反射后形成均匀的照度分布,然后使用光度计或光谱仪对光线进行测量,可以获得样品的相关参数。 在积分球吸收光谱测量中&…

Python入门03变量

目录 1 什么是变量2 变量声明3 变量命名规则4 变量类型5 类型转换总结 1 什么是变量 编程语言中变量就像容器一样,可以用来存放东西 我的变量就像杯子一样,可以用来盛放各种饮料。在Python中变量用来存放各种各样的数据,比如整数、浮点数、…

NLP的使用

参考: Apache openNLP 简介 - 链滴 (ld246.com) opennlp 模型下载地址:Index of /apache/opennlp/models/ud-models-1.0/ (tencent.com) OpenNLP是一个流行的开源自然语言处理工具包,它提供了一系列的NLP模型和算法。然而,Open…

Linux的基本指令(3)

16.cal指令 cal命令可以用来显示公历(阳历)日历。公历是现在国际通用的历法,又称格列历,通称阳历。“阳历”又名“太阳历”,系以地球绕行太阳一周为一年,为西方各国所通用,故又名“西历”。 命…

Java特殊文件

Properties 读取数据 package com.itheima.d1;import java.io.FileNotFoundException; import java.io.FileReader; import java.nio.charset.StandardCharsets; import java.util.Properties; import java.util.Set;public class Test1 {public static void main(String[] arg…

语音合成综述Speech Synthesis

一、语音合成概述 语音信号的产生分为两个阶段,信息编码和生理控制。首先在大脑中出现某种想要表达的想法,然后由大脑将其编码为具体的语言文字序列,及语音中可能存在的强调、重读等韵律信息。经过语言的组织,大脑通过控制发音器…

Jmeter脚本录制:抓取IOS手机请求包

现在移动端的项目越来越多,今天给大家介绍一下,在IOS下Jmeter如何抓包。 1、电脑连上wifi 2、Jmeter中配置“HTTP代理服务器” 1)启动Jmeter; 2)“测试计划”中添加“线程组”; 3)“测试计划”中添加“HTTP代理服务器”&…

基于Springboot的冬奥会科普平台(有报告),Javaee项目,springboot项目。

演示视频: 基于Springboot的冬奥会科普平台(有报告),Javaee项目,springboot项目。 项目介绍: 采用M(model)V(view)C(controller)三层…

2024北京林业大学计算机考研分析

24计算机考研|上岸指南 北京林业大学 特色优势 Characteristics & Advantages:信息学院创建于2001年,是一个年轻而有朝气的学院。学院秉承“结构、特色、质量、创新”的八字方针,坚持以“质量提升、行业融合”为核心的内涵式发展战略&am…

Go 语言中的 Switch 语句详解

switch语句 使用switch语句来选择要执行的多个代码块中的一个。 在Go中的switch语句类似于C、C、Java、JavaScript和PHP中的switch语句。不同之处在于它只执行匹配的case,因此不需要使用break语句。 单一case的switch语法 switch 表达式 { case x:// 代码块 cas…

信息收集小练习

信息收集小练习 本文章无任何恶意攻击行为,演示内容都合规无攻击性 演示如何绕过cdn获取真实ip 使用多地ping该网站 发现有很多ip地址,证明有cdn 此处使用搜索引擎搜索,得到ip 演示nmap工具的常用参数 此处以testfire.net为例 使用多地p…