advice是aspect方法,当特定joinpoint 执行点时advice被触发。JBoss AOP提供了五种advice类型。默认是around advice,它能被应用到所有execution模式上。
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 } }
public void [advice name]([annotated parameter], [annotated parameter],...[annotated parameter])
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返回值。
public void [advice name]([annotated parameter], [annotated parameter],...[annotated parameter])不同与其他advice类型,after-throwing advice有强制annotation参数,次参数是被joinpoint抛出的异常。
public void [advice name]([annotated parameter], [annotated parameter],...[annotated parameter]) public [return type] [advice name]([annotated parameter], [annotated parameter],...[annotated parameter])
除了@Arg,其他所有annotation是但单独唯一的,每个advice最多有一个advice参数。
由于大多数参数代表了上下文的值,所以它们依赖于joinpoint类型。如果advice接受一个上下文的值作为参数,但在joinpoint执行过程中不可得,参数值为null。@return则是一个异类,如果advice有这个参数,它不会拦截不返回值的joinpoint。@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拦截到异常时接收非空异常。
以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){ } }
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){ ... } }
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。
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 }
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。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 }
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 parameterpublic 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) {} }