spring技术内幕7-AOP基础

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

2、AOP和OOP的区别:

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

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的一个子项目。

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:指将切面应用到目标对象,并导致代理对象创建的过程。

5、AOP的体系结构:

AOP联盟定义的AOP体系结构如下:

层次3:

语言和开发环境                

                                                      AOP程序

                                                  基础 + 切面 + 配置

                               解析/类型                            检查/优化

层次2:

面向方面系统

                      配置模型                            逻辑配置                              AOP模型

                  (高层API)                                                                       (高层API)

                                                                                                                编织逻辑

层次1:

底层编织

实现模块                    反射              元数据处理                   程序处理                  类加载器框架                    拦截器框架

层次3语言和开发环境:基础是指待增加对象或者目标对象;切面通常包括对于基础的增加应用;配置是指AOP体系中提供的配置环境或者编织配置,通过该配置AOP将基础和切面结合起来,从而完成面对目标对象的编织实现。

层次2面向方面系统:配置模型,逻辑配置和AOP模型是为上层的语言和开发环境提供支持的,主要功能是将需要增强的目标对象、切面和配置使用AOP的API转换、抽象、封装成面向方面中的逻辑模型。

层次1底层编织实现模块:主要是将面向方面系统抽象封装的AOP模型编织进待增强的目标对象的实现技术。

6、Advice通知:

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

(1)以一个方法为例,讲解Spring中通知的类型:

public 返回类型   方法名(参数列表){    --->环绕通知

                  方法前处理代码               -->前置通知

try{

             方法具体实现(方法体)....

              方法后处理代码              --> 后置通知

  }Catch(异常类型 e){

            异常处理...                -->例外通知

}finally{

           最后处理代理....       -->最终通知

}

}

(2)前置通知BeforeAdvice:

a、前置通知的源码如下:

package org.springframework.aop;

import org.aopalliance.aop.Advice;

public interface BeforeAdvice extends Advice{

}

其中Advice是AOP联盟提供的通知接口。

b、前置通知子接口方法前置通知MethodBeforeAdvice:

在前置通知BeforeAdvice的继承关系中,定义了为待增强目标方法设置的前置增强接口方法前置通知MethodBeforeAdvice,源码如下:

public interface MethodBeforeAdvice extends BeforeAdvice{

         //前置方法通知的回调方法,在目标对象方法被调用之前调用

        //method参数是目标方法的反射对象,args是目标方法的输入参数数组

          void before(Method method,Object[] args,Object target) throws Throwable;

}

c、前置方法通知实现类CountingBeforeAdvice:

方法前置通知MethodBeforeAdvice接口的实现类比较多,我们以最简单的CountingBeforeAdvice为例,分析方法前置通知所做的具体操作,CountingBeforeAdvice的源码如下:

public class CountingBeforeAdvice extends MethodCounter imlements MethodBeforeAdvice{

             //实现方法前置通知MethodBeforeAdvice接口的方法

                 public void before(Method m, Object[] args,Object target) throw Throwable {

                  //以目标对象方法作为参数,调用父类MethodCounter的count方法统计方法调用次数

                        count(m);

                  }

        }

CounterBeforeAdvice的父类MethodCounter的源码如下:

public class MethodCounter implements Serializable {

            //方法名->方法调用次数的map集合,存储方法的调用次数

            private HashMap<String,Integer> map = new HashMap<String,Integer>();

            //所有方法的总调用次数

            private int allCount;

           //统计方法调用次数,CountingBeforeAdvice的调用入口

            protected void count(Method m){

                 count(m.getName());

            }

            //统计指定名称方法的调用次数

            protected void count(String methodName){

                //从方法名->方法调用次数集合中获取指定名称方法的调用次数

                  Integer i = map.get(methodName);

                //如果调用次数不为null,则将调用次数加1,如果调用次数为null,则设置调用次数为1

                   i = (i != null) ? new Integer(i.intValue() +1) : new Integer(1);

                //重新设置集合中保存的方法调用次数

                  map.put(menthodName,i);

                 //所有方法总调用次数加1

                    ++allCount;

            }

           //获取指定名称方法的调用次数

            public int getCalls(String methodName){

                Integer i = map.get(methodName);

                return (i != null ? i.intValue() : 0);

            }

            //获取所有方法的总调用次数

             public int getCalls(){

                    return allCounts;

             }

             public boolean equals(Object other){

                    return (other != null && other.getClass() == this.getClass());

              }

              public int hashCode(){

                  return getClass().hashCode();

               }

}

(3)最终通知AfterAdvice:

a、最终通知AfterAdvice的源码:

public interface AfterAdvice extends Advice {

}

和前置通知一样,最终通知也是继承了AOP联盟定义的Advice接口。

b、最终通知的子接口后置通知AfterReturningAdvice:

最终通知的继承体系中,后置通知AfterReturningAdvice接口继承了最终通知接口,源码如下:

public interface AfterReturningAdvice extends AfterAdvice{

  //后置通知的回调方法,在目标方法对象调用结束并成功返回之后调用

 //returnValue参数为目标方法对象的返回值,method参数为目标方法对象,args为目标方法对象的输入参数

 void afterReturning(Object returnValue,Method method,Object[] args,Object target) throws Throwable;

}

c、后置通知的实现类CountingAfterReturningAdvice:

后置通知接口AfterReturningAdvice的实现类也必将多,我们以CountingAfterReturningAdvice类为例,分析后置通知的具体操作:

public class CountingAfterReturningAdvice extends MethodCounter imlements AfterReturningAdvice{

     //实现后置通知AfterReturningAdvice的回调方法

     public void afterReturning(Object o,Method m,Object[] args,Object target) throws Throwable {

         //调用父类MethodCounter的count方法,统计方法的调用次数

           count(m);

    }

}

(4)例外通知ThrowAdvice:

a、例外通知ThrowAdvice的源码

例外通知ThrowAdvice接口也继承自最终通知AfterAdvice,其源码如下:

public interface ThrowsAdvice extends AfterAdvice{

}

b、例外通知实现类CountingThrowsAdvice:

例外通知并没有指定需要实现的接口方法,它在抛出异常时被回调,这个回调是AOP利用JDK的反射机制来完成的,我们以其实现类CountingThrowsAdvice为例分析例外通知的用法,源码如下:

public static class CountingThrowsAdvice extends MethodCounter implments ThrowsAdvice {

     //当抛出IO类型的异常时的回调方法,统计异常被调用的次数

     public void afterThrowing(IOException ex) throws Throwable{

             count(IOException.class.getName());

     }

    //当抛出UncheckedException类型异常时的回调方法,统计异常被调用的次数

     public void afterThrowing(UncheckedException ex) throws Throwable {

             count(UncheckedException.class.getName());

     }

}

7、Pointcut切入点:

Pointcut切入点决定通知Advice应该作用于哪个连接点,即通过Pointcut切入点来定义目标对象中需要使用AOP增强的方法集合,这些集合的选取可以按照一定的规则来完成。

Pointcut通常意味着标识方法,这些需要增强的方法可以被某个正则表达式进行标识,或者根据指定方法名进行匹配等。

(1)Pointcut切入点源码:

public interface Pointcut {

   //获取类过滤器

   ClassFilter getClassFilter();

  //获取匹配切入点的方法

   MethodMatcher getMethodMatcher();

  //总匹配的标准切入点实例

   Pointcut TRUE = TruePointcut.INSTANCE;

}

(2)Pointcut接口的实现类JdkRegexpMethodPointcut:

查看Pointcut切入点的继承体系,发现Pointcut切入点的实现类非常的多,如针对注解配置的AnnotationMatchingPointcut、针对正则表达式的JdkRegexpMethodPointcut等等,我们以JdkRegexpMethodPointcut为例,分析切入点匹配的具体实现,源码如下:

public class JdkRegexpMethodPointcut extends AbstractRegexpMethodPointcut {

   //要编译的正则表达式模式

   private Pattern[] compilePatterns = new Pattern[0];

   //编译时要排除的正则表达式模式

   private Pattern[] compiledExclusionPatterns = new Pattern[0];

  //将给定的模式字符串数组初始化编译的正则表达式模式

   protected void initPatternRepresentation(String[] patterns) throws PatternSyntaxException {

      this.compiledPatterns = compilePatters(patterns);

   }

   //将给定的模式字符串数组初始化为编译时要排除的正则表达式模式

    protected void initExcludedPatternRepresentation(String[] excludedPatterns) throws PatternSyntaxException {

         this.compiledExclusionPatterns = compilePatterns(excludedPatterns);

       }

   //使用正则表达式匹配给定的名称

     protected boolean matches(Sting pattern,int patternIndex){

          Matcher matcher = this.compiledPatterns[patternIndex].mathcer(pattern);

          return matcher.matches();

      }

  //使用要排除的正则表达式匹配给定的名称

    protected boolean matchesExclusion(String candidate,int patternIndex){

         Mathcer matcher = this.compiledExclusionPatterns[patternIndex].matcher(candidate);

         return matcher.matches();

    }

  //将给定的字符串数组编译为正则表达式的模式

   private Pattern[] compilePatterns(String[] source) throws PatternSystaxExceptioin {

          Pattern[] destination = new Pattern[source.length];

          for(int i=0;i<source.length;i++){

               destination[i] = Pattern.compile(source[i]);

           }

          return destination;

    }

}

从上面的源码分析中,我们可以看到,最简单的使用正则表达式匹配的Pointcut切入点基本功能就是根据正则表达式判断方法名等是否匹配。

8、Advisor通知器:

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

(1)Advisor通知器

Advisor通知器的源码如下:

public interface Advisor {

    //获取切面的通知Advice

     Advice getAdvice();

  //判断这个通知是否和某个特定的实例对象相关

    boolean isPerInstance();

}

(2)Advisor通知器的实现类DefaultPointcutAdvisor:

查看Advisor通知器的继承体系,发现Advisor的实现类很多,我们以最常用的DefaultPointcutAdvisor为例,分析通知器的工作原理。

a、DefaultPointcutAdvisor的源码如下:

public class DefaultPointcutAdvisor extends AbstractGenericPointcutAdvisor implements Serializable {

    //默认切入点

    //Pointcut.TRUE在切入点中的定义为:Pointcut TRUE = TruePointcut.INSTANCE;

     private Pointcut pointcut = Pointcut.TRUE;

   //无参构造方法,创建一个空的通知器

     public DefaultPointcutAdvisor(){

      }

   //创建一个匹配所有方法的通知器

    public DefaultPointcutAdvisor(Advice advice){

          this(Pointcut.TRUE,advice);

    }

  //创建一个指定切入点和通知的通知器

    public DefaultPointcutAdvisor(Pointcut pointcut,Advice advice){

         this.pointcut = pointcut;

         setAdvice(advice);

    }

   //为通知设置切入点

   public void setPointcut(Pointcut pointcut){

           this.pointcut = (pointcut != null ? pointcut :Pointcut.TRUE);

   }

   //获取切入点

    public Pointcut getPointcut(){

          return this.pointcut;

    }

   public String toString(){

          return getClass().getName() + ": pointcut [" + getPointcut() + "];advice [" + getAdvcie() + "]";

   }

}

上述源码中,通知器的默认切入点是Pointcut.TRUE,Pointcut.TRUE在切入点中的定义为:Pointcut TRUE = TruePointcut.INSTANCE;

b、TruePointcut:

TruePointcut作用通知器默认的切入点,其主要功能是配置默认的类过滤器和方法匹配器,即定义String AOP对于哪些类的哪些方法起作用,源码如下:

class TruePointcut imlements Pointcut,Serializable{

  //INSTANCE是TruePointcut类的一个常量单件,即整个应用中只有这一个,不会创建第二个实例对象,确保该实例对象的唯一性,单态模型

    public static final TruePointcut INSTANCE = new TruePointcut();

    //单态模式构造方法

     private TruePointcut(){

     }

   //获取切入点的类过滤器

     public ClassFilter getClassFilter(){

          return ClassFilter.TRUE;

     }

   //获取切入点的方法匹配器

   public MethodMatcher getMethodMatcher(){

         return MethodMatcher.TRUE;

   }

   //获取单态模式对象的方法

   private Object readResolve(){

       return INSTANCE;

   }

  public String toString(){

        return "Pointcut.TRUE";

  }

}

从TruePointcut的源码我们看到,切入点使用TrueClassFilter作为类过滤器,匹配任意的类,使用TrueMethodMatcher作为方法匹配器,匹配任意的方法。下面我们继续分析TrueClassFilter类过滤器和TrueMethodMatcher方法匹配器。

c、TrueClassFilter:

TrueClassFilter作为默认切入点的默认类过滤器,主要告诉切入点对哪些类进行增强,源码如下:

class TrueClassFilter implements ClassFilter,Serializable {

    //单态模式

     public static final TrueClassFilter INSTANCE = new TrueClassFilter();

   //单态模式的构造方法

    private TrueClassFilter(){

    }

   //切入点过滤匹配类的方法,默认对所有的类都增强

     public boolean matches(Class clazz){

          return true;

      }

    //获取单态模式对象的方法

    private Object readResolve(){

        return INSTANCE;

     }

    public String toString(){

         return "ClassFilter.TRUE";

    }

}

d、TrueMethodMatcher:

TrueMethodMatcher作为默认切入点的默认方法匹配器,主要告诉切入点对哪些方法进行增强,源码如下:

class TrueMethodMatcher implements MethodMatcher,Serializable {

      //单态模式

      public static final TrueMethodMathcer INSTANCE = new TrueMethodMatcher();

      //单态模式构造方法

       private TrueMethodMatcher(){

       }

      //不支持运行时调用 

       public boolean isRuntime(){

             return false;

       }

     //切入点匹配方法时调用,默认匹配所有的方法

      public boolean matches(Method method,Class targetClass){

            return true;

      }

    //运行时调用将抛出异常

     public boolean matches(Method method,Class targetClass,Object[] args){

           throw new UnsupportedOperationException();

      }

    //获取单态模式对象的方法

      private Object readResolve(){

           return INSTANCE;

      }

     public String toString(){

           return "MethodMatcher.TRUE";

      }

}

从上面方法匹配器的源码,我们可以看出,切入点方法进行匹配时不支持运行时的匹配,如果在运行时进行匹配将抛出异常。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值