设计模式--动态代理

动态代理是 Java 中一种常见的设计模式,它允许在运行时创建一个实现一组接口的代理类对象。Java 提供了 java.lang.reflect 包来支持动态代理的实现。在 JDK 中,可以使用 java.lang.reflect.Proxy 类和 java.lang.reflect.InvocationHandler 接口来创建动态代理。

下面是一个简单的示例,展示如何使用 JDK 的动态代理来代理一个接口。

步骤概述

  1. 定义接口:首先定义一个接口,代理对象将实现这个接口。

  2. 实现 InvocationHandler 接口:创建一个类实现 InvocationHandler 接口,该接口包含一个方法 invoke,在该方法中定义代理对象的行为。

  3. 使用 Proxy 类创建代理对象:使用 Proxy.newProxyInstance 方法来创建代理对象,该方法接受类加载器、要实现的接口数组和 InvocationHandler 实现类的实例。

当涉及到理解 JDK 动态代理中的 InvocationHandler 接口时,我们可以用更通俗易懂的方式来解释它的作用和功能。

示例

假设有一个接口 UserService

public interface UserService {void save(String userName);void delete(String userName);
}

 现在我们实现一个 InvocationHandler 接口的类 UserServiceProxy,用于处理代理对象的方法调用:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;public class UserServiceProxy implements InvocationHandler {private Object target;public UserServiceProxy(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("Before invoking " + method.getName());// 执行目标对象的方法Object result = method.invoke(target, args);System.out.println("After invoking " + method.getName());return result;}
}

UserServiceProxy 中,我们实现了 InvocationHandler 接口,并覆写了 invoke 方法来定义代理对象的行为。在这个例子中,我们简单地在方法调用前后输出日志。

接下来,我们可以使用 Proxy.newProxyInstance 方法创建代理对象:

import java.lang.reflect.Proxy;public class Main {public static void main(String[] args) {// 创建真实对象UserService realService = new UserServiceImpl();// 创建代理对象UserService proxy = (UserService) Proxy.newProxyInstance(UserService.class.getClassLoader(),new Class[] { UserService.class },new UserServiceProxy(realService));// 调用代理对象的方法proxy.save("Alice");proxy.delete("Bob");}
}

解释代码

  1. 创建真实对象

UserService realService = new UserServiceImpl();

 这里假设 UserServiceImplUserService 接口的实现类

     2.创建代理对象

UserService proxy = (UserService) Proxy.newProxyInstance(UserService.class.getClassLoader(),new Class[] { UserService.class },new UserServiceProxy(realService)
);
  • Proxy.newProxyInstance 方法创建一个代理对象,需要传入类加载器、要实现的接口数组和 InvocationHandler 实现类的实例。
  • UserService.class.getClassLoader() 获取 UserService 接口的类加载器。
  • new Class[] { UserService.class } 指定代理对象实现的接口,这里只有 UserService
  • new UserServiceProxy(realService)InvocationHandler 的实现类实例,用于处理代理对象的方法调用

调用代理对象的方法

proxy.save("Alice");
proxy.delete("Bob");

这些方法调用会被 UserServiceProxy 中的 invoke 方法拦截,并在方法执行前后输出日志。

注意事项

  • 动态代理只能代理接口,不能代理类。
  • Proxy.newProxyInstance 创建的代理对象会实现指定的接口,并委托 InvocationHandler 处理方法调用。

以上就是一个简单的 JDK 动态代理的示例,展示了如何利用 Proxy 类和 InvocationHandler 接口来实现代理对象的创建和方法拦截。

什么是 InvocationHandler?

在使用 JDK 动态代理时,你需要定义一个接口,这个接口是代理对象和真实对象都要实现的。接着,你需要编写一个类来实现 InvocationHandler 接口。这个实现类承担了代理对象的调用处理逻辑,也就是说,当通过代理对象调用接口方法时,实际上会调用 InvocationHandlerinvoke 方法来处理这个调用。

InvocationHandler 的核心功能

InvocationHandler 接口中定义了一个方法 invoke,它接收三个参数:

  • Object proxy:代理对象本身。
  • Method method:被调用的方法对象。
  • Object[] args:方法调用时传入的参数。

invoke 方法中,你可以编写代码来决定如何处理这个方法调用。通常的情况下,你会根据需要:

  • 执行一些额外的逻辑(例如日志记录、性能监控等)。
  • 将方法调用委托给真实对象的对应方法。
  • 根据条件决定是否拦截、修改方法的调用行为等。
为什么需要 InvocationHandler?

动态代理的核心思想是在运行时生成代理对象,这些代理对象能够拦截对其所代理的接口方法的调用,并且能够自定义处理逻辑。InvocationHandler 提供了这样一个处理入口,让你能够控制代理对象如何处理方法调用,从而实现了动态代理的灵活性和可扩展性。

示例场景

举个例子,假设有一个 UserService 接口,其中有一个 getUserById(int id) 方法。你想要在每次调用这个方法时记录日志。你可以通过 JDK 动态代理来实现这个功能:

  1. 定义一个 UserService 接口。
  2. 编写一个 UserServiceImpl 类实现 UserService 接口,提供实际的方法逻辑。
  3. 编写一个 LogInvocationHandler 类实现 InvocationHandler 接口,在 invoke 方法中添加日志记录逻辑。
  4. 使用 Proxy.newProxyInstance 方法创建代理对象,将 UserService 接口和 LogInvocationHandler 关联起来。

这样,当通过代理对象调用 getUserById 方法时,实际上会先进入 LogInvocationHandlerinvoke 方法,你可以在这里编写代码来记录日志,然后再将调用委托给 UserServiceImpl 的相应方法。

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

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

相关文章

Hive怎么调整优化Tez引擎的查询?在Tez上优化Hive查询的指南

文章目录 在Tez上优化Hive查询的指南调优指南理解Tez中的并行化理解mapper数量理解reducer数量 并发案例1:未指定队列名称案例2:指定队列名称并发的指南/建议 容器复用和预热容器容器复用预热容器 一般Tez调优参数 在Tez上优化Hive查询的指南 在Tez上优…

golang中的corn

引包 "github.com/robfig/cron/v3"使用 taskCron : cron.New(cron.WithSeconds()) _, err : taskCron.AddFunc("* * * * * *", func() {doing.work})每一个*的含义 秒(Seconds) 是 0–59 * / , - 分(Minutes&#xff…

HTML播放flv

页面效果&#xff1a; 代码如下&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" …

森林火灾扑救特类车辆有哪些_鼎跃安全

森林消防是在森林火灾发生时&#xff0c;为了保护森林资源&#xff0c;防止火势蔓延&#xff0c;采取了一系列的应用措施&#xff0c;针对自然环境中的火灾消防工作。森林灭火主要包括预警、预防措施、火情监测、火势控制和灭火等&#xff0c;森林火灾发生的地形往往复杂崎岖&a…

如何判断自己遇到的攻击是SQL注入攻击

要判断自己遇到的攻击是否是SQL注入攻击&#xff0c;可以从以下几个方面进行观察和分析&#xff1a; 攻击方式识别&#xff1a; 观察输入字段&#xff1a;如果攻击者在输入字段&#xff08;如用户名、密码框&#xff09;中输入了非正常的字符或语句&#xff0c;特别是与SQL语法…

【银河麒麟】高可用触发服务器异常重启,处理机制详解

1.服务器环境以及配置 【机型】物理机 处理器&#xff1a; Intel 内存&#xff1a; 126G 【内核版本】 4.19.90-25.16.v2101.ky10.x86_64 【银河麒麟操作系统镜像版本】 Kylin-Server-10-SP2-Release-Shenzhen-Metro-x86-Build01-20220619 Kylin-HA-10-SP2-Release-S…

NGINX_七 nginx 高级应用

七 nginx 高级应用 1 使用alias实现虚拟目录 location /test { # /var/www/qianfeng/index.htmlalias /var/www/qianfeng/;index index.html; }location /test { #/var/www/qianfeng/test/index.htmlroot /var/www/qianfeng/;index index.html; }2 通过 stub_status 模…

yolov10打包为exe

一、前言 本节实验将官方yolov10推理程序打包为exe运行 二、代码 首先下载官方代码至本机&#xff0c;并使用conda创建虚拟环境&#xff0c;并安装好yolov10所需库 conda create --prefix E:/pyenv/myYolo10 python3.8 pip install -r requirements.txt 下载官方模型权重 …

深入理解和实现Windows进程间通信(消息队列)

常见的进程间通信方法 常见的进程间通信方法有&#xff1a; 管道&#xff08;Pipe&#xff09;消息队列共享内存信号量套接字 下面&#xff0c;我们将详细介绍消息队列的原理以及具体实现。 什么是消息队列&#xff1f; Windows操作系统使用消息机制来促进应用程序与操作系…

Git 查看当前分支是基于哪个分支拉取(源头分支)

场景&#xff1a; 项目中使用 Git 管理代码仓库的时候&#xff0c;随着项目的持续迭代及项目的扩展&#xff0c;多版本并行开发是非常常见的事情&#xff0c;多版本并行开发就伴随着多分支&#xff0c;随着 Git 的分支越拉越多&#xff0c;这时候很容易造成分支的混乱&#xf…

AI学习指南机器学习篇-朴素贝叶斯分类器

AI学习指南机器学习篇-朴素贝叶斯分类器 1. 介绍 在机器学习中&#xff0c;朴素贝叶斯分类器是一种简单而有效的分类算法。它基于贝叶斯定理和特征条件独立性假设&#xff0c;可以被用来解决多种分类问题。本篇博客将深入探讨朴素贝叶斯分类器的基本原理&#xff0c;包括如何…

【TensorFlow深度学习】量化压缩技术在降低模型体积中的应用

量化压缩技术在降低模型体积中的应用 量化压缩技术在降低模型体积中的应用1. 引言2. 量化压缩基础3. 实战:使用TensorFlow Lite进行模型量化4. 评估量化效果5. 结果分析与优化建议6. 结语量化压缩技术在降低模型体积中的应用 在深度学习领域,模型的体积和推理速度成为了实际…

蓝牙模块在车载系统中的应用与集成:现状、挑战与未来展望

随着科技的快速发展&#xff0c;蓝牙技术已经深入到我们生活的方方面面&#xff0c;其中车载系统中的应用尤为显著。蓝牙模块作为一种无线通信技术&#xff0c;不仅为驾驶者提供了更加便捷的操作体验&#xff0c;同时也提升了驾驶的安全性。本文旨在分析蓝牙模块在车载系统中的…

selenium框架学习

概念 WEB自动化框架 三大组件: selenium IDE 浏览器插件,实现脚本录制WebDriver 实现对浏览器的各种操作(API包)Grid 实现同时对多个用例进行执行,用例在多个浏览器同步执行环境搭建 1、安装selenium: pip install selenium2、安装浏览器 3、安装浏览器驱动(对应的驱…

FFmpeg源码:ff_h2645_extract_rbsp函数分析

一、ff_h2645_extract_rbsp函数的声明 ff_h2645_extract_rbsp函数的声明放在FFmpeg源码&#xff08;本文演示用的FFmpeg源码版本为5.0.3&#xff0c;该ffmpeg在CentOS 7.5上通过10.2.1版本的gcc编译&#xff09;的头文件libavcodec/h2645_parse.h中。 /*** Extract the raw (u…

东郊到家类型小程序APP软件基于SpringBoot开发的系统源码

项目背景 在快节奏的现代生活中&#xff0c;人们越来越追求高效、便捷的生活方式。上门服务作为一种新型的服务模式&#xff0c;正逐渐受到广大用户的青睐。而这一切的背后&#xff0c;离不开技术的强大支撑。今天&#xff0c;我们就来探讨一下上门服务类型软件的技术魅力&…

常用的基于WebGL的三维WebGIS框架

1. Three.js Three.js 是一款运行在浏览器中的3D引擎&#xff0c;官方地址为https://threejs.org/&#xff0c;你可以用它创建各种三维场景&#xff0c;包括摄影机、光影、材质等各种对象。 2. Babylon.js Babylon.js是一个JavaScript开源框架&#xff0c;基于WebGL和TypeSc…

React Native性能优化红宝书

一、React Native介绍 React Native 是Facebook在React.js Conf2015 推出的开源框架&#xff0c;使用React和应用平台的原生功能来构建 Android 和 iOS 应用。通过 React Native&#xff0c;可以使用 JavaScript 来访问移动平台的 API&#xff0c;使用 React 组件来描述 UI 的…

WebHttpServletRequestResponse(完整知识点汇总)

额外知识点 Web核心 Web 全球广域网&#xff0c;也成为万维网&#xff08;www&#xff09;&#xff0c;可通过浏览器访问的网站 JavaWeb 使用Java技术来解决相关Web互联网领域的技术栈 JavaWeb技术栈 B/S架构&#xff1a;Browser/Server&#xff0c;即浏览器/服务器 架构模式…

ROS2 概念以及通信方式

一、ros2的相关概念 ROS 2&#xff08;Robot Operating System 2&#xff09;是一个用于机器人开发的开源平台&#xff0c;它提供了一系列工具和库&#xff0c;用于构建机器人应用程序。相较于前身ROS&#xff08;ROS 1&#xff09;&#xff0c;ROS 2在设计上考虑了更多的实时…