Tuesday, 15 January 2013

java - Spring AOP reentrant aspects -



java - Spring AOP reentrant aspects -

is possible create reentrant aspects spring aop (or aspectj)?

here example:

@log public int calcfibonacci(int n) { if(n <= 1) { homecoming n; } else { homecoming calcfibonacci(n - 1) + calcfibonacci(n - 2); } }

and aspect:

@aspect public class loggingaspect { @around("@annotation(log)") public object measure(proceedingjoinpoint pjp, log log) throws throwable { // log relevant info log annotation , pjp ... homecoming pjp.proceed(); }

}

now i'd know how many times calcfibonacci called (counting in recurrent calls).

is there way accomplish this?

okay, cannot solve elegantly in spring aop - see first remark andrei stefan's answer. if in aop application code needs know aspect's existence or phone call aspect-related code, bad design , anti-aop. thus, here have aspectj solution you.

first of all, in aspectj there more execution() pointcuts, e.g. call(). thus, counting joinpoints annotated @log yield result twice big actual number of recursive calls calcfibonacci(int). because of this, pointcut should not just

class="lang-java prettyprint-override">@annotation(log)

but

class="lang-java prettyprint-override">execution(* *(..)) && @annotation(log)

actually, still not plenty because if multiple methods contain @log annotations? should calls counted? no, calcfibonacci(int)! should restrict "fibonacci phone call counter" more like:

class="lang-java prettyprint-override">execution(* *..application.calcfibonacci(int)) && @annotation(log)

here compileable sample code:

annotation:

package de.scrum_master.app; import java.lang.annotation.retention; import java.lang.annotation.retentionpolicy; @retention(retentionpolicy.runtime) public @interface log {}

application recursive fibonacci method:

package de.scrum_master.app; public class application { public static void main(string[] args) { int fibonaccinumber = 6; system.out.println("fibonacci #" + fibonaccinumber + " = " + new application().calcfibonacci(fibonaccinumber)); } @log public int calcfibonacci(int n) { homecoming n <= 1 ? n : calcfibonacci(n - 1) + calcfibonacci(n - 2); } }

aspect, version 1:

package de.scrum_master.aspect; import org.aspectj.lang.joinpoint; import org.aspectj.lang.annotation.aspect; import org.aspectj.lang.annotation.before; import de.scrum_master.app.log; @aspect public class loggingaspect { int count = 0; @before("execution(* *..application.calcfibonacci(int)) && @annotation(log)") public void measure(joinpoint thisjoinpoint, log log) { system.out.println(thisjoinpoint + " - " + ++count); } }

output, version 1:

class="lang-none prettyprint-override">execution(int de.scrum_master.app.application.calcfibonacci(int)) - 1 execution(int de.scrum_master.app.application.calcfibonacci(int)) - 2 (...) execution(int de.scrum_master.app.application.calcfibonacci(int)) - 24 execution(int de.scrum_master.app.application.calcfibonacci(int)) - 25 fibonacci #6 = 8

now, if phone call fibonacci method twice?

int fibonaccinumber = 6; system.out.println("fibonacci #" + fibonaccinumber + " = " + new application().calcfibonacci(fibonaccinumber)); fibonaccinumber = 4; system.out.println("fibonacci #" + fibonaccinumber + " = " + new application().calcfibonacci(fibonaccinumber)); class="lang-none prettyprint-override">execution(int de.scrum_master.app.application.calcfibonacci(int)) - 1 execution(int de.scrum_master.app.application.calcfibonacci(int)) - 2 (...) execution(int de.scrum_master.app.application.calcfibonacci(int)) - 24 execution(int de.scrum_master.app.application.calcfibonacci(int)) - 25 fibonacci #6 = 8 execution(int de.scrum_master.app.application.calcfibonacci(int)) - 26 execution(int de.scrum_master.app.application.calcfibonacci(int)) - 27 (...) execution(int de.scrum_master.app.application.calcfibonacci(int)) - 33 execution(int de.scrum_master.app.application.calcfibonacci(int)) - 34 fibonacci #4 = 3

uh-oh!!!

we need either reset counter in between calls (and create sure whole thing thread-safe using threadlocal or so) or utilize aspect instantiation per command flow instead of singleton aspect, utilize here fun of it:

aspect, version 2:

class="lang-java prettyprint-override">package de.scrum_master.aspect; import org.aspectj.lang.joinpoint; import org.aspectj.lang.annotation.aspect; import org.aspectj.lang.annotation.before; import de.scrum_master.app.log; @aspect("percflow(execution(* *.calcfibonacci(int)) && !cflowbelow(execution(* *.calcfibonacci(int))))") public class loggingaspect { int count = 0; @before("execution(* *.calcfibonacci(int)) && @annotation(log)") public void measure(joinpoint thisjoinpoint, log log) { system.out.println(thisjoinpoint + " - " + ++count); } }

note:

i have shortened bundle , class specification *in order create source code more readable. can utilize de.scrum_master.app.application or abbreviation of in order avoid collisions similar class/method names. the @aspect annotation had parameter says: "create 1 instance per execution of fibonacci method, exclude recursive ones."

output, version 2:

class="lang-none prettyprint-override">execution(int de.scrum_master.app.application.calcfibonacci(int)) - 1 execution(int de.scrum_master.app.application.calcfibonacci(int)) - 2 (..) execution(int de.scrum_master.app.application.calcfibonacci(int)) - 24 execution(int de.scrum_master.app.application.calcfibonacci(int)) - 25 fibonacci #6 = 8 execution(int de.scrum_master.app.application.calcfibonacci(int)) - 1 execution(int de.scrum_master.app.application.calcfibonacci(int)) - 2 (..) execution(int de.scrum_master.app.application.calcfibonacci(int)) - 8 execution(int de.scrum_master.app.application.calcfibonacci(int)) - 9 fibonacci #4 = 3

now, looks much better. :)))

enjoy!

java spring aspectj spring-aop aspect

No comments:

Post a Comment