Java中的动态代理(JDK Proxy VS CGLib)

前言

动态代理可以说是Java基础中一个比较重要的内容,这块内容关系到Spring框架中的AOP实现原理,所以特别写了一篇作为个人对这块知识的总结。这部分内容主要包括:JDK Proxy和CGLib的基本介绍、二者的实现原理、代码示例等。

什么是动态代理

动态代理是指程序在运行过程中,动态代理的实现是基于某个特点类而生成的增强类,也就是说,动态代理生成的类会顶替掉原来的类而被执行从而做到达到增强的目前

目前流行的动态代理机制有两种:JDK Proxy 和 CGLIB

这两个人实现动态代理的方式不同,前者是要求被代理类必须继承接口,而后者则没有这个要求,因为他是通过生成代理类的子类来进行构造的。

JDK Proxy

JDK Proxy 是Java官方提供的一种动态代理类实现方式,它的实现动态代理特点如下:

  • 代理类实现接口
  • 通过反射生产代理类

为什么被代理类要实现接口

可能有人会有疑惑为什么说JDK Proxy仅支持实现了接口的被代理类,这个问题需要分几点来回答

【首先】生成的代理类是通过反射的方式构建的,这个过程中,这个代理类可以认为就是被代理类的“克隆体”,但是与之不同的其中一个点的是,这个生产的代理类也需要继承Proxy才行,但是假如被代理类继承了其他类,但是Java中是不允许多继承的,所以被代理类是不能继承其他类,否则就会出现多继承的错误!!!

【其次】在Java中实现接口和继承类是不冲突的,为了在生产代理类后,我们可以拿到被代理类的同名方法然后进行重写(重写内容后续说明),所以JDK Proxy选择了实现接口的这种方式

代码示例

以下是动态代理的一个代码示例:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;// 定义接口
interface UserService {void save();
}// 实现接口的具体类
class UserServiceImpl implements UserService {public void save() {System.out.println("Save user");}
}// 实现InvocationHandler接口来自定义代理逻辑
class MyInvocationHandler implements InvocationHandler {private Object target;public MyInvocationHandler(Object target) {this.target = target;}public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("Before method");Object result = method.invoke(target, args);System.out.println("After method");return result;}
}public class Main {public static void main(String[] args) {UserService userService = new UserServiceImpl();// 创建InvocationHandler对象MyInvocationHandler handler = new MyInvocationHandler(userService);// 通过Proxy类的静态方法创建代理对象UserService proxy = (UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(),userService.getClass().getInterfaces(),handler);// 调用代理对象的方法proxy.save();}
}

在这段代码中

【1】我们先看接口和被代理类,实现比较简单,接口实现类重写接口方法。

【2】MyInvocationHandler是一个接口实现类,它的目的是为了创建自定义的代理规则逻辑,target属性就是被代理类,MyInvocationHandler的构造方法必须将被代理对象的引用传递进来,然后进行属性赋值

【3】MyInvocationHandler中的invoke()方法是最核心的,它才是实现动态代理规则逻辑的主要部分

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("Before method");Object result = method.invoke(target, args);System.out.println("After method");return result	
}

在这个方法中,method.invoke这个语句就是我们被代理类的方法执行,这个方法中通过传递被代理类target,在内部调用这个被代理类的方法,然后在metho.invoke方法上下就可以执行其他前置后置增强逻辑,也就是说,我们生成的动态代理类的同名方法是通过实现前置逻辑和后置逻辑+被代理类的同名方法来实现动态代理,这样既不会丢失原本逻辑也可以做大增强!!!

【4】在main方法中

proxy.save()

这个方法其实即是动态代理生成的方法,也就是MyInvocationHandler中的invoke方法

图解(逻辑)

在这里插入图片描述

CGLIB

CGLIB是非官方创建的动态代理库。它通过继承被代理类来创建代理类。CGLib不需要被代理类实现接口,它能够代理普通的类。CGLib通过修改字节码来实现代理。

代码示例

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;import java.lang.reflect.Method;// 被代理类
class RealSubject {public void doSomething() {System.out.println("RealSubject doSomething");}
}// 代理类
class ProxySubject implements MethodInterceptor {private Object target; // 被代理对象public Object getInstance(Object target) {this.target = target;Enhancer enhancer = new Enhancer();enhancer.setSuperclass(this.target.getClass());enhancer.setCallback(this);return enhancer.create();}public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("Before method: " + method.getName());Object result = proxy.invokeSuper(obj, args);System.out.println("After method: " + method.getName());return result;}
}public class CGLIBDynamicProxyExample {public static void main(String[] args) {ProxySubject proxy = new ProxySubject();RealSubject realSubject = (RealSubject) proxy.getInstance(new RealSubject());realSubject.doSomething();}
}

【1】在这段代码中,我们会发现在ProxySubject类中的getInstance方法也需要传入被代理类target,但与JDK 不同的是,这个方法内部是为了获取他的class类,然后生成一个代理类继承自被代理类。

【2】与JDK Proxy中类似,也有一个类似于invoke方法的intercept方法,他内部执行的proxy.invokeSuper(obj,args),其实就是调用父类的目标方法,也就是被代理类的方法

【3】在main方法中,我们会发现其创建代理类的方式和JDK Proxy很相似,都需要把代理规则类放入到代理工厂中进行创建,最后创建出来的对象就是被代理对象的子类

图解

在这里插入图片描述

仅为个人学习笔记
感谢阅读

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

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

相关文章

C# 如何将使用的Dll嵌入到.exe应用程序中?

文章目录 前言详细实操简要步骤 前言 有没有想自己开发的exe保留一点神秘,不想让他人知道软件使用了哪些dll; 又或许是客户觉得一个软件里面的dll文件太多了,能不能简单一点,直接双击.exe就可以直接运行了,别搞那么多乱七八糟的。…

Three.js相机参数及Z-Fighting问题的解决方案

本主题讨论透视相机以及如何为远距离环境设置合适的视锥体。 推荐:用 NSDT编辑器 快速搭建可编程3D场景 透视相机是一种投影模式,旨在模仿人类在现实世界中看待事物的方式。 这是渲染 3D 场景最常用的投影模式。 - three.js 如果你看一下 Three.js 文档…

优思学院|六西格玛中的概率分布有哪些?

为什么概率分布重要? 概率分布是统计学中一个重要的概念,它帮助我们理解随机变量的分布情况以及与之相关的概率。在面对具体问题时,了解概率分布可以帮助我们选择适当的检验或分析策略,以解决问题并做出合理的决策。 常见的概率…

【二】kubernetes master单节点拓展为集群

#服务器 #部署 #云原生 #k8s 一、 前言 一、ubuntu20.04上搭建containerd版( 1.2.4 以上)k8s及kuboard V3 接上文中,我们已经部署好了单节点master的k8s集群,在生产环境中,单节点的master肯定是不行的,那…

科技探究之旅--亲子研学活动

2023年8月26日,广州市从化区齐家社会工作服务中心(以下简称“齐家”)的“星乐园-乡村儿童公益辅导服务项目”组织了新开村及西湖村助学点24对亲子到广州市白云区文搏3D打印基地进行“科技探究之旅--亲子研学”活动,旨在发现、点燃…

用Go编写ChatGPT插件

ChatGPT插件平台有望成为影响深远的"下一件大事",因此对于开发者来说,有必要对ChatGPT插件的开发有一定的了解。原文: Writing a ChatGPT Plugin With Go[1] 我工作的附带福利之一是偶尔可以接触试用一些很酷的新技术,最近的一项技…

实战:基于卷积的MNIST手写体分类

前面实现了基于多层感知机的MNIST手写体识别,本章将实现以卷积神经网络完成的MNIST手写体识别。 1. 数据的准备 在本例中,依旧使用MNIST数据集,对这个数据集的数据和标签介绍,前面的章节已详细说明过了,相对于前面章…

控制goroutine 的并发执行数量

goroutine的数量上限是1048575吗? 正常项目,协程数量超过十万就需要引起重视。如果有上百万goroutine,一般是有问题的。 但并不是说协程数量的上限是100多w 1048575的来自类似如下的demo代码: package mainimport ( "fmt" "ma…

MySQL的mysql-bin.00xx binlog日志文件的清理

目录 引言手工清理配置自动清理 引言 公司一个项目生产环境mysql数据盘占用空间增长得特别快,经过排查发现是开启了mysql的binlog日志。如果把binlog日志关闭,如果操作万一出现问题,就没有办法恢复数据,很不安全,只能…

WPF怎么实现文件拖放功能winform怎么实现拖拽功能

WPF怎么实现文件拖放功能winform怎么实现文件拖拽功能,在管理员模式下wpf winform怎么实现文件的拖拽功能 WPF实现文件拖放功能,正常情况并没有什么问题,但是如果你的程序使用管理员身份启动,你就会发现文件拖放功能就会失效。同…

css元素定位:通过元素的标签或者元素的id、class属性定位

前言 大部分人在使用selenium定位元素时,用的是xpath元素定位方式,因为xpath元素定位方式基本能解决定位的需求。xpath元素定位方式更直观,更好理解一些。 css元素定位方式往往被忽略掉了,其实css元素定位方式也有它的价值&…

全新纠错码将量子计算提效10倍!

上周,来自两个研究小组的最新模拟报告称,一类新兴的量子纠错码的效率比目前的“黄金标准”(即表面码)高出一个数量级。 量子纠错码的工作原理都是将大量容易出错的量子比特转换成更小的“受保护”量子比特,这些量子比特…

前端Vue仿企查查天眼查高管信息列表组件

随着技术的不断发展,传统的开发方式使得系统的复杂度越来越高。在传统开发过程中,一个小小的改动或者一个小功能的增加可能会导致整体逻辑的修改,造成牵一发而动全身的情况。为了解决这个问题,我们采用了组件化的开发模式。通过组…

Ansible学习笔记8

group模块: 创建一个group组: [rootlocalhost ~]# ansible group1 -m group -a "nameaaa gid5000" 192.168.17.105 | CHANGED > {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}…

穿上App外衣,保持Web灵魂——PWA温故

早在2015年,设计师弗朗西斯贝里曼和Google Chrome的工程师亚历克斯罗素提出“PWA(渐进式网络应用程序)”概念,将网络之长与应用之长相结合,其核心目标就是提升 Web App 的性能,改善 Web App以媲美Native的流…

HttPClient简介及示例:学习如何与Web服务器进行通信

文章目录 前言一、引入依赖二、使用步骤1.创建被调用者2.创建调用者三、结果被调用者服务:调用者服务: 总结 前言 欢迎来到本篇博客,这是一个关于HttPClient的入门案例的指南。🎉 在今天的网络世界中,与服务器进行数据…

qt.qpa.plugin:找不到Qt平台插件“wayland“|| (下载插件)Ubuntu上解决方案

相信大家也都知道这个地方应该做什么,当然是下载这个qt平台的插件wayland,但是很多人可能不知道怎么下载这个插件。 那么我现在要说的这个方法就是针对这种的。 sudo apt install qtwayland5完事儿了奥兄弟们。 看看效果 正常了奥。

FFmpeg5.0源码阅读——FFmpeg大体框架(以GIF转码为示例)

摘要:前一段时间熟悉了下FFmpeg主流程源码实现,对FFmpeg的整体框架有了个大概的认识,因此在此做一个笔记,希望以比较容易理解的文字描述FFmpeg本身的结构,加深对FFmpeg的框架进行梳理加深理解,如果文章中有…

java从入门到起飞(六)——用Socket实现网络通信

文章目录 背景网络编程网络编程三要素 2.DatagramSocket之UDP通信程序2.1 UDP发送数据2.2UDP接收数据2.3 3. Socket之TCP通信程序3.1TCP发送数据3.2TCP接收数据 背景 网络编程 ● 计算机网络 是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线…

Matlab图像处理-加法运算

加法运算 图像加法运算的一个应用是将一幅图像的内容叠加到另一幅图像上,生成叠加图像效果,或给图像中每个像素叠加常数改变图像的亮度。 在MATLAB图像处理工具箱中提供的函数imadd()可实现两幅图像的相加或者一幅图像和常量的相加。 程序代码 I1 i…