In this section we will transform our application to avoid multiple instances of the Fibonacci class to be created (I bet that you have been thinking we should had used the singleton pattern from the beginning…).
This example is very useful as it shows how we can abstract of the application the fact we are using the singleton pattern.
In this case, we want to intercept the calls to new Fibonacci and return a unique previously-created (if not exist, we will create one) instance. To do so, first we have to define a hookset that matches the constructor calls:
Hookset theHookset = new PrimitiveHookset( Instantiation.class, new NameCS("reflex.examples.fibonacci.app.Fibonacci"), new InstantiationOfOS("reflex.examples.fibonacci.app.Fibonacci") );
Here we use a new kind of operation: instantiation. The instantiation operation matches all statements using the new
keyword. In our case we are interested in instantiations occurring in the Fibonacci class (new NameCS(“reflex.examples.fibonacci.app.Fibonacci”)
), instantiations that must be of Fibonacci objects (new InstantiationOfOS(“reflex.examples.fibonacci.app.Fibonacci”)
).
The declaration of the metaobject definition follows:
MODefinition theMO = new MODefinition.SharedMO(new SingletonHandler());
Here we use the SingletonHandler
class, which is in charge of avoiding multiple instance creation. The class definition is:
public static class SingletonHandler { private Fibonacci itsInstance = null; public Object ensureSingleton(IExecutionPointClosure aClosure){ if (itsInstance == null){ itsInstance = (Fibonacci) aClosure.proceed(); } return itsInstance; } }
As you can see, the SingletonHandler
class has only one method: ensureSingleton
. This method does a check if the field itsInstance
is null
(it is null
when the method is called the first time): if it does, the variable is initialized with the result of calling proceed
in the closure object. This closure object is a self-contained unit that is able to invoke the original operation (in our case the new Fibonacci()
constructor call). Finally, the instance field is returned. This code ensures that only one Fibonacci
instance will exist.
(You can find more information about this closure object here).
Finally, the code of the definition and configuration of the behavioral link is:
BLink theLink = Links.get(theHookset, theMO); theLink.setControl(Control.AROUND); theLink.setCall(Control.AROUND, SingletonHandler.class.getName(), "ensureSingleton", Parameter.CLOSURE);
In the code above we set the control of the link as AROUND
, meaning that it will replace the original operation. Then we specified that the method ensureSingleton
should be called with one unique parameter: Parameter.CLOSURE
, this is a ready-to-use parameter that forces Reflex to generate the infrastructure necessary to allow the invocation of the original operation. Note that if you do not specify this parameter, you will not be able to invoke the original operation.
The complete code of the link provider that defines and configures this link is here. The command to run this example is:
Windows %java -classpath "lib\javassist.jar;build\reflex-core.jar;build\reflex-examples.jar" reflex.examples.tutorial.SingletonAspect Linux %java -classpath "lib/javassist.jar:build/reflex-core.jar:build/reflex-examples.jar" reflex.examples.tutorial.SingletonAspect
The output should be the same as executing the Fibonacci class without the aspect. You can verify the that the aspect is working adding a System.out.println(…)
line to the Fibonacci class constructor:
public Fibonacci(){ System.out.println("----- Fibonacci Constructor -----"); }
This line should be printed once.