Jboss AOP之Advice

    advice是aspect方法,当特定joinpoint 执行点时advice被触发。JBoss AOP提供了五种advice类型。默认是around advice,它能被应用到所有execution模式上。

around advice

public Object [advice name]([Invocation] invocation) throws
Throwable
{
    try{
       // do something before joinpoint execution
       // execute the joinpoint and get its return value
       Object returnValue = invocation.invokeNext();
       // do something after joinpoint has executed successfully ...
       // return a value
       return returnValue;
    }catch(Exception e){
       //handle any exceptions arising from calling the joinpoint
       throw e;
    }finally{
       //Take some action once the joinpoint has completed
       successfully or not
    }
}
    Invocation可以是org.jboss.aop.joinpoint.Invocation或它的子类。around advice封装了一个joinpoint,它必须在它的执行过程中在jointpoint处理execution,这个可以通过调用invocation.invokeNext()实现。然而该方法在joinpoint的下一个around advice处执行execution。在链的结尾invokeNext()方法会在它自己的joinpoint处执行。被around advice返回的值将替代jointpoint的返回值。例如,两个around advice绑定到一个joinpoint上,第一个around advice通过调用invokeNext()触发第二个around advice。第二个around advice也会通过invokeNext()触发joinpoint execution。结果,第二个advice接受到joinpoint返回值,第二个advice的返回值将被第一个advice接受,最后advice的返回值将会替代joinpoint处的返回值。一般来说,around advice会简单返回joinpoint的返回值。
    如果around advice需要完全替代joinpoint execution,可以跳过invokeNext()。同时会跳过chain中的around advices。也可以调用invokeTarget(),直接调用jointpoint目标,跳过任何advices。

Before/After/After-Throwing/Finally Advices

    这些advice是更加轻量级,因为它们并没有包装joinpoint,为了避免每个joinpoint创建Invocation对象。Jboss AOP为这些advice提供了JoinPoint Bean。这些beans包含了诸如joinpoint的相关信息,如Invokecation,但不提供invokeNext()方法。

Before Advice Signature

    before advice在joinpoint之前执行,before advice必须遵循这种形式:
public void [advice name]([annotated parameter], [annotated parameter],...[annotated parameter])

After Advice Signature

    因为after advice在joinpoint之后执行,它可以返回值来替换joinpoint的返回值。
public void [advice name]([annotated parameter], [annotated parameter],...[annotated parameter])
public [return type] [advice name]([annotated parameter],[annotated parameter],...[annotated parameter])
    在第一个签名中,after advice不会覆写joinpoint返回值,使用第二个签名,after advice替代jointpoint返回值。

After-Throwing Advice Signature

    次advice仅在joinpoint执行后抛出java.lang.Throwalbe或它的子类异常被触发。
public void [advice name]([annotated parameter], [annotated parameter],...[annotated parameter])
    不同与其他advice类型,after-throwing advice有强制annotation参数,次参数是被joinpoint抛出的异常。

Finally Advice Signature

    finally advice在jointpoint执行后从finally块内被触发。
public void [advice name]([annotated parameter], [annotated parameter],...[annotated parameter])
public [return type] [advice name]([annotated parameter], [annotated parameter],...[annotated parameter])

Annotated Advice Parameters

    @JoinPoint,用来引用joinpointbeans,其他所有annotation用在参数(这些参数包含joinpoint行下文)之上。注意的是,annotation参数类型依赖于被advice拦截的joinpoint。
    Jboss AOP接受从type分配而来的任何类型。例如joinpoint它的目标是类型POJO,annotation参数接受的类型必须是POJO,要么是它的子类,要么实现了POJO。
    无论joinpointbean参数的类型是什么,次规则同样适用与around advice的默认签名。例如around advice拦截到一个方法执行,可以接收MethodInvocation或Invocation。此前说过,around advice使用Invocation实例,而其他的advice使用JoinPointBean对象。
    仅有一个annotation参数@Thrown是强制的。

    除了@Arg,其他所有annotation是但单独唯一的,每个advice最多有一个advice参数。

    由于大多数参数代表了上下文的值,所以它们依赖于joinpoint类型。如果advice接受一个上下文的值作为参数,但在joinpoint执行过程中不可得,参数值为null。@return则是一个异类,如果advice有这个参数,它不会拦截不返回值的joinpoint。
    @Args,若在joinpoint执行过程中不可得,返回空数组。

@Thrown annotated parameter

@Thrown annotation仅对after-throwing和finally advice可用。

public class Aspect{
   public void throwing1(@Thrown RuntimeException thrownException){
   }
   public void throwing2(){
   }
}

<aop>
   <aspect class="Aspect"/>
   <bind pointcut="...">
      <throwing aspect="Aspect" name="throwing1"/>
      <throwing aspect="Aspect" name="throwing2"/>
   </bind>
</aop>

    advice throwing1遵循次规则,但是thorwing2不会,因为它不包含@thrown annotation标注的参数。

    对于finally advice,@Thrown annotation尽在@Return参数出现时才可用。通过这种方式,finally advice能标识是否返回值有效。如果@thrown参数为null,意味着joinpoint正常返回,@return参数是有效的。否则包含@return参数会被忽略。如果joinpoint没有拦截到异常,finally advice没有接收joinpoint返回值,@thrown参数是可选的,其值为null。    
public class Aspect{
    public void finally1(@Thrown Throwable thrownException){
       ...
    }
    public void finally2(){
       ...
    }
    public void finally3(@Return int returnedValue, @Thrown
      Throwable thrownException){
       if (thrownException == null){
       //We returned normally, the @Return parameter is valid
           int i = returnedValue;
       }else{
         //An exception happened while invoking the target joinpoint
       //The return value is invalid
      }
    }
    public void finally4(@Return int returnedValue){
     ...
    }
}
<aop>
    <aspect class="Aspect"/>
    <bind pointcut="execution(public int *->*(..))">
        <finally aspect="Aspect" name="finally1"/>
        <finally aspect="Aspect" name="finally2"/>
        <finally aspect="Aspect" name="finally3"/>
        <finally aspect="Aspect" name="finally4"/>
    </bind>
</aop>
   @thrown在advice finally1()和finally2()中不是强制的,且他们没有@return参数,所以这两个advice是有效的。除此之外,finally1()在joinpoint拦截到异常时接收非空异常。
   finally4()advice是无效的,因为finally advice如果包含@return参数,必须要@thrown参数。

JoinPoint Arguments

    可以通过@arg和@args来接收joinpoint参数。这两者的区别在于:@arg,每个参数等同与joinpoint中的单个参数。@Args,一个对象数组参数,它接收包含所有joinpoint参数。
    后者独立于joinpoint参数类型,且它允许修改参数值。基于@Args的修改都会延伸到joinpoint。然而使用@Args意味着参数数组需要重新创建。调用invocation.getArguments()和setArguments()也一样会创建。使用@Arg更加轻量级些,但适用于无需变更joinpoint参数的情况。
    当使用@Arg,参数类型依赖于joinpoint处被拦截的对象的类型。并不是所有的Joinpoint目标参数都被包括在advice方法中。advice能接收与其相关的execution的参数值。
    Jboss AOP与Joinpoint匹配advice参数:推断joinpoint参数在advice中的位置。匹配过程如下:
    1)每个advice 参数将被匹配到第一个类型相同,未匹配的参数,顺序一致。
    2)advice中未匹配的参数以额外的步骤处理。

    以demo来说明其机制:

public class POJO{
  void method(Collection arg0,List arg1, int arg2, String arg3){}
}
 
<aop>
  <aspect class="MyAspect"/>
    <bind pointcut="execution(* POJO->method(..))">
    <before aspect="MyAspect" name="advice"/>
  </bind>
</aop>

    Class POJO是仅包含一个方法的java对象,当调用次方法调用前,希望触发MyAspect.advice()。Pojo.method()接收四个参数,它们都能通过@arg参数来获取。假如MyAspect.advice()有以下签名:

public class MyAspect
{
    public void advice(@Arg Collection param0, @Arg List param1,
     @Arg int param2, @Arg String param3){
    }
}

则MyAspect.advice()的参数能完全匹配PoJo.method()的参数
param0 <- arg0
param1 <- arg1
param2 <- arg2
param3 <- arg3
如果MyAspect.advice()签名稍微变更,匹配结果与上例一致。因为Collection派生出List。

public class MyAspect{
public void advice (@Arg Collection param0, @Arg Collection
    param1, @Arg int param2, @Arg String param3){
    ...
    }
}

如果MyAspect.advice()接收一个参数,java.lang.Object

public class MyAspect{
    public void advice(@Arg Object param0){
     ...
    }
}
匹配如下:param0 <- arg0,因为joinpoint参数类型没有Object,需要额外的步骤处理:arg0是第一个未匹配的参数,且派生至Object,所以将它赋值给para0。
可以强制匹配,例如
public class MyAspect{
   public void advice (@Arg(index=1) Collection param1){
    ...
   }
}

Overloaded Advices

    方法名称可以被重载,在不同场合下被拦截。例如,为每个invocation类型准备一个trace advice。可以使用相同的名字trace,仅重载具体的Invocation类型。
public class AroundAspect{
    public Object trace(MethodInvocation invocation) throws Throwabl{
       try{
          System.out.println("Entering method: " +invocation.getMethod()");
          return invocation.invokeNext(); // proceed to next advice or actual call
       }finally{
          System.out.println("Leaving method: " +invocation.getMethod()");
       }
    }
    public Object trace(ConstructorInvocation invocation) throwsThrowable{
       try{
          System.out.println("Entering constructor: " +invocation.getConstructor()");
          return invocation.invokeNext(); // proceed to next advice
          //or actual call
       }finally{
          System.out.println("Leaving constructor: " +
          invocation.getConstructor()");
       }
    }
}

class POJO{
   public POJO(){}
   public someMethod(){}
}
<aop>
   <aspect class="AroundAspect"/>
   <bind pointcut="all(POJO)">
     <advice aspect="AroundAspect" name="trace"/>
   </bind>
</aop>
    当调用POJO的构造器,Jboss AOP会调用trace(ConstructorInvocation invocation),当调用POJO的方法时,Jboss Aop调用trace(MethodInvoation invocation)。这说明,Jboss AOP会为joinpoint拦截选择最合适的advice method。

Annotated-parameter Signature

   advice的选择过程遵循annotation参数签名的优先级:
   @JoinPoint > @Target > @Caller > @Throwable = @Return > @Arg > @Args
Presence priority
   规则很简单,advice(仅接收一个joinpoint bean(@joinpoint)作为它的参数)比其他advice(接收其他annotation)有更高的优先级。
public class POJO{
    String someMethod(String s){}
}
<aop>
   <aspect class="OneAspect"/>
   <bind pointcut="execution(* POJO->someMethod(..))">
     <after aspect="OneAspect" name="after"/>
   </bind>
</aop>
public class OneAspect{
   public void after(@JoinPoint MethodJoinPoint mjp){} //1
   public String after(@Target POJO pojo, @Return String ret, @ArgString arg0){} //2
}
当Pojo.someMethod()调用时,第一个OneAspect.after()会被选中,因为@JoinPoint的优先级更高。
public class OneAspect{
   public void after(@Target POJO pojo){} //1
   public String after(@Return String ret, @Arg String arg0){} //2
}
次例,也是第一个会被选中,因为@Target比@Return的优先级高。一次比较advice中的方法的优先级,选出优先级高的。

public class OneAspect
{
  public void after(@JoinPoint MethodJoinPoint mjp, @Target POJO pojo){} //1
  public String after(@JoinPoint MethodJoinPoint mjp, @Return String ret){} //2
}

advice中的第一个方法匹配更多的参数,所以会选中

public class OneAspect{
   public void before(@Arg String s, @Arg int i){} //1
   public String before(@Arg String s){} //2
}

Assignability Degree

   assignability degree规则的优先级低于priority parameter。assignability degree是类层与参数类型之间的距离。

public interface POJOInterface{}
public class POJOSuperClass extends java.lang.Object{}
public class POJO extends POJOSuperClass implements POJOInterface{
   void method(){}
}


<aop>
   <aspect class="OneAspect"/>
   <bind pointcut="execution(* POJO->method(..))">
     <before aspect="OneAspect" name="before"/>
   </bind>
</aop>


public class OneAspect{
  public void before(@Target POJO target){} //1
  public void before(@Target POJOInterface target){} //2
  public void before(@Target POJOSuperClass target){} //3
  public void before(@Target Object target){} //4
}

   POJO作为joinpoint的目标,OneAspect.before(@Target POJO)的degree为0。OneAspect.before(@Target POJOInterface)和OneAspect.before(@Target POJOSuperClass)的degree为1。OneAspect.before(@Target Object)的degree为2。最后Jboss会选择degree最低的method。

注意,基于@Arg的assignability degree是advice所有@arg参数的degree总和。

public class POJO{
    public void method(POJO argument0, String argument1, int argument2)
}
<aop>
    <aspect class="OneAspect"/>
    <bind pointcut="execution(* POJO->method(..))">
       <before aspect="OneAspect" name="before"/>
    </bind>
</aop>
public class OneAspect{
   public void before(@Arg POJO p, @Arg String s, @Arg int i){} //1
   public void before(@Arg POJOSuperClass p, @Arg String s, @Arg int i){}//2
   public void before(@Arg POJO p, @Arg Object s, @Arg int i){} //3
   public void before(@Arg Object p, @Arg Object s, @Arg int i){}//4
}

第一个advice的degree为0+0+0,基本类型没有父类,所以degree为0。

第二个advice的degree为1+0+0。
第三个advice的degree为0+1+0。
第四个advice的degree为2+1+0。

Return Types

    对于annotationed 参数类型的around advice来说,存在第三个规则:返回类型。次规则同样适用于after和finally advice。
    注意的是,advice的return type必须派生给Joinpoint type,这取决于Jboss AOP分配advice返回值给JoinPoint返回值。
public class POJO{
   public Collection method(int arg0, boolean arg1, short arg2) {...}
}
<aop>
    <aspect class="OneAspect"/>
    <bind pointcut="execution(* POJO->method(..))">
       <advice aspect="OneAspect" name="around"/>
    </bind>
</aop>
public class OneAspect{
    public Collection around(@JoinPoint Invocation inv, @Arg int
       param0) throws Throwable {...} //1
    public List around(@JoinPoint Invocation inv, @Arg boolean
       param1) throws Throwable {...} //2
}

第一个advice的degree为0,因为在类的继承树上它花费了0步从Conllection到Collection。第二个advice的degree为1。所以Jboss AOP选择第一个advice。

public class OneAspect{
   public Collection after(@Arg int param0) {...} //1
   public List after(@Arg boolean param1) { ... } //2
   public void after(@Arg short param2) { ... }//3
}

因为JoinPoint有返回类型,而第三个advice没有返回类型,被排除规则之外。所以advice的重载有四个规则:

    1)presence of annotated parameter
    2)assignability degree of annotated parameter
    3)presence of non-void return type
    4)assignability degree of return value type

A Match

  如果JBoss AOP不能找到最高优先级的advice,任意选择一个advice。
public class POJO{
    public void method(int arg0, long arg1) {...}
}
<aop>
   <aspect class="OneAspect"/>
   <bind pointcut="execution(* POJO->method(..))">
      <before aspect="OneAspect" name="before"/>
   </bind>
</aop>
public class OneAspect{
    public void advice(@Arg int arg0) {}
    public void advice(@Arg long arg1) {}
}

你可能感兴趣的:(Jboss AOP之Advice)