The JBoss AOP API
Aspects, Interceptors and Advices
All aspects, advices, and interceptors are represented by factories. Notice that, internally, JBoss AOP does not differentiate advices from interceptors. They are both represented as interceptor instances, which results in uniform, transparent handling of interceptors and advices. This way, an interceptor chain is used for intercepting a joinpoint every time it is executed, and may contain even typed advices.
For that, JBoss AOP provides a factory capable of transforming advices into interceptors, the
AdviceFactory
.Take a look at the main classes used to represent those elements, all located in the
org.jboss.aop.advice
package:AspectFactory
: responsible for creating an instance of an aspect. While you can provide your own implementation of this interface, the most commonly used implementation is theGenericAspectFactory
, that creates aspect instances given the class of the aspect to be created.AspectDefinition
: this is how aspect factories are managed internally. Whenever you declare an aspect, JBoss AOP creates anAspectDefinition
. This definition contains the aspect factory, the aspect scope, and the aspect name.InterceptorFactory
: responsible for creating instances of interceptors. To represent a simple interceptor, theGenericInterceptorFactory
is used; theAdviceFactory
represents advices.
The next example illustrates how you use those classes to represent an advice:
// aspect class
public class MyAspect
{
public void helloWorld()
{
System.out.println(“Hello world!”);
}
public Object helloWorld2(Invocation invocation) throws Throwable
{
System.out.println(“Hello world!”);
return invocation.invokeNext();
}
}
-----
public static void main(String [] args)
{
AspectFactory aspectFactory = new GenericAspectFactory(MyAspect.class, null);
AspectDefinition aspectDefinition = new AspectDefinition(“myAspect”, Scope.PER_INSTANCE, aspectFactory);
AdviceFactory adviceFactory = new AdviceFactory(aspectDefinition, “helloWorld”, AdviceType.BEFORE);
...
}
In the first part of the example, we declare the
MyAspect
class, with two advices: helloWorld
and helloWorld2
. In the second part, we represent the aspect MyAspect
and the advice helloWorld
using the classes we have just learned. First, an aspect factory is created. The constructor of GenericAspectFactory
must receive the class of the aspect, and an xml element. This second parameter allows to pass extra configuration to the aspect class. Since we are not loading anything from an xml file, the second parameter must be null
.Next, we create an aspect definition with the factory, defining also the name of our aspect (could be any unique name you wanted), and the scope of the aspect. Finally, we create an advice factory. This factory's constructor receives the aspect definition, the name of the advice, and the type of the advice. A second constructor is also provided. If you are declaring around advices, you can omit the advice type, as follows:
// declare the helloWorld2 advice as being of the default advice type, which is around
new AdviceFactory(aspectDefinition, “helloWorld2”);
Naturally, creating those elements is not enough, you must deploy them through our API. We will see how to do this in the next topics.
Binding Aspects to Pointcuts
Advices and interceptors are useless unless they are bound to a pointcut expression, indicating when they should be called. This takes us to the most important class of the dynamic AOP API, the
org.jboss.aop.advice.AdviceBinding
. This class contains a pointcut expression and a series of interceptor factories. Optionally, it can also contain a cflow expression.It provides one constructor for dynamic AOP operations:
AdviceBinding(String pointcutExpression, String cflow)
Designed specially for dynamic AOP operations, this constructor creates a binding, defining the pointcut expression and the cflow expression you intend to use. If there is no cflow expression, the second parameter should be
null
. While there is a second constructor provided by AdviceBinding
. It is for internal use only, so always use the one above.The following methods add advices and interceptors to a binding:
addInterceptor(Class clazz)
Adds an interceptor to the binding. Always use this method when you are binding interceptors.addInterceptorFactory(InterceptorFactory interceptorFactory)
Adds an interceptor factory to the binding. You must use it to add advices, in the form ofAdviceFactory
instances (as we have seen above, this class is an implementation ofInterceptorFactory
, and creates interceptor adapters to call your advice).
AspectManager
Most of the functionalities provided by JBoss AOP have an common entry point: the
org.jboss.aop.AspectManager
class. This class is our facade and provides all sort of AOP functionalities. Among them, you can find the dynamic AOP methods:addAspectDefinition(AspectDefinition definition)
: deploys an aspectaddAdviceBinding(AdviceBinding binding)
: deploys a bindingremoveAdviceBinding(String bindingName)
: removes a bindingremoveAdviceBindings(Collection bindings)
: removes a collection of bindings.
Advisor and InstanceAdvisor
Every weaved class implements the
org.jboss.aop.Advised
interface. Through this interface, you can access the class Advisor
, a sort of AspectManager with scope limited to that single class, and the InstanceAdvisor
, with an even more specific scope, a single instance.Take a look at the methods you can call on the
Advised
interface:Advisor _getAdvisor()
InstanceAdvisor _getInstanceAdvisor()
insertInterceptor(Interceptor)
Inserts an interceptor in the beginning of all interceptor chains associated with the advised instance.appendInterceptor(Interceptor)
Adds the interceptor to the end of the instance's chainsremoveInterceptor(String)
Removes an interceptor that has been inserted or appended. The parameter specifies the name of the interceptor to be removed.
Putting it all together
Take a look at the following example:
AdviceBinding binding = new AdviceBinding(“execution(public !static void Pojo->*(int, long))”, null);
binding.addInterceptor(MyInterceptor.class);
AspectManager.getInstance().addAdviceBinding(binding);
This example adds a binding dynamically. It is the API equivalent of the xml binding below:
<aop>
<binding pointcut="execution(public !static void Pojo->*(int, long))">
<interceptor class="MyInterceptor.class/"/>
</binding>
</aop>
Now, an example with advices: // declares the aspect class
AspectFactory aspectFactory = new GenericAspectFactory(MyAspect.class, null);
AspectDefinition aspectDefinition = new AspectDefinition(“myAspect”, Scope.PER_INSTANCE, aspectFactory);
AspectManager manager = AspectManager.instance();
// deploys the aspect
manager.addAspectDefinition(aspectDefinition);
// creates the advice factory
AdviceFactory adviceFactory = new AdviceFactory(aspectDefinition, “helloWorld”, AdviceType.BEFORE);
// creates the binding
AdviceBinding binding = new AdviceBinding(“execution(public !static void Pojo->*(int, long))”, null);
binding.addInterceptorFactory(adviceFactory);
// adds the binding
manager.addAdviceBinding(binding);
The example above declares and deploys an aspect, then creates an advice factory to represent an advice of this aspect. This advice factory is added to a new advice binding, that is registered at AspectManager by a call to
AspectManager.addAdviceBinding
method. It is the API counterpart of the xml below:<aop>
<aspect name="”myAspect”" class="”MyAspect”/">
<binding pointcut="”execution(public !static void Pojo->*(int, long))”>
<before aspect="”myAspect”" name="”helloWorld”/">
</binding>
</aop>
To remove the advice binding, you can call the
removeAdviceBinding
method:manager.removeAdviceBinding(binding.getName());
Notice that the
AdviceBinding.addInterceptor
and AdviceBinding.addInterceptorFactory
methods can be called only before you deploy your binding. Doing so after that will cause inconsistencies.Our last example adds an interceptor to a single instance of the class
Pojo
: // create Pojo
Pojo pojo = new Pojo();
// retrieve its instance advisor
InstanceAdvisor instanceAdvisor = ((Advised) pojo)._getInstanceAdvisor();
// append an interceptor to the chains of this instance only
instanceAdvisor.append(new MyInterceptor());
// executing someMethod will trigger MyInterceptor
pojo.someMethod();
If you create another instance of
Pojo
, this instance will not be intercepted by MyInterceptor, as the interceptor has been added to the InstanceAdvisor of pojo: Pojo otherPojo = new Pojo();
pojo.someMethod(); // will not be intercepted by MyInterceptor
Wrapping up
This second part of the tutorial showed the main classes involved in the dynamic AOP API. To add an aspect at runtime you have to
- declare the aspect, by adding an AspectDefinition to AspectManager
- create a binding, with the pointcut expression of your choice, and add interceptor factories to it
- add the binding to AspectManager (it is at this point that the binding will be deployed to your system)
- create an AdviceBinding containing interceptors and add it to AspectManager as above
- use the InstanceAdvisor API
Next Steps
-> Take a look at our dynamic AOP example. It is available as part of our releases. Run the example and play with it:
- add advices to the advice binding, instead of interceptors;
- create an advice binding that contains interceptors and advices mixed.
-> Get engaged! Help us design a state of the art dynamic AOP API, which will be part of a future release:
http://www.jboss.com/index.html?module=bb&op=viewtopic&p=4166298#4166298
No comments:
Post a Comment