【Spring】— Spring AOP

news2024/11/14 6:15:47

目录

    • 一、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/439631.html

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

相关文章

芴甲氧羰酰基-氨基-聚乙二醇-巯基吡啶Fmoc-NH-PEG-OPSS

修饰性PEG芴甲氧羰基-氨基-聚乙二醇-巯基吡啶Fmoc-NH-PEG-OPSS是保护氨基的PEG衍生物之一 结构式&#xff1a; 芴甲氧羰酰基-氨基-聚乙二醇-巯基吡啶Fmoc-NH-PEG-OPSS聚乙二醇化可以提高聚乙二醇分子的稳定性&#xff0c;降低其免疫原性&#xff0c;仅用于科研实验。 FMOC-NH…

骨传导风靡蓝牙耳机市场 AI赋能有望打破行业技术桎梏

一、骨传导耳机行业概述 骨传导耳机是运用骨传导技术应用制造的耳机&#xff0c;听到的大部分声音都是声波经过空气到达骨膜振动进而将声音传入内耳&#xff0c;另一种方式是声波通过骨震动可以直接传至内耳。骨传导耳机可分为骨传导扬声器技术应用耳机、骨传导麦克风技术应用…

关于比较中设置极大值,常设置的0x3f3f3f3f

无穷大常量 int型变量的取值范围&#xff1a;[-2^31, 2^31 - 1] -> [-2147483648, 2147483647] 0x7fffffff 2147483647 (2^31 - 1) (1 << 31) - 1 0x3fffffff 1073741823 (2^30 - 1) (1 << 30) - 1 0x3f3f…

产品经理必读 | 俞军产品经理十二条军规

最近在学习《俞军产品方法论》&#xff0c;觉得俞军总结的十二条产品经理原则非常受用&#xff0c;分享给大家。 01. 产品经理首先是产品的深度用户 自己设计的产品都没使用过的产品经理&#xff0c;如何明白用户使用的问题&#xff0c;如何解决问题&#xff0c;所以产品经理肯…

java版工程项目管理系统源代码-功能清单 图文解析

Java版工程项目管理系统 Spring CloudSpring BootMybatisVueElementUI前后端分离 功能清单如下&#xff1a; 首页 工作台&#xff1a;待办工作、消息通知、预警信息&#xff0c;点击可进入相应的列表 项目进度图表&#xff1a;选择&#xff08;总体或单个&#xff09;项目显示…

工信部第369批新品公示冷藏车占比显著提升,新能源“卡位战”已悄然打响

一、冷藏车行业概述 随着货物储运的种类不断增多&#xff0c;有些货物在储运过程中易受到外界温度、湿度等条件影响而发生腐烂变质。为了保持易腐货物的本来品质和使用价值&#xff0c;在运输途中不发生腐烂变质和数量上的短缺&#xff0c;提高货物运输的安全性&#xff0c;减…

​windows通过修改路由表,通过特定的网卡访问特定IP​

windows通过修改路由表&#xff0c;通过特定的网卡访问特定IP 方式&#xff1a;修改路由表&#xff0c;指定的IP网段走指定的无线网卡。 步骤1&#xff1a;查看无线网卡的网关信息。终端里输入ipconfig&#xff0c;找到无线网卡对应的网关信息&#xff1b;这里是192.168.44.1…

深度学习训练营Resnet之鸟类识别

深度学习训练营之鸟类识别 原文链接理论知识储备为什么会提出ResNetResNet 环境介绍前置工作设置GPU导入数据并进行查找 数据处理可视化数据配置数据集 残差网络的介绍构建残差网络模型训练开始编译 结果可视化训练样本和测试样本预测 原文链接 &#x1f368; 本文为&#x1f5…

2023-04-19 算法面试中常见的递归和回溯问题

递归和回溯 0 递归与回溯的异同 参考文章 递归与回溯递归与回溯的理解回溯和递归区别 比较 递归回溯定义为了描述问题的某一状态&#xff0c;必须用到该状态的上一状态&#xff0c;而描述上一状态&#xff0c;又必须用到上一状态的上一状态……这种用自已来定义自己的方法…

Netty使用Google Protobuf进行编解码

文章目录 一、概述1、编解码基础2、Netty编解码器3、Protobuf概述 二、Protobuf基本使用1、引入jar包2、下载Protobuf3、编写Student.proto4、生成StudentPOJO类5、服务器端6、客户端7、验证一下吧 三、Netty使用Protobuf发送多类型对象1、编写Student.proto2、生成MyDataInfo.…

测试基础概念常见测试开发模型

文章目录&#xff1a;一.什么是需求&#xff08;1&#xff09;用户需求 &#xff08;2&#xff09;软件需求 二.测试用例 &#xff08;1&#xff09;测试用例的含义 &#xff08;2&#xff09;测试用例的作用 三.开发模型和测试模型&#xff08;1&#xff09;软件生命周期…

2023MathorcupC题电商物流网络包裹应急调运与结构优化问题建模详解+模型代码(一)

电商物流网络包裹应急调运与结构优化问题 第三次继续写数模文章和思路代码了,不知道上次美赛和国赛大家有没有认识我,没关系今年只要有数模比赛艾特我私信我,要是我有时间我一定免费出文章代码好吧!博主参与过十余次数学建模大赛,三次美赛获得过二次M奖一次H奖,国赛二等…

MySQL:JDBC 详细内容

文章目录 Day 04&#xff1a;一、JDBC1. 数据库驱动2. 概述3. 第一个 JDBC 程序4. JDBC 中对象的解释 二、改进 JDBC 程序1. 思路2. 实践注意点3. 分析4. 结果5. 代码 三、SQL 注入问题四、PreparedStatement 对象1. 实践注意点2. 分析&#xff08;增、删、改、查&#xff09;3…

电脑能录屏吗?当然可以!看看这3种方法!

案例&#xff1a;电脑有录屏功能吗&#xff1f; “我的客户让我发一个项目展示的视频&#xff0c;完成这个任务需要对电脑进行录制。问题是&#xff0c;台式电脑有录屏功能吗&#xff1f;笔记本电脑有录屏功能吗&#xff1f;电脑能录屏吗&#xff1f;有没有好心人解答一下我的…

一遍讲清楚:偏向锁到轻量级锁的升级过程(为什么耗资源)

目录 上原理&#xff1a; 细说原理&#xff1a; 什么是锁记录呢&#xff1f; 什么是Mark Word 呢&#xff1f; 上图解&#xff1a; 上原理&#xff1a; 偏向锁使⽤了⼀种等到竞争出现才释放锁的机制&#xff0c;所以当其他线程尝试竞争偏向锁时&#xff0c; 持有偏向锁的…

Java基础——IO流+字节/字符流使用

IO流 &#xff08;1&#xff09;IO流的概述&#xff1a; IO流也称为输入&#xff0c;输出流&#xff0c;就是用来读写数据的。I表示input&#xff0c;是数据从硬盘文件读入到内存的过程&#xff0c;称之输入&#xff0c;负责读。O表示output&#xff0c;是内存程序的数据从内…

【socket通信】python实现简单socket通信| server和client

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、socket通信基础知识1.1基础知识1.2socket类型 二、socket python实现2.1.服务器代码 server.py2.2.客户端代码 client.py2.3.如何运行 补充的计网知识 前言…

2023年TikTok网红营销:从短视频到直播,多维度提升品牌价值

随着TikTok的持续热度&#xff0c;TikTok网红营销已经成为了品牌推广中不可忽视的一部分。在2023年&#xff0c;TikTok网红营销将会继续保持强劲的发展势头。本文Nox聚星将会详细介绍2023年TikTok网红营销的发展趋势&#xff0c;并探讨品牌应该如何抓住这些趋势来提高自己的推广…

「 JavaSE 」说说什么是泛型的类型擦除?

「 JavaSE 」说说什么是泛型的类型擦除&#xff1f; 参考&鸣谢 面试官&#xff1a;说说什么是泛型的类型擦除&#xff1f; Dr Hydra Java泛型类型擦除以及类型擦除带来的问题 蜗牛大师 文章目录 「 JavaSE 」说说什么是泛型的类型擦除&#xff1f;一、前言二、类型擦除做了…

Windows应急响应排查思路

「作者简介」&#xff1a;CSDN top100、阿里云博客专家、华为云享专家、网络安全领域优质创作者 「推荐专栏」&#xff1a;对网络安全感兴趣的小伙伴可以关注专栏《网络安全入门到精通》 Windows应急响应 一、用户分析1、普通用户2、隐藏用户3、克隆账户 二、日志分析1、Window…