【Spring Boot】Spring AOP动态代理,以及静态代理

目录

  • Spring AOP代理
    • 一. 代理的概念
    • 二. 静态代理
    • 三. JDK代理
      • 3.1 重写 invoke 方法进⾏功能增强
      • 3.2 通过Proxy类随机生成代理对象
    • 四. CGLIB代理
      • 4.1 自定义类来重写intercept方法
      • 4.2 通过Enhancer类的create方法来创建代理类
    • 五. AOP源码剖析
  • 总结(重中之重,精华)

Spring AOP代理

一. 代理的概念

根据前面的学习想必大家都已经对Spring AOP有所了解了,接下来我们先来回忆一下什么是Spring AOP?

AOP:一种对于集中的事情进行统一处理解决的思想;

Spring AOP:Spring通过运用AOP统一解决的思想所诞生的产物;
例如:拦截器,适配器,统一结果返回,统一异常处理,以及统一通知处理,以上这些在我们前面的文章中都讲述过,已经有些遗忘的小伙伴可以翻看前面的文章进行稳固一下…

好!接下来我们进入正题…

AOP的底层原理实现的代理模式,那么什么是代理模式呢?
通过举一个栗子~大家就应该能够了解了:有些 小伙伴可能通过一些线上平台租过房子,那么这个线上平台就是我们说的中介,是在我们跟房子房东之间的纽带,为啥我们要找中介呢?

  1. 中介能够帮我们提前去验收要出租房子的质量来进行出租价格的评定
  2. 中介能够在我们住房期间能够对房子的进行一个改造升级

通过上面的栗子中的 中介就是代理,我们通过中介能够达到我们最终的要求,这就是代理模式简单来说就是通过一个代理类能够间接的调用目标方法
在这里插入图片描述

然而代理模式又分为两种:

  1. 静态代理
  2. 动态代理(两种)

静态代理和动态代理的主要区别就是:静态代理的代理对象的一开始就定好的,而动态代理就跟他的命名一样,是动态化的,是由系统随机调度生成的一个代理对象
按照上面的例子来说就是,静态代理A的房子,那么A房子的中介人一直是这个人,而动态代理是中介公司看现在哪一个中介在摸鱼,就让哪一个中介去干活~
当然了,在面试中主要考查的是动态代理;

动态代理(主要是通过反射来完成的代理模式)

  1. JDK代理
  2. CGLIB代理

在接下来的讲解中我们将围绕以上几种代理进行展开,由于JDK代理和CGLIB代理是面试中的重中之重,篇幅较长,我们后面慢慢讲述,先就简单的,软的柿子——静态代理来捏~~

二. 静态代理

静态代理:由程序员创建代理类或特定⼯具⾃动⽣成源代码再对其编译,在程序运⾏前代理类的
.class⽂件就已经存在了
什么意思呢?简单来理解就是它的代理对象已经定死了,不会在修改了。

代理(中介,帮房东出租房⼦)

public class HouseProxy implements HouseSubject{//将被代理对象声明为成员变量private HouseSubject houseSubject;public HouseProxy(HouseSubject houseSubject) {this.houseSubject = houseSubject;}@Overridepublic void rentHouse() {//开始代理System.out.println("我是中介, 开始代理");//代理房东出租房⼦houseSubject.rentHouse();//代理结束System.out.println("我是中介, 代理结束");}
}

这段代码不需要看明白,只用记住,代理对象通过renHouse方法来加强房子的质量,以及代理对象写死了就行了,重点全在动态代理中

三. JDK代理

在上面讲过,动态代理于静态代理的区别在于动态代理的代理对象是系统生成的,而JDK动态代理是通过:
JDK动态代理有⼀个最致命的问题是其只能代理实现了接口的类

  1. 通过实现InvocationHandler接口,重写 invoke 方法来加强被代理的对象(加强房子的质量)
  2. 通过Proxy类来随机生成一个代理对象

3.1 重写 invoke 方法进⾏功能增强

实现InvocationHandler接口,重写的 invoke 方法的伪代码如下,了解他们的区别和调用的方法即可:

public interface InvocationHandler {
/**
* 参数说明
* proxy:代理对象
* method:代理对象需要实现的方法,即其中需要重写的方法
* args:method所对应方法的参数
*/
public Object invoke(Object proxy, Method method, Object[] args)throws Throwable;
}

InvocationHandler接口是Java动态代理的关键接口之⼀,它定义了⼀个单⼀方法 invoke() ,⽤于
处理被代理对象的方法调⽤.通过实现 InvocationHandler 接口,可以对被代理对象的方法进⾏功能增强.

3.2 通过Proxy类随机生成代理对象

通过Proxy类来随机生成一个代理对象伪代码如下:

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

Proxy 类中使⽤频率最⾼的方法是: newProxyInstance() ,这个方法主要⽤来⽣成⼀个代理
对象

这个方法⼀共有3个参数:
Loader:类加载器,⽤于加载代理对象.
interfaces:被代理类实现的⼀些接口(这个参数的定义,也决定了JDK动态代理只能代理实现了接口的
⼀些类)
h:实现了InvocationHandler接口的对象

⾃定义 InvocationHandler 并重写 invoke 方法,在 invoke 方法中我们会调⽤⽬标方
法(被代理类的方法)并⾃定义⼀些处理逻辑
3. 通过 Proxy.newProxyInstance(ClassLoader loader,Class<?>[]
interfaces,InvocationHandler h) 方法创建代理对象

四. CGLIB代理

JDK动态代理有⼀个最致命的问题是其只能代理实现了接口的类,而CGLIB却能够实现接口也能代理类

  1. 通过⾃定义 MethodInterceptor 并重写 intercept 方法加强被代理的对象(加强房子的质量)
  2. 通过Enhancer类的create()创建代理类

和JDK动态代理不同,CGLIB(CodeGenerationLibrary)实际是属于⼀个开源项⽬,如果你要使⽤它
的话,需要⼿动添加相关依赖

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

4.1 自定义类来重写intercept方法

⾃定义 MethodInterceptor 并重写 intercept 方法, intercept ⽤于增强⽬标方法,和JDK动态代理中的 invoke 方法类似

代码如下:

import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;public class CGLIBInterceptor implements MethodInterceptor {//⽬标对象, 即被代理对象private Object target;	public CGLIBInterceptor(Object target){this.target = target;}@Overridepublic Object intercept(Object o, Method method, Object[] objects,MethodProxy methodProxy) throws Throwable {// 代理增强内容System.out.println("我是中介, 开始代理");//通过反射调⽤被代理类的方法Object retVal = methodProxy.invoke(target, objects);//代理增强内容System.out.println("我是中介, 代理结束");return retVal;}
}

MethodInterceptor 和JDK动态代理中的 InvocationHandler 类似,它只定义了⼀个方
法 intercept() ,⽤于增强⽬标方法

4.2 通过Enhancer类的create方法来创建代理类

通过Enhancer类的create()创建代理类

伪代码如下:

public static Object create(Class type, Callback callback) {
//...代码省略
}

参数说明:
type:被代理类的类型(类或接口)
callback:⾃定义方法拦截器MethodInterceptor

五. AOP源码剖析

SpringAOP主要基于两种方式实现的:JDK及CGLIB的方式

然而为什么会有两种不同的代理方式?以及什么时候用不同的代码方式呢?
在下面的部分源码中我们可以了解到

//创建代理⼯⼚
ProxyFactory proxyFactory = new ProxyFactory();proxyFactory.copyFrom(this);/**
* 检查proxyTargetClass属性值,spring默认为false
* proxyTargetClass 检查接口是否对类代理, ⽽不是对接口代理
* 如果代理对象为类, 设置为true, 使⽤cglib代理
*/
if (!proxyFactory.isProxyTargetClass()) {//是否有设置cglib代理if (shouldProxyTargetClass(beanClass, beanName)) {//设置proxyTargetClass为true,使⽤cglib代理proxyFactory.setProxyTargetClass(true);} else {/*** 如果beanClass实现了接口,且接口⾄少有⼀个⾃定义方法,则使⽤JDK代理* 否则CGLIB代理(设置ProxyTargetClass为true )* 即使我们配置了proxyTargetClass=false, 经过这⾥的⼀些判断还是可能会将其设为true*/evaluateProxyInterfaces(beanClass, proxyFactory);}
}

从上面的部分源码来看,接下来是我自己用大白话总结出来的道理,
首先会创建一个代理工厂,用于创建两种不同的代理对象,然后源码中有一个proxyTagertClass属性来进行判断,如果proxyTagertClass为false,且实现了接口,则用代理工厂来创建一个JDK代理的对象,如果没有实现接口或者proxyTagertClass为true,则用代理工厂来创建一个CGLIB代理的对象

根据proxyTagertClass属性来创建代理对象的情况如下表格:

proxyTargetClass⽬标对象代理方式
false实现了接口jdk代理
false未实现接口(只有实现类)cglib代理
true实现了接口cglib代理
true未实现接口(只有实现类)cglib代理
注意:这里的版本不同,创建的方式也会不同,在Spring版本中proxyTagertClass属性默认的为false,然而在Spring Boot 2.0版本以后proxyTagertClass属性则默认的是true ,也就是默认使用CGLIB来代理

总结(重中之重,精华)

两大重要的动态代理:

  1. JDK动态代理
  2. CGLIB动态代理

以及它两个的区别:

  1. JDK代理主要用于实现接口,不能直接用于类;而CGLIB在实现接口和类上都可以使用
  2. JDK代理通过重写invoke方法来进行功能增强,通过proxy类来生成随机代理对象,而CGLIB代理通过自定义类来重写intercept方法,通过Enhancer类的create方法来创建代理类

从动态代理的部分源码来看,接下来是我自己用大白话总结出来的过程,
首先会创建一个代理工厂,用于创建两种不同的代理对象,然后源码中有一个proxyTagertClass属性来进行判断,如果proxyTagertClass为false,且实现了接口,则用代理工厂来创建一个JDK代理的对象,如果没有实现接口或者proxyTagertClass为true,则用代理工厂来创建一个CGLIB代理的对象

以及proxyTagertClass属性在Spring Boot 2.0版本之前默认是false,而在2.0版本以后默认为true

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

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

相关文章

ASP.NET MVC-razor编写-2-svg中使用js+添加事件监听

环境&#xff1a;win10 效果 初始状态&#xff1a; 鼠标移入某个text&#xff08;比如KS primer&#xff09;时&#xff0c;text和连接的线条与箭头都变色&#xff1a; 鼠标移出时回复正常。 如果是移入另一种红色的text&#xff08;比如Cell Sceening Tag&#xff09;&…

第一次构建一个对话机器人流程解析(一)

1.问答机器人的组成 1.1 问答机器人的组成结构图 2. 问答机器人的组成-机器人的个人属性 所谓的机器人一般具备有个人的属性,这些属性固定,形成了机器人的个人偏好 在实现过程中,此处使用一个xml配置文件,配置了机器人的个人年龄、性别、职业等内容,同时包含常见有关于…

可信验证解释

学习目标&#xff1a;可信验证解释 可信验证是一种基于计算机技术和安全机制&#xff0c;用于确保系统、程序或数据的完整性和可信性的方法。以下是关于可信验证的详细解释&#xff1a;一、定义与原理 可信验证指的是利用计算机技术和安全机制&#xff0c;对系统、程序或数据…

一.2 程序被其他程序翻译成不同的格式(编译)

hello程序的生命周期是从一个高级C语言程序开始的&#xff0c;因为这种形式能够被人读懂。然而&#xff0c;为了在系统上运行hello.c程序&#xff0c;每条C语句都必须被其他程序转化为一系列的低级机器语言指令。然后这些指令按照一种称为可执行目标程序&#xff08;也称为可执…

【分布式系统】注册中心Zookeeper

目录 一.Zookkeeper 概述 1.Zookkeeper 定义 2.Zookkeeper 工作机制 3.Zookkeeper 特点 4.Zookkeeper 数据结构 5.Zookkeeper 应用场景 统一命名服务 统一配置管理 统一集群管理 服务器动态上下线 软负载均衡 6.Zookkeeper 选举机制 第一次启动选举机制 非第一次…

U盘管理软件有哪些?3款好用的软件亲测有效!

在数字化办公与数据交换日益频繁的今天&#xff0c;U盘作为便携的存储设备&#xff0c;其重要性不言而喻。 然而&#xff0c;U盘的使用也带来了数据泄露、病毒感染等安全隐患。为了有效管理U盘&#xff0c;确保数据安全与合规性&#xff0c;市场上涌现出了众多U盘管理软件。 小…

揭秘:源代码防泄密的终极秘籍

在当今信息科技高度发达的时代&#xff0c;源代码作为企业最核心的资产之一&#xff0c;其安全性不言而喻。源代码的泄露可能导致企业技术机密被竞争对手获取&#xff0c;进而威胁到企业的市场竞争力和长远发展。因此&#xff0c;源代码防泄密成为了企业信息安全工作的重中之重…

解决win10报“无法加载文件……profile.ps1,因为在此系统上禁止运行脚本”的问题

打开命令行报错 解决方法 使用管理员权限打开PowerShell&#xff1a;WinX, 选择“Windows PowerShell&#xff08;管理员&#xff09;” 输入&#xff1a;Set-ExecutionPolicy -ExecutionPolicy RemoteSigned 输入&#xff1a;y确认修改安全策略 &#xff1a;y确认修改安全策略…

vue + element ui 实现侧边栏导航栏折叠收起

首页布局如下 要求点击按钮,将侧边栏收缩, 通过 row 和 col 组件&#xff0c;并通过 col 组件的 span 属性我们就可以自由地组合布局。 折叠前 折叠后 <template><div class"app-layout" :class"{ collapse: app.isFold }"><div class&…

Java-Sql注入以及如何解决

sql脚本注入: 如果sql语句使用字符串拼接&#xff0c;可能会出现字符串的拼接&#xff0c;导致sql注入。 #是会先进行预编译&#xff0c;传进来的参数通过占位符填入到已经完成编译的语句中去。

树莓派4B_OpenCv学习笔记21:OpenCV_haar人脸识别

今日继续学习树莓派4B 4G&#xff1a;&#xff08;Raspberry Pi&#xff0c;简称RPi或RasPi&#xff09; 本人所用树莓派4B 装载的系统与版本如下: 版本可用命令 (lsb_release -a) 查询: ​ Opencv 版本是4.5.1&#xff1a; ​ Python 版本3.7.3&#xff1a; 今日学习&#xff…

python实现接口自动化

代码实现自动化相关理论 代码编写脚本和工具实现脚本区别是啥? 代码&#xff1a; 优点&#xff1a;代码灵活方便缺点&#xff1a;学习成本高 工具&#xff1a; 优点&#xff1a;易上手缺点&#xff1a;灵活度低&#xff0c;有局限性。 总结&#xff1a; 功能脚本&#xff1a;工…

【Ubuntu】详细说说Parallels DeskTop安装和使用Ubuntu系统

希望文章能给到你启发和灵感~ 如果觉得文章对你有帮助的话,点赞 + 关注+ 收藏 支持一下博主吧~ 阅读指南 开篇说明一、基础环境说明1.1 硬件环境1.2 软件环境二、Ubuntu系统的使用2.1 系统的下载2.2 系统的安装2.3 安装桌面版(可选)2.3.1 安装/更新apt2.3.2 安装桌面版2.3…

【Go】函数的使用

目录 函数返回多个值 init函数和import init函数 main函数 函数的参数 值传递 引用传递&#xff08;指针&#xff09; 函数返回多个值 用法如下&#xff1a; package mainimport ("fmt""strconv" )// 返回多个返回值&#xff0c;无参数名 func Mu…

华贝甄选绿色积分模式的可信赖之处揭秘

华贝甄选是天贝集团旗下的数字产融生态领先品牌&#xff0c;业务涵盖 PPP 产业、金融生态、国际投资、智慧能源、数字产业、智慧产业、三农产业、生物科技等领域。其优势在于通过多维系统助力 DAO 组织系统打造&#xff0c;实现财富与健康双丰收&#xff1b;打造全新生态体系&a…

FreeRTOS和UCOS操作系统使用笔记

FreeRTOS使用示例 任务创建与删除 #define START_TASK_PRIO 1 //任务优先级 (1) #define START_STK_SIZE 128 //任务堆栈大小 (2) TaskHandle_t StartTask_Handler; //任务句柄 (3) void start_task(void *pvParameters);//任务函数 (4)#define TASK1_…

vue3 学习 之 vue3使用

为什么要学习vue3呢&#xff1f; vue2.0也是现在比较稳定的一个版本&#xff0c;社区还有周边都比较完善&#xff0c;如果不是非必要其实我们不需要着急直接升级到vue3.0; 那为什么还要学习&#xff0c;主要是还是为了了解一下vue3.0相较于2.0的优势和特性&#xff0c;方便之后…

跳转控制语句—break和continue

break语句我本人只在switch语句和循环语句中遇见&#xff0c;continue则只在循环语句中遇见&#xff1b; 下面我来记录一下&#xff0c;它俩的不同之处&#xff1a; 1.break 相比之下&#xff0c;break是比较简单的&#xff0c;就是跳出循环体&#xff0c;执行循环体下方的代…

刷题之删除有序数组中的重复项(leetcode)

删除有序数组中的重复项 这题简单题&#xff0c;双指针&#xff0c;一个指针记录未重复的数的个数&#xff0c;另一个记录遍历的位置。 以下是简单模拟&#xff0c;可以优化&#xff1a; class Solution { public:int removeDuplicates(vector<int>& nums) {int l0…