Spring framework Day20:Spring AOP xml配置示例三

news2025/1/13 7:54:05

前言

本章节我们继续学习 AspectJ!

AspectJ是一个基于Java语言的面向切面编程(AOP)的扩展框架,它的诞生解决了很多传统面向对象编程的问题。在传统的面向对象编程中,开发者通常会将一些通用功能或者横切关注点(cross-cutting concern)手动地嵌入到业务逻辑中,导致代码难以维护和理解。而AspectJ的出现则让开发者可以通过一种简单而优雅的方式来解决这些问题,使得应用程序的代码变得更加清晰、易于维护。

在本文中,我们将介绍AspectJ的基本概念、语法和使用方法。首先,我们将简要介绍AOP的概念,并与传统的面向对象编程作对比,以突出AspectJ的优越性。其次,我们将详细阐述AspectJ的语法和组成部分,包括切入点、通知和切面等。最后,我们将提供一些实际的示例来帮助读者更好地理解如何使用AspectJ来解决常见的应用程序开发问题。

相信通过本文的介绍,读者将会深入理解AspectJ的强大之处,掌握如何使用这个框架来构建高效、易于维护的应用程序。

一、开始学习

1、新建项目,结构如下

2、添加 spring 依赖
 
    <!-- spring 的核心依赖 -->
    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.23</version>
        </dependency>
 
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.4.5</version>
        </dependency>
 
         <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.8</version>
        </dependency>
 
 
    </dependencies>
3、在 service 包下新建一个类 UserService 类
@Slf4j
public class UserService {

    /**
     * 目标方法,也就是需要被增强的方法
     * 因此它就是一个连接点
     * @param name
     */
    public String add(String name){
        log.info("添加用户..." + name);
        return "success";
    }

}
4、在 aspect 包下新建一个 ServiceAspect 切面类

@Slf4j
public class ServiceAspect {

    /**
     * 自定义前置通知,可以给一个参数
     * 这个参数为连接点(JoinPoint)
     * 通过这个连接点可以拦截目标方法的参数等信息
     *
     * @param jp
     */
    public void before(JoinPoint jp) {
        log.info("执行前置通知,拦截的目标方法参数:" + jp.getArgs()[0]);
    }

    /**
     * 后置通知
     *
     * @param jp        连接点
     * @param returnVal 目标的方法的返回值
     */
    public void afterReturning(JoinPoint jp, Object returnVal) {
        log.info("后置通知,目标方法返回值:" + returnVal);
    }

    /**
     * 环绕通知
     *
     * @param jp 连接点,继承 JoinPoint 接口
     * @return
     */
    public Object around(ProceedingJoinPoint jp) throws Throwable {
        log.info("环绕通知前,目标方法参数:" + jp.getArgs()[0]);
        // 调用目标方法对象的方法
        Object returnVal = jp.proceed();
        log.info("环绕通知后,目标方法返回值:" + returnVal);
        return returnVal;
    }

    /**
     * 异常通知,当目标对象产生异常时会执行
     * 后置通知将不会再生效
     *
     * @param jp 连接点
     * @param e  目标方法产生的异常对象
     */
    public void afterThrowing(JoinPoint jp, Exception e) {
        log.info("异常通知,异常信息:" + e.getMessage());
    }

    /**
     * 最终通知,不管有没有异常产生最终通知都会被执行
     * @param jp 连接点
     */
    public void after(JoinPoint jp){
        log.info("最终通知");
    }

}

这个示例中的ServiceAspect类是一个使用AspectJ编写的切面类,其中包含了几个不同类型的通知方法。

  1. before(JoinPoint jp) 方法是一个前置通知。在目标方法执行之前被拦截执行。它接收一个JoinPoint对象作为参数,可以通过该对象获取目标方法的参数信息。在这个示例中,我们使用log.info方法记录了拦截的目标方法参数。

  2. afterReturning(JoinPoint jp, Object returnVal) 方法是一个后置通知。在目标方法成功返回之后被拦截执行。它接收一个JoinPoint对象和目标方法的返回值作为参数。在这个示例中,我们使用log.info方法记录了目标方法的返回值。

  3. around(ProceedingJoinPoint jp) 方法是一个环绕通知。它可以在目标方法执行的前后进行处理。在这个示例中,我们首先通过log.info方法记录了目标方法的参数信息,然后调用jp.proceed()方法来执行目标方法,最后再通过log.info方法记录了目标方法的返回值。注意,在环绕通知中必须显式地调用jp.proceed()方法来触发目标方法的执行。

  4. afterThrowing(JoinPoint jp, Exception e) 方法是一个异常通知。当目标方法抛出异常时被拦截执行。它接收一个JoinPoint对象和抛出的异常对象作为参数。在这个示例中,我们使用log.info方法记录了异常信息。

  5. after(JoinPoint jp) 方法是一个最终通知。无论目标方法是否成功执行,最终总会被执行。在这个示例中,我们使用log.info方法输出了一条简单的日志。

通过编写这些通知方法,我们可以根据不同的需求,在目标方法的不同执行阶段进行拦截和处理。例如,可以在前置通知中进行参数验证,后置通知中记录返回值,异常通知中处理异常情况等。AspectJ提供了灵活的语法和丰富的可操作性,使得开发人员可以轻松地实现横切关注点的处理。

 5、在 rsources 下新建一个 beans.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/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- 装配目标对象 -->
    <bean id="userService" class="edu.nf.ch20.service.UserService"/>
    <!-- 装配切面 -->
    <bean id="serviceAspect" class="edu.nf.ch20.aspect.ServiceAspect"/>
    <!-- 配置 AOP -->
    <aop:config>
        <!-- 配置切入点 -->
        <aop:pointcut id="myPointcut" expression="execution(* edu.nf.ch20.service.UserService.*(..))"/>
        <!-- 配置切面,ref 引用切面的 bean 的 id -->
        <aop:aspect ref="serviceAspect">
            <!-- 配置各种通知,method 属性值指定通知的方法名,
                 pointcut-ref 属性引用切入点的  bean 的 id
            -->

            <!-- 前置通知 -->
            <aop:before method="before" pointcut-ref="myPointcut"/>

            <!-- 注意:后置通知有一个 returning 属性对应方法返回值的参数名-->
            <aop:after-returning method="afterReturning" pointcut-ref="myPointcut" returning="returnVal"/>

            <!-- <aop:around>标签用于配置环绕通知,即在目标方法执行前后进行一些额外的处理。
                    method="around"表示环绕通知的方法名是"around",这个方法需要在配置类中定义。
                    pointcut-ref="myPointcut"表示引用了名为"myPointcut"的切点,即指定了目标方法的执行位置。 -->
            <aop:around method="around" pointcut-ref="myPointcut"/>

            <!-- <aop:after>标签用于配置最终通知,即在目标方法执行之后进行一些额外的处理。
                    method="after"表示后置通知的方法名是"after",这个方法需要在配置类中定义。
                    pointcut-ref="myPointcut"表示引用了名为"myPointcut"的切点,即指定了目标方法的执行位置。           -->
            <aop:after method="after" pointcut-ref="myPointcut"/>

            <!--  <aop:after-throwing>标签用于配置异常通知,即在目标方法抛出异常时进行一些额外的处理。
                    method="afterThrowing"表示异常通知的方法名是"afterThrowing",这个方法需要在配置类中定义。
                    pointcut-ref="myPointcut"表示引用了名为"myPointcut"的切点,即指定了目标方法的执行位置。
                    throwing="e"表示在异常通知方法中传递了一个名为"e"的参数,用于接收目标方法抛出的异常。          -->
            <aop:after-throwing method="afterThrowing" pointcut-ref="myPointcut" throwing="e"/>

        </aop:aspect>
    </aop:config>

</beans>

XML配置代码是使用Spring AOP来实现切面编程的示例。XML配置代码是使用Spring AOP来实现切面编程的示例。

<!-- 装配目标对象 -->
<bean id="userService" class="edu.nf.ch20.service.UserService"/>
<!-- 装配切面 -->
<bean id="serviceAspect" class="edu.nf.ch20.aspect.ServiceAspect"/>

在这个示例中,userService是一个具体的业务类,serviceAspect是一个切面类,用于包含横切关注点的逻辑。

继续,配置AOP:

<aop:config>
    <!-- 配置切入点 -->
    <aop:pointcut id="myPointcut" expression="execution(* edu.nf.ch20.service.UserService.*(..))"/>

    <!-- 配置通知 -->
    <aop:aspect ref="serviceAspect">
        <aop:before method="before" pointcut-ref="myPointcut"/>
        <aop:after-returning method="afterReturning" pointcut-ref="myPointcut" returning="returnVal"/>
        <aop:around method="around" pointcut-ref="myPointcut"/>
        <aop:after method="after" pointcut-ref="myPointcut"/>
        <aop:after-throwing method="afterThrowing" pointcut-ref="myPointcut" throwing="e"/>
    </aop:aspect>
</aop:config>

<aop:config>标签中,首先定义了切入点(pointcut),用于确定哪些方法将被拦截。在这个示例中,切入点被命名为myPointcut,它使用了表达式语言来匹配edu.nf.ch20.service.UserService类的所有方法。

然后,通过<aop:aspect>标签配置了不同类型的通知(advice),包括<aop:before>(前置通知)、<aop:after-returning>(后置通知)、<aop:around>(环绕通知)、<aop:after>(最终通知)和<aop:after-throwing>(异常通知)。这些通知指向了切面类中对应的方法。

总结来说,这段XML配置代码实现了对edu.nf.ch20.service.UserService类的方法进行切面编程,并在不同的执行阶段触发对应的通知方法。这样可以将横切关注点的业务逻辑与核心业务逻辑分离,提高了代码的可维护性和可重用性。

具体来说,这段代码的作用如下:

  1. 配置目标对象和切面:通过配置<bean>元素,将目标对象和切面类实例化并装配到Spring容器中。目标对象是具体的业务类,而切面类包含了与横切关注点相关的逻辑。

  2. 配置切入点:使用<aop:pointcut>元素配置切入点,即确定哪些方法将被拦截和应用切面的逻辑。在这个示例中,切入点使用表达式execution(* edu.nf.ch20.service.UserService.*(..)),表示拦截edu.nf.ch20.service.UserService类的所有方法。

  3. 配置通知:通过<aop:before><aop:after-returning><aop:around><aop:after><aop:after-throwing>等元素,配置了不同类型的通知(advice)。这些通知指向切面类中的相应方法,并在目标对象的方法执行前、后、返回结果时、发生异常时等特定时机触发执行。

通过以上配置,可以实现以下主要功能:

  • 前置通知(Before Advice):在目标方法执行前执行切面中的方法,可以做一些准备工作、参数验证等操作。

  • 后置通知(After Advice):在目标方法执行后执行切面中的方法,可以进行一些清理工作、记录日志、统计时间等操作。

  • 环绕通知(Around Advice):在目标方法执行前后,或者替代目标方法执行,都可以执行切面中的方法。可以自由控制目标方法的执行,并进行一些额外的处理。

  • 最终通知(After-returning Advice):在目标方法正常返回结果后执行切面中的方法,可以对返回结果进行处理或记录日志。

  • 异常通知(After-throwing Advice):在目标方法发生异常时执行切面中的方法,可以处理异常、记录日志或进行补偿操作。

通过将横切关注点与核心业务逻辑分离,AOP提供了一种更加灵活和可维护的代码组织方式。它可以帮助开发人员在不改变原有业务逻辑的情况下,统一处理与业务无关的横切关注点,提高代码的可重用性、可维护性和可测试性。

6、测试
public class Main {

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        UserService bean = context.getBean(UserService.class);
        bean.add("qiu");
    }

}

 运行结果

 

 二、总结

上一章我们使用了MethodBeforeAdvice、AfterReturningAdvice、MethodInterceptor和ThrowsAdvice接口这四个接口去重写它们的方法来实现通知。这样有什么不好呢,每一次编写切面类的时候都需要实现这些接口,太繁重了,还有,重写异常通知时需要自己编写,而且方法名必须是afterThrowing,写错了还不行。

本章节通过使用 AspectJ ,我们自己来定义通知,自己编写。那么使用 AspectJ 的时候,可以实现接口重写他们的方法来实现通知,也可以自定义通知,它们有什么区别呢?

MethodBeforeAdvice、AfterReturningAdvice、MethodInterceptor和ThrowsAdvice接口是Spring框架中用于实现不同类型通知的接口。

  1. MethodBeforeAdvice:这个接口用于实现前置通知,即在目标方法执行之前被触发的通知。它的before方法会在目标方法执行前被调用,可以在该方法中添加需要执行的逻辑。

  2. AfterReturningAdvice:这个接口用于实现后置通知,即在目标方法成功返回后被触发的通知。它的afterReturning方法会在目标方法成功返回后被调用,可以在该方法中对返回结果进行处理或记录日志等操作。

  3. MethodInterceptor:这个接口用于实现环绕通知,即在目标方法的执行过程中进行拦截并控制其执行。它的invoke方法会在目标方法执行前后被调用,通过该方法可以手动控制目标方法的执行,包括传入参数、获取返回结果等。

  4. ThrowsAdvice:这个接口用于实现异常通知,即在目标方法抛出异常时被触发的通知。它没有定义具体的方法,而是通过在实现类中定义异常处理方法来实现不同类型异常的处理。

与自定义通知相比,这些接口提供了一种约定和规范化的方式来实现通知逻辑。开发者只需实现相应的接口,并将其配置到Spring容器中,框架会在合适的时机调用相应的方法。这种方式可以使通知与被拦截的目标方法解耦,并且能够与Spring的AOP功能无缝集成。

而自定义通知则更加灵活,开发者可以根据需要编写自己的通知类,不受特定接口的限制。自定义通知可以通过注解、切面表达式或者基于XML的配置来实现,具有更高的自由度和可定制性。开发者可以自己控制通知的触发时机和执行逻辑,更好地满足特定业务需求。

总之,使用Spring的通知接口可以快速实现常见类型的通知,并与Spring的AOP功能结合使用;而自定义通知则更加灵活,可以更好地满足自定义需求,但需要更多的开发额外处理。选择采用哪种方式取决于具体场景和需求。

 

三、gitee 案例

案例完整地址:https://gitee.com/qiu-feng1/spring-framework.git

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

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

相关文章

开发者基于 chroot 打造的工具macOS Containers

导读macOS Containers 是一群开发者基于 chroot 打造的工具&#xff0c;能让你在 macOS 用 Docker 运行 macOS 镜像。 macOS Containers 官网写道&#xff1a; 容器从根本上改变了现代软件的开发和部署方式。包括 FreeBSD、Solaris、Linux 甚至 Windows 在内的多种操作系统都支…

RabbitMQ的五种常见消费模型

目录 引言1. 简单队列模型&#xff08;Simple Queue Model&#xff09;优缺点及适用场景代码示例 2. 工作队列模型&#xff08;Work Queue Model&#xff09;优缺点及适用场景代码示例 3. 发布/订阅模型&#xff08;Publish/Subscribe Model&#xff09;优缺点及适用场景代码示…

论文解析-moETM-多组学整合模型

论文解析-moETM 参考亮点动机发展现状现存问题 功能方法Encoder改进Decoder改进 评价指标生物保守性批次效应移除 实验设置结果多组学数据整合cell-topic mixture可解释性组学翻译性能评估RNA转录本、表面蛋白、染色质可及域调控关系研究1. 验证同一主题下&#xff0c;top gene…

Android12之DRM基本接口实现(二)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

Android性能优化系列-腾讯matrix-流量监控之TrafficPlugin源码分析

前言 本篇进行matrix框架的网络流量监控模块的代码分析。你可能想&#xff0c;为什么需要对流量进行监控呢&#xff1f;我们平常进行的网络接口请求都是一些必要的操作&#xff0c;监控它的意义何在&#xff1f;首先我们要明确流量监控的对象是什么&#xff0c;是上行&#xf…

【学习笔记】RabbitMQ-5 消息的可靠性投递 以及示例代码

参考资料 RabbitMQ官方网站RabbitMQ官方文档噼咔噼咔-动力节点教程 文章目录 八、RabbitMQ的确认机制 -confirm8.1 Confirm 模式简介8.2 具体代码设置8.2.1 **设置思路**&#xff1a;8.2.2 **代码实现**8.2.2.1 开启生产者的确认模式.8.2.2.2 实现接口ComfirmCallback8.2.2.3 配…

Transformer模型 | Transformer模型描述

谷歌推出的BERT模型在11项NLP任务中夺得SOTA结果,引爆了整个NLP界。而BERT取得成功的一个关键因素是Transformer的强大作用。谷歌的Transformer模型最早是用于机器翻译任务,当时达到了SOTA效果。Transformer改进了RNN最被人诟病的训练慢的缺点,利用self-attention机制实现快…

浪涌防护:TSS管的工作原理与应用?|深圳比创达EMC

浪涌防护&#xff1a;TSS管的工作原理与应用&#xff1f;相信不少人是有疑问的&#xff0c;今天深圳市比创达电子科技有限公司就跟大家解答一下&#xff01; 一、TSS工作原理 TSS半导体放电管是一种电压开关型瞬态抑制二极管&#xff0c;即涌压抑制晶体管&#xff0c;或称为导…

大中小企业自招人力及劳务派遣招聘

抖音直播招聘报白是一种通过直播方式展示职位信息并与求职者互动的招聘方式。在抖音平台上&#xff0c;企业或人力资源公司可利用直播将职位以视频直播的方式展现&#xff0c;这种方式可给求职者带来更强的代入感和真实性&#xff0c;解决其对岗位真假难辨的信任问题。 图片 …

SCB-Dataset3 公开 学生课堂行为数据集: A Benchmark for Detecting Student Classroom Behavior

公开 学生课堂行为数据集 SCB-Dataset 2 Student Classroom Behavior dataset b站&#xff1a;https://www.bilibili.com/video/BV1D34y1g76E/ arxiv: https://arxiv.org/pdf/2310.02522.pdf github: https://github.com/Whiffe/SCB-dataset 百度云&#xff1a;https://pan…

如何选择适合的发电机测试设备?

选择适合的发电机测试设备需要考虑电机的额定功率和负载需求&#xff0c;选择能够满足需求的测试设备&#xff0c;确保测试设备的功率范围覆盖发电机的额定功率&#xff0c;并有一定的余量。常见的发电机测试项目包括电压、电流、频率、功率因数、转速、温度等参数的测试&#…

PCB布线时如何保证100M以上信号的稳定性?

PCB布线时是电子工程中非常重要的环节&#xff0c;对于保证信号的稳定性和完整性至关重要&#xff0c;若电子工程师遇上100M以上信号的布线需求&#xff0c;该如何设计来保证其稳定性&#xff1f;下面或许能给你些参考。 1、选择合适的传输介质 对高速信号&#xff0c;选择合适…

基于 SaaS 搭建的党建小程序源码系统 带完整的搭建教程

随着互联网技术的发展和应用的普及&#xff0c;传统的党建模式已经难以满足现代社会的需求。为了更好地服务党员和群众&#xff0c;提高党组织的凝聚力和战斗力&#xff0c;基于 SaaS搭建的党建小程序源码系统应运而生。小程序的出现可以很好的解决大多数问题&#xff0c;方便了…

数字孪生与智慧城市:重塑未来城市生活的奇迹

今天&#xff0c;我们将探讨数字孪生和智慧城市两个颠覆性技术&#xff0c;它们正引领着未来城市生活的巨大变革。随着科技的飞速发展&#xff0c;数字孪生和智慧城市成为实现可持续发展和提升居民生活质量的关键策略。 数字孪生&#xff1a;实现现实与虚拟的完美融合 数字孪生…

AI工具在工作中的“大作用”

现如今科技的发展让我们的生活越来越便利&#xff0c;一些AI工具的出现&#xff0c;更对我们的工作有莫大的帮助。 AI工具的辅助就像给上班族提供了一种更加高级的“摸鱼方法”&#xff0c;大大提高了打工人的工作效率。如果有一种什么都能回答你&#xff0c;甚至能帮助你完成…

用例图包含关系、扩展关系、泛化关系解析(最全总结,非常详细)

一、用例图中的各种关系 a&#xff09;参与者与用例间的关联关系&#xff1a;参与者与用例之间的通信&#xff0c;也成为关联或通信关系。 b&#xff09;用例与用例之间的关系&#xff1a;包含关系&#xff08;include&#xff09;、扩展关系&#xff08;extend&#xff09;、…

智慧机场航线监测系统:提升航空运输安全与效率的新一步

在当今世界&#xff0c;空中出行已经成为越来越多人生活的一部分。人们频繁地乘坐飞机来往各地&#xff0c;全球航空旅行需求不断增长&#xff0c;航空运输业已经变得越来越复杂。在这个复杂性不断增强的行业中&#xff0c;智慧机场应用航线监测系统成为了航空领域关键的发展趋…

LeetCode2652——倍数之和

LeetCode2562 自己的解法&#xff1a; 官方给的解法&#xff08;不需要创建额外的数组&#xff0c;更为简洁&#xff0c;效率更高&#xff09;&#xff1a;

操作指南 | 如何通过Moonbeam DApp在OpenGov投票

除了Polkassembly或Polkadot.js以外&#xff0c;Moonbeam自己的DApp也可以直接参与链上治理。该界面简洁完整&#xff0c;对用户来说非常方便。 首先进入https://apps.moonbeam.network/moonbeam&#xff0c;连接你的钱包至DApp。Moonbeam DApp支持很多类型的钱包&#xff0c;…

Unity游戏开发中ngui和ugui区别与优缺点详解

Unity3D是一款跨平台的游戏开发引擎&#xff0c;它支持多种平台&#xff0c;包括PC、移动设备和主机。在Unity3D中&#xff0c;UI系统是游戏开发中非常重要的一部分&#xff0c;它负责游戏中的用户界面的显示和交互。 对惹&#xff0c;这里有一个游戏开发交流小组&#xff0c;…