口语化答案

好的,面试官。jdk 的动态代理主要是依赖Proxy类InvocationHandler 接口。jdk 动态代理要求类必须有接口。在进行实现的时候,首先要定义接口,比如MyService,这个接口就是我们的正常功能的实现。但是希望在不更改MyService 的情况下增加额外功能,那么我们需要定义一个实现InvocationHandler 接口的实现类,同时在方法实现上面增加额外的逻辑。最后通过 Proxy 的 newProxyInstance 将二者结合到一起。就实现了动态代理。

题目解析

大家不要觉得动态代理很难理解,按照这个步骤其实你发现很简单。记忆的过程和 cglib 对比着看,就很轻松,面试也是属于常考一点的题目。

面试得分点

InvocationHandler 增强,Proxy 创建代理

题目详细答案

JDK 动态代理主要依赖于java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口来实现。

实现步骤

定义接口:定义需要代理的接口。

实现接口:创建接口的实现类。

创建调用处理器:实现InvocationHandler接口,并在invoke方法中定义代理逻辑。

创建代理对象:通过Proxy.newProxyInstance方法创建代理对象。

代码 Demo

假设我们有一个简单的服务接口MyService和它的实现类MyServiceImpl,我们将通过 JDK 动态代理为MyService创建一个代理对象,并在方法调用前后添加日志。

1. 定义接口
public interface MyService {void performTask();
}
2. 实现接口
public class MyServiceImpl implements MyService {@Overridepublic void performTask() {System.out.println("Performing task");}
}
3. 创建调用处理器
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;public class LoggingInvocationHandler implements InvocationHandler {private final Object target;public LoggingInvocationHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("Logging before method execution: " + method.getName());Object result = method.invoke(target, args);System.out.println("Logging after method execution: " + method.getName());return result;}
}
4. 创建代理对象并使用
import java.lang.reflect.Proxy;public class MainApp {public static void main(String[] args) {// 创建目标对象MyService myService = new MyServiceImpl();// 创建调用处理器LoggingInvocationHandler handler = new LoggingInvocationHandler(myService);// 创建代理对象MyService proxyInstance = (MyService) Proxy.newProxyInstance(myService.getClass().getClassLoader(),myService.getClass().getInterfaces(),handler);// 调用代理对象的方法proxyInstance.performTask();}
}

详细解释

1、 接口定义和实现

MyService是一个简单的接口,定义了一个方法performTask。

MyServiceImpl是MyService的实现类,实现了performTask方法。

2、 调用处理器

LoggingInvocationHandler实现了InvocationHandler接口。它的invoke方法在代理对象的方法调用时被调用。invoke方法接收三个参数:proxy:代理对象。method:被调用的方法。args:方法参数。在invoke方法中,我们在方法调用前后添加了日志打印。

3、 创建代理对象

使用Proxy.newProxyInstance方法创建代理对象。

newProxyInstance方法接收三个参数:类加载器:通常使用目标对象的类加载器。接口数组:目标对象实现的所有接口。调用处理器:实现了InvocationHandler接口的实例。

4、 使用代理对象

通过代理对象调用方法时,实际调用的是LoggingInvocationHandler的invoke方法。

在invoke方法中,首先打印日志,然后通过反射调用目标对象的方法,最后再打印日志。

JDK动态代理通俗详解

面试官您好,关于JDK动态代理,我用一个生活中的例子来帮助理解:

快递代收点类比

想象你网购了一件商品:

  1. 商家(MyServiceImpl):实际发货的人
  2. 快递代收点(Proxy):中间代理点
  3. 代收点规则(InvocationHandler):代收点提供的额外服务(比如验货、暂存)

实现步骤详解

1. 定义服务接口(购物清单)

// 就像网购时商家承诺的服务标准
public interface ShoppingService {void deliverItem();  // 送货服务String checkQuality(); // 验货服务
}

2. 实际商家实现(真实发货)

public class Amazon implements ShoppingService {@Overridepublic void deliverItem() {System.out.println("亚马逊发货中...");}@Overridepublic String checkQuality() {return "正品保障";}
}

3. 创建代收点规则(增值服务)

public class ProxyService implements InvocationHandler {private Object target;  // 真实的商家对象public ProxyService(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 前置增强:代收点验货if(method.getName().equals("deliverItem")) {System.out.println("【代收点】快递消毒中...");}// 执行原方法(让商家正常发货)Object result = method.invoke(target, args);// 后置增强:签收服务if(method.getName().equals("deliverItem")) {System.out.println("【代收点】已签收,短信通知客户");}return result;}
}

4. 创建代收点(生成代理)

public class Client {public static void main(String[] args) {// 真实商家ShoppingService amazon = new Amazon();// 创建代理规则InvocationHandler handler = new ProxyService(amazon);// 建立代收点(生成代理实例)ShoppingService proxy = (ShoppingService) Proxy.newProxyInstance(amazon.getClass().getClassLoader(),amazon.getClass().getInterfaces(),handler);// 客户通过代收点购物proxy.deliverItem();System.out.println("验货结果:" + proxy.checkQuality());}
}

输出结果

【代收点】快递消毒中...
亚马逊发货中...
【代收点】已签收,短信通知客户
验货结果:正品保障

关键点说明

  1. 必须要有接口:就像必须通过电商平台下单,不能直接找路边摊
  2. InvocationHandler是核心:所有增强逻辑都在这里实现
  3. Proxy.newProxyInstance三要素
    • 类加载器:用原来的就行
    • 接口数组:说明要代理哪些服务
    • 处理规则:怎么增强这些服务

实际项目应用

在我们电商系统中:

  1. 支付服务接口用JDK代理添加日志
  2. 订单服务接口用JDK代理添加事务
  3. 商品服务接口用JDK代理做缓存

与CGLIB对比记忆

特性

JDK动态代理

CGLIB

依赖

必须实现接口

不需要接口

原理

实现相同接口

继承目标类

性能

反射调用稍慢

直接调用更快

适用场景

Spring默认对接口的代理

代理普通类

这样设计既保持了规范性(面向接口编程),又能灵活添加通用功能。

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

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

相关文章

自然语言处理的相关概念与问题

目录 一、学科的产生与发展 1、什么是自然语言? 2、自然语言处理技术的诞生 二、技术挑战 三、基本方法 1、方法概述 理性主义方法 经验主义方法 2、传统的统计学习方法 3、深度学习方法 词向量表示 词向量学习 开源工具 四、应用举例 1、汉语分词 …

Anthropic MCP架构深度解析:下一代AI工具集成协议的设计哲学

本文深入剖析Anthropic提出的模型通信协议(MCP),揭示其如何重构AI与工具生态的交互范式,打造安全高效的智能体基础设施。 引言:AI工具集成的"巴别塔困境" 当前AI生态面临的核心挑战: #mermaid-svg-lSpYBxzxD5oiYwcL {font-family:"trebuchet ms",verd…

【注意】HCIE-Datacom华为数通考试,第四季度将变题!

最近,数据通信圈子可热闹坏啦!好几个渠道都证实了,HCIE - Datacom实验考试马上要有大变化咯! 这可不是啥小道消息,也不是那种试点的传言,而是从IE内部技术交流会上得到的确切消息。 这边联系了华为认证的好…

MySql 硬核解析系列 一 MySQL的锁机制

MySQL 的锁机制是其并发控制的核心,直接影响数据库的性能、一致性与可用性。本文将从底层原理、锁的分类、实现机制、锁的粒度、锁的兼容性、死锁处理、InnoDB 的行锁实现、MVCC 与锁的关系等多个维度,进行硬核、深度解析,适用于希望深入理解 MySQL 并发控制机制的开发者与 …

7.软件工程

软件生命周期软件生命周期什么是软件工程?以工程化的原则和方法来开发软件,其目的是提高软件生产率、提高软件质量、降低软件成本。软件工程3大组成部分:方法、工具、过程。什么是软件生命周期:经过开发、使用和维护,直…

C 语言结构体与 Java 类的异同点深度解析

在编程语言的发展历程中,C 语言的结构体与 Java 的类扮演着至关重要的角色。作为面向过程编程的经典代表,C 语言的结构体为数据封装提供了基础形式;而 Java 作为纯面向对象语言,类则是其核心语法结构。二者既存在一脉相承的设计思想,又因编程语言范式的差异呈现出显著区别…

C++、STL面试题总结(二)

1. 必须实现拷贝构造函数的场景 核心问题:默认拷贝构造的缺陷 C 默认的拷贝构造函数(浅拷贝),会直接拷贝指针 / 引用成员的地址。若类包含引用成员或指向堆内存的指针,浅拷贝会导致 “多个对象共享同一份资源”&…

IntelliJ IDEA2024 错误‘http://start.spring.io/‘的初始化失败,请检查URL、网络和代理设置。

下载新版本的intellij idea2024创建项目时,服务器URL报错误http://start.spring.io/的初始化失败,请检查URL、网络和代理设置。错误消息:Cannot download http://start.spring.io/:Permission denied:getsockopt,具体如下图&#…

从零开始的云计算生活——第三十八天,避坑落井,Docker容器模块

一.故事背景 在综合使用了之前全部的知识完成项目之后,接下来将学习更简单的方法来对之前的命令进行使用,马上进入容器模块 二. Docker概述 Docker简介 Docker,翻译过来就是码头工人 Docker是一个开源的应用容器引擎,让开发者…

Python与自动化运维:构建智能IT基础设施的终极方案

Python与自动化运维:构建智能IT基础设施的终极方案 引言:运维革命的Python引擎 在DevOps理念席卷全球的今天,企业IT基础设施的复杂度呈指数级增长。某跨国银行的数据显示,采用Python构建的自动化运维体系使其服务器部署效率提升400%,故障响应时间缩短至原来的1/8。本文将…

HarmonyOS应用开发环境搭建以及快速入门介绍

下载并安装DevEco Studio,这是华为官方提供的HarmonyOS应用开发IDE。访问华为开发者联盟官网下载对应操作系统的版本。安装完成后,配置HarmonyOS SDK和必要的工具链。 确保计算机满足开发环境要求,包括Windows 10 64位或macOS 10.14及以上操…

RocketMQ与Kafka 消费者组的‌重平衡操作消息顺序性对比

RocketMQ 的重平衡机制本身不会直接影响消息顺序,但消费模式的选择和使用需注意以下细节:重平衡机制RocketMQ消费者组的重平衡策略是每隔20秒从Broker获取消费组的最新消费进度,并根据订阅信息重新分配消息队列。该策略主要影响消息拉取的均衡…

学习 Android(十四)NDK基础

学习 Android(十四)NDK基础 Android NDK 是一个工具集,可让我们使用 C 和 C 等语言以原生代码实现应用的各个部分。对于特定类型的应用,这可以帮助我们重复使用以这些语言编写的代码库。 接下来,我们将按照以下步骤进行…

宝塔(免费版9.2.0)的docker拉取仓库失败的加速方法

宝塔docker拉取仓库失败 完美加速方法_宝塔docker加速-CSDN博客 版本:免费版 9.2.0 https://docker.1ms.run 其他的试了很多 都不行 最后不要用宝塔的控制面板(很卡),直接在linux中用命令行,效果就很好了。

文献解读-生境分析亚区域选择+2D_DL+3D_DL-局部晚期食管鳞状细胞癌新辅助化疗免疫治疗反应预测

研究标题:结合亚区域放射组学与多通道二维或三维深度学习模型预测局部晚期食管鳞状细胞癌(LA-ESCC)患者对新辅助化疗免疫治疗(NACI)的反应借鉴点:建模思路(看流程图理解就够了)引言食…

机器学习第四课之决策树

目录 简介 一.决策树算法简介 二. 决策树分类原理 1.ID3算法 1.1 熵值 1.2 信息增益 1.3 案例分析 ​编辑 2.C4.5 2.1 信息增益率 2.2.案例分析 3.CART决策树 3.1基尼值和基尼指数 3.2案例分析 三、决策树剪枝 四、决策树API 五、电信客户流失 六、回归树 七. 回归…

Java面试题和答案大全

一、Java基础知识 1. Java语言特点 题目: 请说明Java语言的主要特点? 答案: 面向对象:Java是纯面向对象的语言,支持封装、继承、多态 平台无关性:一次编译,到处运行(Write Once, Run Anywhere) 简单性:语法简洁,去掉了C++中的指针、多重继承等复杂特性 安全性:提…

用NAS如何远程访问:详细教程与实用技巧

在信息时代,家用NAS(网络附加存储)成为家庭数据存储和管理的热门设备。它不仅可以作为家庭照片、视频、工作文件的集中存储中心,还支持远程访问,方便用户随时随地获取数据。那么,如何配置和实现家用NAS的远…

Qt-桌面宠物

目录 一,演示(部分功能) 二,开发环境准备 三,部分代码实现 1.创建基础窗口 2.实现宠物动画 3.添加交互功能 4.系统托盘集成 5.行为模式实现 6.状态管理系统 7.资源打包部署 四,接受定制 一&…

C++编程学习(第19天)

局部变量和全局变量每一个变量都有其有效作用范围,这就是变量的作用域,在作用域以外是不能访问这些变量的。局部变量在一个函数内部定义的变量是局部变量,它只在本函数范围内有效,也就是说只有在本函数内才能使用他们,…