本文摘自 博文--《Spring设计思想》AOP设计基本原理java
Spring 提供了AOP(Aspect Oriented Programming) 的支持, 那么,什么是AOP呢?本文将经过一个另一个角度来诠释AOP的概念,帮助你更好地理解和使用Spring AOP。spring
读完本文,你将了解到:
1. Java程序运行在JVM中的特征
2. Java程序的执行流【了解AOP、链接点(Join Point)、切入点(point cut) 的概念 】
3. 引入了代理模式的Java程序执行流(AOP实现的机制)
4. Spring AOP的工做原理
当咱们在某个类Foo中写好了一个main()方法,而后执行java Foo,你的Java程序之旅就开启了,以下:编程
public class Foo {设计模式
public static void main(String[] args) { 框架
// your codes begins hereui
}lua
}spa
那么在这个执行的过程当中,JVM都为你干了什么呢?.net
当你执行java Foo 的时候,JVM会建立一个主线程main,这个主线程以上述的main()方法做为入口,开始执行你的代码。每个线程在内存中都会维护一个属于本身的栈(Stack),记录着整个程序执行的过程。栈里的每个元素称为栈帧(Stack Frame),栈帧表示着某个方法调用,会记录方法调用的信息;实际上咱们在代码中调用一个方法的时候,在内存中就对应着一个栈帧的入栈和出栈。线程
关于虚拟机线程栈(JVM Stack)
关于虚拟机线程栈(JVM Thread Stack)的模型不是本文的重点,因此就不此处展开,读者能够查看个人另一篇博文,这里面有很是详尽的介绍 。 《Java虚拟机原理图解》三、JVM运行时数据区 ]
在某个特定的时间点,一个Main线程内的栈会呈现以下图所示的状况:
从线程栈的角度来看,咱们能够看到,JVM处理Java程序的基本单位是
方法调用
。实际上,JVM执行的最基本单位的指令(即原子操做)是汇编语言性质的机器字节码。这里之因此讲方法调用
时Java程序的基本执行单位,是从更宏观的角度看待的。如何获取到虚拟机线程栈中的内容(即方法调用过程)?
试想一下,如何可以获取到JVM线程栈中的方法调用的内容? 我相信全部的Java programmer都知道这个答案。Java Programmer几乎天天都能看到它------当咱们的代码抛出异常而未捕获或者运行时出现了Error错误时,咱们会受到一个很是讨厌的Log信息,以下:
固然,除了代码抛出异常外,咱们仍是能够其余方式察觉JVM线程栈内的内容。能够经过Thread.dumpStack()方法建立一个假的Exception实例,而后将这个Exception实例记录的当前线程栈的内容输出到标准错误流中。例如我在某处代码里执行了Thread.dumpStack()方法,输出了以下的结果:
若是从虚拟机线程栈的角度考虑Java程序执行的话,那么,你会发现,真个程序运行的过程就是方法调用的过程。咱们按照方法执行的顺序,将方法调用排成一串,这样就构成了Java程序流。
咱们将上述的线程栈里的方法调用按照执行流排列,会有以下相似的图:
基于时间序列,咱们能够将方法调用排成一条线。而每一个方法调用则能够当作Java执行流中的一个节点。这个节点在AOP的术语中,被称为Join Point,即链接点。 一个Java程序的运行的过程,就是若干个链接点链接起来依次执行的过程。
在咱们正常的面向对象的思惟中, 咱们考虑的是如何按照时间序列经过方法调用来实现咱们的业务逻辑。那么,什么是AOP(即面向切面的编程)呢?
一般面向对象的程序,代码都是按照时间序列纵向展开的,而他们都有一个共性:即都是已方法调用做为基本执行单位展开的。 将方法调用当作一个链接点,那么由链接点串起来的程序执行流就是整个程序的执行过程。
AOP(Aspect Oriented Programming)则是从另一个角度来考虑整个程序的,AOP将每个方法调用,即链接点做为编程的入口,针对方法调用进行编程。从执行的逻辑上来看,至关于在以前纵向的按照时间轴执行的程序横向切入。至关于将以前的程序横向切割成若干的面,即Aspect.每一个面被称为切面。
因此,根据个人理解,AOP本质上是针对方法调用的编程思路。
既然AOP是针对切面进行的编程的,那么,你须要选择哪些切面(即 链接点Joint Point)做为你的编程对象呢?
由于切面本质上是每个方法调用,选择切面的过程实际上就是选择方法的过程。那么,被选择的切面(Aspect)在AOP术语里被称为切入点(Point Cut). 切入点实际上也是从全部的链接点(Join point)挑选本身感兴趣的链接点的过程。
Spring AOP框架中经过 方法匹配表达式来表示切入点(Point Cut),至于详细的表达式语法是什么 不是本文的重点,请读者自行参考Spring相应的说明文档。
既然AOP是针对方法调用(链接点)的编程, 如今又选取了你感兴趣的本身感兴趣的连接点---切入点(Point Cut)了,那么,AOP能对它作什么类型的编程呢?AOP能作什么呢?
了解这个以前,咱们先要知道一个很是重要的问题: 既然AOP是对方法调用进行的编程,那么,AOP如何捕获方法调用的呢? 弄清楚这个问题,你不得不了解设计模式中的代理模式了。下面咱们先来了解一下引入了代理模式的Java程序执行流是什么样子的。
咱们假设在咱们的Java代码里,都为实例对象经过代理模式建立了代理对象,访问这些实例对象必需要经过代理,那么,加入了proxy对象的Java程序执行流会变得稍微复杂起来。
咱们来看下加入了proxy对象后,Java程序执行流的示意图:
由上图能够看出,只要想调用某一个实例对象的方法时,都会通过这个实例对象相对应的代理对象, 即执行的控制权先交给代理对象。
关于代理模式
代理模式属于Java代码中常常用到的、也是比较重要的设计模式。代理模式能够为某些对象除了实现自己的功能外,提供一些额外的功能,大体做用以下图所示:
![]()
关于代理模式的详细介绍和分析,请参考个人另外一篇博文:
Java动态代理机制详解(JDK 和CGLIB,Javassist,ASM)
加入了代理模式的Java程序执行流,使得全部的方法调用都通过了代理对象。对于Spring AOP框架而言,它负责控制着真个容器内部的代理对象。当咱们调用了某一个实例对象的任何一个非final的public方法时,整个Spring框架都会知晓。
此时的SpringAOP框架在某种程度上扮演着一个上帝的角色:它知道你在这个框架内所作的任何操做,你对每个实例对象的非final的public方法调用均可以被框架察觉到!
既然Spring代理层能够察觉到你所作的每一次对实例对象的方法调用,那么,Spring就有机会在这个代理的过程当中插入Spring的本身的业务代码。
前面已经介绍了AOP编程首先要选择它感兴趣的链接点----即切入点(Point cut),那么,AOP能对切入点作什么样的编程呢? 咱们先将代理模式下的某个链接点细化,你会看到以下这个示意图所表示的过程:
为了下降咱们对Spring的AOP的理解难度,我在这里将代理角色的职能进行了简化,方便你们理解。(注意:真实的Spring AOP的proxy角色扮演的只能比这复杂的多,这里只是简化,方便你们理解,请不要先入为主)代理模式的代理角色最起码要考虑三个阶段:
1. 在调用真正对象的方法以前,应该须要作什么?
2. 在调用真正对象的方法过程当中,若是抛出了异常,须要作什么?
3.在调用真正对象的方法后,返回告终果了,须要作什么?
AOP对这个方法调用的编程,就是针对这三个阶段插入本身的业务代码。
如今咱们假设当前RealSubject这个角色的类是 org.luanlouis.springlearning.aop.FooService ,当前这个链接点对应的方法签名是:public void foo()。那么上述的代理对象的三个阶段将会有如下的处理逻辑:
1. 在调用真正对象的方法以前,
proxy会告诉Spring AOP: "我将要调用类org.luanlouis.springlearning.aop.FooService 的public void foo() ,在调用以前,你有什么处理建议吗?";
Spring AOP这时根据proxy提供的类名和方法签名,而后拿这些信息尝试匹配是否在其感兴趣的切入点内,若是在感兴趣的切入点内,Spring AOP会返回 MethodBeforeAdvice处理建议,告诉proxy应该执行的操做;
2. 在调用真正对象的方法过程当中,若是抛出了异常,须要作什么?
proxy告诉Spring AOP: “我调用类org.luanlouis.springlearning.aop.FooService 的public void foo()过程当中抛出了异常,你有什么处理建议?”
Spring AOP根据proxy提供的类型和方法签名,肯定了在其感兴趣的切入点内,则返回相应的处理建议ThrowsAdvice,告诉proxy这个时期应该采起的操做。
3.在调用真正对象的方法后,返回告终果了,须要作什么?
proxy告诉Spring AOP:"我调用类org.luanlouis.springlearning.aop.FooService 的public void foo()结束了,并返回告终果你如今有什么处理建议?";
Spring AOP 根据proxy提供的类型名和方法签名,肯定了在其感兴趣的切入点内,则返回AfterReturingAdivce处理建议,proxy获得这个处理建议,而后执行建议;
上述的示意图中已经明确代表了Spring AOP应该作什么样的工做:根据proxy提供的特定类的特定方法执行的特定时期阶段给出相应的处理建议。要完成该工做,Spring AOP应该实现:
1.肯定本身对什么类的什么方法感兴趣? -----即肯定 AOP的切入点(Point Cut),这个能够经过切入点(Point Cut)表达式来完成;
2. 对应的的类的方法的执行特定时期给出什么处理建议?------这个须要Spring AOP提供相应的建议 ,即咱们常说的Advice。