Spring AOP介绍及源码分析

1、AOP介绍


举个例子来讲明一下吧!如今系统中有不少的业务方法,如上传产品信息、修改产品信息、发布公司库等;如今须要对这些方法的执行作性能监控,看每一个业务方法的执行时间;在不改变原业务代码的基础上,也许咱们会这么作:
正则表达式

Offer接口:
编程


Offer实现:
app


Offer代理:
框架


咱们要经过下面的方式来使用:
模块化


上面的例子的输出为:
函数


上面的例子中,OfferProxy实现了IOffer,而全部的业务实现均委托给其成员offer;能够想像,这应该就是最简单的AOP的实现了;但这种方式会存在一个问题:若是有很是多的这种业务对象须要性能监控,咱们就须要写一样多的XyzProxy来知足需求,这也是很是巨大的工做量。
性能

上面说到了代理,咱们先看看代理模式吧!
学习

 2、代理模式及实现

下面是代理模式的类图:
this


代理模式类图
编码

代理模式中,存在一个称为ProxyObject的代理对象和RealObject的真实对象,它们都实现了相同的接口;在调用的地方持有ProxyObject的实例,当调用request()方法时,ProxyObject能够在执行RealObject.request()先后作一些特定的业务,甚至不调用RealObject.request()方法。

目前实现代理模式的方式有两种:基于JDK的动态代理和基于CGLIB字节码的代理。

2.1 JDK动态代理

JDK动态代理,顾名思义,是基于JDK的反射(reflect)机制;在JDK中,提供了InvocationHandler这个接口,下面是JDK里面的注释:

 

InvocationHandler is the interface implemented by the invocation handler of a proxy instance.

Each proxy instance has an associated invocation handler. When a method is invoked on a proxy instance, the method invocation is encoded and dispatched to the invoke method of its invocation handler.

 

简单翻译,意思是说:该接口由被代理对象的handler所实现;当调用代理对象的方法时,该方法调用将被编码,而后交给代理对象的invoke方法去执行;

是否是有一种豁然开朗的感受呢?没错,答案就在你心中。

这样,上面的代码就能够改为下面的实现方式:


调用端:


经过这种方式,你不须要为针对每个业务写一个代理对象,就能够很轻松地完成你的需求;但也许你已经注意到了,JDK的动态代理,在建立代理对象(上面红色代码部分)时,被代理的对象须要实现接口(即面向接口编程);

这就是JDK的动态代理,简单吧!下面看看CGLIB代理方式。

2.2 CGLIB代理

若是目标对象没有实现任何接口,那怎么办呢?不用担忧,你能够用CGLIB来实现代理:


调用端:


使用CGLIB建立的代理对象,其实就是继承了要代理的目标类,而后对目标类中全部非final方法进行覆盖,但在覆盖方法时会添加一些拦截代码(上面CglibProxyFactory类中的intercept方法)。

下面看看Spring中是如何实现AOP的。

 

3、Spring AOP的实现


 

3.1 Spring AOP的几个概念

Spring AOP中的几个基本概念,每次学习AOP都被这几个概念折腾的很不爽,咱们在这里再把这几个概念描述一遍,力争把这几个概念搞清,在每次review这块内容的时候能够很快上手。

  1. 切面(Aspect):切面就是一个关注点的模块化,如事务管理、日志管理、权限管理等;
  2. 链接点(Joinpoint):程序执行时的某个特定的点,在Spring中就是一个方法的执行;
  3. 通知(Advice):通知就是在切面的某个链接点上执行的操做,也就是事务管理、日志管理等;
  4. 切入点(Pointcut):切入点就是描述某一类选定的链接点,也就是指定某一类要织入通知的方法;
  5. 目标对象(Target):就是被AOP动态代理的目标对象;

用一张图来形象地表达AOP的概念及其关系以下:


 

3.2 Spring AOP中切入点、通知、切面的实现

理解了上面的几个概念后,咱们分别来看看Spring AOP是如何实现这些概念的;

  1. 切入点(Pointcut):它定义了哪些链接点须要被织入横切逻辑;在Java中,链接点对应哪些类(接口)的方法。所以,咱们都能猜到,所谓的切入点,就是定义了匹配哪些娄的哪些方法的一些规则,能够是静态的基于类(方法)名的值匹配,也能够是基于正则表达式的模式匹配。来看看Spring AOP Pointcut相关的类图:


在Pointcut接口的定义中,也许你已经想到了,ClassFilter是类过滤器,它定义了哪些类名须要拦截;典型的两个实现类为TypePatternClassFilterTrueClassFilter(全部类均匹配);而MethodMatcher为方法匹配器,定义哪些方法须要拦截。

在上面的类图中:

  • StaticMethodMatch与DynamicMethodMatch的区别是后者在运行时会依据方法的参数值进行匹配。
  • NameMatchMethodPointCut根据指定的mappedNames来匹配方法。
  • AbstractRegexpMethodPointCut根据正则表达式来匹配方法。
  1. 通知(Advice):通知定义了具体的横切逻辑。在Spring中,存在两种类型的Advice,即per-class和per-instance的Advice。

所谓per-class,即该类型的Advice只提供方法拦截,不会为目标对象保存任何状态或者添加新的特性,它也是咱们最多见的Advice。下面是per-class的类图:


  • BeforeAdvice:在链接点前执行的横切逻辑。
  • AfterReturningAdvice:在链接点执行后,再执行横切逻辑。
  • AfterAdvice:通常由程序本身实现,当抛出异常后,执行横切逻辑。
  • AroundAdvice:Spring AOP中并无提供这个接口,而是采用了AOP Alliance的MethodInteceptor接口;经过看AfterReturningAdvice的源码咱们知道,它是不能更改链接点所在方法的返回值的(更改引用);但使用的MethodInteceptor,全部的事情,都不在话下。

在上面的类图中,还有两种类没有介绍,那就是***AdviceAdapter和***AdviceInteceptor,咱们以AfterReturningAdviceInterceptor为例来讲明:


该类实现了MethodInterceptor和AfterAdvice接口,同时构造函数中还有一个AfterReturningAdvice实例的参数;这个类存在的做用是为了什么呢?对,没错,Spring AOP把全部的Advice都适配成了MethodInterceptor,统一的好处是方便后面横切逻辑的执行(参看下一节),适配的工做即由***AdviceAdapter完成;

哈哈,Spring AOP的代码也不过如此嘛:所谓的AfterReturningAdvice,经过适配成MethodInterceptor后,其实就是在invoke方法中,先执行目标对象的方法,再执行的AfterReturningAdvice所定义的横切逻辑。你如今明白它为何不能修改返回值的引用了吧?

对于per-instance的Advice,目前只有一种实现,就是Introduction,使用的场景比较少,有兴趣的同窗能够本身研究一下,呵呵!

  1. 切面(Aspect):在Spring中,Advisor就是切面;但与一般的Aspect不一样的是,Advisor一般只有一个Pointcut和一个Advice,而Aspect则能够包含多个Pointcut和多个Advice,所以Advisor是一种特殊的Aspect。但,这已经够用了!

接下来看下per-class Advisor的类图:


其实没有什么好看的,前面已经说过,Advisor包含一个Pointcut和一个Advisor;在AbstractGenericPointcutAdvisor中,持有一个Advice的引用;下面的几个实现,均是针对前面提到的几种不一样的Pointcut的实现。

 

3.3 Spring AOP实现的基本线索

咱们选择ProxyFactoryBean做为入口点和分析的开始。ProxyFactoryBean是在Spring IoC环境中,建立AOP应用的最底层方法,从中,能够看到一条实现AOP的基本线索。

全部的逻辑从如下的方法开始,咱们主要针对单例的代理对象的生成:


下面咱们深刻到SpringAOP核心代码的内部,看看代理对象的生成机制,拦截器横切逻辑以及织入的实现。

 

3.4  代理对象的生成

对于getSingletonInstance()方法返回了什么,这就是代理对象如何产生的逻辑了,然咱们须根溯源,看看传说中的proxy究竟是如何一步一步的产生的。


ProxyFactoryBean是AdvisedSupport的子类,Spring使用AopProxy接口把AOP代理的实现与框架的其余部分分离开来。在AdvisedSupport中经过这样的方式来获得AopProxy,固然这里须要获得AopProxyFactory的帮助 ,从JDK或者cglib中获得想要的代理对象:


这个DefaultAopProxyFactory是Spring用来生成AopProxy的地方,它包含JDK和Cglib两种实现方式。让我接着往里面看:


能够看到其中的代理对象能够由JDK或者Cglib来生成,JdkDynamicAopProxy类和Cglib2AopProxy都实现的是AopProxy的接口,咱们进入JdkDynamicAopProxy实现中看看Proxy是怎样生成的:


用Proxy包装target以后,经过ProxyFactoryBean获得对其方法的调用就被Proxy拦截了, ProxyFactoryBean的getObject()方法获得的其实是一个Proxy了,target对象已经被封装了。对 ProxyFactoryBean这个工厂bean而言,其生产出来的对象是封装了目标对象的代理对象。

 

3.5 拦截器的做用

前面分析了SpringAOP实现中获得Proxy对象的过程,接下来咱们去探寻Spring AOP中拦截器链是怎样被调用的,也就是Proxy模式是怎样起做用的。
还记得在JdkDynamicAopProxy中生成Proxy对象的时候,有一句这样的代码吗?

return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);

这里咱们的JdkDynamicAopProxy实现了InvocationHandler这个接口,this参数对应的是InvocationHandler对象,也就是说当 Proxy对象的函数被调用的时候,InvocationHandler的invoke方法会被做为回调函数调用:



上面所说的目标对象方法的调用,是经过AopUtils的方法调用,使用反射机制来对目标对象的方法进行的:


接下来,咱们来看具体的ReflectiveMethodInvocation中proceed()方法的实现,也就是拦截器链的实现机制:


从上面的分析咱们看到了Spring AOP拦截机制的基本实现,好比Spring怎样获得Proxy,怎样利用JAVA Proxy以及反射机制对用户定义的拦截器链进行处理。

 

3.6 织入的实现

在上面调用拦截器的时候,通过一系列的注册,适配的过程之后,拦截器在拦截的时候,会调用到预置好的一个通知适配器,设置通知拦截器,这是一系列Spring设计好为通知服务的类的一个,是最终完成通知拦截和实现的地方,例如对 MethodBeforeAdviceInterceptor的实现是这样的:


能够看到通知适配器将advice适配成Interceptor之后,会调用advice的before方法去执行横切逻辑。这样就成功的完成了before通知的织入。

相关文章
相关标签/搜索