七、Spring 面向切面编程(AOP)学习总结

文章目录

  • 一、初识面向切面编程(AOP)
    • 1.1 什么是 AOP
    • 1.2 AOP的应用场景
    • 1.3 Aop 在 Spring 中的作用
      • 1.3.1 Aop 的核心概念
    • 1.4 使用 Spring 实现 AOP
      • 1.4.1 方式一:使用 Spring API 接口实现 AOP 【主要是SpringAPI接口实现】
      • 1.4.2 方式二:自定义类来实现 AOP【主要是切面定义】
      • 1.4.3 方式三:使用注解实现 AOP




一、初识面向切面编程(AOP)


  •        在以往的企业开发过程中,一些已经写完的功能可以会在原本的基础上进行扩展,这个时候就需要去修改原有的代码,将新扩展的内容完善进去。但是这个动作其实是企业级开发的大忌,因为原本好用的代码很可能因为新增的内容导致出现问题。


           而解决这个问题会用到代理模式,将要新扩展的功能维护到代理角色中,再用代理角色去调用真实角色来实现功能的扩展,这样做不但将新扩展的功能实现类,原本的功能代码也无需变动,更加稳妥。而这也是 Spring AOP 的实现机制。

在这里插入图片描述



1.1 什么是 AOP

        AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

  • 如下图所示:AOP 就是通过一些方式,在原有业务逻辑不变的情况下将一些扩展功能完成
    在这里插入图片描述




1.2 AOP的应用场景


  • 日志记录: 记录调用方法的入参和结果返参。

  • 用户的权限验证: 验证用户的权限放到AOP中,与主业务进行解耦。

  • 性能监控: 监控程序运行方法的耗时,找出项目的瓶颈。

  • 事务管理: 控制Spring事务,Mysql事务等。

  • AOP可以拦截指定的方法,并且对方法增强,比如:事务、日志、权限、性能监测等增强,而且无需侵入到业务代码中,使业务与非业务处理逻辑分离。




1.3 Aop 在 Spring 中的作用


       AOP 采取横向抽取机制(动态代理),取代了传统纵向继承机制的重复性代码,其应用主要体现在事务处理、日志管理、权限控制、异常处理等方面。

       主要作用是分离功能性需求和非功能性需求,使开发人员可以集中处理某一个关注点或者横切逻辑,减少对业务代码的侵入,增强代码的可读性和可维护性。

       简单的说,AOP 的作用就是保证开发者在不修改源代码的前提下,为系统中的业务组件添加某种通用功能。

1.3.1 Aop 的核心概念


  • 提供声明式事务;允许用户自定义切面

    • 横切关注点: 跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志,安全,缓存,事务等等…

    • 切面(ASPECT): 横切关注点被模块化的特殊对象。即,它是一个类。

    • 通知(Advice): 切面必须要完成的工作。拦截到连接点之后,对切入点增强的内容。即,它是类中的一个方法。

    • 目标(Target): 被代理的目标对象。

    • 代理(Proxy): 指生成的代理对象

    • 切入点(PointCut): 指需要对那些 JointPoint 进行拦截,即被拦截的连接点

    • 连接点(JointPoint): 指那些被拦截到的点,在 Spring 中,指可以被动态代理拦截目标类的方法

    • 植入点(Weaving): 指把增强代码应用到目标上,生成代理对象的过程

通俗理解

横切关注点:在业务代码种需要新增的业务,即为关注点
切面:将关注点维护成一个类,即新增的业务就是切面
通知:切面中具体的方法,也就是具体需要完成的业务就是通知。
目标:即需要在那段业务新增业务代码,即目标对象。
代理:代理目标对象的对象
切入点:需要通知在那个位置执行新的业务代码

注:目标和代理被 Spring 完成了,InvocationHandler、Proxy



  • Spring AOP中,通过 “通知(Advice)” 定义横切逻辑,Spring中支持5种类型的Advice:即AOP在不改变原有代码的情况下,去增加新的功能。
通知类型连接点
Before(前置通知)通知方法在目标方法调用之前执行,前置通知不会影响目标方法的执行,除非此处抛出异常
After(后置通知)通知方法在目标方法返回或者异常后调用(必然会执行)
After-Returning(最终通知)通知方法在目标方法返回后调用,如果连接点抛出异常,则不会执行
After-Throwing(抛出异常后通知)通知方法在目标方法抛出异常后调用
Around(环绕通知)环绕目标方法的通知,例如方法调用。这是最强大的一种通知类型。环绕通知可以在方法调用前后完成自定义的行为。它可以选择是否继续执行目标方法或直接返回自定义的返回值又或抛出异常将执行结束。

注:目标方法就是连接点



1.4 使用 Spring 实现 AOP


1.4.1 方式一:使用 Spring API 接口实现 AOP 【主要是SpringAPI接口实现】


  • 创建 Maven 项目并在 pom.xml 中引入如下依赖(使用 AOP 织入需要此依赖)

    <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver --><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.4</version></dependency>
    
    • 在service包下,定义UserService业务接口和UserServiceImpl实现类

      • UserService 接口

        public interface UserService {void add();void query();void delete();void update();
        }
        
      • UserServiceImpl 实现类

        public class UserServiceImpl implements UserService {@Overridepublic void add() {System.out.println("新增一个用户");}@Overridepublic void query() {System.out.println("查询用户信息");}@Overridepublic void delete() {System.out.println("删除一个用户");}@Overridepublic void update() {System.out.println("修改用户信息");}
        }
        
    • 在 log 包下定义 一个前置增强和一个后置增强类

      • 前置通知增强类

        import org.springframework.aop.MethodBeforeAdvice;
        import java.lang.reflect.Method;//MethodBeforeAdvice : 前置通知
        public class BeforeLog implements MethodBeforeAdvice {/** method:要执行的目标对象的方法* args:参数* target:目标对象* */@Overridepublic void before(Method method, Object[] args, Object target) throws Throwable {System.out.println(target.getClass().getName() + "的" + method.getName() + "被执行了");}
        }
        
      • 后置通知增强类

        import org.springframework.aop.AfterReturningAdvice;
        import java.lang.reflect.Method;// AfterReturningAdvice : 后置通知
        public class AfterLog implements AfterReturningAdvice {/** returnValue: 返回值* method:要执行的目标对象的方法* args:参数* target:目标对象* */@Overridepublic void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {System.out.println("执行了"+method.getName()+"方法,返回结果为:"+returnValue);}
        }
        
    • 最后去 spring 的文件中注册 , 并实现aop切入实现 , 注意导入约束,配置 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/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttps://www.springframework.org/schema/aop/spring-aop.xsd"><!--注册bean--><bean id="afterLog" class="com.sys.log.AfterLog"/><bean id="beforeLog" class="com.sys.log.BeforeLog"/><bean id="userService" class="com.sys.service.impl.UserServiceImpl"/><!--配置 Spring aop:需要导入aop约束--><!--实现 aop 的方式一:使用原生 Spring API接口--><aop:config><!--配置切入点:execution 表达式:execution(要执行的位置!)execution(* *) :第一个 * 代表返回值的类型任意,第二个 * 代表类,如果为 * 就代表所有类execution(* com.sys.XXX.*(..)):.*(..):表示任何方法名,括号表示参数,两个点表示任何参数类型--><aop:pointcut id="pointcut" expression="execution(* com.sys.service.impl.UserServiceImpl.*(..))"/><!--配置 Advice(通知)--><!--前置加后置约等于环绕通知--><aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/><aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"/></aop:config></beans>
      
      • Spring AOP切入点@Pointcut – execution表达式
    • 创建 MyTest 测试类

      public class MyText {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");// 需要注意的是:AOP的代理是动态代理,也就是此处代理的并不是实现类,而是实现类实现的接口UserService userService = (UserService) context.getBean("userService");userService.add();userService.update();userService.query();userService.delete();}
      }
      
    • 执行结果 :因为接口定义的方法都是 Void 所以没有返回值
      在这里插入图片描述



1.4.2 方式二:自定义类来实现 AOP【主要是切面定义】


  • 不使用 Spring 提供的 API 接口,使用自定义的 diy 类,搭配 xml 的 aop 配置实现

    • 创建 diy 工具类

      public class DiyPointCut {public void before(){System.out.println("======方法执行前======");}public void after(){System.out.println("======方法执行后======");}
      }
      
    • 创建 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/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttps://www.springframework.org/schema/aop/spring-aop.xsd"><!-- 将 Diy 工具类注册到 SPring 容器种 --><bean id="diyPointCut" class="com.sys.diy.DiyPointCut"/><bean id="userService" class="com.sys.service.impl.UserServiceImpl"/><!-- 配置 aop --><aop:config><!--自定义切面,ref 要引用的类--><aop:aspect ref="diyPointCut"><!--配置切入点--><aop:pointcut id="pointcut" expression="execution(* com.sys.service.impl.UserServiceImpl.*(..))"/><!--配置通知before:前置通知after:后置通知--><aop:before method="before" pointcut-ref="pointcut"/><aop:after method="after" pointcut-ref="pointcut"/></aop:aspect></aop:config></beans>
      
    • 修改 MyTest,其他代码内容不变

      public class MyTest {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");// 需要注意的是:AOP的代理是动态代理,也就是此处代理的并不是实现类,而是实现类实现的接口UserService userService = (UserService) context.getBean("userService");userService.add();System.out.println("-------------------------------------------------");userService.update();System.out.println("-------------------------------------------------");userService.query();System.out.println("-------------------------------------------------");userService.delete();}
      }
      
    • 执行结果



1.4.3 方式三:使用注解实现 AOP


  • @Aspect : 被该注解标注的类就是一个切面

  • @Before: 标注切面类中的方法为前置通知

    • 参数:需要传入切入点,即 execution 表达式
  • @After: 标注切面类中的方法为后置通知

    • 参数:需要传入切入点,即 execution 表达式
  • @Around: 标注切面类中的方法为环绕通知

    • 参数:需要传入切入点,即 execution 表达式

    • 被标记为环绕通知的方法还需要搭配 proceed() 来完成通知,如果没有该方法,那么切入点对应的方法不执行,也可以理解为这个方法就是执行切入点中的方法用的

      • proceed() : 通过这个方法判断切入点中的方法在环绕通知中的那个位置执行


  • 代码示例:(前置通知和后置通知)

    • 新增 Diy 切面类

      @Aspect // 将该类标记为一个切面
      public class AnnotationPointCut {// 将该方法标记为前置通知@Before("execution(* com.sys.service.impl.UserServiceImpl.*(..))")public void before(){System.out.println("=====方法执行前=====");}// 将该方法标记为后置通知@After("execution(* com.sys.service.impl.UserServiceImpl.*(..))")public void after(){System.out.println("=====方法执行后=====");}
      }
      
    • 修改 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/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttps://www.springframework.org/schema/aop/spring-aop.xsd"><bean id="userService" class="com.sys.service.impl.UserServiceImpl"/><!--方式三:使用注解--><bean id="annotationPointCut" class="com.sys.diy.AnnotationPointCut"/><!--开启aop注解支持! JDK(默认是 proxy-target-class="false")  cglib(proxy-target-class="true")--><aop:aspectj-autoproxy/></beans>
      
    • 其他代码不变,运行测试类,执行结果


  • 环绕通知代码示例

    • 修改 Diy 切面类

      @Aspect // 将该类标记为一个切面
      public class AnnotationPointCut {@Around("execution(* com.sys.service.impl.UserServiceImpl.*(..))")public void around(ProceedingJoinPoint jp) throws Throwable{System.out.println("环绕前");Signature signature = jp.getSignature();// 获得签名System.out.println("signature:"+signature);jp.proceed(); //执行方法System.out.println("环绕后");}}
      
    • 其他代码不变,运行测试类,执行结果

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Huggingface使用

文章目录 前置安装Huggingface介绍NLP模块分类transformer流程模块使用详细讲解tokennizermodeldatasetsTrainer Huggingface使用网页直接体验API调用本地调用(pipline)本地调用&#xff08;非pipline&#xff09; 前置安装 anaconda安装 使用conda创建一个新环境并安装pytorc…

Django之JWT库与SimpleJWT库的使用

Django之JWT库与SimpleJWT库的使用 JWTJWT概述头部(header)载荷(payload)签名(signature) Django使用JWT说明jwt库的使用安装依赖库配置settings.py文件配置urls.py文件创建视图配置权限 SimpleJWT库的使用安装SimpleJWT库配置Django项目配置路由创建用户接口测试身份认证自定义…

2023年华数杯数学建模C题思路代码分析 - 母亲身心健康对婴儿成长的影响

# 1 赛题 C 题 母亲身心健康对婴儿成长的影响 母亲是婴儿生命中最重要的人之一&#xff0c;她不仅为婴儿提供营养物质和身体保护&#xff0c; 还为婴儿提供情感支持和安全感。母亲心理健康状态的不良状况&#xff0c;如抑郁、焦虑、 压力等&#xff0c;可能会对婴儿的认知、情…

proj库配置与使用(window11,vs2019,x64)

前置安装依赖 1.SQLite3 安装 亲测 (97条消息) SQLite3源码下载与编译&#xff08;开发环境&#xff1a;Win10VS2022&#xff09;_sqlite3 下载_林夕07的博客-CSDN博客 2.TIFF 亲测 (97条消息) Win11下基于cmake-3.26.3 完美编译 TIFF-4.5.0源码_tiff 编译_GIS子枫的博客-C…

Android Gradle 骚操作,将两个项目合并到一个项目中

1. 前言 在工作中&#xff0c;由于各种原因&#xff0c;导致需要将两个可单独运行的App项目&#xff0c;合并到一个git仓库里&#xff0c;且单独的App项目里还有其他Module模块。 如果只是将两个项目复制到同一个文件夹下&#xff0c;还是得单独打开各个项目&#xff0c;是很不…

自然语言处理学习笔记(三)————HanLP安装与使用

目录 1.HanLP安装 2.HanLP使用 &#xff08;1&#xff09;预下载 &#xff08;2&#xff09;测试 &#xff08;3&#xff09;命令行 &#xff08;4&#xff09;测试样例 3.pyhanlp可视化 4. HanLP词性表 1.HanLP安装 HanLP的 Python接口由 pyhanlp包提供&#xff0c;其安装…

OSPF综合实验

实验题目如下&#xff1a; 实验拓扑如下&#xff1a; 实验要求如下&#xff1a; 【1】R4为ISP&#xff0c;其上只能配置IP地址: R4与其他所有直连设备间使用公有 【2】R3---R5/6/7为MGRE环境&#xff0c;R3为中心站点 【3】整个OSPF环境IP地址为172.16.0.0/16 【4】所有设备…

Elasticsearchr入门

首先在官网下载elasticsearch8.9版本&#xff0c;以及8.9版本的kibana。 解压&#xff0c;点击es8.9bin目录下的elasticsearch.bat文件启动es 如图所示即为成功。 启动之后打开idea&#xff0c;添加依赖 <dependency><groupId>com.fasterxml.jackson.core</g…

SpringBoot 日志文件

一、日志的作用 日志是程序的重要组成部分&#xff0c;想象一下&#xff0c;如果程序报错了&#xff0c;不让你打开控制台看日志&#xff0c;那么你能找到报错的原因吗 答案是否定的&#xff0c;写程序不是买彩票&#xff0c;不能完全靠猜&#xff0c;因此日志对于我们来说&a…

Flutter游戏引擎Flame系列笔记 - 1.Flame引擎概述

Flutter游戏引擎Flame系列笔记 1.Flame引擎概述 - 文章信息 - Author: 李俊才(jcLee95) Visit me at: https://jclee95.blog.csdn.netEmail: 291148484163.com. Shenzhen ChinaAddress of this article:https://blog.csdn.net/qq_28550263/article/details/132119035 【介绍】…

【笔记】第94期-冯永吉-《湖仓集一体关键技术解读》-大数据百家讲坛-厦大数据库实验室主办20221022

https://www.bilibili.com/video/BV1714y1j7AU/?spm_id_from333.337.search-card.all.click&vd_sourcefa36a95b3c3fa4f32dd400f8cabddeaf

[LitCTF 2023]Http pro max plus

打开环境后提示说&#xff0c;只允许在本地访问&#xff0c;本地访问&#xff0c;还是想到了XFF字段 好家伙的&#xff0c;直接被嘲讽&#xff0c;还是了解太少了&#xff0c;都不知道还有没有其他方式可以控制ip地址信息 经过查看wp&#xff0c;得知一种新的方式 Client-IP …