【Spring】Spring AOP

news2024/11/10 19:09:48

目录

    • 一、Spring AOP简介
      • 1.什么是AOP
      • 2.AOP术语
    • 二、AspectJ开发
      • 1.基于XML的声明式AspectJ
        • 1.1 配置切面
        • 1.2 配置切入点
        • 1.3 配置通知
      • 2.基于注解的声明式AspectJ

一、Spring AOP简介

1.什么是AOP

    AOP的全称是Aspect-Oriented Programming,即面向切面编程(也称面向方面编程),是面向对象编程(OOP)的一种补充,目前已成为一种比较成熟的编程方式。

    在传统的业务处理代码中,通常都会进行事务处理、日志记录等操作。虽然使用OOP可以通过组合或者继承的方式来达到代码的重用,但如果要实现某个功能(如日志记录),相同的代码仍然会分散到各个方法中。这样,如果想要关闭某个功能,或者对其进行修改,就必须修改所有相关方法。这不但增加了开发人员的工作量,而且提高了代码的出错率。

    为了解决这一问题,AOP思想随之产生。AOP采取横向抽取机制,将分散在各个方法中的重复代码提取出来,然后在程序编译或运行时再将这些提取出来的代码应用到需要执行的地方。这种采用横向抽取机制的方式,采用传统的OOP思想显然是无法办到的,因为OOP只能实现父子关系的纵向重用。虽然AOP是一种新的编程思想,但却不是OOP的替代品,它只是OOP的延伸和补充。

    在AOP思想中,通过Aspect(切面)可以分别在不同类的方法中加入事务、日志、权限和异常等功能。

    AOP的使用使开发人员在编写业务逻辑时可以专心于核心业务,而不用过多地关注于其他业务逻辑的实现,这不但提高了开发效率,而且增强了代码的可维护性。目前流行的AOP框架有两个,分别为Spring AOP和AspectJ。Spring AOP使用纯Java实现,不需要专门的编译过程和类加载器,在运行期间通过代理方式向目标类植入增强的代码。AspectJ是一个基于Java语言的AOP框架,从Spring 2.0开始,Spring AOP引入了对AspectJ的支持,AspectJ扩展了Java语言,提供了一个专门的编译器,在编译时提供横向代码的植入。

2.AOP术语

    AOP术语包括Aspect、Joinpoint、Pointcut、Advice、Target Object、Proxy和Weaving,对于这些专业术语的解释,具体如下。

  • Aspect(切面):在实际应用中,切面通常是指封装的用于横向插入系统功能(如事务、日志等)的类,该类要被Spring容器识别为切面,需要在配置文件中通过元素指定。
  • Joinpoint(连接点):在程序执行过程中的某个阶段点,它实际上是对象的一个操作,例如方法的调用或异常的抛出。在Spring AOP中,连接点就是指方法的调用。
  • Pointcut(切入点):是指切面与程序流程的交叉点,即那些需要处理的连接点。通常在程序中,切入点指的是类或者方法名,如某个通知要应用到所有以add开头的方法中,那么所有满足这一规则的方法都是切入点。
  • Advice(通知增强处理):AOP框架在特定的切入点执行增强处理,即在定义好的切入点处所要执行的程序代码。可以将其理解为切面类中的方法,它是切面的具体实现。
  • Target Object(目标对象):是指所有被通知的对象,也称为被增强对象。如果AOP框架采用的是动态的AOP实现,那么该对象就是一个被代理对象。
  • Proxy(代理):将通知应用到目标对象之后,被动态创建的对象。
  • Weaving(织入):将切面代码插入目标对象上,从而生成代理对象的过程。

二、AspectJ开发

    AspectJ是一个基于Java语言的AOP框架,它提供了强大的AOP功能。Spring 2.0以后,Spring AOP引入了对AspectJ的支持,并允许直接使用AspectJ进行编程,而Spring自身的AOP API也尽量与AspectJ保持一致。新版本的Spring框架建议使用AspectJ来开发AOP。使用AspectJ实现AOP有两种方式:

  • 一种是基于XML的声明式AspectJ;
  • 另一种是基于注解的声明式AspectJ。

1.基于XML的声明式AspectJ

    基于XML的声明式AspectJ是指通过XML文件来定义切面、切入点及通知,所有的切面、切入点和通知都必须定义在<aop:config>元素内。Spring配置文件中的<beans>元素下可以包含多个<aop:config>元素,一个<aop:config>元素中又可以包含属性和子元素,其子元素包括<aop:pointcut><aop:advisor><aop:aspect>。在配置时,这3个子元素必须按照此顺序来定义。在<aop:aspect>元素下,同样包含属性和多个子元素,通过使用<aop:aspect>元素及其子元素就可以在XML文件中配置切面、切入点和通知。常用元素的配置代码如下所示。

 	<!-- 定义切面Bean -->
    <bean id="myAspect" class="com.ssm.aspectj.xmL.MyAspect" />
    <aop:config>
        <!-- 1.配置切面 -->
        <aop:aspect id="aspect" ref="myAspect">
            <!-- 2.配置切入点 -->
                <aop:pointcut id="myPointCut" expression="execution(* com.ssm.aspectj.*.*(..))" />
                <!-- 3.配置通知-->
                    <!-- 前置通知-->
                    <aop:before method="myBefore" pointcut-ref="myPointCut" />
                    <!-- 后置通知 -->
                    <aop:after-returning method="myAfterReturning"
                         pointcut-ref="myPointCut" returning="returnVal" />
                    <!-- 环绕通知-->
                    <aop:around method="myAround" pointcut-ref="myPointCut" />
                    <!-- 异常通知-->
                    <aop:after-throwing method="myAfterThrowing"
                         pointcut-ref="myPointCut" throwing="e" />
                    <!-- 最终通知-->
                    <aop:after method="myAfter" pointcut-ref="myPointCut" />
        </aop:aspect>
    </aop:config>

1.1 配置切面

    在Spring的配置文件中,配置切面使用的是<aop:aspect>元素,该元素会将一个已定义好的Spring Bean转换成切面Bean,所以要在配置文件中先定义一个普通的Spring Bean(如上述代码中定义的myAspect)。定义完成后,通过<aop:aspect>元素的ref属性即可引用该Bean。配置<aop:aspect>元素时,通常会指定id和ref两个属性。

在这里插入图片描述

1.2 配置切入点

    在Spring的配置文件中,切入点是通过<aop:pointcut>元素来定义的。当<aop:pointcut>元素作为<aop:config>元素的子元素定义时,表示该切入点是全局切入点,可以被多个切面所共享;当<aop:pointcut>元素作为<aop:aspect>元素的子元素时,表示该切入点只对当前切面有效。在定义<aop:pointcut>元素时,通常会指定id和expression两个属性。

在这里插入图片描述
    在上述配置代码片段中,execution(* com.ssm.jdk.*.*(..))就是定义的切入点表达式,该切入点表达式的意思是匹配com.ssm.jdk包中任意类的任意方法的执行。

    其中execution是表达式的主体。

  • 第1个 * 表示的是返回类型,使用 * 代表所有类型;com.ssm.jdk表示的是需要拦截的包名,
  • 第2个 * 表示的是类名,使用代表所有的类; 第3个 * 表示的是方法名,使用表示所有方法;
  • 后面的()表示方法的参数,其中的“…”表示任意参数。需要注意的是,第1个*与包名之间有一个空格。

    上面示例中定义的切入点表达式只是开发中常用的配置方式,而Spring AOP中切入点表达式的基本格式如下:

在这里插入图片描述

  • modifiers-pattern:表示定义的目标方法的访问修饰符,如public、private等。
  • ret-type-pattern:表示定义的目标方法的返回值类型,如void、String等。
  • declaring-type-pattern:表示定义的目标方法的类路径,如com.ssm.jdk.UserDaoImpl。
  • name-pattern:表示具体需要被代理的目标方法,如add()方法。
  • param-pattern:表示需要被代理的目标方法包含的参数,本章示例中目标方法参数都为空。
  • throws-pattern:表示需要被代理的目标方法抛出的异常类型。

    提示:带有问号(?)的部分(如modifiers-pattern、declaring-type-pattern和throws-pattern)表示可选配置项,其他部分属于必须配置项。

    想要了解更多切入点表达式的配置信息,读者可以参考Spring官方文档的切入点声明部分(Declaring a pointcut)。

1.3 配置通知

    在配置代码中,分别使用<aop:aspect>的子元素配置了5种常用通知,这些子元素不支持再使用子元素,但在使用时可以指定一些属性。

在这里插入图片描述
    【示例3-1】了解了如何在XML中配置切面、切入点和通知后,接下来通过一个案例来演示如何在Spring中使用基于XML的声明式AspectJ,具体实现步骤如下。

    (1)创建一个名为chapter03的动态Web项目,导入Spring构架所需求的JAR包到项目的lib目录中,并发布到类路径下。
    同时,导入AspectJ框架相关的JAR包,具体如下。 spring- aspects-4.3.6.RELEASE.jar:Spring为AspectJ提供的实现,Spring的包中已经提供。 aspectjweaver-1.8.10.jar:是AspectJ框架所提供的规范,读者可以通过网址(下载)。

    (2)在chapter03项目的src目录下创建一个com.ssm.aspectj包,在该包中创建接口UserDao,并在接口中编写添加和删除的方法。

UserDao.java

package com.ssm.aspectj;

public interface UserDao {
    //添加用户方法
    public void addUser();
    //删除用户方法
    public void deleteUser();
}

    (3)在com.ssm.aspectj包中创建UserDao接口的实现类UserDaoImpl,该类需要实现接口中的方法。

UserDaoImpl.java

package com.ssm.aspectj;

/**
 * 功能描述
 *
 * @author: 衍生星球
 * @date: 2023年04月20日 8:20
 */

public class UserDaoImpl implements UserDao {

    public void addUser() {
        System.out.println("添加用户");
    }

    public void deleteUser() {
        System.out.println("删除用户");
    }
}

    (4)在chapter03项目的src目录下创建一个com.ssm.aspectj.xml包,在该包中创建切面类MyAspect,并在类中分别定义不同类型的通知。

MyAspect.java

package com.ssm.aspectj.xml;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;

/**
 * 切面类,在此类中编写通知
 *
 * @author: 衍生星球
 * @date: 2023年04月20日 8:25
 */

public class MyAspect {
    //前置通知
    public void myBefore(JoinPoint joinPoint) {
        System.out.print("前置通知:模拟执行权限检查...");
        System.out.print("目标类是:"+ joinPoint.getTarget());
        System.out.println(", 被植入增强处理的目标方法:" +joinPoint.getSignature().getName());
    }
    //后置通知
    public void  myAfterReturning(JoinPoint joinPoint) {
        System.out.print("后置通知:模拟记录日志...");
        System.out.println("被植入增强处理的目标方法:" +joinPoint.getSignature().getName());
    }
    /**
     *
     * 环绕通知
     * ProceedingJoinPoint是 JoinPoint的子接口,表示可执行目标方法
     * 1.必须是Object类型的返回值
     * 2.必须接受一个参数,类型为ProceedingJoinPoint
     * 3.必须是 throws Throwable
     */
    public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        //开始
        System.out.print("环绕开始:执行目标方法之前,模拟开启事务...");
        //执行当前目标方法
        Object obj = proceedingJoinPoint.proceed();
        //结束
        System.out.print("环绕结束:执行目标方法之后,模拟关闭事务...");
        return obj;
    }
    //异常通知
    public void myAfterThrowing(JoinPoint joinPoint,Throwable e) {
        System.out.print("异常通知:出错了" + e.getMessage());
    }
    //最终通知
    public void myAfter() {
        System.out.print("最终通知:模拟方法结束后释放资源...");
    }

}

     分别定义了5种不同类型的通知,在通知中使用了JoinPoint接口及其子接口ProceedingJoinPoint作为参数来获得目标对象的类名、目标方法名和目标方法参数等。

     注意:环绕通知必须接收一个类型为ProceedingJoinPoint的参数,返回值也必须是Object类型,且必须抛出异常。异常通知中可以传入Throwable类型的参数来输出异常信息。

     (5)在com.ssm.aspectj.xml包中创建配置文件applicationContext.xml,并编写相关配置。

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/c"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 1.目标类 -->
    <bean id="userDao" class="com.ssm.aspectj.UserDaoImpl" />
    <!-- 2.切面 -->
    <bean id="myAspect" class="com.ssm.aspectj.xml.MyAspect" />
    <!-- 3.aop编程 -->
    <aop:config>
        <!-- 1.配置切面 -->
        <aop:aspect id="aspect" ref="myAspect">
            <!-- 2.配置切入点 -->
            <aop:pointcut id="myPointCut" expression="execution(* com.ssm.aspectj.*.*(..))" />
            <!-- 3.配置通知-->
            <!-- 前置通知-->
            <aop:before method="myBefore" pointcut-ref="myPointCut" />
            <!-- 后置通知 -->
            <aop:after-returning method="myAfterReturning"
                                 pointcut-ref="myPointCut" returning="returnVal" />
            <!-- 环绕通知-->
            <aop:around method="myAround" pointcut-ref="myPointCut" />
            <!-- 异常通知-->
            <aop:after-throwing method="myAfterThrowing"
                                pointcut-ref="myPointCut" throwing="e" />
            <!-- 最终通知-->
            <aop:after method="myAfter" pointcut-ref="myPointCut" />
        </aop:aspect>
    </aop:config>

</beans>

     注意在AOP的配置信息中,使用<aop:after-returning>配置的后置通知和使用<aop:after>配置的最终通知虽然都是在目标方法执行之后执行,但它们是有区别的。后置通知只有在目标方法成功执行后才会被植入,而最终通知不论目标方法如何结束(包括成功执行和异常中止两种情况),它都会被植入。另外,如果程序没有异常,异常通知将不会执行。

    (6)在com.ssm.aspectj.xml包下创建测试类TestXmlAspectJ,在类中为了更加清晰地演示几种通知的执行情况,这里只对addUser()方法进行增强测试。

TestXmlAspectJ.java

package com.ssm.aspectj.xml;

import com.ssm.aspectj.Dao.UserDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * 功能描述
 *
 * @author: 衍生星球
 * @date: 2023年04月20日 11:19
 */

public class TestXmlAspectJ {
    public static void main(String[] args) {
        //定义配置文件路径
        String xmlPath = "com/ssm/aspectj/xml/applicationContext.xml";
        //初始化Spring容器,加载配置文件
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
        //获取实例UserDao
        UserDao userDao = (UserDao)applicationContext.getBean("userDao");
        //执行添加用户方法
        userDao.addUser();

    }
}

2.基于注解的声明式AspectJ

    基于XML的声明式AspectJ实现AOP编程虽然便捷,但是存在一些缺点,那就是要在Spring文件中配置大量的代码信息。为了解决这个问题,AspectJ框架为AOP的实现提供了一套注解,用以取代Spring配置文件中为实现AOP功能所配置的臃肿代码。关于AspectJ注解的介绍如表所示。

在这里插入图片描述

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

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

相关文章

一种引入过渡阶段和高斯变异的改进算术优化算法(TGAOA)-附代码

一种引入过渡阶段和高斯变异的改进算术优化算法(TGAOA) 文章目录 一种引入过渡阶段和高斯变异的改进算术优化算法(TGAOA)1.算术优化算法2. 改进算术优化算法2.1 重构数学加速优化器 MOA2.2 新策略的引入2.3 具有一致性的高斯变异策略2.4具有一致性的高斯变异策略 3.实验结果4.参…

YOLOv8 应用轻量级通用上采样算子CARAFE

特征上采样是现代卷积神经网络架构中的关键操作,例如特征金字塔。其设计对于密集预测任务,如目标检测和语义/实例分割至关重要。在本研究中,我们提出了一种称为内容感知特征重组(CARAFE)的通用、轻量级且高效的操作符,以实现这一目标。CARAFE具有以下几个优点:(1)大的…

模型评估与选择

一、问题 在现实任务中&#xff0c;我们往往有多种学习算沾了供选择&#xff0c;甚至对同 -个学习算法&#xff0c;当使用不同的参数配置时也会产生不 同的模型 . 那么&#xff0c;我们该选用 哪一个学习算法、使用哪一种参数配置呢? 理想的解决方案当然是对候选模型的泛化误…

Java线程详解

线程是CPU调度和分配的基本单位&#xff0c;是操作系统可以识别的最小执行和调度单位&#xff0c;每个线程都有自己特定的独立的内存区域&#xff0c;当然也与其他线程共享堆内存&#xff0c;文件队列以及其他内核资源&#xff0c;Java虚拟机允许一个应用拥有多个线程并发工作。…

AI绘画——Lora模型 小人书·连环画 xiaorenshu

目录 小人书连环画 使用说明&#xff1a; Instructions for v1.5: Instructions for v1.0: 下载地址 模型演示&#xff08;多图预警&#xff09; Night Sky YOZORA Model 模型 Lora模型xiaorenshu pastel-mix模型pastel-waifu-diffusion.vae模型Lora模型xiaorenshu 小人书…

JavaSE05|数组的定义与使用

文章目录 JavaSE05|**数组的定义与使用**1.数据基本概念**2.** **数组是引用类型****3.** **数组的应用场景** JavaSE05|数组的定义与使用 1.数据基本概念 定义&#xff1a;可以看成是相同类型元素的一个集合 1.1数组初始化 动态初始化&#xff1a;在创建数组时&#xff0c…

使用 Kubectl Patch 命令更新资源

Kubectl patch 命令允许用户对运行在 Kubernetes 集群中的资源进行局部更新。相较于我们经常使用的 kubectl apply 命令&#xff0c;kubectl patch 命令在更新时无需提供完整的资源文件&#xff0c;只需要提供要更新的内容即可。 Kubectl patch 支持以下 3 种 patch 类型&…

ROS学习第三十三节——Arbotix使用

https://download.csdn.net/download/qq_45685327/87718484 1.介绍 通过 URDF 结合 rviz 可以创建并显示机器人模型&#xff0c;不过&#xff0c;当前实现的只是静态模型&#xff0c;如何控制模型的运动呢&#xff1f;在此&#xff0c;可以调用 Arbotix 实现此功能。 Arboti…

第九章 桥接模式

文章目录 前言一、桥接模式(Bridge)-基本介绍完整代码Brand 手机接口&#xff0c;定义行为Vivo 实现 Brand 接口XiaoMi 实现 Brand 接口Phone 抽象桥接类FoldedPhone 实现桥接 &#xff08;折叠样式手机&#xff09;UpRightPhone 实现桥接&#xff08;直立样式手机&#xff09;…

干货 | 被抑郁情绪所困扰?来了解CBT吧!

Hello&#xff0c;大家好&#xff01; 这里是 壹脑云科研圈 &#xff0c;我是 喵君姐姐~ 我们的情绪就像是一组正弦波&#xff0c;有情绪很高涨的时刻&#xff0c;也会有情绪低落的瞬间&#xff0c;也会有情绪平稳的时候。 这种情绪上的变化非常正常&#xff0c;也正是因为这…

shell的变量功能

文章目录 shell的变量功能什么是变量&#xff1f;变量的可变性与方便性影响bash环境操作的变量脚本程序设计(shell script)的好帮手 变量的使用与设置&#xff1a;echo、变量设置规则、unset变量的使用(echo)变量设置的规定使用案例 环境变量的功能用env观察环境变量与常见环境…

数据结构(五)—— 栈与队列(2)

一、接上章 栈与队列的中等、困难题。 堆是一块动态内存 栈是先进后出的堆的一种方法 队列是一种先进先出的线性表 二、题 2.5 150 逆波兰表达式求值 很有意思的一道题&#xff0c;将中缀表达式 4 13 / 5&#xff0c;转化为后缀表达式之后&#xff1a;["4", &qu…

深入理解机器学习——过拟合(Overfitting)与欠拟合(Underfitting)

分类目录&#xff1a;《深入理解深度学习》总目录 机器学习的主要挑战是我们的算法必须能够在先前未观测的新输入上表现良好&#xff0c;而不只是在训练集上表现良好。在先前未观测到的输入上表现良好的能力被称为泛化&#xff08;Generalization&#xff09;。通常情况下&…

测试需求平台6-数据持久化与PyMySQL使用

✍此系列为整理分享已完结入门搭建《TPM提测平台》系列的迭代版&#xff0c;拥抱Vue3.0将前端框架替换成字节最新开源的arco.design&#xff0c;其中约60%重构和20%新增内容&#xff0c;定位为从 0-1手把手实现简单的测试平台开发教程&#xff0c;内容将囊括基础、扩展和实战&a…

信息安全技术 健康医疗数据安全指南 付下载地址

声明 本文是学习GB-T 39725-2020 信息安全技术 健康医疗数据安全指南. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 数据使用管理办法示例 第一章 总则 第一条 为规范数据使用流程&#xff0c;根据国家相关法律法规及相关规定&#xff0c;特制定本…

imgaug Augment Polygons 对标注图片和polygons的数据增强

对于本地化进行图像的增强&#xff0c;大家都是非常好操作的。但是&#xff0c;对于标注信息一起增强&#xff0c;还是稍微有一些难度的&#xff0c;麻烦很多。 我是遇到一个数据集非常少的任务&#xff0c;只有40张图。就直接标记了去训练&#xff0c;发现几乎不拟合&#xf…

这一篇搞定Spring

文章目录 一、引言1.1 原生web开发中存在哪些问题&#xff1f; 二、Spring框架2.1 概念2.2 访问与下载 三、Spring架构组成四、山寨版的Spring容器4.1准备工作4.2 山寨IOC容器4.3 配置文件告诉容器 管理哪些bean4.4 相关类4.5 测试 容器 五、构建Maven项目5.1 新建项目5.2 选择…

深度强化学习——AlphaGo实例讲解(5)

现在我们来分析AlphaGo这个实例&#xff0c;看看深度强化学习是怎么样用来玩围棋游戏的 AlphaGo的主要设计思路&#xff1a; 首先是训练&#xff0c;要分3步来做&#xff1a; 1、behavior cloning&#xff1a;这是一种模仿学习&#xff0c;alphaGo模仿人类玩家&#xff0c;从…

STM32平衡小车 pid简单学习

自动控制系统 自动控制系统可分为开环控制系统和闭环控制系统。 1、开环控制系统开环控制系统(open-loop control system)指被控对象的输出(被控制量)对控制器(controller)的输出没有影响。在这种控制系统中&#xff0c;不依赖将被控量反送回来以形成任何闭环回路。 2、闭环控…

c++入门(下)

C入门&#xff08;下&#xff09; 对于C的基础语法的讲解&#xff0c;由想要实现多次重复的函数&#xff0c;引出宏函数和inline的内联函数的对比&#xff0c;对于inline的讲解和运用&#xff0c;在后&#xff0c;C语言中的NULL和C中独特的nullptr的相比两者的比较&#xff0c…