深度理解 Spring AOP

一、什么是AOP(面向切面编程)?🍉

AOP 为 Aspect Oriented Programming 的缩写,意思为面向切面编程,是通过预编译方式 和运行期 动态代理 实现程序功能的统一维护的一种技术。

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

二、AOP 的作用及其优势🍉

作用:在程序运行期间,在不修改源码的情况下对方法进行功能增强

优势:减少重复代码,提高开发效率,并且便于维护

三、AOP 的底层实现🍉

实际上,AOP 的底层是通过 Spring 提供的的动态代理技术实现 的。在运行期间,Spring通过动态代理技术动态的生成代理对象,代理对象方法执行时进行增强功能的介入,在去调用目标对象的方法,从而完成功能的增强。

动态代理的作用

  • 1)在目标类源代码不改变的情况下,增加功能。
  • 2)减少代码的重复
  • 3)专注业务逻辑代码
  • 4)解耦合,让你的业务功能和日志,事务非业务功能分离。

四、AOP 相关概念🍉

Spring 的 AOP 实现底层就是对动态代理的代码进行了封装 ,封装后我们只需要对需要关注的部分进行代码编写,并通过配置的方式完成指定目标的方法增强。

在正式讲解 AOP 的操作之前,我们必须理解 AOP 的相关术语,常用的术语如下:

  1. 切面(Aspect)
    一个切面就是一个代理对象= 要为那些类生成代理+要添加的额外功能是那些

在这里插入图片描述

  1. 切入点(pointcut):将来要为那些类生成代理对象
  2. 通知/ 增强(advice):就是要添加的额外功能

生活案例:给面包之间涂果酱
在这里插入图片描述

注意:切面(Aspect)=切入点(pointcut)+通知点(advice,额外功能)

在这里插入图片描述

  1. AOP中的(面向切面编程)通知
    前置通知:目标方法之前的额外功能 MethodBeforeAdvice

环绕通知:它是spring框架为我们提供的一种可以在代码中手动控制增强方法何时执行的方式 MethodInterceptor(Interceptor [ɪntəˈsɛptə]拦截器)

后置通知:目标方法之后的额外功能 AfterReturningAdvice(returning[rɪˈtɜːrnɪŋ])

异常通知:执行异常的额外功能 ThrowsAdvice

最终通知:一定执行的额外功能

在这里插入图片描述

  1. Target(目标对象):代理的目标对象
  2. 代理(Proxy[ˈprɑːksi]):一个类被AOP注入增强后,就产生一个结果代理类
  3. 连接点(joinPoint):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法(额外功能),因为spring只支持方法类型的连接点

五、切面编程步骤🍉

1、五种通知类🥝

AOP 切面=切入点+通知

前置通知:MethodBeforeAdvice
后置通知:AfterReturnAdvice
环绕通知:MethodInterceptor
异常通知:ThrowsAdvice(throw [θroʊz])
最终通知

通知的配置语法:

<aop : 通知类型 method=“切面类中方法名” pointcut=“切点表达式"> </aop:通知类型>
  • 切点表达式的抽取

当多个增强的切点表达式相同时,可以将切点表达式进行抽取,在增强中使用 pointcut-ref 属性代替 pointcut 属性来引用抽取后的切点表达式。

<aop:config><!--引用myAspect的Bean为切面对象--><aop:aspect ref="myAspect"><aop:pointcut id="myPointcut" expression="execution(* com.tjcu.aop.*.*(..))"/><aop:before method="before" pointcut-ref="myPointcut"></aop:before></aop:aspect>
</aop:config>

aop植入的配置

<aop:config><aop:aspect ref=“切面类”><aop:before method=“通知方法名称” pointcut=“切点表达式"></aop:before></aop:aspect>
</aop:config>

切点表达式的写法:

execution([修饰符] 返回值类型 包名.类名.方法名(参数))

2、AOP的开发步骤🥝

1、开发目标类

2、开发通知类,确定额外功能

3、管理通知类

4、配置切入点 确定要为那些类添加额外功能

5、将目标类和切面类的对象创建权交给 spring

6、组装切面 切入点+通知(额外功能)

3、AOP编程所需要的依赖🥝

这里是引用引入依赖
spring-aop
spring-expression
spring-aspects

六、AOP实现前置通知案例🍉

1、目标接口类🥝

public interface CityService {public void login();public  void add(String name);
}

2、目标实现类(核心功能)🥝

/*** @Description:目标对象*/
public class CityServiceImpl implements CityService{@Overridepublic void login() {//前置通知:System.out.println("嘻嘻哈哈");//执行核心的业务逻辑 调用DaoSystem.out.println("登录调用Dao");}@Overridepublic void add(String name) {//前置通知:System.out.println("嘻嘻哈哈");//执行核心的业务逻辑 调用DaoSystem.out.println("添加调用Dao");}
}

3、前置通知(额外功能)动态代理代码🥝

/*** @Description: 通知类。额外功能*/
public class MyBeforeAdvice implements MethodBeforeAdvice {/*** 额外功能书写的方法* 参数1:代理对象当前调用的方法* 参数2:当前代理对象调用的方法的参数* 参数3:目标对象(被代理的对象)* @param method* @param objects* @param o* @throws Throwable*/@Overridepublic void before(Method method, Object[] objects, Object o) throws Throwable {System.out.println("嘻嘻哈哈");}
}

4、将目标类和切面类的对象创建权交给 spring🥝

<!--管理目标对象--><bean class="before.CityServiceImpl" id="cityService"></bean><!--管理通知类 动态代理实现AOP--><bean id="myBeforeAdvice" class="before.MyBeforeAdvice"></bean>

5、在 spring.xml 中配置织入关系(前置功能)🥝

<!--组装切面--><aop:config><!--配置切入点id:切入点的唯一标识expression:切入点表达式,为那些类添加额外功能execution() 切入点表达式的一种 精确到要添加到额外功能的方法*:通配空格:before.CityServiceImpl.*:所有方法(..):所有参数--><aop:pointcut id="pc1" expression="execution(* before.CityServiceImpl.*(..))"/><!--组装切面=切入点+通知advice-ref:通知的idpointcut-ref:切入点的id--><aop:advisor advice-ref="myBeforeAdvice" pointcut-ref="pc1"></aop:advisor></aop:config>

6、测试代码🥝

  @Testpublic void testMethodBeforeAdvice() {//启动工厂ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("before/spring.xml");//获取组件 目标对象就是代理对象CityService cityService = (CityService) ctx.getBean("cityService");//目标对象就是代理对象  class com.sun.proxy.$Proxy4System.out.println(cityService.getClass());    //调用方法,通过代理类调用目标类cityService.add("123");}

注意:获取组件时,目标对象就是代理对象

在这里插入图片描述

七、Spring中的环绕通知案例🍉

1、dao层接口🥝

public interface StudentDao {/*** 登录* @param name*/public void login(String name);/*** 分页* @param name* @return*/public String pageShow(String name);
}

2、dao层实现类🥝

public class StudentDaoImpl implements StudentDao{@Overridepublic void login(String name) {//循环10000次for (int i = 0; i < 10000; i++) {}System.out.println("数据库实现登录");}@Overridepublic String pageShow(String name) {//循环10000次for (int i = 0; i < 10000; i++) {}System.out.println("数据库实现分页");return name;}
}

3、目标接口类🥝

public interface StudentService {/*** 登录* @param name*/public void login(String name);/*** 分页* @param name* @return*/public String pageShow(String name);
}

4、目标实现类🥝

public class StudentServiceImpl implements StudentService{private StudentDao studentDao;public void setStudentDao(StudentDao studentDao) {this.studentDao = studentDao;}@Overridepublic void login(String name) {System.out.println("登录日志");studentDao.login(name);}@Overridepublic String pageShow(String name) {System.out.println("分页日志");String s = studentDao.pageShow(name);return s;}
}

5、环绕通知🥝

核心方法: Object proceed = methodInvocation.proceed(); 放行
public class StudentAroundAdvice implements MethodInterceptor {/*** 参数 内部封装者当前的代理对象 方法的参数,执行方法等** @param methodInvocation* @return* @throws Throwable*/@Overridepublic Object invoke(MethodInvocation methodInvocation) throws Throwable {//1.控制事务System.out.println("控制事务");//method Invocation 方法调用System.out.println("当前调用方法的名字" + methodInvocation.getMethod().getName());//arguments参数 methodInvocation:方法调用System.out.println("当前的参数为:" + methodInvocation.getArguments()[0]);System.out.println("--------------");//记录当前的时间 单位毫秒long begin = System.currentTimeMillis();System.out.println("调用查询的数据库");//放行,执行目标方法 proceed:继续做proceedObject proceed = methodInvocation.proceed();//记录结束的时间 单位毫秒long end = System.currentTimeMillis();System.out.println("dao执行所用时间" + (end - begin));return proceed;}
}

6、将目标类和切面类的对象创建权交给 spring🥝

  <!--管理dao组件--><bean id="studentDao" class="com.tjcu.dao.StudentDaoImpl"></bean><!--管理Service组件/目标对象--><bean id="studentService" class="com.tjcu.service.StudentServiceImpl"><!--注入值--><property name="studentDao" ref="studentDao"></property></bean><!--管理通知组件--><bean id="studentAroundAdvice" class="com.tjcu.advice.StudentAroundAdvice"></bean>

7、在 applicationContext.xml 中配置织入关系,aop相关配置🥝

 <!--aop相关配置  切面=切点+环绕通知--><aop:config><!--切入点 execution:[eksɪˈkjuːʃn]执行--><aop:pointcut id="pointcut" expression="execution(* com.tjcu.service.StudentServiceImpl.*(..))"/><!--组装切面  advisor[ [ædˈvaɪzər]]:顾问  advice-ref:通知  pointcut-ref:切入点--><aop:advisor advice-ref="studentAroundAdvice" pointcut-ref="pointcut"></aop:advisor></aop:config>

8、测试代码🥝

  @Testpublic void AroundAdviceTest() {//启动工厂ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("com/tjcu/Spring/ApplicationContext.xml");//获取组件 目标对象就是代理对象StudentService studentService = (StudentService) context.getBean("studentService");//调用方法,通过代理类调用目标类studentService.pageShow("通过代理类调用调用目标类");}

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

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

相关文章

代码随想录额外题目| 数组02 ●189旋转数组 ●724寻找数组中心索引

#189旋转数组 很快写出来但是用了个新数组&#xff0c;不好 void rotate(vector<int>& nums, int k) {vector<int> res(nums.size(),0);for(int i0;i<nums.size();i){int newiik;if(newi>nums.size()-1) newinewi%nums.size();res[newi]nums[i];}numsr…

vue组件(个人学习笔记三)

目录 友情提醒第一章、vue的组件1.1&#xff09;什么是SPA应用1.2&#xff09;vue的组件简介1.3&#xff09;vue工程中的main.js文件 第二章、Vue组件的使用2.1&#xff09;一般组件的自定义2.2&#xff09;注册组件&#xff1a;全局注册和局部注册2.2.1&#xff09;全局注册&a…

和chatgpt学架构04-路由开发

目录 1 什么是路由2 如何设置路由2.1 安装依赖2.2 创建路由文件2.3 创建首页2.4 编写HomePage2.5 更新路由配置2.6 让路由生效 3 测试总结 要想使用vue实现页面的灵活跳转&#xff0c;其中路由配置是必不可少的&#xff0c;我们在做开发的时候&#xff0c;先需要了解知识点&…

Vue--》打造个性化医疗服务的医院预约系统(二)

今天开始使用 vue3 + ts 搭建一个医院预约系统的前台页面,因为文章会将项目的每一个地方代码的书写都会讲解到,所以本项目会分成好几篇文章进行讲解,我会在最后一篇文章中会将项目代码开源到我的GithHub上,大家可以自行去进行下载运行,希望本文章对有帮助的朋友们能多多关…

基于Python+多层RNN+Tensorflow藏头诗与歌词智能生成-深度学习算法应用(含全部工程源码)+训练数据集

目录 前言总体设计系统整体结构图系统流程图 运行环境Python 环境Tensorflow 环境PyCharm环境 模块实现古诗生成1. 数据预处理2. 模型构建3. 模型训练及保存4. 使用模型生成古诗5. 产生藏头诗6. 用词云展示生成的古诗 歌词生成1. 数据预处理2. 模型构建3. 模型训练并保存4. 生成…

基于Kitti数据集的智能驾驶目标检测系统(PyTorch+Pyside6+YOLOv5模型)

摘要&#xff1a;基于Kitti数据集的智能驾驶目标检测系统可用于日常生活中检测与定位行人&#xff08;Pedestrian&#xff09;、面包车&#xff08;Van&#xff09;、坐着的人&#xff08;Person Sitting&#xff09;、汽车&#xff08;Car&#xff09;、卡车&#xff08;Truck…

【字符流】案例:文件到集合

案例&#xff1a;文件到集合 1.需求&#xff1a; 把文本文件中的数据读取到集合&#xff0c;并遍历集合。要求&#xff1a;文件中的每一行数据是一个集合元素 2.思路 创建字符缓冲输入流对象创建ArrayList集合对象调用字符缓冲输入流对象的方法读数据把读取到的字符串数据存…

Java IO

stream 流 是一种抽象概念&#xff0c;它代表了数据的无结构化传递。按照流的方式进行输入输出&#xff0c;数据被当成 无结构的字节序或字符序列。从流中取得数据的操作称为提取操作&#xff0c;而向流中添加数据的操作称为插入操作。用来进行输入输出操作的流就称为IO 流。换…

spring复习:(50)@Configuration注解配置的singleton的bean是什么时候被创建出来并缓存到容器的?

一、主类&#xff1a; 二、配置类&#xff1a; 三、singleton bean的创建流程 运行到context.refresh(); 进入refresh方法&#xff1a; 向下运行到红线位置时&#xff1a; 会实例化所有的singleton bean.进入finisheBeanFactoryInitialization方法&#xff1a; 向下拖动代…

JavaWeb课程设计项目实战(06)——项目编码实践3

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl 在本教程教程中&#xff0c;我们实现学生列表的显示。 Student 请在bean包下创建Student类&#xff0c;代码如下&#xff1a; package com.cn.bean; /*** 本文作者&#…

数据科学团队的角色分工

描述数据科学团队中角色分工常用下列维度。进一步以数据可视化直观表达的能力雷达图: ML Ops - 机器学习运维 Data Pipelines - 数据流水线 Database - 数据库 Data Viz - 数据可视化 Storytelling - 数据讲故事 Business Insights - 业务洞察 Reporting - 报告 Experimentatio…

【100天精通python】Day10:函数的创建和调用,参数传递,返回值,变量作用域以及匿名函数

目录 1. 函数的创建和调用 1.1 函数的创建 1.2 调用函数 2 参数传递 2.1 传递方式 2.2 形参和实参 2.3 位置参数 2.4 关键字参数 2.5 可变参数 2.6 为参数设置默认值 3 返回值 4 变量的作用域 4.1 局部变量 4.2 嵌套变量 4.3 全局变量 5 匿名函数&#xff0…

X86设备启动过程

文章目录 一、电源自检二、BIOS自检三、引导设备选择四、主引导记录4.1 0x7c0 五、加载操作系统 x86计算机启动过程&#xff0c;主要分为这几个阶段&#xff1a;电源自检、BIOS自检、引导设备的选择、主引导记录、加载操作系统。 一、电源自检 当我们按下开关键后&#xff0c;…

uni-app image加载错误 404 替换为默认图片

双层v-for 使用item修改 aitem.cat_icon || defaultPic绑定图片src属性为aitem.cat_icon 如果aitem.cat_icon的值为空字符串或undefined&#xff0c;那么默认图片defaultPic被显示出来当图片加载错误时,触发handleImageError方法,将aitem传进去 <!-- 页面--><view …

Java 知识合集 | 多线程与并发

&#x1f468;&#x1f3fb;‍&#x1f4bb; 热爱摄影的程序员 &#x1f468;&#x1f3fb;‍&#x1f3a8; 喜欢编码的设计师 &#x1f9d5;&#x1f3fb; 擅长设计的剪辑师 &#x1f9d1;&#x1f3fb;‍&#x1f3eb; 一位高冷无情的编码爱好者 大家好&#xff0c;我是 DevO…

直接插入排序、希尔排序、直接选择排序、堆排序、冒泡排序——“数据结构与算法”

各位CSDN的uu们你们好呀&#xff0c;今天小雅兰的内容是数据结构与算法啦&#xff0c;是排序&#xff01;&#xff01;&#xff01;下面&#xff0c;让我们进入七大排序的世界吧&#xff01;&#xff01;&#xff01; 排序的概念及其运用 排序的概念 排序&#xff1a;所谓排序…

19 数组静态初始化练习

语法&#xff1a; 数据类型[ ] 数组名称 {元素1&#xff0c;元素2&#xff0c;元素3}; public class Demo1 {public static void main(String[] args) {int[] arr {0,1,2,3};System.out.println(arr);System.out.println(arr[0]);System.out.println(arr[1]);System.out.pri…

SpringBoot中间件—ORM(Mybatis)框架实现

目录 定义 需求背景 方案设计 代码展示 UML图 实现细节 测试验证 总结 源码地址&#xff08;已开源&#xff09;&#xff1a;https://gitee.com/sizhaohe/mini-mybatis.git 跟着源码及下述UML图来理解上手会更快&#xff0c;拒绝浮躁&#xff0c;沉下心来搞 定义&#x…

【C++杂货铺】拷贝构造函数

&#x1f4d6;定义 拷贝构造函数是构造函数的一个重载&#xff0c;它的本质还是构造函数&#xff0c;那就意味着&#xff0c;只有在创建对象的时候&#xff0c;编译器才会自动调用它&#xff0c;那他和普通的构造函数有什么区别呢&#xff1f; 拷贝构造函数&#xff0c;是创建…

【数学建模】为什么存在最优策略?

一、说明 在进行优化回归过程&#xff0c;首先要看看是否存在最优策略&#xff1f; 在有限马尔可夫决策过程 &#xff08;MDP&#xff09; 中&#xff0c;最优策略被定义为同时最大化所有状态值的策略。换句话说&#xff0c;如果存在最优策略&#xff0c;则最大化状态 s 值的策…