千家信息网

如何用最通俗的方法讲spring中的AOP

发表于:2026-11-03 作者:千家信息网编辑
千家信息网最后更新 2026年11月03日,这篇文章给大家介绍如何用最通俗的方法讲spring中的AOP,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。写这个系列的目的(可以跳过不看)自己写这个系列的目的,是因为自己是个比较
千家信息网最后更新 2026年11月03日如何用最通俗的方法讲spring中的AOP

这篇文章给大家介绍如何用最通俗的方法讲spring中的AOP,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。

写这个系列的目的(可以跳过不看)

自己写这个系列的目的,是因为自己是个比较笨的人,我曾一度怀疑自己的智商不适合干编程这个行业.因为在我自学的过程中,看过无数别人的文章,博客,心得.可那一大堆的术语,技术基础,根本就难以阅读理解.再加上网上文章良莠不齐,很多文章都是写给已经懂的人看的. 一个例子: Q:什么是控制反转. A:将对在自身对象中的一个内置对象的控制反转,反转后不再由自己本身的对象进行控制这个内置对象的创建,而是由第三方系统去控制这个内置对象的创建. ??? 这种专业的解释,在我看来就不是给学习的人看的,而是给已经有相当经验的人读的. 难道不能用更加感性的解释这些东西吗? 编程语言作为一门语言,我认为一定程度上是相当感性的东西,既然我们互相说话时可以听懂,那么,这些技术就可以在一定程度上,完全可以用感性的方式解释.

AOP

AOP是面向切面. 得,说了白说. 什么是面向切面?这是个问题,但我们先不着急回答.

一. 举个例子

我们的代码结构,大多都是MVC模式.那我们用MVC举个简单的例子. MVC模式的话,最常用的spring + springMVC + Mybatis,大多数是下面的样子.

┌───────────────┐|   view        | 用户界面├───────────────┤|   Controller  | 控制层├───────────────┤|   Service     | 服务层,也叫业务层├───────────────┤|   Dao         | 持久层,也叫数据访问层├───────────────┤|   Database    | 数据库└───────────────┘

外加Model作为数据载体在各个层之间传来传去 只要是接触过JavaWeb开发的,上面都看的懂,如果这个不懂暂时还是不要深究这些知识,先以框架应用为主.

这个Service是个UserService,作用只有一个:保存用户.

┌────────────────────┐|   view             |├────────────────────┤|   Controller       |├────────────────────┤|   UserService.add  | ───> 新增用户├────────────────────┤|   Dao              |├────────────────────┤|   Database         |└────────────────────┘

现在,有需求要我们在Service中增加日志,开发这个功能的同事不在了,经理要求我们来做这个需求. 那么根据面向对象的理念. 调用service方法前要调用一个日志方法,调用完后要调用一个日志方法,那么代码中就要增加两行代码. 于是,代码成了这样:

┌────────────────────┐|   view             |├────────────────────┤|   Controller       |├────────────────────┤|   log.info...      | ───> 调用日志|   UserService.add  | ───> 新增用户|   log.info...      | ───> 调用日志├────────────────────┤|   Dao              |├────────────────────┤|   Database         |└────────────────────┘

这么写正确吗? 答案是当然正确,需求解决了,日志正常保存了,领导很高兴.

第二天,又有需求要控制service的事务,OK,然后代码变成了这样.

┌────────────────────┐|   view             |├────────────────────┤|   Controller       |├────────────────────┤|   Transaction      | ───> 事务管理|   log.info...      | ───> 调用日志|   UserService.add  | ───> 新增用户|   log.info...      | ───> 调用日志|   Transaction      | ───> 事务管理├────────────────────┤|   Dao              |├────────────────────┤|   Database         |└────────────────────┘

第三天,项目经理要求我们记录方法的运行所用时间,小意思,然后代码变成了这样

┌────────────────────┐|   view             |├────────────────────┤|   Controller       |├────────────────────┤|   beginTime        | ───> 开始时间|   Transaction      | ───> 事务管理|   log.info...      | ───> 调用日志|   UserService.add  | ───> 新增用户|   log.info...      | ───> 调用日志|   Transaction      | ───> 事务管理|   endTime          | ───> 结束时间├────────────────────┤|   Dao              |├────────────────────┤|   Database         |└────────────────────┘

第四天,客户要求我们实现新增用户时的权限校验,好说,代码变成了这样

┌────────────────────┐|   view             |├────────────────────┤|   Controller       |├────────────────────┤|   permissions      | ───> 校验权限|   beginTime        | ───> 开始时间|   Transaction      | ───> 事务管理|   log.info...      | ───> 调用日志|   UserService.add  | ───> 新增用户|   log.info...      | ───> 调用日志|   Transaction      | ───> 事务管理|   endTime          | ───> 结束时间├────────────────────┤|   Dao              |├────────────────────┤|   Database         |└────────────────────┘/*不知道你们怎么想,但我现在,仍然觉得这种写法挺好的.通俗易懂,可读性强到不知道哪里去了.当然有个前提,这个项目只有这一个方法.*/

第五天,开发UserService的同事回来一看,一脸懵逼,明明走的时候只有一行,现在却有一大堆不知道什么意思的代码存在. 得了,他在此基础上开发,增加各种方法,功能. 随着需求的变动,慢慢的,一百个service都变成了上面那个样子. 接着记录日志的功能有了变动,你发现会影响到调用的类,搜了一下整个项目,发现有特么几千个地方都调用了这个方法. 你心中一万头草泥马掠过,然后提出了辞职. 接着有个新人接手了这个项目,打开一看,这映入眼帘的一大坨屎一样的是特么什么东西?? 硬着头皮改了几版,每个版本都有BUG,领导一怒之下,项目作废了,重金找了几个人重新做了.

==那么问题来了== 现在的需求搞砸了可以辞职,项目烂了可以重做. 那世界上第一个遇到这个问题的人,是怎么解决的?技术就是这技术,重做不还是一样会遇到这些问题吗?那我还怎么拓展功能呢?难道所有项目最终都会走向这种绝境? 于是前人开始探索:

┌───────────────┐|   Controller  ||               ┼───────> 拓展方法写在这?├───────────────┤|   Service     |├───────────────┤|               ┼───────> 拓展方法写在这?|    Dao        |└───────────────┘

这不都是一回事吗?根本不解决问题.

我等凡夫俗子解决不了,但世界上有的是自带外挂的人. 于是,有那么几个人就找到了解决办法! 既然我要拓展方法,就要在你的方法里写代码,那么我能不能在不改你代码的基础上来拓展呢?

==比如在这个地方!==

┌───────────────┐|   Controller  |├───────────────┼───────> 比如,写在这!|   Service     |└───────────────┘

what the hell ?????? 你是让我在一个莫须有的地方写代码?

当然不是,大神的意思是代码要这样写.

┌───────────────┐|   Controller  |      ┌───────────────┐├───────────────┼──────┤ log.info...   ||   Service     |      └───────────────┘└───────────────┘

但执行的时候,会是这样

┌───────────────┐|   Controller  |├───────────────┤|   log.info... ||   Service     |└───────────────┘

what the hell ??????????????????????????????? 这是为什么?

==答案很简单:== 首先你既然打算了解AOP,那你应该是有一些Java基础的. 我们都知道Java文件是没有用的,这就相当于一个txt文件,当程序真正运行的时候,是把Java文件通过编译器编译成class文件来运行的. 那么我们好像看到重点了,编译器! 既然最终运行的是class文件,而class文件又是编译器生成的,那我们是不是可以在编译器上动动手脚呢? 没错,想要达成上面的效果,就需要一个不同的编译器. 把日志这段代码编译到service中 当然了,这个编译器不需要我们开发,而是早有人搞定了.(若是我能开发,我早就去谷歌开发者大会上做演讲了= =) 这就是大名鼎鼎的 :==ajc编译器==(他属于AspectJ框架,可以单独使用此框架写个例子,可以加深理解)

1. 想象一下,有这么两坨东西┌────────────────────┐|   Controller.java  |├────────────────────┤           ┌─────────────────┐|   Service.java     |           |  log.info.java  |└────────────────────┘           └─────────────────┘2. 然后开始编译 : 编译器开始把Service.java中的代码编织成一个class文件.3. ajc编译器开始编译 : ajc开始把log.info.java编织插入到service中.┌────────────────────┐|   Controller.java  |    _________________├────────────────────┤ _/ log.info.java   ||   Service.java     |  \ ________________|└────────────────────┘4. 就像上面,把log.info织入到这个这个方法中.5. 最终形成的calss文件,就是这样┌───────────────┐|   Controller  |├───────────────┤|   log.info... ||   Service     |└───────────────┘

成了,问题解决了.有了这个编译器,前面的问题有迎刃而解. ==ps : ajc编译器并不是唯一解决办法==

那么问题又又来了,==面向切面是什么?== 也许你已经有答案了.

┌───────────────┐|   Controller  |        ┌──────────────────────────────────┐├───────────────┼──────> | 我们在这个角度写代码,就是面向切面.  ||   Service     |        |                                  |├───────────────┼──────> | 我们在这个角度写代码,就是面向切面.  ||      Dao      |        └──────────────────────────────────┘└───────────────┘

我们站在那条线的角度写代码,就是面向切面. 那条线,是不是将原本的代码分成了两个部分呢? 更形象一点

           ┌───────────────┐                |   Controller  |                └───────────────┘───────────────────────────────────── 这条线,像不像把整个代码切开的一条线呢?那么这条线,就叫切面───────────────────────────────────── 如 : 日志调用───────────────────────────────────── 如 : 调用时间───────────────────────────────────── 如 : 事务管理                ┌───────────────┐                |   Service     |                ├───────────────┤                |      Dao      |                └───────────────┘

上面的每一个如,就是一个切面类,也相当于一个切面,毕竟没有人规定我们必须只有一个切面不是?

面向切面是一种人们面对难题时的解决方案,他并不是一个新的语言或是新的技术. 他消除了面向对象的一系列编程陋习.或者说解决了面向对象的一些缺点.

到了这里,我相信有些人还是不太懂面向切面的意义或者作用是什么,没关系,不要气馁,你一定比我当时可聪明多了.请继续往下看.

        这张图可能并不太能直观的表达面向切面意义有多么重大.         ┌───────────────┐         |   Controller  |         └───────────────┘         ─────────────────────────         ┌───────────────┐         |   Service     |         ├───────────────┤         |      Dao      |         └───────────────┘                 那么,这张图呢?     ┌────────────────┬────────────────┬────────────────┬────────────────┬────────────────┬────────────────┐     |   Controller1  |   Controller2  |   Controller3  |   Controller4  |   Controller5  |   Controller6  |     └────────────────┴────────────────┴────────────────┴────────────────┴────────────────┴────────────────┘─────────────────────── 面向切面的方法,可以织入到所有的service中,而不需要改变service的代码 ──────────────────────────────────────────────── 权限校验 ────────────────────────────────────────────────────────────────────────────────────────────────────── 日志调用 ────────────────────────────────────────────────────────────────────────────────────────────────────── 调用时间 ────────────────────────────────────────────────────────────────────────────────────────────────────── 事务管理 ───────────────────────────────────────────────────────────────────────────────     ┌────────────────┬────────────────┬────────────────┬────────────────┬────────────────┬────────────────┐     |   Service1     |   Service2     |   Service3     |   Service4     |   Service5     |   Service6     |     └────────────────┴────────────────┴────────────────┴────────────────┴────────────────┴────────────────┘─────────────────────── 面向切面的方法,可以织入到所有的Dao中,而不需要改变Dao的代码 ──────────────────────────────────────────────────────── 日志调用 ────────────────────────────────────────────────────────────────────────────────────────────────────── 调用时间 ───────────────────────────────────────────────────────────────────────────────     ┌────────────────┬────────────────┬────────────────┬────────────────┬────────────────┬────────────────┐     |      Dao1      |      Dao2      |      Dao3      |      Dao4      |      Dao5      |      Dao6      |     └────────────────┴────────────────┴────────────────┴────────────────┴────────────────┴────────────────┘                  我只需要声明一个类,就可以该类中的方法插入到任意类的位置      

有些人说,用代理模式就可以解决上面所说的编程时的一系列问题. 没错! 面向切面 ─── 是思想. 代理模式 ─── 是思想的实现.

二. 画个图片

上面画过了

三. 写个代码

只讲概念,后面讲例子

四. 说个人话

切面的几大概念

1.切面 (Aspect) :

拓展的类 概念 : Aspect 声明类似于Java中的类声明,在Aspect中会包含着一些切点以及相应的增强. 人话 : 创建一个类,这个类中的<增强>将插入到<目标对象>代码中.

2.连接点 (Joint point) :

调用点 概念 : 表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它 joint point. 人话 : 被<切点>所包含的内容, 如切点为 userService.add()方法 : 连接点就是userService.add() 如切点为 com.xxx包下的所有service : com.xxx包下的所有service的任意一个方法 如切点为 com.xxx包下的所有add开头的方法 : com.xxx包下的所有add开头的方法

3.切点 (Pointcut):

在哪里增强 概念 : 表示一组<连接点>,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的增强将要发生的地方.或是我们要织入的地方 人话 : 要往哪里插入 如 userService.add()方法 或 com.xxx包下的所有service方法 或 com.xxx包下的所有add开头的方法

4.增强 (Advice) :

拓展方法 概念 : Advice 定义了在<切点>里面定义的程序点具体要做的操作,它通过 before、after 和 around 来区别是在每个<连接点>之前、之后还是代替执行的代码. 人话 : 切面类中需要定义的,连接点方法调用前的拓展,之后的拓展,或者之前和之后的拓展.

5.目标对象 (Target) :

被增强的目标 概念 : 增强(Advice) 的目标对象.. 人话 : 被增强的目标

6.织入 (Weaving):

插入的动作 概念 : 将 Aspect 和其他对象连接起来, 并创建 Adviced object 的过程. 人话 : 将切面类插入进去

例如:我们要在service的add方法里里增加日志┌───────────────┐|   Controller  |└───────────────┘─────────────────────────────────────    <-- com.xxx.service.UService  ─── <切点>┌───────────────┐      ┌────────────┐    <-- log.info所在的类           ─── <切面>| UService.add  |      |  log.info  |    <-- log.info方法就是           ─── <增强>├───────────────┤      └────────────┘|      Dao      |└───────────────┘UService.add的调用就是   ─── <连接点>UService                ─── <目标对象>

到这里你应该理解什么是面向切面了,如果你还是不理解,不怕,可能是我的表达方式不适合你,全网有很多对于面向对象的优秀理解. 虽然筛选好文章需要时间,我可以帮你找出来,但其实在筛选的过程中,更是一个理解的过程.

关于如何用最通俗的方法讲spring中的AOP就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。

方法 代码 日志 切面 编译 对象 编译器 就是 事务 时间 问题 事务管理 文件 概念 用户 管理 面的 项目 需求 开发 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 蓝思科技飞鸽互联网 盈实互联网科技 中资互联网科技 中正互联网科技招聘 成都玖崟互联网科技有限公司乐商圈怎么样 移动互联网科技股 南山科技园互联网公司有哪些 互联网公司科技股 荆州市昕露互联网络科技有限公司 广西互联网科技孵化中心 滨湖区互联网智能科技产品有哪些 2021互联网科技公司排名 互联网相关科技技术 半导体是互联网高科技吗 互联网科技机房 广西科技产业互联网是什么 玉溪新华互联网科技怎么选 恒生科技指数 中概互联网 河南商务圈互联网科技有限公司 黑龙江互联网科技公司 百单网互联网科技有限公司怎么样 圆梦互联网科技 互联网科技logo设计 互联网巨头投资的硬核科技 互联网科技有哪些公司 南平创想时代互联网科技有限公司 未来五年的互联网科技 互联网科技公司营业执照怎么办理 华中科技大学互联网的牛人 工业互联网科技成果转化
0