作为一名Java后端开发,AOP这个牛皮哄哄的名词一定听过、用过;但是对于刚入行的新手,甚至部分有几年开发经验的程序员来说,在初次理解它的时候,都会有点点的吃力;因为和我们一开始就接触的面向对象编程(OOP)思想有些出入,加上概念比较的空泛,导致很多人一直以来都有那么点似懂非懂的感觉;(懂了,但只懂了一点点…)

今天,咱就以一个生活中的故事场景,一起来好好理解一下;

在讲故事场景之前,还是得先说一下AOP常用到的一些概念、名词;说在前面,方便一会儿带入场景理解!

什么是AOP?

AOP (Aspect Orient Programming),直译过来就是 面向切面编程。AOP 是一种编程思想,是面向对象编程(OOP)的一种补充。AOP的目的是实现关注点的分离;

就这么不长不短的一句话,可以看出AOP的作用还是非常重要的。但是懵逼也就从这里开始了;

什么是关注点分离?莫慌!还有更多难理解的呢,慢慢往后面看,到时候一起来解释。

AOP 领域中的特性术语:

  • 通知(Advice): AOP 框架中的增强处理。通知描述了切面何时执行以及如何执行增强处理。
  • 连接点(join point): 连接点表示应用执行过程中能够插入切面的一个点,这个点可以是方法的调用、异常的抛出。在 Spring AOP 中,连接点总是方法的调用。
  • 切点(PointCut): 可以插入增强处理的连接点。
  • 切面(Aspect): 切面是通知和切点的结合。
  • 引入(Introduction):引入允许我们向现有的类添加新的方法或者属性。
  • 织入(Weaving): 将增强处理添加到目标对象中,并创建一个被增强的对象,这个过程就是织入。

好!这些都是官方文档中给到的解释;

AOP的生活场景

AOP不好理解往往就是被上面这几个概念给带到沟里去了;因为实在是太空泛了,第一次看到完全不知道在说什么鬼,这些字全部都认识,可组成的词儿放这里一个都不懂了,下面就以一个生活中的场景来理解一下;

码锅是一个土生土长的农村娃,从记事开始,老家的家人就是吃着山头的一个天然矿泉水,到现在几十年过去了,那股山泉依然在,但是前些年,家里盖房子,换了宅基地,山泉的海拔比新的宅基地低,自然山泉水就无法到家了!

刚刚好的是,国家政策正好扶持发展农村的基础建设,村里面建起了自来水水库,国家出资将水管牵到每家每户;大家免费用水。

可是,免费的东西,大家总是不那么的珍惜,有些家庭常年把水开着,导致自来水严重浪费,想用水的用户用不上,导致村委很难管理;

为了抵制这种现象,村委决定对自来水进行象征性的收费,每户自来水入户的水管上都装上水表,自己充卡买水,费用用完,自来水就停了,一下子就从根本上解决了自来水的浪费问题;

简单的故事说完了,我们来想一下,给每户装水表的动作就是一个典型的AOP场景;

  • AOP

    AOP的目的是做到关注点的分离,用户只关注用水,有水就行,装水表之前和之后、对用户的正常用水行为都不会产生任何影响;村委关注的是每个家庭用了多少水,你家怎么用的,我不管;加水表这个动作,就是对整个用水过程产生了一些增强(计费);家里面每个水龙头的用水,水表都会记录用水量并扣费;

  • 切点

    此时水龙头代表了每一个切点(PointCut),只要你放水,水表(切面)就会计费;在代码里面切点体现为每一个用水的方法,比如:洗菜的方法、洗衣服的方法等等;

  • 连接点

    水龙头放水时,出水之前,水表会检测是否还有费用;出水之后,水表会扣除对应的费用;如果水龙头长时间放水不关(异常),水表自动关闭总阀门;那么这里说到的出水之前、之后、异常的情况就是AOP中的连接点(join point);在Spring代码中就体现为,方法执行之前(Before),方法执行之后(After),方法执行异常(AfterThrowing)等各个点。

  • 通知

    要分两个层面去说

    • 增强什么?

      也就是上面描述的,检查余额,扣费这些动作,就是对用水过程进行的增强

    • 何时执行?(执行时机)

      也就是上面说到的,出水前,出水之后,出水异常在这些时机;

      代码中体现为以下几种:

      前置通知(before):在执行业务代码前做些操作

      后置通知(after):在执行业务代码后做些操作,无论是否发生异常,它都会执行,比如关闭连接对象

      异常通知(afterThrowing):在执行业务代码后出现异常,需要做的操作,比如回滚事务

      返回通知(afterReturning):在执行业务代码后无异常,会执行的操作

      环绕通知(around):一个方法包含了上面说到的4种场景

  • 织入

    在水龙头放水的时候,出水之前检查余额,出水后扣除费用的整个过程称之为织入;

好了,结合场景的理解说完了,我想到这里,你对AOP的各个关键名词应该理解的差不多了,如果还不理解,建议把上面的场景再读一下;

既然概念都说通了,代码要咋实现呢?理解之后,代码部分其实是非常简单的。

代码实现

  • 创建一个SpringBoot项目并引入AOP的依赖

    <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
    
  • 定义一个放水的接口

    @RestController
    @RequestMapping("/family")
    public class FamilyController {@GetMapping("tapwater")public String tapWater() {Random ran = new Random();// 模拟一个出水时长int i = ran.nextInt(10);System.out.println("水来了.....时长:" + i);// 模拟异常出水,大于5为异常情况if (i > 5) {throw new RuntimeException();}return "水来了.....";}
    }
    

    访问接口:127.0.0.1:8080/family/tapwater 会出现以下日志;

  • 定义一个水表切面

    通俗的理解,就是安装一个水表

    @Aspect
    @Component
    public class MeterAspect {/*** 方法执行之前* * com.lupf.tapwater.rest.FamilyController.*(..) 指定了切点* @Before 指定了连接点 也就是执行时机*/@Before("execution(* com.lupf.tapwater.rest.FamilyController.*(..))")public void balance(){System.out.println("出水前,检查余额");}/*** 方法成功执行之后的增强*/@AfterReturning("execution(* com.lupf.tapwater.rest.FamilyController.*(..))")public void afterReturning(){System.out.println("正常出水,扣除水费");}/*** 方法执行之后 成功异常都执行的增强*/@After("execution(* com.lupf.tapwater.rest.FamilyController.*(..))")public void deduction(){System.out.println("出水操作执行完成");}/*** 异常之后的增强*/@AfterThrowing("execution(* com.lupf.tapwater.rest.FamilyController.*(..))")public void afterThrowing(){System.out.println("出水异常,关闭阀门");}
    }
    
    • 表达式

      以下通过表达式的方式指明了切点的匹配规则;

      指明切点的方式有很多种,匹配规则也是丰富多样,不过并不是本的重点,所以暂时不展开,有机会再通过文章进行详细的说明

      execution(* com.lupf.tapwater.rest.FamilyController.*(..))
      
    • 连接点

      指定执行的时机

      @Before:方法执行之前;

      @AfterReturning:方法成功执行并返回;

      @After:方法执行之后,不管成功还是失败;

      @AfterThrowing:方法执行异常。

  • 测试

    此时再次执行:127.0.0.1:8080/family/tapwater 就会出现以下日志;

    • 正常情况

      image-20210704202807730

    • 异常情况

  • 环绕通知

    @Aspect
    @Component
    public class MeterAspect {/*** 方法执行之前* * com.lupf.tapwater.rest.FamilyController.*(..) 指定了切点* @Around 指定了连接点 也就是执行时机为环绕通知*/@Around("execution(* com.lupf.tapwater.rest.FamilyController.*(..))")public Object around(ProceedingJoinPoint pj) throws Throwable{Throwable throwable;try {System.out.println("检查余额");Object proceed = pj.proceed();System.out.println("正常出水,扣除水费");return proceed;} catch (Throwable t) {System.out.println("出水异常,关闭阀门");throwable = t;}System.out.println("出水操作执行完成");throw throwable;}
    }
    

    最终执行的效果和上面测试的情况一样

AOP能做什么?

通过上面的示例,我们不妨来思考一下,AOP可以帮助我们做哪些事情?是不是一切关注点分离的事情都可以做了,比如:

  • 鉴权(不管什么模块,用户、订单等,都需要鉴权)
  • 操作日志(不管什么角色,什么权限的何种操作,都需要进行记录)
  • 请求响应数据规范(不管什么业务,请求和响应的格式规范必须得通过)
  • 数据加解密(不管什么接口,接收请求的解密,响应数据的加密都是需要做的)

只要是和具体的业务无关,且大家都在关注的事情,那么都可以通过AOP去抽离这些关注点并将其统一维护,提高代码的复用性;

总结

AOP的概念真正理解之后,会对你日常业务开发带来很多的便捷;难就难在理解它的这个过程,也只有真正理解了,才能发挥出其独有的魅力;那些繁琐的官方概念确实给我们的理解上挖了不少的坑,但是看了这篇,有没有助你拨开那层迷雾呢?

原来理解 AOP 可以这么简单相关推荐

  1. 简单理解AOP(面向切面编程)

    来源:http://www.cnblogs.com/jyh317/p/3834271.html AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式 ...

  2. 理解AOP思想(面向切面编程)

    本文旨在帮助还没有理解AOP的童鞋看透弄懂AOP,也欢迎高手批评指正. 先说一个Spring是什么吧,大家都是它是一个框架,但框架这个词对新手有点抽象,以致于越解释越模糊,不过它确实是个框架的,但那是 ...

  3. 理解神经网络,从简单的例子开始(2)使用python建立多层神经网络

    这篇文章将讲解如何使用python建立多层神经网络.在阅读这篇文章之前,建议先阅读上一篇文章:理解神经网络,从简单的例子开始.讲解的是单层的神经网络.如果你已经阅读了上一篇文章,你会发现这篇文章的代码 ...

  4. 理解神经网络,从简单的例子开始(1)7行python代码构建神经网络

    理解神经网络,从简单的例子开始(1)7行python代码构建神经网络 前言 本文分为两个部分,第一个部分是一个简单的实例:9行Python代码搭建神经网络,这篇文章原文为:原文链接, 其中中文翻译版来 ...

  5. Spring AOP 简介以及简单用法

    Spring AOP 简介以及简单用法 如果你去面试java开发, 那么Spring的AOP和DI几乎是必问的问题. 那么AOP是什么呢? 一. AOP 所谓Aop就是 Aspect-Oriented ...

  6. 理解git结构与简单操作(四)合并分支的方法与策略

    接上节,此时的dev分支与master分支的进度就不一样了,所以需要将dev分支与master分支同步.这里需要的就是合并分支的操作,大家应该都知道用git merge或者git rebase. gi ...

  7. 一张图理解AOP关键概念

    一张图理解AOP关键概念 Aspect(切面) Joint Point(连接点) Advice(通知) Pointcut(切入点) Weaving(织入) 这里以跟踪方法调用为例,Calculate为 ...

  8. aop实现原理_从宏观的实现原理和设计本质入手,带你理解 AOP 框架的原理

    点击上方"Java知音",选择"置顶公众号" 技术文章第一时间送达! 作者:FeelsChaotic juejin.im/post/5c57b2d5e51d45 ...

  9. 从宏观的实现原理和设计本质入手,带你理解 AOP 框架的原理

    点击上方"Java知音",选择"置顶公众号" 技术文章第一时间送达! 作者:FeelsChaotic juejin.im/post/5c57b2d5e51d45 ...

最新文章

  1. 不用深度学习,怎么提取图像特征?
  2. 26.SpringBoot事务注解详解
  3. Markdown编辑表格时如何输入竖线('|', pipe,vertical bar)
  4. Python 开发的 10 个小贴士,你知道几个?
  5. 同一主机上WordPress博客更换域名简易八步骤(2)
  6. 步进电机为什么无法高速启动?
  7. mysql 6.2 安装教程_CentOS 6.2 安装 MySQL 5.7.28的教程(mysql 笔记)
  8. curses.h: No such file or directory
  9. 简明人体结构(一):人体结构学习方式的整体引导
  10. 触发器在计算机中的作用,d触发器的原理是什么_d触发器的功能及作用
  11. FineReport新建数据连接
  12. 早上还在改 Bug,晚上就被裁了
  13. 方正书版PS文件转换PDF文件PHP源程序 发布说明
  14. eclipse报 The word is not correctly spelled问题
  15. VR和AR将如何发展下去?哪个更有前景?
  16. 微信小程序跳转第三方页面
  17. 力扣1705——吃苹果的最大数目(贪心+优先队列)
  18. 终端(terminal)打印彩色文字
  19. Windows和Ubuntu双系统完全独立(双硬盘)的安装方法
  20. [软件发布]WAP网站在线浏览器 WapReader

热门文章

  1. nodeJS学习(9)--- nodeJS模块:exports vs module.exports
  2. 第三周PLECS仿真实验
  3. 项目管理综述(需要完善)
  4. 【数据库系统设计】关系数据库标准语言SQL(2)
  5. 【MyBatis笔记】10-多对一左连接查询分步查询(查询所有订单及订单对应的客户)
  6. SqlServer 日期时间格式 字符串相互转换 及相关函数
  7. matplotlib绘图跳过时间段的处理方案
  8. 一文讲清:数据分析与数据挖掘到底有什么区别?
  9. 企业部署BI系统怎么能一直做下去,PDCA闭环是关键
  10. AfxMessageBox