Spring源码分析----AOP概念(Advice,Pointcut,Advisor)和AOP的设计与实现

一、基本知识

1.1.AOP基本概念:
    Aspect-Oriented Programming,面向方面编程的简称,Aspect是一种新的模块化机制,用来描述分散在对象、类或方法中的横切关注点(crosscutting concern),从关注点中分离出横切关注点是面向方面程序设计的核心所在。分离关注点使得解决特定领域问题的代码从业务逻辑中独立出来,业务逻辑的代码中不再含有针对特定领域问题代码的调用,业务逻辑同特定领域问题的关系通过方面来封装、维护,这样原本分散在整个应用程序中的变动就可以很好地管理起来。

1.2.AOP和OOP的区别:

    面向方面编程AOP和面向对象编程OOP在字面上虽然非常类似,但却是面向不同领域的两种设计思想。
    OOP针对业务处理过程的实体及其属性和行为进行抽象封装,以获得更加清晰高效的逻辑单元划分。
    而AOP则是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。

1.3. AOP常用的技术以及实现:
常用的AOP技术有:
    (1).AspectJ:源代码和字节码级别的方面编织器,用户需要使用不同于Java的新语言。
    (2).AspectWerkz:AOP框架,使用字节码动态编织器和XML配置。
    (3).JBoss-AOP:基于拦截器和元数据的AOP框架,运行在JBoss应用服务器上。
AOP中使用的一些实现技术有:、
    (1).BCEL:Byte-Code Engineering Library,Java字节码操作类库。
    (2).Javassist:Java字节码操作类库,JBoss的一个子项目。

1.4.面向方面编程(AOP)的常用术语:
    (1).切面Aspect: Aspect 声明类似于 Java 中的类声明,在 Aspect 中会包含着一些切入点Pointcut 以及对切入点进行相应的操作的通知Advice。
    (2).连接点Joint point:表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它连接点jointpoint。
    (3).切入点Pointcut:表示一组连接点jointpoint,这些连接点或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的操作处理通知Advice将要发生的地方。
    (4).通知Advice:Advice 定义了在切入点pointcut 里面定义的程序点具体要做的操作和处理,它通过 before、after 和 around 来区别是在每个切入点之前、之后还是代替执行的代码。
    (5).目标对象Target:代理的目标对象,即切面要被应用到的目标对象。
    (6).织入Weave:指将切面应用到目标对象,并导致代理对象创建的过程。

二、Advice通知:

    Advice通知是AOP联盟定义的一个接口,定义当拦截到连接点做相应的处理操作,为切面增强提供织入接口。在spring AOP中,通知主要描述Spring AOP围绕方法调用而注入切面的行为,Spring AOP的通知扩展了AOP联盟的通知接口,提供了前置通知BeforeAdvice、后置通知AfterReturningAdvice、最终通知AfterAdvice和例外通知ThrowsAdvice等。 (1). 以一个方法为例,讲解 Spring 中通知的类型:

[java]  view plain  copy
  1. /** 
  2.  * proxy: 代理对象。 一般不使用该对象 
  3.  * method: 正在被调用的方法 
  4.  * args: 调用方法传入的参数 
  5.  */  
  6. @Override  
  7. public Object invoke(Object proxy, Method method, Object[] args)  
  8.         throws Throwable {  // 环绕通知 @Around  
  9.     String methodName = method.getName();  
  10.     //打印日志  
  11.     System.out.println("[before] The method " + methodName + " begins with " + Arrays.asList(args));  
  12.   
  13.     //调用目标方法  
  14.     Object result = null;  
  15.   
  16.     try {  
  17.         //前置通知 @Before  
  18.         result = method.invoke(target, args);  
  19.   
  20.         //返回通知 @AfterReturning, 可以访问到方法的返回值  
  21.     } catch (NullPointerException e) {  
  22.         e.printStackTrace();  
  23.         //异常通知 @AfterThrowing, 可以访问到方法出现的异常  
  24.     }  
  25.   
  26.     //后置通知 @After. 因为方法可以能会出异常, 所以访问不到方法的返回值  
  27.   
  28.     //打印日志  
  29.     System.out.println("[after] The method ends with " + result);  
  30.   
  31.     return result;  
  32. }   

Spring源码分析----AOP概念(Advice,Pointcut,Advisor)和AOP的设计与实现_第1张图片

[java]  view plain  copy
  1. public interface Advice {  
  2.   
  3. }  
  4.   
  5. public interface BeforeAdvice extends Advice {  
  6.   
  7. }  
  8.   
  9.     // 前置增强接口,使用这个前置接口需要实现一个回调函数  
  10.     public interface MethodBeforeAdvice extends BeforeAdvice {  
  11.   
  12.         /** 
  13.          * 作为回调函数,该方法的实现在Advice中被配置到目标方法后,会调用目标方法时被回调。 
  14.          * @param method Method对象,是目标方法的反射对象 
  15.          * @param args 对象数组,包含目标方法的输入参数 
  16.          * @param target   
  17.          * @throws Throwable 
  18.          */  
  19.         void before(Method method, Object[] args, Object target) throws Throwable;  
  20.   
  21.     }  
  22.   
  23. public interface AfterAdvice extends Advice {  
  24.   
  25. }  
  26.   
  27. public interface AfterReturningAdvice extends AfterAdvice {  
  28.     // 该方法也是一个回调方法  
  29.     void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable;  
  30.   
  31. }  
  32.   
  33. public interface ThrowsAdvice extends AfterAdvice {  
  34.   
  35. }  

三、Pointcut切点

    Pointcut切入点决定通知Advice应该作用于哪个连接点,即通过Pointcut切入点来定义目标对象中需要使用AOP增强的方法集合,这些集合的选取可以按照一定的规则来完成。
    Pointcut通常意味着标识方法,这些需要增强的方法可以被某个正则表达式进行标识,或者根据指定方法名进行匹配等。下面是Pointcut设计:

Spring源码分析----AOP概念(Advice,Pointcut,Advisor)和AOP的设计与实现_第2张图片

(1).Pointcut切入点源码:

[java]  view plain  copy
  1. public interface Pointcut {    
  2.     //获取类过滤器    
  3.     ClassFilter getClassFilter();    
  4.   
  5.     //获取匹配切入点的方法        
  6.     MethodMatcher getMethodMatcher();    
  7.   
  8.     //总匹配的标准切入点实例    
  9.     Pointcut TRUE = TruePointcut.INSTANCE;    
  10. }   
    在Pointcut的基本接口定义中可以看到,需要返回一个MethodMatcher。对于Point的匹配判断功能,具体是由这个返回的MethodMatcher来完成的,也就是说,由这个MethodMatcher来判断是否需要对当前方法调用进行增强,或者是否需要对当前调用方法应用配置好Advice通知。
    2).在Pointcut的类继承关系中,以正则表达式切点JdkRegexpMethodPointcut的实现原理为例,来具体了解切点Pointcut的工作原理。该类完成通过正则表达式对方法名进行匹配的功能。
[java]  view plain  copy
  1. public class JdkRegexpMethodPointcut extends AbstractRegexpMethodPointcut {    
  2.     //要编译的正则表达式模式    
  3.     private Pattern[] compiledPatterns = new Pattern[0];    
  4.     //编译时要排除的正则表达式模式    
  5.     private Pattern[] compiledExclusionPatterns = new Pattern[0];    
  6.   
  7.     //将给定的模式字符串数组初始化为编译的正则表达式模式    
  8.     protected void initPatternRepresentation(String[] patterns) throws PatternSyntaxException {    
  9.         this.compiledPatterns = compilePatterns(patterns);    
  10.     }    
  11.   
  12.     //将给定的模式字符串数组初始化为编译时要排除的正则表达式模式    
  13.     protected void initExcludedPatternRepresentation(String[] excludedPatterns) throws PatternSyntaxException {    
  14.         this.compiledExclusionPatterns = compilePatterns(excludedPatterns);    
  15.     }    
  16.   
  17.     //使用正则表达式匹配给定的名称    
  18.     protected boolean matches(String pattern, int patternIndex) {    
  19.         Matcher matcher = this.compiledPatterns[patternIndex].matcher(pattern);    
  20.         return matcher.matches();    
  21.     }    
  22.   
  23.     //使用要排除的正则表达式匹配给定的名称    
  24.     protected boolean matchesExclusion(String candidate, int patternIndex) {    
  25.         Matcher matcher = this.compiledExclusionPatterns[patternIndex].matcher(candidate);    
  26.         return matcher.matches();    
  27.     }    
  28.   
  29.     //将给定的字符串数组编译为正则表达的模式    
  30.     private Pattern[] compilePatterns(String[] source) throws PatternSyntaxException {    
  31.         Pattern[] destination = new Pattern[source.length];    
  32.         for (int i = 0; i < source.length; i++) {    
  33.             destination[i] = Pattern.compile(source[i]);    
  34.         }    
  35.         return destination;    
  36.     }    
  37. }    
    从上面的源码分析中,我们可以看到,最简单的使用正则表达式匹配的Pointcut切入点基本功能就是根据正则表达式判断方法名等是否匹配。

四、Advisor通知器:

    当完成对目标对象方法的增强行为操作(Advice)和切入点Point的设计开发之后,需要一个对象将目标对象、增强行为和切入点三者结合起来,通知器Advisor就是一个实现这个功能的对象,即通过Advisor通知器,可以定义那些目标对象的那些方法在什么地方使用这些增加的行为。
(1).Advisor通知器:
    Advisor通知器的源码如下:

[java]  view plain  copy
  1. public interface Advisor {    
  2.     //获取切面的通知Advice    
  3.     Advice getAdvice();    
  4.   
  5.     //判断这个通知是否和某个特定的实例对象相关    
  6.     boolean isPerInstance();    
  7. }    
(2). Advisor通知器的实现类DefaultPointcutAdvisor:
    查看Advisor通知器的继承体系,发现Advisor的实现类很多,我们以最常用的DefaultPointcutAdvisor为例,分析通知器的工作原理。
a. DefaultPointcutAdvisor源码如下
[java]  view plain  copy
  1. public class DefaultPointcutAdvisor extends AbstractGenericPointcutAdvisor implements Serializable {    
  2.     //默认切入点  — - - ——>  
  3.     //Pointcut.TRUE在切入点中的定义为:Pointcut TRUE = TruePointcut.INSTANCE;   
  4.     private Pointcut pointcut = Pointcut.TRUE;    
  5.   
  6.     //无参构造方法,创建一个空的通知器    
  7.     public DefaultPointcutAdvisor() {    
  8.     }    
  9.   
  10.     //创建一个匹配所有方法的通知器    
  11.     public DefaultPointcutAdvisor(Advice advice) {    
  12.         this(Pointcut.TRUE, advice);    
  13.     }    
  14.   
  15.     //创建一个指定切入点和通知的通知器    
  16.     public DefaultPointcutAdvisor(Pointcut pointcut, Advice advice) {    
  17.         this.pointcut = pointcut;    
  18.         setAdvice(advice);    
  19.     }    
  20.   
  21.     //为通知设置切入点    
  22.     public void setPointcut(Pointcut pointcut) {    
  23.         this.pointcut = (pointcut != null ? pointcut : Pointcut.TRUE);    
  24.     }    
  25.   
  26.     //获取切入点    
  27.     public Pointcut getPointcut() {    
  28.         return this.pointcut;    
  29.     }    
  30.     public String toString() {    
  31.         return getClass().getName() + ": pointcut [" + getPointcut() + "]; advice [" + getAdvice() + "]";    
  32.     }    
  33. }    
b.TruePointcut:
    TruePointcut作用通知器默认的切入点,其主要功能是配置默认的类过滤器和方法匹配器,即定义Spring AOP对于哪些类的哪些方法其作用,源码如下:
[java]  view plain  copy
  1. class TruePointcut implements Pointcut, Serializable {    
  2.     //INSTANCE是TruePointcut类的一个常量单件,即整个应用中只有这个一个,    
  3.     //不会创建第二个实例对象,确保该实例对象的唯一性,单例模型    
  4.     public static final TruePointcut INSTANCE = new TruePointcut();    
  5.   
  6.     //单态模式构造方法    
  7.     private TruePointcut() {    
  8.     }    
  9.   
  10.     //获取切入点的类过滤器    
  11.     public ClassFilter getClassFilter() {    
  12.         return ClassFilter.TRUE;    
  13.     }    
  14.     //获取切入点的方法匹配器    
  15.     public MethodMatcher getMethodMatcher() {    
  16.         return MethodMatcher.TRUE;  //  ————>  
  17.     }    
  18.   
  19.     //获取单态模式对象的方法    
  20.     private Object readResolve() {    
  21.         return INSTANCE;    
  22.     }    
  23.     public String toString() {    
  24.         return "Pointcut.TRUE";    
  25.     }    
  26. }    
  27.   
  28. //———————————————  
  29. public interface MethodMatcher {  
  30.   
  31.     boolean matches(Method method, Class targetClass);  
  32.   
  33.     boolean isRuntime();  
  34.   
  35.     boolean matches(Method method, Class targetClass, Object... args);  
  36.   
  37.     MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;  
  38.   
  39. }  
    从TruePointcut的源码我们看到,切入点使用TrueClassFilter作为类过滤器,匹配任意的类; 使用TrueMethodMatcher作为方法匹配器,匹配任意的方法。下面我们继续分析TrueClassFilter类过滤器和TrueMethodMatcher方法匹配器。
c.TrueClassFilter:
    TrueClassFilter作为默认切入点的默认类过滤器,主要告诉切入点对哪些类进行增强,源码如下

[java]  view plain  copy
  1. class TrueClassFilter implements ClassFilter, Serializable {    
  2.     //单态模式    
  3.     public static final TrueClassFilter INSTANCE = new TrueClassFilter();    
  4.   
  5.     //单态模式的构造方法    
  6.     private TrueClassFilter() {    
  7.     }    
  8.   
  9.     //切入点过滤匹配类的方法,默认对所有的类都增强    
  10.     public boolean matches(Class clazz) {    
  11.         return true;    
  12.     }    
  13.   
  14.     //获取单态模式对象的方法    
  15.     private Object readResolve() {    
  16.         return INSTANCE;    
  17.     }    
  18.     public String toString() {    
  19.         return "ClassFilter.TRUE";    
  20.     }    
  21. }     
d.TrueMethodMatcher:
    TrueMethodMatcher作为默认切入点的默认方法匹配器,主要告诉切入点对哪些方法进行增强,源码如下:

[java]  view plain  copy
  1. class TrueMethodMatcher implements MethodMatcher, Serializable {    
  2.     //单态模式    
  3.     public static final TrueMethodMatcher INSTANCE = new TrueMethodMatcher();    
  4.     //单态模式构造方法    
  5.     private TrueMethodMatcher() {    
  6.     }    
  7.   
  8.     //不支持运行时调用    
  9.     public boolean isRuntime() {    
  10.         return false;    
  11.     }    
  12.   
  13.     //切入点匹配方法时调用,默认匹配所有的方法    
  14.     public boolean matches(Method method, Class targetClass) {    
  15.         return true;    
  16.     }    
  17.   
  18.     //运行时调用将抛出异常    
  19.     public boolean matches(Method method, Class targetClass, Object[] args) {    
  20.         throw new UnsupportedOperationException();    
  21.     }    
  22.   
  23.     //获取单态模式对象的方法    
  24.     private Object readResolve() {    
  25.         return INSTANCE;    
  26.     }    
  27.     public String toString() {    
  28.         return "MethodMatcher.TRUE";    
  29.     }    
  30. }    
    从上面方法匹配器的源码,我们可以看出,切入点对方法进行匹配时不支持运行时的匹配,如果在运行时进行匹配将抛出异常。

六、AOP的设计与实现

    在Spring AOP实现中,使用的核心技术是动态代理,而这种动态代理实现上是JDK的一个特性。通过JDK的动态代理特性,可以为任意Java对象创建代理对象,对于具体使用来说,这个特性是通过Java Reflection API来完成的。
    Proxy模式: http://blog.csdn.net/ochangwen/article/details/51475807
1.Spring AOP的设计分析
    AOP模块是Spring的核心模块,虽然Java社区里AspectJ是最完整的AOP框架,但Spring AOP也提供了另外一种实现,这种实现并不是AspectJ的竞争者,相反,Spring AOP还将AspectJ集成了进来,为IoC容器和Spring应用开发提供了一个一致性的AOP解决方案。
     Spring AOP的核心技术是动态代理 。以动态代理技术为基础设计出了一系列AOP的横切实现,比如前置通知,返回通知,异常通知等。同是,Spring AOP还提供了一系列的Pointcut来匹配切入点,可以使用现有的切入点来设计横切面,也可以扩展相关的Pointcut方法来实现切入需要。
    为了让AOP起作用,需要完成一系列过程,如,需要为目标对象建立代理对象,这个代理对象可以通过使用JDK的Proxy来完成,也可以通过第三方的类生成器CGLIB来完成。然后,还需要启动代理对象的拦截器来完成各种横切面的织入,这一系列的织入设计是通过一系列Adapter来实现的。通过一系列Adapter的设计,可以把AOP的横切面设计和Proxy模式有机地结合起来,从而实现在AOP中定义好的各种织入方式。具体实现后面分析。

2.Spring AOP的应用场景
    Spring AOP为IoC的使用提供了更多的便利,一方面,应用可以直接使用AOP的功能,设计应用的横切关注点,把跨越应用程序多个模块的功能抽象出来,并通过简单的AOP的使用,灵活地编制到模块中,比如可以通过AOP实现应用程序中的日志功能。另一方面,在Spring内部,一些支持模块也是通过Spring AOP来实现的,如事务处理。从这两个角度就已经看到Spring AOP的核心地位了。

你可能感兴趣的:(aop,spring)