草庐IT

Spring AOP官方文档学习笔记(一)之AOP概述

shame11 2023-03-30 原文

1.AOP简介

(1) Spring的关键组件之一就是AOP框架,它是对Spring IoC的补充(这意味着如果我们的IOC容器不需要AOP的话就不用引入AOP),此外,AOP亦是对OOP的补充,OOP的关注点在于类,而AOP的关注点在于切面,它可以将分散在不同类不同方法中重复的代码逻辑抽取出来,称之为通知(Advice),然后在运行时通过动态代理技术将“通知”组合进原有对象中,这样就能在实现原有预期效果的情况下达到减少代码冗余的目的

(2) 在Spring中,AOP主要用于两大方面,一是提供了声明式服务(比如声明式事物管理:@Transactional注解),二是让用户实现自定义切面,实现代码解偶,用于作为OOP的补充,一个简单的例子如下

//我们想在ExampleA中的每个方法中记录该方法的开始执行时间和结束执行时间
@Component
public class ExampleA {
    //在每个方法业务代码执行前和执行后,都有一个System.out.println用于打印执行时间
    public void register() {
        System.out.println(System.currentTimeMillis() + " 开始执行...");
        //注册相关的业务逻辑代码...
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(System.currentTimeMillis() + " 结束执行...");
    }

    public void sendEmail() {
        System.out.println(System.currentTimeMillis() + " 开始执行...");
        //发送邮件相关的业务逻辑代码...
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(System.currentTimeMillis() + " 结束执行...");
    }
}

//现在,我们期望把"记录该方法的开始执行时间和结束执行时间"这一段代码逻辑提取出来,这样,之后再往ExampleA中添加了新的方法后,我们就不需要再手动添加两个 System.out.println 语句了,而被提取的这一段代码逻辑,被称之为通知(Advice),而通知加上要被增强的业务代码(比如ExampleA中的注册,发送邮件相关的业务逻辑代码)就形成了一个切面
//使用@Aspect注解定义切面类,用于声明定义一个个切面,你可能会疑惑为啥这个注解是由org.aspectj.aspectjrt提供的,那是因为虽然名字叫Spring AOP,给人的感觉好像是Spring单独开发的,但其实是Spring整合了AspectJ AOP框架(用AspectJ AOP的写法和定义方式,底层由Spring封装实现)一同实现的这个AOP功能
@Aspect
@Component
public class Logger {
    //1.重复的代码逻辑(System.out.println),即通知的提取:对应(3)和(5),用于记录方法的开始执行时间和结束执行时间
    //2.指出要被增强的业务代码:假如有两个类ExampleA和ExampleB,我们需要记录ExampleA中每个方法的开始执行时间和结束执行时间,而ExampleB类不用,这就是(1)所发挥的作用
    //3.执行要被增强的业务代码:仅仅指出要被增强的业务代码有哪些还不行,我们还需要调用这些业务代码,从而使它真正的被执行,这是(2)和(4)发挥的作用,(2)中的joinPoint称之为切入点,我们可以将它视为要被增强的业务代码(register,sendEmail)的抽象,而(4)joinPoint.proceed() 就代表着业务代码的执行,如同 thread.start() 代表着线程执行一样
    //4.将通知和被增强的业务代码整个组合起来,称之为切面,即下面的recordTime方法,它就代表一个切面
    @Around("execution(public * cn.example.spring.boke.ExampleA.*(..))")           //(1)
    public void recordTime(ProceedingJoinPoint joinPoint) throws Throwable {       //(2)
        System.out.println(System.currentTimeMillis() + " 开始执行...");            //(3)
        joinPoint.proceed();                                                       //(4)
        System.out.println(System.currentTimeMillis() + " 结束执行...");            //(5)
    }
}

//之后,使用@EnableAspectJAutoProxy开启注解AOP功能
@Configuration
@EnableAspectJAutoProxy
@ComponentScan("cn.example.spring.boke")
public class Config {

}

//然后我们就可以去掉ExampleA的方法中的开始和结束时执行时间打印,因为我们已经把它们抽象提取出来了
@Component
public class ExampleA {
    public void register() {
        //注册相关的业务逻辑代码...
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void sendEmail() {
        //发送邮件相关的业务逻辑代码...
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

//启动容器,执行register和sendEmail方法,可以看见我们的执行时间日志打印了出来,之后添加进ExampleA类中的新方法,也都会进行时间的打印,而无需我们手动的添加两个System.out.println语句,这便是AOP的强大功能
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
ctx.getBean(ExampleA.class).register();
ctx.getBean(ExampleA.class).sendEmail();

2.AOP术语

(1) Aspect:切面 = 通知 + 切入点,如我们例子中所示的recordTime方法

(2) Join point:连接点,可以理解为类中的一个一个方法

(3) Advice:通知,即某个切面要在某个切入点上所要采取的行动,通常而言就是各个类各个方法中重复逻辑的抽象,比如上面这个例子中开始ExampleA中的2个方法中都有开始和结束时间打印,它们是重复的逻辑,因此我们可将它提取出来,称之为通知

(4) Pointcut:切入点,会被增强的连接点,比如上面的例子中我们对ExampleA中的方法register和sendEmail进行了增强,那么这些被增强的方法就有了一个新的称谓,即切入点,而其它的类中的方法没有被增强,还是一个个普通的方法,而这些普通的方法称之为连接点

(5) Introduction:引介,它的概念同通知差不多,只不过通知是针对切入点所提供的增强的逻辑,而引介是针对Class类,它可以在不修改原有类的代码的前提下,在运行期为原始类动态添加新的属性/方法

(6) Target object:目标对象,即会被增强的方法所属的对象,如上面例子中的ExampleA对象

(7) AOP proxy:AOP代理对象,是在目标对象上被增强了过后所产生的新对象,Spring采用动态代理技术来实现AOP,底层实现为JDK动态代理或CGLIB动态代理

(8) Weaving:织入,它代表一个动作,即将Advice组合进Target object中,从而产生AOP proxy这么的一个过程

3.AOP通知类型

(1) 根据通知与切入点的执行关系,Spring提供了5种通知类型,如下:

  • Before advice:前置通知,即通知在切入点执行之前执行

  • After returning advice:返回通知,即通知在切入点"正常"执行之后执行

  • After throwing advice:异常通知,即通知在切入点触发异常之后执行

  • After (finally) advice:后置通知,即无论切入点以何种方式执行(正常或异常),通知都会执行

  • Around advice:环绕通知,既可以在切入点之前执行通知,又可以在切入点之后执行,甚至可以不用执行切入点,它是最为强大的通知,我们上面例子中的recordTime就使用的是环绕通知

注意:After returning advice与After throwing advice两者是互斥的,因为如果方法调用成功无异常,则会有返回值;如果方法抛出了异常,则不会有返回值,因此这两个通知只会有一个执行

(2) Spring建议能使用具体的通知就去使用具体的通知,比如能用Before advice的情况下就不用去使用Around advice

4.Spring AOP特征

(1) Spring AOP是基于纯java实现的,不需要特殊的编译过程也不需要去控制类加载器的层次结构

(2) Spring AOP目前的切入点类型只能是方法,不能是变量或其它的类型,如果我们想要切入变量,可以使用AspectJ AOP框架,Spring与AspectJ无缝集成

(3) Spring AOP与IOC容器紧密集成,如果我们只是想要单独使用一个AOP框架,那么可以使用AspectJ

(4) Spring AOP支持基于注解的配置,也支持基于xml文件的配置,同IOC一样

(5) Spring AOP默认使用jdk动态代理,因此只要一个类实现了某个接口,那么它就能被代理,但如果我们的某个类没有实现接口,则会采用cglib动态代理来生成代理对象,同时我们也可以强制使用cglib动态代理作为默认选项

有关Spring AOP官方文档学习笔记(一)之AOP概述的更多相关文章

  1. Matlab imread()读到了什么 (浅显 当复习文档了) - 2

    matlab打开matlab,用最简单的imread方法读取一个图像clcclearimg_h=imread('hua.jpg');返回一个数组(矩阵),往往是a*b*cunit8类型解释一下这个三维数组的意思,行数、数和层数,unit8:指数据类型,无符号八位整形,可理解为0~2^8的数三个层数分别代表RGB三个通道图像rgb最常用的是24-位实现方法,即RGB每个通道有256色阶(2^8)。基于这样的24-位RGB模型的色彩空间可以表现256×256×256≈1670万色当imshow传入了一个二维数组,它将以灰度方式绘制;可以把图像拆分为rgb三层,可以以灰度的方式观察它figure(1

  2. LC滤波器设计学习笔记(一)滤波电路入门 - 2

    目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称

  3. CAN协议的学习与理解 - 2

    最近在学习CAN,记录一下,也供大家参考交流。推荐几个我觉得很好的CAN学习,本文也是在看了他们的好文之后做的笔记首先是瑞萨的CAN入门,真的通透;秀!靠这篇我竟然2天理解了CAN协议!实战STM32F4CAN!原文链接:https://blog.csdn.net/XiaoXiaoPengBo/article/details/116206252CAN详解(小白教程)原文链接:https://blog.csdn.net/xwwwj/article/details/105372234一篇易懂的CAN通讯协议指南1一篇易懂的CAN通讯协议指南1-知乎(zhihu.com)视频推荐CAN总线个人知识总

  4. 深度学习部署:Windows安装pycocotools报错解决方法 - 2

    深度学习部署:Windows安装pycocotools报错解决方法1.pycocotools库的简介2.pycocotools安装的坑3.解决办法更多Ai资讯:公主号AiCharm本系列是作者在跑一些深度学习实例时,遇到的各种各样的问题及解决办法,希望能够帮助到大家。ERROR:Commanderroredoutwithexitstatus1:'D:\Anaconda3\python.exe'-u-c'importsys,setuptools,tokenize;sys.argv[0]='"'"'C:\\Users\\46653\\AppData\\Local\\Temp\\pip-instal

  5. 阿里云RDS——产品系列概述 - 2

    基础版云数据库RDS的产品系列包括基础版、高可用版、集群版、三节点企业版,本文介绍基础版实例的相关信息。RDS基础版实例也称为单机版实例,只有单个数据库节点,计算与存储分离,性价比超高。说明RDS基础版实例只有一个数据库节点,没有备节点作为热备份,因此当该节点意外宕机或者执行重启实例、变更配置、版本升级等任务时,会出现较长时间的不可用。如果业务对数据库的可用性要求较高,不建议使用基础版实例,可选择其他系列(如高可用版),部分基础版实例也支持升级为高可用版。基础版与高可用版的对比拓扑图如下所示。优势 性能由于不提供备节点,主节点不会因为实时的数据库复制而产生额外的性能开销,因此基础版的性能相对于

  6. ruby - 我正在学习编程并选择了 Ruby。我应该升级到 Ruby 1.9 吗? - 2

    我完全不是程序员,正在学习使用Ruby和Rails框架进行编程。我目前正在使用Ruby1.8.7和Rails3.0.3,但我想知道我是否应该升级到Ruby1.9,因为我真的没有任何升级的“遗留”成本。缺点是什么?我是否会遇到与普通gem的兼容性问题,或者甚至其他我不太了解甚至无法预料的问题? 最佳答案 你应该升级。不要坚持从1.8.7开始。如果您发现不支持1.9.2的gem,请避免使用它们(因为它们很可能不被维护)。如果您对gem是否兼容1.9.2有任何疑问,您可以在以下位置查看:http://www.railsplugins.or

  7. Ruby 等同于 Sphinx 文档生成器? - 2

    Ruby有一些不错的文档生成器,例如Yard、rDoc,甚至Glyph。问题是Sphinx可以做网站、PDF、epub、LaTex等。它在重组文本中完成所有这些事情。在Ruby世界中有替​​代方案吗?也许是程序的组合?如果我也能使用Markdown就更好了。 最佳答案 自1.0版以来,Sphinx有了“域”的概念,它是从Python和/或C以外的语言标记代码实体(如方法调用、对象、函数等)的方法。有一个rubydomain,所以你可以只使用Sphinx本身。您唯一会缺少的(我认为)是Sphinx使用autodoc从源代码自动创建文档

  8. ruby - 我如何学习 ruby​​ 的正则表达式? - 2

    如何学习ruby​​的正则表达式?(对于假人) 最佳答案 http://www.rubular.com/在Ruby中使用正则表达式时是一个很棒的工具,因为它可以立即将结果可视化。 关于ruby-我如何学习ruby​​的正则表达式?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/1881231/

  9. ruby-on-rails - 在 irb 中阅读文档 - 2

    我怀念ipython的一件事是它有一个?为特定功能挖掘文档的运算符。我知道ruby​​有一个类似的命令行工具,但是我在irb中调用它非常不方便。ruby/irb有类似的东西吗? 最佳答案 Pry是IPython的Ruby版本,它支持?命令来查找有关方法的文档,但语法略有不同:pry(main)>?File.dirnameFrom:file.cinRubyCore(CMethod):Numberoflines:6visibility:publicsignature:dirname()Returnsallcomponentsofthef

  10. ruby - 使用 Nokogiri 和 Ruby 从 html 文档获取链接和 href 文本? - 2

    我正在尝试使用nokogirigem提取页面上的所有url及其链接文本,并将链接文本和url存储在散列中。FooBar我想回去{"Foo"=>"#foo","Bar"=>"#bar"} 最佳答案 这是一个单行:Hash[doc.xpath('//a[@href]').map{|link|[link.text.strip,link["href"]]}]#=>{"Foo"=>"#foo","Bar"=>"#bar"}拆分一点可以说更具可读性:h={}doc.xpath('//a[@href]').eachdo|link|h[link.t

随机推荐