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
}
}
如果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 parameter2)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) {}
}