深度理解 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{
    @Override
    public void login() {
        //前置通知:System.out.println("嘻嘻哈哈");

        //执行核心的业务逻辑 调用Dao
        System.out.println("登录调用Dao");
    }

    @Override
    public void add(String name) {
        //前置通知:System.out.println("嘻嘻哈哈");
        //执行核心的业务逻辑 调用Dao
        System.out.println("添加调用Dao");
    }
}

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

/**
 * @Description: 通知类。额外功能
 */
public class MyBeforeAdvice implements MethodBeforeAdvice {
    /**
     * 额外功能书写的方法
     * 参数1:代理对象当前调用的方法
     * 参数2:当前代理对象调用的方法的参数
     * 参数3:目标对象(被代理的对象)
     * @param method
     * @param objects
     * @param o
     * @throws Throwable
     */
    @Override
    public 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:通知的id
      pointcut-ref:切入点的id
    -->
        <aop:advisor advice-ref="myBeforeAdvice" pointcut-ref="pc1"></aop:advisor>
    </aop:config>

6、测试代码🥝

  @Test
    public void testMethodBeforeAdvice() {
        //启动工厂
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("before/spring.xml");
        //获取组件 目标对象就是代理对象
        CityService cityService = (CityService) ctx.getBean("cityService");
        
        //目标对象就是代理对象  class com.sun.proxy.$Proxy4
        System.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{
    @Override
    public void login(String name) {
        //循环10000次
        for (int i = 0; i < 10000; i++) {

        }
        System.out.println("数据库实现登录");

    }

    @Override
    public 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;
    }

    @Override
    public void login(String name) {
        System.out.println("登录日志");
      studentDao.login(name);
    }

    @Override
    public 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
     */
    @Override
    public 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:继续做proceed
        Object 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、测试代码🥝

  @Test
    public void AroundAdviceTest() {
        //启动工厂
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("com/tjcu/Spring/ApplicationContext.xml");

        //获取组件 目标对象就是代理对象
        StudentService studentService = (StudentService) context.getBean("studentService");

        //调用方法,通过代理类调用目标类
        studentService.pageShow("通过代理类调用调用目标类");
    }

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

深度理解 Spring IOC

Spring容器高层视图 Spring 启动时读取应用程序提供的Bean配置信息&#xff0c;并在Spring容器中生成一份相应的Bean配置注册表&#xff0c;然后根据这张注册表实例化Bean&#xff0c;装配好Bean之间的依赖关系&#xff0c;为上层应用提供准备就绪的运行环境。 Bean缓存池&…

OJ练习第143题——二叉树展开为链表

二叉树展开为链表 力扣链接&#xff1a;114. 二叉树展开为链表 题目描述 给你二叉树的根结点 root &#xff0c;请你将它展开为一个单链表&#xff1a; 展开后的单链表应该同样使用 TreeNode &#xff0c;其中 right 子指针指向链表中下一个结点&#xff0c;而左子指针始终…

postman批量执行请求,通过json传参

1、创建请求 "authUid":"{{authUid}}", 加粗为需要替换的参数 2、组装JSON 可通过Excel自动填充功能构造数据 [ {"authUid":"18700000001"}, {"authUid":"18725929202"} ] 3、批量执行请求配置 4、然后start r…

PuTTY连接服务器报错Connection refused

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

Cloudreve搭建云盘系统,并实现随时访问

文章目录 1、前言2、本地网站搭建1.环境使用2.支持组件选择3.网页安装4.测试和使用5.问题解决 3、本地网页发布1.cpolar云端设置2.cpolar本地设置 4、公网访问测试5、结语 1、前言 自云存储概念兴起已经有段时间了&#xff0c;各互联网大厂也纷纷加入战局&#xff0c;一时间公…

RabbitMQ惰性队列使用

说明&#xff1a;惰性队列是为了解决消息堆积问题&#xff0c;当生产者生产消息的速度远高于消费者消费消息的速度时&#xff0c;消息会大量的堆积在队列中&#xff0c;而队列中存放的消息数量是有限的&#xff0c;当超出数量时&#xff0c;会造成消息的丢失。而扩容队列&#…

uniapp app运行到ios详细流程

uniapp运行到IOS真机调试&#xff08;windows系统&#xff09; 工具步骤1.首先数据线连接电脑和手机2.右键点击桌面上的HBuilder&#xff0c;打开文件所在位置3.打开HBuilder编辑器里要运行的项目&#xff0c;点击运行>运行到手机或模拟器>运行到IOS APP基座>勾选你的…

LangChain大型语言模型(LLM)应用开发(五):评估

LangChain是一个基于大语言模型&#xff08;如ChatGPT&#xff09;用于构建端到端语言模型应用的 Python 框架。它提供了一套工具、组件和接口&#xff0c;可简化创建由大型语言模型 (LLM) 和聊天模型提供支持的应用程序的过程。LangChain 可以轻松管理与语言模型的交互&#x…

【每日一题】——C - Standings(AtCoder Beginner Contest 308 )

&#x1f30f;博客主页&#xff1a;PH_modest的博客主页 &#x1f6a9;当前专栏&#xff1a;每日一题 &#x1f48c;其他专栏&#xff1a; &#x1f534; 每日反刍 &#x1f7e1; C跬步积累 &#x1f7e2; C语言跬步积累 &#x1f308;座右铭&#xff1a;广积粮&#xff0c;缓称…

ArcGIS、ENVI、InVEST、FRAGSTATS等多技术融合提升环境、生态、水文、土地、土壤、农业、大气等领域的数据分析

一、 空间数据获取与制图 1.1 软件安装与应用讲解 1.2 空间数据介绍 1.3海量空间数据下载 1.4 ArcGIS软件快速入门 1.5 Geodatabase地理数据库 二、 ArcGIS专题地图制作 2.1专题地图制作规范 2.2 空间数据的准备与处理 2.3 空间数据可视化&#xff1a;地图符号与注记 …

十一、正则表达式详解:掌握强大的文本处理工具(三)

文章目录 &#x1f340;贪婪模式&#x1f340;应用的场景&#x1f340;总结 &#x1f340;非贪婪模式&#x1f340;应用的场景&#x1f340;总结 &#x1f340;贪婪模式与非贪婪模式在爬虫的应用&#x1f340;转义字符&#x1f340;正则表达式常见函数 &#x1f340;贪婪模式 在…

如何查看小程序的APPID和AppSecret

小程序APPID可以在手机上打开小程序后&#xff0c;点击右上角三点&#xff1a; 然后点击中间位置的小程序名称&#xff0c;进入小程序介绍页面&#xff1a; 点击“更多资料”后&#xff0c;进入页面就可以看到上方有APPID&#xff1a; 另一种方法&#xff1a; 在微信公众平台登…

【iOS】CALayer的理解与简单使用

文章目录 前言一、UIView与CALayer的关系二、CALayer的简单使用1.圆角与裁剪2.contents3.边框属性 总结 前言 在实现网易云音乐demo开发的过程中&#xff0c;通过查阅网上资料&#xff0c;发现了我们可以对我们的视图进行裁剪来实现美观的体现&#xff0c;例如这样&#xff1a…

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

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

C语言程序运行需要的两大环境《C语言进阶》

目录 程序的翻译环境和执行环境 翻译环境分为两部分&#xff0c;编译链接 第一步&#xff1a;预编译&#xff08;预处理&#xff09; 第二步&#xff0c;编译 第三步&#xff1a;汇编 关于运行环境分为四点&#xff1a; 关于链接库 程序的翻译环境和执行环境 在 ANSI C(标…

C语言第七课----------函数的定义及使用--------C语言重要一笔

作者前言 个人主页::小小页面 gitee页面:秦大大 一个爱分享的小博主 欢迎小可爱们前来借鉴 __________________________________________________________ 目录 1.函数是什么 2. 库函数 3. 自定义函数 4. 函数参数 5. 函数调用 6. 函数的嵌套调用和链式访问 7. 函数的声…

海康摄像头开发笔记(一):连接防爆摄像头、配置摄像头网段、设置rtsp码流、播放rtsp流、获取rtsp流、调优rtsp流播放延迟以及录像存储

文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/131679108 红胖子(红模仿)的博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬结…

物联网如何为智慧城市提供动力

智慧城市可以创造一个基础设施顺畅、效率提升的乌托邦&#xff0c;改善城市地区的生活质量&#xff0c;促进当地经济发展。 其影响意义重大&#xff0c;预计到 2024 年智慧城市基础设施的收入将超过 1000 亿美元。 从改善公共交通到解决犯罪问题和提高能源效率——应有尽有&am…

Fatdog64 Linux 814发布

导读Fatdog64 Linux是一个小型、桌面、64位的Linux发行版。 最初是作为Puppy Linux的衍生品&#xff0c;并增加了一些应用程序。该项目最新的版本&#xff0c;Fatdog64 814&#xff0c;是8xx系列的最后一个版本&#xff0c;未来的版本将转向9xx基础。 尽管它是该系列的最后一个…

【Ranking】50 Matplotlib Visualizations, Python实现,源码可复现

详情请参考博客: Top 50 matplotlib Visualizations 因编译更新问题&#xff0c;本文将稍作更改&#xff0c;以便能够顺利运行。 1 Ordered Bar Chart 有序条形图有效地传达项目的排名顺序。但是&#xff0c;将图表上方的指标值相加&#xff0c;用户将从图表本身获得准确的信息…