常见面试题-JDK和CGLIB动态代理

news2024/11/16 11:36:53

JDK 动态代理和 CGLIB 动态代理对比

  1. JDK 动态代理只能代理实现了接口的类,而 CGLIB 可以代理未实现任何接口的类。另外CGLIB 动态代理是通过生成一个被代理类的子类来拦截被代理类的方法调用,因此不能代理声明为final 类型的类和方法
  2. 就二者的效率来说,大部分情况都是JDK 动态代理更优秀,随着 JDK 版本的升级,这个优势更加明显。
  3. JDK 动态代理利用了拦截器、反射机制生成一个代理接口的匿名类,在调用具体方法前调用 InvokeHandler 来处理;CGLIB 动态代理利用了 ASM 框架,将代理对象类的 class 文件加载进来,通过修改其字节码生成子类来处理

JDK动态代理底层原理:

假如目前有一个接口 HelloService、实现类HelloServiceImpl(包含一个 say() 方法,需要被增强)、增强类MyInvocationHandler

在 JDK 动态代理中,生成的代理类 $Proxy1 是继承 Proxy 并且实现 HelloService 接口,当调用代理类的方法时,会进入到拦截器 MyInvocationHandler 的 invoke 方法中,下边为代理类生成代码:

// 生成代理对象
HelloService helloService = (HelloService) Proxy.newProxyInstance(MyInvocationHandler.class.getClassLoader(), new Class[]{HelloService.class}, new MyInvocationHandler());
helloService.say();

通过上述代码拿到的 helloService 对象其实就是 JDK 动态代理对象,我们可以通过添加 VM options 来将动态代理对象保存下来,添加 VM options 如下:

-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true

之后生成的动态代理对象如下(这里为了更直观的看代理类,因此只保留了最关键的代码),say() 其实就是定义在 HelloService 中需要被增强的方法,那么当调用 helloService.say() 时,其实就是调用 $Proxy1.say() 方法,在该方法中会调用 h.invoke() 方法,这里的 h 就是我们自己定义的 MyInvocationHandler 拦截器,之后就会进入到拦截器的 invoke 方法,

import com.example.nettystudy.JdkProxyTest.HelloService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy1 extends Proxy implements HelloService {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    ...
    
    public final void say() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    ...
}

下边来看一下拦截器的 invoke 方法,该方法有 3 个参数,第一个参数 proxy 也就是上边的代理类对象, method 就是接口中的 say 方法,那么在拦截器中就会执行我们自己添加的增强操作了

public class MyInvocationHandler implements InvocationHandler {

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("方法执行前");
        // 这里 HelloServiceImpl 是被代理对象,被代理对象执行方法
        Object result = method.invoke(new HelloServiceImpl(), args);
        System.out.println("方法执行后");
        return result;
    }
}

cglib动态代理底层原理

cglib 采用底层的字节码技术,为一个类创建子类,并且在子类中使用方法去拦截所有的父类调用,并织入横切逻辑

cglib 使用如下:

// Human.java
public class Human {
    public void info() {
        System.out.println("Human invoke info");
    }
    public void fly() {
        System.out.println("Human invoke fly");
    }
}

// CGLibProxy.java  拦截器
class CGLibProxy implements MethodInterceptor {
     
  
	 // CGLib需要代理的目标对象 
    private Object targetObject;
  
    public Object getProxyInstance(Object obj) { 
     
        this.targetObject = obj;  
        //1. 创建一个工具类
        Enhancer enhancer = new Enhancer();
        // 2.设置父类--可以是类或者接口
        enhancer.setSuperclass(obj.getClass());  
        //3. 设置回调函数
        enhancer.setCallback(this);  
        //4. 创建子类对象,即代理对象
        Object proxyObj = enhancer.create();  
        // 返回代理对象 
        return proxyObj;
    }  
  
    public Object intercept(Object proxy, Method method, Object[] args,
                            MethodProxy methodProxy) throws Throwable {
        System.out.println("方法执行前增强处理");
        // 执行目标目标对象方法
        Object obj = method.invoke(targetObject, args);
        System.out.println("方法执行后增强处理");
        return obj;
    }  
}

// TestCglibProxy.java 测试类
public class TestCglibProxy {
	public static void main(String[] args) {
		// 创建被代理对象
		Human man = new Human();
		// 添加如下代码,获取代理类源文件
		String path = CGLibProxy.class.getResource(".").getPath();
		System.out.println(path);
		System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, path);

		CGLibProxy cgLibProxy = new CGLibProxy();
		Object obj = cgLibProxy.getProxyInstance(man);
		System.out.println(obj.getClass());
		Human hu = (Human)obj;
		hu.info();
		hu.fly();
	}
}

上边程序输出为:

在这里插入图片描述

可以根据红色输出的路径找到我们生成的代理类的 class 文件

在这个 Human 类,也就是需要被增强的类中,我们定义了两个方法 info()、fly(),那么 cglib 生成的子类会继承 Human 类,并且重写这两个方法,生成的代理类如下:

在代理类中,会先将拦截器赋值给 var10000,之后再调用 var10000.intercept 这个方法,也就是我们自己定义的拦截器的拦截方法CGLibProxy#intercept()

public class Human$$EnhancerByCGLIB$$a1812f09 extends Human implements Factory {
	// ...    省略其余代码
    public final void info() {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        if (var10000 != null) {
            var10000.intercept(this, CGLIB$info$0$Method, CGLIB$emptyArgs, CGLIB$info$0$Proxy);
        } else {
            super.info();
        }
    }
    // ...
}

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

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

相关文章

信息系统项目管理师 教材目录、考试大纲、考情

文章目录 考情考试大纲第1章 信息化发展第2章 信息技术发展第3章 信息系统治理第4章 信息系统管理第5章 信息系统工程第6章 项目管理概论第7章 项目立项管理第8章 项目整合管理第9章 项目范围管理272第10章 项目进度管理297第11章 项目成本管理334第12章 项目质量管理358第13章…

【图像卷积与卷积层】的基本概念与区别

图像卷积 卷积操作是指将一个滤波器(也称为卷积核或内核)应用于输入图像的小块区域,然后将滤波器在整个图像上滑动,逐步计算出输出特征图。这个过程可以帮助网络学习到图像的局部特征,因为每个卷积核都可以学习到不同…

VulnHub Prime_Series_Level-1

一、信息收集 1.nmap扫描 ┌──(root💀kali)-[~/桌面] └─# arp-scan -l┌──(root💀kali)-[~/桌面] └─# nmap -sS -A -p- 192.168.103.202发现开放了22和80端口 2.web页面 打开80端口的web页面,是一张静态的图片,没什么价…

【Seata源码学习 】 扫描@GlobalTransaction注解 篇一

1. SeataAutoConfiguration 自动配置类的加载 基于SpringBoot的starter机制,在应用上下文启动时,会加载SeataAutoConfiguration自动配置类 # Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfigurationio.seata.spring.boot.aut…

探寻知识的新路径——电大搜题助您开启学习新纪元

江西开放大学和广播电视大学一直以来都是许多自学者和职场人士追寻知识的圣地。然而,对于许多学子来说,学习的过程也常常充满了困惑和挑战。为了帮助这些学习者通过更高效、便捷的方式获取知识,江西开放大学推出了一款创新的学习工具——电大…

谈谈越来越无效的拥塞控制

简单看一个图: 它不是互联网本身,但这是典型网络的必要组件,它决定了 flow 如何从从一边流向另一边:一条 flow 经过交换节点通过 NIC 被导入一条链路前在 buffer 中排队。 现如今大多数工程师的工作都在折腾那个单独的盒子&…

231112-中文错别字识别与纠正问题的大模型与小模型调研

A. 引言 当前,以ChatGPT为代表的大语言模型(Large Language Models, LLMs)正引领着新一轮工业革命。ChatGPT最开始的研究领域隶属于NLP的一个子问题,其输入是text,输出也是text。在从文本输入到文本输出的诸多应用场景…

C语言从入门到精通之【概述】

#include指令和头文件 例如#include <stdio.h>&#xff0c;我们经常看到C文件最上面会有类似这样的语句&#xff0c;它的作用相当于把stdio.h文件中的所有内容都输入该行所在的位置。实际上&#xff0c;这是一种“拷贝-粘贴”的操作。 #include这行代码是一条C预处理器…

Smart Link 和 Monitor Link应用

定义 Smart Link常用于双上行链路组网&#xff0c;提高接入的可靠性。 Monitor Link通过监视上行接口&#xff0c;使下行接口同步上行接口状态&#xff0c;起到传递故障信息的作用。 Smart Link&#xff0c;又叫做备份链路。一个Smart Link由两个接口组成&#xff0c;其中一个…

木疙瘩踩坑日记-容易忽略的一些BUG

在一开始玩家务必很清楚这三个概念 图形&#xff1a;舞台上元素的最小单位。软件自带的以及外部导入的图片默认都是图形&#xff01;最朴素的元素&#xff01;可以添加预制动画、关键帧动画、进度动画&#xff08;软件自带的形状&#xff09; 元件&#xff1a;一个可以内部封…

高性能收发原始数据包的框架(Netmap)

一、Netmap 简介 Netmap 是一个高性能收发原始数据包的框架&#xff0c;由 Luigi Rizzo 等人开发完成&#xff0c;其包含了内核模块以及用户态库函数。其目标是&#xff0c;不修改现有操作系统软件以及不需要特殊硬件支持&#xff0c;实现用户态和网卡之间数据包的高性能传递。…

Git系列之分支与标签的使用及应用场景模拟

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是君易--鑨&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的博客专栏《Git实战开发》。&#x1f3af;&#x1f3af; &a…

【网络奇遇记】我和因特网的初相遇2 —— 三种交换方式

&#x1f308;个人主页&#xff1a;聆风吟 &#x1f525;系列专栏&#xff1a;网络奇遇记、数据结构 &#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 文章目录 前言一. 电路交换1.1 电路交换讲解1.2 电路交换实例 二. 分组交换1.1 分组交换讲解1.2 分组交换实例…

Go 14岁了

今天我们庆祝Go开源十四周年&#xff01;Go度过了美好的一年&#xff0c;发布了两个功能齐全的版本和其他重要的里程碑。 我们在2月份发布了Go 1.20&#xff0c;在8月份发布了Go 1.21&#xff0c;更多地关注实现改进而不是新的语言更改。 在Go 1.20中&#xff0c;我们预览了配置…

基于Python+Django的图书管理系统

项目介绍 图书是人类文明传播的一个重要方式&#xff0c;很多历史悠久的文明都是通过图书来进行传递的&#xff0c;虽然随着时代的进步电子信息技术发展很快&#xff0c;但是纸质图书的地位仍然是非常稳固的&#xff0c;为了能够让知识拥有更加快捷方便的传递方式我们开发了本…

Typora-PicGo-七牛云图床

Typora-PicGo-七牛云图床 问题描述&#xff1a; 每次使用Typora写完笔记后&#xff0c;想要将笔记上传至CSDN会发现一个问题&#xff0c;由于没有配置图床&#xff0c;笔记中的图片需要一张一张的上传到CSDN&#xff0c;非常麻烦&#xff0c;若使用PicGo并搭配七牛云的10G免费…

Django配置文件,request,链接mysql方法,Orm简介

三板斧问题(views.py) HttpResponse # 返回的是字符串render # 渲染一个HTML静态文件&#xff0c;模板文件redirect # 重定向的 在视图文件中得视图函数必须要接收一个形参request&#xff0c;并且&#xff0c;视图函数也要有返回值&#xff…

Linux - 基础IO(重定向 - 重定向模拟实现 - shell 当中的 重定向)- 下篇

前言 上一篇博客当中&#xff0c;我们对 文件 在操作系统当中是 如何就管理的&#xff0c;这个问题做了 详细描述&#xff0c;本篇博客将基于上篇 博客当中的内容进行 阐述&#xff0c;如有疑问&#xff0c;请参考上篇博客&#xff1a; Linux - 基础IO&#xff08;Linux 当中…

matlab 多自由度的车辆垂向振动模型 车辆平稳性研究

1、内容简介 略 17-可以交流、咨询、答疑 多自由度的车辆垂向振动模型 多自由度的车辆垂向振动模型&#xff0c;包含四分之一车体模型、半车模型和整车模型 垂向振动模型、四分之一车体模型、半车模型和整车模型 2、内容说明 略 3、仿真分析 略 4、参考论文 略 链接&…

第七章 块为结构建模 P3|系统建模语言SysML实用指南学习

仅供个人学习记录 块行为建模 块提供了行为情境&#xff0c;行为这个 SysML 词条覆盖了块如何处理输如/输出和其内部状态改变的所有描述。 块可以指定某个行为作为其主行为或者分类器行为&#xff0c;该行为在块实例化后启动执行。其他行为可以指定为方法&#xff0c;提供了处…