这里就不扯什么是AOP以及AspectJ的历史了,在一般大众的认知中,AOP就是在方法的前后做一些事情(Spring默认也就是方法级别的AOP)。接下来,让我们一步步实现AOP。
这是一个很普通的方法UserService#sayHello。
如何在sayHello前后做一些事情呢?最粗暴的做法是:
但这并不符合开闭原则,也不通用。如果需要在OrderService#order方法前后也做一些事情,就要把代码拷贝一份。
所以,我们必须将sayHello和sayHello前后要做的事情进行分离,而分离的目的是为了下次更好的相聚(组合)。
Spring AOP的做法是,抽象出以下概念:
- Pointcut
- Advice
- Advisor
Pointcut俗称切点,可以简单理解为"怎么切/切哪里",也就是一个匹配规则,比如我们常见的AspectJExpressionPointcut,允许我们配置切点表达式:
execution(* com.bravo.test.service.UserService.sayHello(..))
Advice则是具体的增强逻辑,即前面说的"方法前后要做的事情"。
而Advisor=Pointcut+Advice。
对于符合Pointcut规则的目标方法应用Advice,在目标方法前后做一些事情。
尽管在我看来这些概念已经很清晰,但对于初学者来说还是有点绕。所以,这里再做一步简化,只留下Advice的概念,丢弃Pointcut和Advisor。没有Pointcut怎么知道哪些方法需要增强呢?交给调用者手动组装。比如:
当然,Spring内部有一个匹配过程,代码大致如下(这里不是Spring的源码):
Advisor持有Pointcut,方法匹配成功,则返回advice用于增强
讲到这,对于如何实现AOP应该有一个模糊的概念了。
接下来我们讨论最难的两个问题:
- 如何把advice嵌入目标方法前后
- 如何链式执行advice
在这里,Spring又抽象出了一个新的概念:MethodInvocation。
这是啥?Spring很清楚,所谓AOP,就是在调用目标方法前后额外执行一些内容。Java的反射已经封装好Method这个类,所以整个过程就是:
那我能不能把这个过程也封装一下呢?我把"Method执行前后需要额外做一些操作"这个过程抽象成MethodInvocation(方法执行)。
里面只定义一个方法proceed,表示执行方法,而且执行的时候要自动把advice也执行掉。
然后,配套的还有MethodInterceptor:
这又是啥?通俗讲就是方法拦截器,在执行方法前做些事情。在Spring中MethodInterceptor继承自Advice,大家直接看成Advice即可。
所以,现在变成了这样:
把经过Interceptor执行Method的这个过程封装成MethodInvocation
接着,我们使用JDK动态代理为userService生成代理对象。代理对象会将一次方法调用委托到目标对象,但在此之前会执行advice:
那么,如何产生链式调用呢?
MethodInvocation就是Filter模式中的FilterChain,持有Interceptor并且负责推进下一个Interceptor
具体代码放在gitee了: