原文参考自这里 http://t.cn/A6623DF2,非常小白友好向,不涉及源码和底层,只从最上层的应用层面来解释 AOP 是个什么东西,我做了很多修改并重新配了图,帮助小伙伴更好理解 AOP~
老规矩,背诵版在文末。点击阅读原文可以直达我收录整理的各大厂面试真题
先来看一个简单的小例子,体会一下 AOP 能解决什么问题:
假设有这么三个类 A B C,它们都拥有各自实现的 do 方法:
如果我们要在 A,B,C 三个类的 do 方法中的最后执行一个名为 log
方法来打印日志,最简单的,我们可以这样写:
这样做可以解决问题,但是总感觉有些别扭,每个类的 do 方法中都调用了打印日志的方法,但是,打印日志其实并不是我们的核心业务,我们却要去花费大力气去处理它
随着系统越来越完善,类似这样的非核心业务也会越来越多,比如权限,异常处理,性能监控等
这样的功能出现在很多类的很多方法中干扰了我们的核心业务代码,怎么解决呢?
AOP 就是为此而生:
看看 AOP 是如何解决的?
从上图可以看出日志记录,性能监控,异常处理这样的非核心功能被单独抽取出来了,与业务代码分离,横切在核心业务代码之上
这就是我们通常所说的面向切面编程(AOP),通过一个例子看看他是如何实现的
创建一个 UserDao 类:
创建一个切面类:
当我们创建 UserDao 的对象并调用 addUser 方法的时候会打印出如下两条记录:
代码语言:javascript复制添加用户
打印日志
So,很神奇吧~ 究竟发生了什么?
明明 addUser 方法里面只有打印 “添加用户” 啊
这就是 Spring AOP 的强大之处,在运行时通过动态代理技术对 UserDao 的 addUser 方法进行了增强,添加了打印日志的功能。
动态代理其实就是在运行时动态的生成目标对象的代理对象,在代理对象中对目标对象的方法进行增强,之前文章 字节一面 • 作文题 • 动态代理具体是怎么实现 有过详细的介绍,小伙伴们可以直接公众号里搜一下看看,下面来解释下 AOP 中几个重要的概念。
通知 Advice
就是会在目标方法执行前后执行的方法,定义增强的逻辑
上面这个例子中:
这个 log 方法就是通知,目标方法是 UserDao 类的 addUser()
在 addUser 执行之后执行了 log 方法,所以 log 方法是后置通知,通过在方法上加上 @After
注解来表示。
通过通知和目标方法的执行顺序我们可以把通知分为五种:
- 前置通知(before):在目标方法执行之前执行
- 后置通知(after):在目标方法执行之后执行
- 后置返回通知(after returning):在目标方法返回之后执行,先执行后置通知再执行后置返回通知
- 异常通知(after throwing):在目标方法抛出异常时执行
- 环绕通知(around): 功能最强大的Advice,可以在目标函数执行中执行,可以自定义执行顺序
这几种通知的执行顺序如下:
连接点 Join point
连接点就是可以应用通知进行增强的方法
因为 Spring Aop 只能针对方法进行增强,所以这里的连接点指的就是需要被增强的方法
如上例中的:
这三个方法都可以是连接点
切入点 Pointcut
应用通知进行增强的目标方法
现在面临的问题是如何去描述这个需要被增强的目标方法(Target object)(换句话说,如何去描述和定位连接点),如果只是一个具体的方法需要增强那简单,通过类名和方法名找到它就可以了,但是往往真实的需求中很多方法需要同样的通知进行增强,Spring AOP 为我们提供了一个描述方法的语法比如上例中的:
execution(* cn.xh.dao.UserDao.addUser(..)
就是用来描述需要应用通知的方法的,这里的含义是com.veal.dao 包 UserDao 类中的参数任意,返回值任意的 addUser 方法,这个表达式称之为切入点表达式。
so,切入点表达式其实就是一个匹配连接点的断言或者表达式。
而应用通知进行增强后的连接点,就称为切入点。
切面 Advisor
切入点和通知的结合
通知定义了需要做什么,切入点定义了在哪些类的哪些方法中执行通知,那么需要将他们 2 个组合起来才有效啊。
切面就是做这个事情的,可以用切面类来表示,通常有2个关键信息:
- 需要增强的目标方法列表,这个通过切入点 (Pointcut) 来指定
- 需要在目标方法中增强的逻辑,这个通过 (Advice) 通知来指定
在 MyAspectLog 这个切面类中,通过切入点表达式指定了切入点 addUser ,并且又包含了通知 log()
织入 Weaving
就是通过动态代理对目标对象方法进行增强的过程
这就是个名词,没什么特殊意义,很多文章和文档中都会用这个词,防止各位小伙伴不清楚,这里提一嘴~
最后放上 AOP 的背诵版(这里不扯源码,多扯点动态代理,后面会有扯源码的背诵版~):