AOPSpringAOP两种动态代理

news2024/11/24 12:00:28

1、什么是AOP

切面编程(Aspect-Oriented Programming,AOP)是一种软件开发方法,旨在通过分离关注点(Concerns)来增强代码的模块性、可维护性和可重用性。

AOP 是 OOP 的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

切面编程通过将交叉关注点(Cross-Cutting Concerns)从主要业务逻辑中抽离出来,从而使代码更易于管理和理解。

作为一个Java工程师,你可能会经常遇到需要处理跨越多个模块和类的问题,例如日志记录、事务管理、安全认证等。这些问题通常与主要的业务逻辑分离,但它们又贯穿在整个应用中。这就是切面编程的用武之地。

以下是切面编程的关键概念和组成部分:

  1. 切面(Aspect): 切面是关注点的模块化,它包含一组跨越多个类的行为。例如,一个日志记录切面可以定义在每个方法执行前后记录日志。切面本身并不包含完整的业务逻辑,而是定义了如何在关注点处执行逻辑。
  2. 连接点(Join Point): 连接点是在应用执行过程中可以插入切面的点,通常是方法调用、对象初始化等。切面可以在连接点前、后、环绕等不同的时机执行。
  3. 通知(Advice): 通知是切面在连接点处执行的具体行为,可以是方法调用、异常处理、环绕逻辑等。主要的通知类型有前置通知(Before)、后置通知(After)、环绕通知(Around)、返回通知(AfterReturning)和异常通知(AfterThrowing)。
  4. 切点(Pointcut): 切点定义了哪些连接点会触发切面的通知。它通过表达式或规则来匹配一组连接点。
  5. 织入(Weaving): 织入是将切面应用到目标代码的过程,即将切面的通知插入到连接点处。织入可以在编译时、类加载时、运行时等不同阶段进行。
  6. 引入(Introduction): 引入是一种动态为类添加新方法或属性的机制,允许切面向现有类添加新功能。这可以用来实现类似于多继承的效果。

切面编程的优势在于它提供了一种清晰的方式来处理横切关注点,将其与主要业务逻辑分离,从而增强代码的可维护性和可扩展性。在 Java 中,切面编程的实现主要通过 Spring AOP 框架来实现,它允许你通过注解或 XML 配置来创建和管理切面。使用切面编程,你可以更加专注于核心业务逻辑,而无需过多关心交叉关注点的处理。

2、作用及其优势

AOP(Aspect-Oriented Programming,面向切面编程)的作用是通过分离交叉关注点(Cross-Cutting Concerns)来提高代码的模块性、可维护性和可重用性。它允许开发人员将横切关注点(如日志、事务、安全性等)从主要业务逻辑中分离出来,使代码更加清晰、可维护和可扩展。

AOP 的优势体现在以下几个方面:

  1. 分离关注点: AOP 通过将横切关注点与主要业务逻辑分离,使代码更加模块化。这使得开发人员可以专注于核心业务逻辑,而不必在各处重复编写相同的交叉关注点代码。
  2. 提高可维护性: 通过将交叉关注点集中处理,AOP 使代码更加易于维护。如果需要修改或扩展横切关注点的逻辑,只需在一个地方进行修改,而不需要在多个地方进行修改。
  3. 降低代码冗余: 由于交叉关注点被分离到切面中,不同的模块可以共享相同的切面,从而避免了代码冗余。例如,多个模块都需要记录日志,可以共享一个日志切面。
  4. 提高可重用性: 切面可以在不同的模块中重用,从而提高了代码的可重用性。当需要在多个项目中使用相同的交叉关注点逻辑时,只需将切面应用到不同的项目中。
  5. 增强代码灵活性: AOP 允许在不修改原始代码的情况下,动态地将切面织入到目标代码中。这使得你可以在运行时根据需求添加或移除切面,而不必修改主要业务逻辑。
  6. 提高代码可读性: 将交叉关注点从主要业务逻辑中抽离,可以使主要业务逻辑更加清晰、简洁,从而提高了代码的可读性和理解性。
  7. 促进团队协作: 通过统一处理交叉关注点,团队成员可以共享相同的设计和实现模式,从而促进团队协作和开发效率。

总而言之,作用及其优势为:

3、AOP底层实现

AOP 的底层实现通常基于动态代理和字节码操作,以在运行时将切面逻辑织入到目标代码中。

在 Java 中,主要有两种常见的 AOP 底层实现方式:基于 JDK 动态代理和基于 CGLIB 字节码操作。

下面分别介绍这两种实现方式的基本原理:

  1. 基于 JDK 动态代理: JDK 动态代理利用 Java 标准库中的 java.lang.reflect.Proxy 类和 java.lang.reflect.InvocationHandler 接口来实现。当一个接口被代理时,JDK 动态代理会在运行时创建一个实现了该接口的代理类,并且每个代理方法都会被重定向到实现 InvocationHandler 接口的对象的调用处理方法。代理方法的调用将由调用处理方法处理,从而允许在调用目标方法之前、之后或环绕时执行切面逻辑。
  2. 基于 CGLIB 字节码操作: CGLIB(Code Generation Library)是一个功能强大的字节码操作库,它能够生成子类并覆盖父类的方法。在基于 CGLIB 的 AOP 实现中,目标类的子类将在运行时动态生成,重写目标方法以织入切面逻辑。这种方式适用于没有实现接口的类。CGLIB 的底层机制涉及了对目标类字节码的修改,从而使得切面逻辑能够在目标类的方法中生效。

AOP 的底层实现大致流程如下:

  1. 定义切面: 定义切面的通知方法,如前置通知、后置通知、环绕通知等。
  2. 创建代理: 在运行时,AOP 框架会根据切面定义和目标对象,生成代理对象。
  3. 织入切面逻辑: 将切面逻辑织入到目标对象的方法中,使得切面逻辑能够在目标方法调用的前后或环绕时执行。
  4. 执行代理方法: 当调用代理对象的方法时,实际上是调用了代理对象中的通知方法。在通知方法内部,会执行切面逻辑,并最终调用目标方法。

需要注意的是,虽然 AOP 通过代理技术实现了切面逻辑的织入,但这种织入是在运行时动态完成的,因此可能会带来一些性能开销。选择使用 JDK 动态代理还是 CGLIB 字节码操作取决于目标类是否实现接口等因素。

总之,AOP 的底层实现主要基于动态代理和字节码操作,通过在运行时将切面逻辑织入到目标代码中,实现了交叉关注点的分离和处理。

4、JDK 的动态代理

目录如下:

目标接口:

目标对象:

增强方法:

代理工具:

package com.xzl.proxy.jdk;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @author 逐梦苍穹
 * @date 2023/8/13 10:28
 */
public class ProxyUtil {
    public static Advice advice = new Advice();
    public static <T> T getProxy(T object) {
        return (T) Proxy.newProxyInstance(object.getClass().getClassLoader(),
                object.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
                        advice.before();
                        Object result = method.invoke(object, objects);
                        advice.afterReturning();
                        return result;
                    }
                });
    }
}

测试方法:

测试结果:

5、cglib 的动态代理

CGLIB(Code Generation Library)是一个功能强大的字节码操作库,用于在运行时生成和修改 Java 类的字节码。CGLIB 动态代理是一种基于字节码操作的动态代理机制,与 JDK 动态代理不同,它可以代理类而不仅仅是接口,适用于那些没有实现接口的类。

CGLIB 被广泛应用于许多 Java 框架和库,其中包括 Spring AOP。

以下是 CGLIB 动态代理的基本原理和使用方式:

  1. 生成子类: CGLIB 动态代理通过生成目标类的子类来实现代理。这个子类会重写目标类的方法,并在方法调用前后添加切面逻辑。这种方式不需要目标类实现接口。
  2. 织入切面逻辑: CGLIB 动态代理在生成的子类中织入切面逻辑,使得切面逻辑能够在目标类的方法中生效。织入可以在方法调用前、后或环绕时进行。
  3. 创建代理对象: 使用 CGLIB 提供的 Enhancer 类创建代理对象。Enhancer 是一个工具类,用于生成代理类的实例。

目录如下:

增强方法:

目标对象:

代理工具:

package com.xzl.proxy.cglib;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * @author 逐梦苍穹
 * @date 2023/8/13 10:28
 */
public class ProxyUtil<T> {
    //创建增强方法
    public static Advice advice = new Advice();

    //创建目标对象
    public final T target;

    //初始化目标对象
    public ProxyUtil(T target){
        this.target = target;
    }

    public T getProxy(T t){
        Enhancer enhancer = new Enhancer();//创建增强器
        enhancer.setSuperclass(t.getClass());//设置父类
        //设置回调
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                advice.before();
                Object invoke = method.invoke(target,objects);
                advice.afterReturning();
                return invoke;
            }
        });
        //创建代理对象并返回
        return (T) enhancer.create();
    }
}

测试:

结果:

6、总结

两种代码实现方式的区别有:

区别解释:

  1. 代理库不同: 第一段代码使用了 JDK 动态代理,通过 Proxy.newProxyInstance 创建代理对象。第二段代码使用了 CGLIB 动态代理,通过 Enhancer 创建代理对象。
  2. 适用范围不同: JDK 动态代理只能代理实现了接口的类,而 CGLIB 动态代理可以代理任何类,包括没有实现接口的类。
  3. 代理对象创建方式不同: 在 JDK 动态代理中,代理对象是通过接口数组来创建的。而在 CGLIB 动态代理中,代理对象是通过设置父类来创建的。
  4. 方法拦截方式不同: JDK 动态代理使用 InvocationHandler 来实现方法拦截。CGLIB 动态代理使用 MethodInterceptor 来实现方法拦截。
  5. 性能差异: 通常情况下,JDK 动态代理比 CGLIB 动态代理要快一些。CGLIB 动态代理涉及到生成目标类的子类,并使用了更多的字节码操作,可能引入一些性能开销。

根据你的需求和场景,可以选择适合的动态代理方式。如果目标类实现了接口,可以考虑使用 JDK 动态代理;如果目标类没有实现接口,可以考虑使用 CGLIB 动态代理。

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

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

相关文章

【Qt高阶】老Qt都不一定清楚的“QObject线程亲和性”【2023.08.13】

老Qt都不一定清楚的“线程亲和性” 与题目无关 感觉自己还挺2&#xff0c;有粉丝点了那个契约者会给up发个鼓励的话&#xff0c;我还以为是人私信发的&#xff0c;都挨个感谢了&#xff0c;后来才意识到是系统自动发的&#x1f623;&#x1f623;&#x1f623;。 自上上期视频对…

⛳ Java集合框架

目录 ⛳ Java集合框架&#x1f3a8; 一、概述&#x1f3ed; 二、Iterator接口&#x1f4ad; 2.1、基础用法&#x1f69c; 2.2、原理&#x1f43e; 2.3、为什么需要iterator接口☁ 2.4、ListIterator接口&#x1f4e2; 2.5、iterator在集合中的实现例子2.5.1、Iterator在ArrayLi…

潜水减压多普勒超声气泡信号识别方法

用三参量模糊分析法识别潜水减压气泡信号的研究 摘要 三参量模糊分析法。建立了 f-p-Δp( 频率、谱峰、峰差) 三参量模糊分析法&#xff0c;综合气泡原本特征以及背景噪声计算评分以识别气泡&#xff0c;最后给予 SPENCERⅠ &#xff5e; Ⅲ分级原理和方法 减压气泡信号的能量…

分布式 - 消息队列Kafka:Kafka生产者发送消息的方式

文章目录 1. Kafka 生产者2. kafaka 命令行操作3. kafka 生产者发送消息流程4. Kafka 生产者的创建5. Kafka 生产者发送消息1. 发送即忘记2. 同步发送3. 异步发送 6. Kafka 消息对象 ProducerRecord 1. Kafka 生产者 不管是把Kafka作为消息队列、消息总线还是数据存储平台&…

Vue3组件库

Vue3组件库 ViteVue3TypescriptTSX 1、项目搭建 1.1、创建项目&#xff08;yarn&#xff09; D:\WebstromProject>yarn create vite yarn create v1.22.19 [1/4] Resolving packages... [2/4] Fetching packages... [3/4] Linking dependencies... [4/4] Building fresh p…

asyncio是什么?

如果把进程比作从A处到B处去这件事&#xff0c;那么线程就是可供选择的多条道路&#xff0c;协程就是道路上特殊路段&#xff08;类似限速&#xff0c;一整条道路都是特殊路段的话&#xff0c;就是全部由协程实现&#xff09; 例图如下&#xff1a; 1. 什么是协程&#xff08…

FPGA应用学习笔记----CORDIC 算法和小结

加减移位操作来运算三角函数&#xff0c;开根号&#xff0c;求对数 圆周旋转模式

沁恒ch32V208处理器开发(四)串口通信

目录 串口资源资源配置同步模式单线半双工模式中断DMA 串口的初始化串口通信的实现 串口资源 资源配置 CH32V208 系列&#xff0c;是基于 RISC-V 指令架构设计的 32 位 RISC 内核 MCU&#xff0c;根据封装的不同&#xff0c;可用的USART串口资源如下表所示&#xff1a; 且US…

完美解决Github提交PR后报错:File is not gofumpt-ed (gofumpt)

问题阐述 最近在Github上提交PR后&#xff0c;遇到了这么一个问题&#xff1a;golangci-lint运行失败&#xff0c;具体原因是File is not gofumpt-ed (gofumpt)。 名词解释 golangci-lint&#xff1a; golangci-lint 是Go语言社区中常用的代码质量检查工具&#xff0c;它可以…

阿里云弹性裸金属服务器详细介绍(原神龙)

阿里云弹性裸金属服务器&#xff08;ECS Bare Metal Server&#xff09;是一种可弹性伸缩的高性能计算服务&#xff0c;原神龙服务器&#xff0c;计算性能与传统物理机无差别&#xff0c;具有安全物理隔离的特点&#xff0c;裸金属服务器分钟级的交付周期。阿里云服务器网分享阿…

现代控制理论step()函数使用方法,多输入多输出系统的阶跃响应图如何只输出一个输入对应输出的阶跃响应图(step(sys)如何单独显示一个子图)

多输入多输出系统的阶跃响应图 考虑以下二阶状态空间模型: A [-0.5572,-0.7814;0.7814,0]; B [1,-1;0,2]; C [1.9691,6.4493]; sys ss(A,B,C,0);这个模型有两个输入和一个输出&#xff0c;因此它有两个通道: 从第一个输入到输出&#xff0c;从第二个输入到输出。每个通道都…

ArcGIS Maps SDK for JavaScript系列之一:在Vue3中加载ArcGIS地图

目录 ArcGIS Maps SDK for JavaScript简介ArcGIS Maps SDK for JavaScript 4.x 的主要特点和功能AMD modules 和 ES modules两种方式比较Vue3中使用ArcGIS Maps SDK for JavaScript的步骤创建 Vue 3 项目安装 ArcGIS Maps SDK for JavaScript创建地图组件 ArcGIS Maps SDK for …

使用 vpn 后 git clone 无法下载项目

使用VPN后 git clone 命令无法下载项目 偶发使用 vpn 后 git clone 项目会卡住&#xff0c;或者报 timeout 错误 当我使用ping github.com是可以ping通的&#xff0c;但是 clone 项目就会卡住。去搜了一番发现&#xff1a; git 工具在使用代理后需要设置 git https.proxy 属…

Linux学习之awk数组

数组的定义&#xff1a; 数组是一种有关联关系的变量&#xff0c;通过下标依次访问。 数组名[下标] 值&#xff0c;下标可以使用数字也可以使用字符串。 数组的遍历&#xff1a; for(变量 in 数组名){ 数组名[变量] 操作 } 数组删除&#xff1a; delete 数组名&#xff0c;就可…

Fireworks CS6 不能把文件拖进去

打开软件安装文件夹 我的是&#xff1a;C:\Program Files\Adobe Fireworks CS6\Adobe Fireworks CS6 在该位置找到文件【Fireworks.exe】 右键属性 取消勾选&#xff0c;【以管理员身份运行此程序】

Mysql数据库第十三课-----------sql语句的拔高3--------直冲云霄

作者前言 &#x1f382; ✨✨✨✨✨✨&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f382; ​&#x1f382; 作者介绍&#xff1a; &#x1f382;&#x1f382; &#x1f382; &#x1f389;&#x1f389;&#x1f389…

Python Opencv实践 - 图像平移

import numpy as np import matplotlib.pyplot as pltimg cv.imread("../SampleImages/pomeranian.png", cv.IMREAD_COLOR)#图像平移 #cv.warpAffine(src, M, dsize[, dst[, flags[, borderMode[, borderValue]]]]) # M是仿射变换矩阵&#xff0c;对于平移来说M是一…

【网络编程】高级IO

文章目录 一、五种IO模型的基本理念二、IO重要概念 1.同步通信与异步通信的对比2.阻塞VS非阻塞三丶非阻塞IO的代码演示四丶IO多路转接select总结 一、五种IO模型的基本理念 首先IO就是 等 数据拷贝&#xff0c;还记得我们之前实现服务器用的read/recv接口的&#xff0c;当时我…

Codeforces Round 892 (Div. 2)

A.最大值只能由自己除&#xff0c;所以无解的情况只能是全部相同&#xff0c;否则直接最大值放c即可 #include<bits/stdc.h> using namespace std; const int N 2e510,mod998244353; #define int long long typedef long long LL; typedef pair<int, int> PII;in…

【TI毫米波雷达笔记】MMWave配置流程避坑

【TI毫米波雷达笔记】MMWave配置流程避坑 在TI SDK目录下的mmwave.h文档说明中 强调了要按以下配置&#xff1a; mmWave API The mmWave API allow application developers to be abstracted from the lower layer drivers and the mmWave link API.The mmWave file should b…