7000字详解 动态代理(JDK动态代理 CGLIB动态代理)与静态代理

代理模式

  • 1. 代理模式 概念
  • 2. 静态代理
  • 3. 动态代理
    • 3.1.JDK动态代理
    • 3.2.CGLIB动态代理
    • 3.3. JDK动态代理和CGLIB动态代理区别
  • 4.静态代理和动态代理区别
  • 5.篇末

1. 代理模式 概念

代理模式是一种设计模式
使用代理对象来替代真实对象,用代理对象去访问目标对象。这样可以保证在不修改目标对象的前提下,还可以增加一些额外的功能,作出扩展。
在这里插入图片描述

代理模式的作用主要是扩展目标对象的功能,在目标对象执行方法的前后,可以自己自定义一些操作。

同时代理模式分为静态代理动态代理

2. 静态代理

静态代理对目标对象的方法增强要手动完成,如果接口增加了新的方法,那么就要更改代码,不够灵活,开发中比较少见静态代理。

JVM层面:
静态代理在编译期讲接口,和接口的实现类,代理类都变成class文件。

静态代理实现:
1. 定义接口A和实现类 (也就是要传输的内容)
2. 创建一个代理类实现接口A (代理类要可以接收传输的内容)
3. 将目标对象注入到代理类中,在代理类的方法中调用目标对象的方法,然后在目标对象方法执行的前后 就可以扩展一些内容。

代码:
1.定义发送内容的接口

package 静态代理;// 1.定义发送内容的接口    目标对象
public interface Message {String send(String message);
}

2.实现发送短信的接口

package 静态代理;//2. 发送内容接口的实现类      就是目标对象处理传输的内容的处理方法
public class MessageImpl implements Message {@Overridepublic String send(String message) {System.out.println("send message:" + message);return message;}
}

3.创建代理类并实现接口

package 静态代理;//3. 代理类 也需要实现接口 且需要将目标对象注入到其中来   代理对象
public class MessageProxy implements Message {//目标对象(Message)注入到代理类中private final Message Message;public MessageProxy(Message message) {this.Message = message;}@Overridepublic String send(String message) {//方法执行前 添加自定义内容System.out.println("before method send()");//在代理类中的方法 调用目标类的方法Message.send(message);//方法执行后 添加自定义内容System.out.println("after method send()");return null;}
}

4.执行代码

public class Main {public static void main(String[] args) {System.out.println("静态代理");//实例化 实现类对象 类型是Message    目标对象//Message sendMessage = new MessageImpl();MessageImpl sendMessage = new MessageImpl();//实例化 代理类对象 完成注入          代理对象MessageProxy messageProxy = new MessageProxy(sendMessage);//执行方法messageProxy.send("hello");}
}

在这里插入图片描述

3. 动态代理

对于静态代理来说,动态代理更加灵活,不需要保证每个目标类都有一个代理类,实现接口也不是必须的,可以直接代理实现类。

JVM层面:
动态代理是在运行时动态生成类字节码,加载到JVM中。

举例:
Spring AOP ,RPC框架使用了动态代理。

动态代理对于框架的学习帮助较大

动态代理实现方法很多 ,这篇介绍 JDK动态代理CGLIB动态代理。

3.1.JDK动态代理

JDK动态代理中 InvocationHandler接口Proxy类是重点。

Proxy类中有一个方法:newProxyInstance() 用来生成代理对象。

    public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)throws IllegalArgumentException{......}

Proxy类中的newProxyInstance()方法有三个参数:
1.loader:类加载器,用来加载代理对象。
2.interfaces: 被代理类实现的一些接口。
3.h: 实现了InvocationHandler接口的对象。

动态代理还需要实现InvocationHandler接口,自定义处理内容的逻辑,这样我们在代理对象调用一个方法的时候,方法会转发到InvocationHandler接口类中的invoke方法

public interface InvocationHandler {/*** 当你使用代理对象调用方法的时候实际会调用到这个方法*/public Object invoke(Object proxy, Method method, Object[] args)throws Throwable;
}

InvocationHandler接口中的invock方法有三个参数:
**1.proxy:**动态生成的代理类。
**2.method:**与代理类调用的方法对应
**3.args:**method方法的参数

动态代理机制:通过Proxy类的newProxyInstance方法 创建的代理类在调用方法的时候,实际调用的InvocationHandler接口中的Invoke方法。
所以需要在Invoke方法中自定义内容。

JDK动态代理实现:
1.定义一个接口和实现类
2.重写InvocationHandler中的Invoke()方法,用来自定义内容。
3.通过Proxy类的newProxyInstance()方法创建代理对象。

代码:
1.定义发送内容的接口

package JDK动态代理;
//目标对象
public interface SdMessage {String send(String message);
}

2.发送内容接口的实现类

package JDK动态代理;
//实现接口的实现类     就是目标对象处理传输的内容的处理方法
public class SdMessageImp implements SdMessage {@Overridepublic String send(String message) {System.out.println("send message:" + message);return message;}
}

3.重写InvocationHandler中的Invoke方法

package JDK动态代理;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
//代理类调用方法时 调用的是InvocationHandler中的Invoke方法 重写它 自定义
public class MyInvocationHandler implements InvocationHandler {/*** 代理类中的真实对象*/private final Object target;public MyInvocationHandler(Object target) {this.target = target;}//其中的method和代理类调用的方法对应@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//方法调用前,自定义的内容System.out.println("before method() " + method.getName());Object result = method.invoke(target,args);//方法调用后,自定义的内容System.out.println("after method() " + method.getName());return result;}
}

4.创建代理类

package JDK动态代理;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
//代理类调用方法时 调用的是InvocationHandler中的Invoke方法 重写它 自定义
public class MyInvocationHandler implements InvocationHandler {/*** 代理类中的真实对象*/private final Object target;public MyInvocationHandler(Object target) {this.target = target;}//其中的method和代理类调用的方法对应@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//方法调用前,自定义的内容System.out.println("before method() " + method.getName());Object result = method.invoke(target,args);//方法调用后,自定义的内容System.out.println("after method() " + method.getName());return result;}
}

5.执行

public class Main {public static void main(String[] args) {//JDK动态代理  其中的参数是目标对象System.out.println("JDK动态代理");SdMessage sdMessage = (SdMessage) ProxyFactory.getProxy(new SdMessageImp());sdMessage.send("hello");}
}

在这里插入图片描述

3.2.CGLIB动态代理

JDK动态代理的缺点是 只可以代理已经实现了接口的实现类。
而CGLIB动态代理可以避免

CGLIB是一个基于ASM的字节码生成库,允许我们在运行时对字节码进行修改和动态生成。CGLIB通过继承方式实现代理,很多开源框架都使用到了CGLIB,例如Spring中的AOP模块中,如果实现了接口那么就采用JDK动态代理,如果没实现,就使用CGLIB动态代理。

CGLIB动态代理MethodInterceptor接口Enhancer类是重点

需要重写MethodInterceptor接口中的Intercept方法,这个方法用来拦截被代理类

public interface MethodInterceptor
extends Callback{// 拦截被代理类中的方法public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,MethodProxy proxy) throws Throwable;
}

**MethodInterceptor接口中的Intercept()**方法有4个参数:
1.obj:被代理的对象(也可以叫做需要增强的对象)
2.method:被拦截的方法(也可以叫做需要增强的方法)
3.args:方法的参数
4.proxy:用于调用原始方法

可以通过Enhancer类来动态获取被代理类,当代理类调用方法的时候,实际调用的是MethodInterceptor接口中的Intercept方法。

CGLIB动态代理实现:
1.定义一个类
2.重写MethodInterceptor接口中的Intercept()方法,这个方法用来拦截被代理类的方法,其实也就是拦截到方法以后,会执行intercept方法,和被代理类的方法。
3.通过Enhancer类的create()方法创建代理类。

CGLIB动态代理代码实现:
CGLIB是开源项目需要引入依赖。

<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.3.0</version>
</dependency>

1.实现一个发送消息的类

package CGLIB动态代理;//发送内容的类
public class CglibSdMessage {public String send(String message) {System.out.println("send message:" + message);return message;}
}

2.自定义MethodInterceptor(方法拦截器)重写其中的Intercept方法

package CGLIB动态代理;import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;import java.lang.reflect.Method;//方法拦截器  重写MethodInterceptor接口中的intercept方法
public class MyMethodInterceptor implements MethodInterceptor {/**** @param o             被代理的对象(需要增强的对象)* @param method        被拦截的方法(需要增强的方法)* @param objects       方法的参数* @param methodProxy   用于调用原始方法* @return* @throws Throwable*/@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {//方法调用前,自定义的内容System.out.println("before method() " + method.getName());Object object =  methodProxy.invokeSuper(o,objects);//方法调用后,自定义的内容System.out.println("after method() " + method.getName());return object;}
}

3.获取代理类(创建代理类)

package CGLIB动态代理;import net.sf.cglib.proxy.Enhancer;//创建代理类 通过Enhancer类的create方法
public class CglibProxyFactory {//Class<?> clazz之前接收的是实现接口的实现类 现在接收的是类 泛型public static Object getProxy(Class<?> clazz) {//创建动态代理类Enhancer enhancer = new Enhancer();//设置类加载器enhancer.setClassLoader(clazz.getClass().getClassLoader());//设置被代理类(目标对象)enhancer.setSuperclass(clazz);//设置方法拦截器enhancer.setCallback(new MyMethodInterceptor());//创建代理类return enhancer.create();}
}

4.调用

public class Main {public static void main(String[] args) {//CGLIB动态代理 其中的参数是被代理类(目标对象)System.out.println("JDK动态代理");CglibSdMessage cglibSdMessage = (CglibSdMessage) CglibProxyFactory.getProxy(CglibSdMessage.class);cglibSdMessage.send("hello");}
}

在这里插入图片描述

3.3. JDK动态代理和CGLIB动态代理区别

  • JDK动态代理只能代理已经实现了接口的类,CGLIB动态代理可以直接代理没有实现接口的(CGLIB动态代理通过生成一个被代理类的子类来拦截被代理类中的方法调用,因为是继承所以不能代理被final修饰的类和方法)
  • JDK动态代理效率更高

4.静态代理和动态代理区别

  • 灵活性:动态代理更加的灵活,接口的实现不是必要的,CGLIB动态代理可以直接代理一个类。静态代理只能代理实现了接口的类,并且接口增加新方法的时候,目标对象的代码和代理对象的代码都要修改。
  • JVM:静态代理在编译期就把接口,实现类,代理类变成Class文件,动态代理在运行时动态生成类字节码文件,加载到JVM中。

5.篇末

我在文章中介绍了静态代理和动态代理,包阔了静态代理的代码实现和动态代理中的2钟代理(JDK动态代理和CGLIB动态代理)的代码实现,已经静/动态代理的区别,和两种动态代理的区别。

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

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

相关文章

虚拟化逻辑架构: LBR 网桥基础管理

目录 一、理论 1.Linux Bridge 二、实验 1.LBR 网桥管理 三、问题 1.Linux虚拟交换机如何增删 一、理论 1.Linux Bridge Linux Bridge&#xff08;网桥&#xff09;是用纯软件实现的虚拟交换机&#xff0c;有着和物理交换机相同的功能&#xff0c;例如二层交换&#…

百面深度学习-自然语言处理

自然语言处理 神经机器翻译模型经历了哪些主要的结构变化&#xff1f;分别解决了哪些问题&#xff1f; 神经机器翻译&#xff08;Neural Machine Translation, NMT&#xff09;是一种使用深度学习技术来实现自动翻译的方法。自从提出以来&#xff0c;NMT模型经历了几个重要的…

HTTP协议发展

HTTP 1.0 -> HTTP 1.1 -> HTTP 2.0 -> HTTP 3.0 (QUIC) 每一代HTTP解决了什么问题&#xff1f; 下图说明了主要功能。 HTTP 1.0 于 1996 年最终确定并完整记录。对同一服务器的每个请求都需要单独的 TCP 连接。 HTTP 1.1 于 1997 年发布。TCP 连接可以保持打开状态…

openGauss学习笔记-132 openGauss 数据库运维-查看openGauss状态

文章目录 openGauss学习笔记-132 openGauss 数据库运维-查看openGauss状态132.1 背景信息132.2 前提条件132.3 操作步骤132.4 参数说明132.5 示例 openGauss学习笔记-132 openGauss 数据库运维-查看openGauss状态 132.1 背景信息 openGauss支持查看整个openGauss的状态&#…

如何在Linux系统安装Nginx并启动

Nginx的介绍 Nginx是一款轻量级的Web服务器/反向代理服务器及电子邮件&#xff08;IMAP/POP3&#xff09;代理服务器。其特点是占有内存少&#xff0c;并发能力强&#xff0c;事实上nginx的并发能力在同类型的网页服务器中表现较好。官网&#xff1a;nginx newsNginx的下载 前往…

docker基础学习笔记

文章目录 Docker简介Linux下安装DockerDocker常用命令Docker网络Docker存储docker-composedockerfile制作镜像私有仓库镜像导入导出参考 Docker简介 定义&#xff1a;Docker是一个开源的应用容器引擎优势&#xff1a; 一键部署&#xff0c;开箱即用&#xff1a;容器使用基于im…

Qt5.15.2静态编译 VS2017 with static OpenSSL

几年前编译过一次Qt静态库:VS2015编译Qt5.7.0生成支持XP的静态库,再次编译,毫无压力。 一.环境 系统:Windows 10 专业版 64位 编译器:visual studio 2017 第三方工具:perl,ruby和python python用最新的3.x.x版本也是可以的 这三个工具都需要添加到环境变量,安装时勾选…

057-第三代软件开发-文件监视器

第三代软件开发-文件监视器 文章目录 第三代软件开发-文件监视器项目介绍文件监视器实现原理关于 QFileSystemWatcher实现代码 关键字&#xff1a; Qt、 Qml、 关键字3、 关键字4、 关键字5 项目介绍 欢迎来到我们的 QML & C 项目&#xff01;这个项目结合了 QML&…

人工智能时代的内容写作

内容不再只是王道&#xff0c;正如俗话所说&#xff1a;它是一种流动的货币&#xff0c;推动了巨大的在线信息和影响力经济。 每个品牌都是一个故事&#xff0c;通过其服务和商品讲述自己。尽管如此&#xff0c;大多数客户还是会通过您的在线内容最了解您。 但随着我们进入人…

每日一题:LeetCode-LCR 143.子结构判断

每日一题系列&#xff08;day 05&#xff09; 前言&#xff1a; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f50e…

汇编:关于栈的知识

1.入栈和出栈指令 2. SS与SP 3. 入栈与出栈 3.1 执行push ax ↑↑ 3.2 执行pop ax ↓↓ 3.3 栈顶超界的问题 4. 寄存器赋值 基于8086CPU编程时&#xff0c;可以将一段内存当作栈来使用。一个栈段最大可以设为64KB&#xff08;0-FFFFH&#xff09;。 1.入栈和出栈指令…

C语言——函数

导读 &#xff1a; 这篇文章主要讲解一下C语言函数的一些基本知识。 前言&#xff1a;函数的概念 C语言中的函数又常常被称为子程序&#xff0c;是用来完成某项特定的工作的一段代码。就像我们生活中的模块化建造技术&#xff0c;类比模块化建房子的过程&#xff1a;整个程序…

高校大学校园后勤移动报修系统 微信小程序uniapp+vue

本文主要是针对线下校园后勤移动报修传统管理方式中管理不便与效率低的缺点&#xff0c;将电子商务和计算机技术结合起来&#xff0c;开发出管理便捷&#xff0c;效率高的基于app的大学校园后勤移动报修app。该系统、操作简单、界面友好、易于管理和维护&#xff1b;而且对后勤…

Python中类的定义和使用细讲

文章目录 前言一、定义类二、创建类的实例三、创建 _ _ init _ _() 方法四、创建类的成员并访问1. 创建实例方法并访问2. 创建数据成员并访问 五、访问限制 前言 在 Python 中&#xff0c;类表示具有相同属性和方法的对象的集合。在使用类时&#xff0c;需要先定义类&#xff0…

Android安卓设置跳转默认应用商店为Google Play 链接跳转到谷歌商店临时解决方法

手机链接默认不跳转 Google Play 因为大部分安卓厂商系统都根据了自己的需求进行了修改,就成为了系统级导流,想要彻底解除可刷写国际版等原生系统即可恢复 解决方法 使用冻结软件(例如 爱玩机手机助手(root)等应用)对 应用商城 进行临时冻结,如需保证正常使用解除冻结状态即可…

清分系统对账

流程1的问题&#xff1a; 1、通道一天的数据会有多少&#xff0c;有二三十万条交易数据吗&#xff1f; 2、如果数据过大都存到一个Map里面去&#xff0c;机器不得挂了 步骤1总结&#xff1a; 1、通过channelNo获取通道T的数据&#xff0c;因为通道是一天一个文件给过来。在转…

首届教师案例教学竞赛一等奖作品上线至和鲸社区,快来学习!

细心的朋友可能已经发现&#xff0c;近期和鲸社区的频道页上线了一个新专区——“优秀参赛作品专区”。 图.和鲸社区频道页 迄今为止&#xff0c;和鲸参与/支持了 500 多场专业数据科学竞赛&#xff0c;包括面向气象、金融、医学、海洋等不同领域的&#xff0c;面向从业者、科学…

.net 8 发布了,试下微软最近强推的MAUI

先看下实现的效果&#xff1a; 下面发下XAML文件&#xff1a; <?xml version"1.0" encoding"utf-8" ?> <ContentPage xmlns"http://schemas.microsoft.com/dotnet/2021/maui"xmlns:x"http://schemas.microsoft.com/winfx/2009/…

案例027:基于微信小程序的校园二手平台的设计与实现

文末获取源码 开发语言&#xff1a;Java 框架&#xff1a;SSM JDK版本&#xff1a;JDK1.8 数据库&#xff1a;mysql 5.7 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.5.4 小程序框架&#xff1a;uniapp 小程序开发软件&#xff1a;HBuilder X 小程序…

基于springboot实现智慧党建系统项目【项目源码】

基于springboot实现智慧党建系统演示 Java技术 Java是由Sun公司推出的一门跨平台的面向对象的程序设计语言。因为Java 技术具有卓越的通用性、高效性、健壮的安全性和平台移植性的特点&#xff0c;而且Java是开源的&#xff0c;拥有全世界最大的开发者专业社群&#xff0c;所以…