锋盈数科-知识库 Logo
首页
软件开发
计算机基础
Hello Halo
新手必读
关于本知识库
登录 →
锋盈数科-知识库 Logo
首页 软件开发 计算机基础 Hello Halo 新手必读 关于本知识库
登录
  1. 首页
  2. 软件开发
  3. JAVA
  4. 【Spring Boot】Spring AOP动态代理,以及静态代理

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

0
  • JAVA
  • 发布于 2024-08-10
  • 0 次阅读
黄健
黄健

本文由 简悦 SimpRead 转码, 原文地址 blog.csdn.net

目录

  • 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;
	}
	
	@Override
	public 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;
	}
	
	@Override
	public 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

标签: #JAVA 991
相关文章

Spring 实现 3 种异步接口 2024-10-18 09:07

大家好,我是苏三~ 如何处理比较耗时的接口? 这题我熟,直接上异步接口,使用 Callable、WebAsyncTask 和 DeferredResult、CompletableFuture等均可实现。 但这些方法有局限性,处理结果仅返回单个值。在某些场景下,如果需要接口异步处理的同时,还持续不断地

重学SpringBoot3-集成Redis(五)之布隆过滤器 2024-10-08 11:24

更多SpringBoot3内容请关注我的专栏:《SpringBoot3》 期待您的点赞👍收藏⭐评论✍ 重学SpringBoot3-集成Redis(五)之布隆过滤器 1. 什么是布隆过滤器? * 基本概念 适用场景 2. 使用 Redis 实现布隆过滤器 * 项目依赖 Redis 配置

SpringBoot整合异步任务执行 2024-10-08 11:24

同步任务: 同步任务是在单线程中按顺序执行,每次只有一个任务在执行,不会引发线程安全和数据一致性等 并发问题 同步任务需要等待任务执行完成后才能执行下一个任务,无法同时处理多个任务,响应慢,影响用 户体验 异步任务: 异步任务是在多线程中同时执行,多个任务可以并发执行,同时处理多个请求,响应快,资源

springboot kafka多数据源,通过配置动态加载发送者和消费者 2024-10-08 11:24

前言 最近做项目,需要支持kafka多数据源,实际上我们也可以通过代码固定写死多套kafka集群逻辑,但是如果需要不修改代码扩展呢,因为kafka本身不处理额外逻辑,只是起到削峰,和数据的传递,那么就需要对架构做一定的设计了。 准备test kafka本身非常容易上手,如果我们需要单元测试,引入ja

SpringBoot 集成 Redis 2024-10-08 11:24

一:SpringBoot 集成 Redis ①Redis是一个 NoSQL(not only)数据库, 常作用缓存 Cache 使用。 ②Redis是一个中间件、是一个独立的服务器;常用的数据类型: string , hash ,set ,zset , list ③通过Redis客户端可以使用多种语

SpringBoot整合QQ邮箱 2024-10-08 11:24

SpringBoot可以通过导入依赖的方式集成多种技术,这当然少不了我们常用的邮箱,现在本章演示SpringBoot整合QQ邮箱发送邮件…. 下面按步骤进行: 1.获取QQ邮箱授权码 1.1 登录QQ邮箱 1.2 开启SMTP服务 找到下图中的SMTP服务区域,如果当前账号未开启的话自己手动开启。

目录

IT 外包服务商

  • 意见投递
  • zyf6619

软件开发应用

主菜单

  • 首页
  • 软件开发
  • 计算机基础
  • Hello Halo
  • 新手必读
  • 关于本知识库
Copyright © 2024 your company All Rights Reserved. Powered by Halo.