Here is my MethodLogger aspect that I will create:
<cfcomponent output="false" implements="wirebox.system.aop.MethodInterceptor" hint="A simple interceptor that logs method calls and their results">​<--- Dependencies ---><cfproperty name="log" inject="logbox:logger:{this}">​<--- init ---><cffunction name="init" output="false" access="public" returntype="any" hint="Constructor"><cfargument name="logResults" type="boolean" required="false" default="true" hint="Do we log results or not?"/><cfscript>instance = {logResults = arguments.logResults};​return this;</cfscript></cffunction>​<--- invokeMethod ---><cffunction name="invokeMethod" output="false" access="public" returntype="any" hint="Invoke an AOP method invocation"><cfargument name="invocation" required="true" hint="The method invocation object: wirebox.system.aop.MethodInvocation"><cfscript>var refLocal = {};var debugString = "target: #arguments.invocation.getTargetName()#,method: #arguments.invocation.getMethod()#,arguments:#serializeJSON(arguments.invocation.getArgs())#";​// log incoming calllog.debug(debugString);​// proceed executionrefLocal.results = arguments.invocation.proceed();​// result logging and returnsif( structKeyExists(refLocal,"results") ){if( instance.logResults ){log.debug("#debugString#, results:", refLocal.results);}return refLocal.results;}</cfscript></cffunction>​</cfcomponent>
You can see that I do some DI via annotations:
<--- Dependencies ---><cfproperty name="log" inject="logbox:logger:{this}">
A normal constructor with one optional argument for logging results:
<--- init ---><cffunction name="init" output="false" access="public" returntype="any" hint="Constructor"><cfargument name="logResults" type="boolean" required="false" default="true" hint="Do we log results or not?"/><cfscript>instance = {logResults = arguments.logResults};​return this;</cfscript></cffunction>
And our invokeMethod implementation:
<--- invokeMethod ---><cffunction name="invokeMethod" output="false" access="public" returntype="any" hint="Invoke an AOP method invocation"><cfargument name="invocation" required="true" hint="The method invocation object: wirebox.system.aop.MethodInvocation"><cfscript>var refLocal = {};var debugString = "target: #arguments.invocation.getTargetName()#,method: #arguments.invocation.getMethod()#,arguments:#serializeJSON(arguments.invocation.getArgs())#";​// log incoming calllog.debug(debugString);​// proceed executionrefLocal.results = arguments.invocation.proceed();​// result logging and returnsif( structKeyExists(refLocal,"results") ){if( instance.logResults ){log.debug("#debugString#, results:", refLocal.results);}return refLocal.results;}</cfscript></cffunction>
As you can see, the before advice part is what happens before the execution of the real method (or more aspects) occurrs. So everything before the call to arguments.invocation.proceed()
:
var refLocal = {};var debugString = "target: #arguments.invocation.getTargetName()#,method: #arguments.invocation.getMethod()#,arguments:#serializeJSON(arguments.invocation.getArgs())#";​// log incoming calllog.debug(debugString);
Then we execute the real method or more aspects (we do not do anything around the method call):
// proceed executionrefLocal.results = arguments.invocation.proceed();
Finally, we do the after advice part which happens after the method or other aspects fire and results are returned:
// result logging and returnsif( structKeyExists(refLocal,"results") ){if( instance.logResults ){log.debug("#debugString#, results:", refLocal.results);}return refLocal.results;}
That's it. I have succesfully created an aspect. What's next!