Spring 中的 AOP 实现

212 阅读5分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

什么是 AOP

说了好多遍的 AOP 还是要拿过来说说,可能每次都会有新的发现吧,深入一点点,我呢尽量不 copy 代码,用自己的理解来说说 AOP 这个玩意。当然,我的理解也是看书之后的理解,不想看的直接看看书,也挺好。

AOP 意为面向切面编程,为什么要面向切面编程呢?就拿面向对象编程来说,我们的数据和对数据的处理都封装在类中,这极大的提高了编程的模块性。

但是总会出现那么一些通用的模块功能,我们常用的做法就是写一个通用类,每个模块需要的时候自己调用,但是呢,这还是比较麻烦的,因为有时候我们不仅需要自己调用可能还需要在一些逻辑判断下再调用。

所以,AOP 应运而生。为了解决各个模块通用的处理问题,我们就在横向统一处理这些模块的通用部分,比方说每个类中都需要的日志管理对象,还有 Service 中的事务管理对象。

AOP 并不是单单属于 Spring,而是属于一个 AOP 联盟,AOP 联盟为 AOP 定义一些标准,Spring 借鸡生蛋而已。

下面就结合 AOP 联盟的标准和 Spring 中对 AOP 标准的实现来说说。

Advice 通知

通知定义的是在连接点,也就是在横切面我们需要做什么样的操作,当然,AOP 联盟只负责提供接口,Spring 就实现这个接口来做具体的操作。

在 Spring 中首先是对 Advice 接口进行了拓展,定义了 BeforeAdvice,AfterAdvice 和 ThrowsAdvice,而每个又有不同的实现,具体说一个。

在 BeforeAdvice 的具体实现中,定义了一个为待增强的目标方法设置前置增强接口 MethodBeforeAdvice,使用这个前置接口实现一个回调函数 before。

具体来说,回调函数会在调用目标方法时被回调。回调方法的参数有目标方法的反射对象 Method,目标方法的参数数组 Object[ ] 。

后置通知和异常通知也是一样,会定义一个回调函数,在目标方法执行之后和出现异常时回调方法。

所以说,通知就这样定义好了,至于怎么回调,以及为哪些目标对象添加通知这不是通知的事。

Pointcut 切点

定义了通知,这些通知是不认人的,管你是人是鬼,我一律通吃,这怎么可以呢,于是就出现了切点,这是啥意思呢,简单说就是定义了哪些方法是目标方法也就是需要添加通知的方法(对了,通知作用最小单位是方法,至于为什么呢,我猜是因为我们的回调方法的参数是 Method 吧)。

嗯,也就是说切点就是定义了一个集合,就是那些可以被通知增强的方法的集合。再形象一点就是切点就是一个筛子,可以筛选出什么样的方法才是目标方法。

还是挺好理解的,对吧,但是我也是看了两遍才有这觉悟的,具体说就是 AOP 联盟和你说了,你想用 AOP 可以,先买个筛子过来,这个时候 Spring 就开始动脑筋了,我该怎么筛选目标方法呢?

结果就是定义了一系列的接口和实现类,说重点的就是,Spring 想了想,嗯,就通过两种方式来确定目标方法吧,一是正则表达式,二是通过匹配方法名。

既然大的方向知道了,具体就是定义了 Pointcut 以及 Pointcut 的实现类,具体的筛选交给 MethodMatcher 接口的 matches 方法来实现,既然有两种筛选形式,那就定义两个实现类。

JdkRegexpMethodPointcut 用来实现正则表达式的筛选,而 NameMatchMethodPointcut 用来实现通过匹配方法名来筛选。

呃呃,所以你看,我们只需要调用这个 matches 方法就可以筛选出来那些需要增强的方法,但是吧,多说一句就是 matches 方法的调用是在动态代理中调用的。通过动态代理回调 invoke 来调用 matches 方法。

至于代理对象的详细情况还是等到后面看看能不能继续分析了。

Advisor 通知器

好了,到这里,我们的通知也有了,切点也有了,自然需要一个东西来结合他们,不然通知再好作用不到切点也是白搭啊,于是就出现了 Advisor 通知器。

通过 Advisor 我们就可以定义应该使用哪个通知并作用到哪个切点。好吧,通知器的种类应该可以有很多,因为我们有很多通知,又有很多切点,它们一组合起来那不得了,所以就说一个最简单的吧。

DefaultPointcutAdvisor,通知器的实现离不开两部分,一是通知,二是切点。那就看看这个默认通知器是如何实现的吧。

private Pointcut pointcut = Pointcut.TRUE;
public DefaultPointcutAdvisor(Advice advice){    
    this(Pointcut.TRUE,advice)
}
---------------------------------
Pointcut TRUE = TruePointcut.INSTANCE;

这样写几行代码可能看起来更加清晰,默认的 Advisor 的实现基于一个通知和默认的切点,关键这个切点返回的永远是 True。SO,全匹配,对所有的方法都适用这个通知。

对了,这里说一个装逼利器, TruePointcut 是一个单例对象,它里面调用的切点筛选接口 MethodMatcher 也是基于单例对象 TrueMethodMatcher 实现的。

所以说,还有面试官问你 Spring 中涉及到的设计模式时,大胆的告诉他单例模式!然后把上面这两个类告诉他,嗯,想想就很美妙哈。

最后

好了,这就这吧,到这里我们就大致说了一下 Spring 中 AOP 的实现,重点的代理对象的生成和拦截器调用的实现好像还没说哈,呃呃,不知道要等到什么时候才能整理出来了。

最后呢,送给大家一句话,所谓迷茫,是你的才华配不上梦想,你的行动赶不上承诺。加油吧各位,我写这篇文章还需要两个多小时呢。