AdviceFactory
.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 the GenericAspectFactory
, 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 an AspectDefinition
. 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, the GenericInterceptorFactory
is used; the AdviceFactory
represents advices.// 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);
...
}
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
.// declare the helloWorld2 advice as being of the default advice type, which is around
new AdviceFactory(aspectDefinition, “helloWorld2”);
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.AdviceBinding(String pointcutExpression, String cflow)
null
. While there is a second constructor provided by AdviceBinding
. It is for internal use only, so always use the one above.addInterceptor(Class clazz)
addInterceptorFactory(InterceptorFactory interceptorFactory)
AdviceFactory
instances (as we have seen above, this class is an implementation of InterceptorFactory
, and creates interceptor adapters to call your advice).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.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.Advised
interface:Advisor _getAdvisor()
InstanceAdvisor _getInstanceAdvisor()
insertInterceptor(Interceptor)
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.
AdviceBinding binding = new AdviceBinding(“execution(public !static void Pojo->*(int, long))”, null);
binding.addInterceptor(MyInterceptor.class);
AspectManager.getInstance().addAdviceBinding(binding);
<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);
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>
removeAdviceBinding
method:AdviceBinding.addInterceptor
and AdviceBinding.addInterceptorFactory
methods can be called only before you deploy your binding. Doing so after that will cause inconsistencies.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();
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
<aop>
<prepare expr="all(Pojo)"/>
</aop>
@Aspect(scope = Scope.PER_VM)
public class MyAspect
{
@Prepare ("all(Pojo)")
public static Pointcut preparePOJO;
@PointcutDef("execution(Pojo->new())")
public static Pointcut aPointcut;
}
@Prepare
annotation, and of a pointcut expression, with the @PointcutDef
expression. Except for the annotation used, their syntax is the same.@Prepare
can also be used to annotate plain-old Java classes:@Prepare
public class Pojo
{
...
}
.aop
suffix. By deploying such an unit at runtime, the aspects contained in it will be automatically deployed to all prepared joinpoints of the previously deployed applications. If you are running the server with load time weaving, all the applications deployed after this moment will also be instrumented by JBoss AOP to insert the aspects contained in the deployment unit, according to the bindings and pointcuts it contains..aop
file is removed from the deploy
dir, the bindings it contains will be automatically removed from the running applications..aop
units, you can use other deployment units and xml files to hot deploy aspects to the server. In this case, the aspect classes are bundled into a non-aop deployment unit (such as a .jar
or an .ear
) and deployed to the server. The binding declarations are deployed to the server separately in an xml file whose name must end with -aop.xml
. The effect of this two-part deployment is the same as you get by deploying an .aop
file, but with one advantage. This type of deployment allows you to redeploy only the *-aop.xml
file, without causing the aspect classes to be reloaded. The simple removal of your *-aop.xml
file will cause the bindings to be undeployed. And the addition of a new *-aop.xml
file will cause the bindings declared in it to be deployed to the server..aop
files.-aop.xml
, containing the declarations of bindings and pointcut expressions.injboss
example, available as part of the JBoss AOP documentation. It shows the different ways of packaging aspectized applications to be run in JBoss AS. Try running those examples and playing with dynamic AOP to see it in action! (For example, run $ ant deploy-basic-lt-war
and try removing the jboss-aop.xml
file from your server deploy
dir in order to have the aspects undeployed)@Arg
, @Target
and @Args
annotated-parameters. Below you will find the complete list of available parameter annotations:@Target
: this annotation indicates that the advice parameter receives the target of the joinpoint.@Caller
: available when intercepting call joinpoints, this annotation is used on the advice parameter that contains the caller.@Arg
: contains one of the joinpoint arguments. It has an optional attribute, index, if you need to indicate the exact joinpoint argument you are referring to (this happens when the attributes have the same type and JBoss AOP cannot infer which argument your @Arg
-annotated parameter refers to).@Args
: this annotation is used on parameters of the type Object[], that contain the full list of the joinpoint arguments. Use this only when necessary, as there is an overhead incurred in creating this array. This annotation differs from @Arg
when it comes to flexibility (your advice will be compatible with any joinpoint, regardless of the type and the number of the joinpoint arguments ) and when you need to edit the value of an argument.@Return
: available only for after and finally advices. Parameters with this annotation will contain the value returned by the advice.@Thrown
: this is available only for after-throwing and finally advices. It allows you to receive the exception thrown by the joinpoint.@JoinPoint
: this annotation is used on reflective parameters, containing joinpoint beans, which allow access to any reflective information regarding the joinpoint. For example, if the joinpoint is a method execution, you can have access to the Method
object that represents that object, and to the Class
object that represents the class declaring that method. For around advices, the joinpoint beans are the Invocation
beans. For the other type of advices, the joinpoint beans are the info beans
. While the first ones allow extra features, like meta-data recording, they are also heavier when compared with the info beans
.@JoinPoint
, @Thrown
, @Return
and @Caller
:<aop>
<aspect class="MyAspect"/>
<bind pointcut="call(public int POJO1->someMethod(..)) AND within(POJO2)">
<finally aspect="MyAspect" name="myAdvice"/>
</bind>
</aop>
----
public class MyAspect
{
public myAdvice(@JoinPoint MethodCall callBean, @Caller POJO2 caller, @Return int returnedValue, @Thrown Throwable thrown)
{
System.out.println(“The execution of joinpoint:”);
System.out.println(callBean.toString());
System.out.println(“Executed by caller: “ + caller);
if (thrown == null)
System.out.println(“Returned “ + returnedValue);
else
System.out.println(“Threw an exception: + thrown);
}
}
<aop>
<aspect class="MyAspect"/>
<bind pointcut="execution(* POJO->someMethod(..) OR execution(POJO->new(..))">
<before aspect="MyAspect" name="loggerAdvice"/>
</bind>
</aop>
----
public class MyAspect
{
public void loggerAdvice(@JoinPoint JoinPointBean joinPointInfo)
{
if (joinPointInfo instanceof MethodExecution)
{
System.out.println(“Intercepting method execution:” + ((MethodExecution) joinPointInfo).getMethod().getDeclaringClass().getName() + “->” + ((MethodInfo) joinPointInfo).getMethod().getName());
}
else if (joinPointInfo instanceof ConstructorExecution)
{
System.out.println(“Intercepting constructor execution:” + ((ConstructorExecution)joinPointInfo).getConstructor().getDeclaringClass().getName() + “->new” );
}
}
}
joinPointBean
every time it gets executed, so it can do a type casting and extract the information it needs. The cost of performing these operations every time the advice gets called can be avoided with the use of overloaded advices. Look at the overloaded implementation of loggerAdvice
below:
public class MyAspect
{
public void loggerAdvice(@JoinPoint MethodExecution methodExecution)
{
System.out.println(“Intercepting method execution:” + ((MethodExecution) joinPointInfo).getMethod().getDeclaringClass().getName() + “->” + ((MethodInfo) joinPointInfo).getMethod().getName());
}
public void loggerAdvice(@JoinPoint ConstructorExecution constructorExecution)
{
System.out.println(“Intercepting constructor execution:” + ((ConstructorExecution)joinPointInfo).getConstructor().getDeclaringClass().getName() + “->new” );
}
}
loggerAdvice
allows a more efficient interception, because JBoss AOP will define when to call which advice method only once per joinpoint, avoiding the extra cost of checking the type of the joinpoint every time it executes. As a plus, you get a cleaner code.Collection
and declare it to be String
. However, you are not supposed to worry about all the rules involved in writing advices. While you can read all the rules at our documentation, there are error messages for each rule you might break. This way, if you break a rule, JBoss AOP will point you what is wrong so you can easily fix it.
<aop>
<aspect class="MyAspect"/>
<bind pointcut="call(public int POJO1->someMethod(..) AND within(POJO2)">
<finally aspect="MyAspect" name="myAdvice"/>
</bind>
</aop>
----
public class MyAspect
{
public myAdvice(@JoinPoint JoinPointBean callBean, @Caller POJO1
caller, @Return int returnedValue, @Thrown Throwable thrown)
{...}
}
POJO1
, while the pointcut expression clearly states that the type of the caller is POJO2
. Unless POJO2
extends POJO1
, we are going to get an error from JBoss AOP like the one below:
org.jboss.aop.advice.NoMatchingAdviceException: No matching finally advice called 'myAdvice' could be found in MyAspect for joinpoint Method called by Constructor[calling=public POJO2(boolean) throws java.lang.Exception,called=public int POJO1.someMethod(boolean) throws java.lang.Exception]
On method 'public void MyAspect.myAdvice(org.jboss.aop.joinpoint.MethodCall,POJO1,int,java.lang.Throwable)'
@Caller-annotated parameter is not assignable from expected type class POJO2
<aop>
<aspect class="MyAspect"/>
<bind pointcut="public int POJO->someMethod(..)">
<before aspect="MyAspect" name="myAdvice"/>
</bind>
</aop>
----
public class MyAspect
{
public myAdvice(@Target POJO pojo, @Arg int arg1, @Arg long arg2)
{
System.out.println("Hello world!");
}
}
"public int POJO->someMethod(..)"
. The advice receives as parameters the target of POJO.someMethod
execution, and two arguments which should be of type int
and long
. Now, take a look at the class POJO
below:
public class POJO
{
public void someMethod(int arg1, long arg2){}
public void someMethod(int arg1, int arg2, long arg3) {}
public void someMethod(String arg1, int arg2, long arg3) {}
// public void someMethod(String arg1, int arg2) {}
}
- someMethod(int arg1, long arg2)
JBoss AOP invokes the advice passing as parameters the POJO
target, and the two arguments received by someMethod
- someMethod(int arg1, int arg2, long arg3)
The same as before, but the advice will receive the values of the first and third arguments, skipping int arg2
.
- someMethod(String arg1, int arg2, long arg3)
Now the advice will receive the values of arg2
and arg3
as the @Arg
-annotated parameters.
Would the last method of POJO
, someMethod(String, int)
, be uncommented, JBoss AOP will throw an InvalidAdviceException
telling you that it cannot find a way of applying your advice to a method that does not receive a long
-typed argument.
Now, look at the second version of our advice below:
public class MyAspect
{
public myAdvice(@Target POJO pojo, @Args Object[] args)
{
if(args.length > 0 && args[0] instanceof Integer)
{
// the intercepted method will receive a new int argument value
args[0] = Integer.valueOf(args[0].intValue() - 1);
}
}
}
This advice receives the list of the intercepted method arguments regardless of the number and type of arguments declared by this method. This would allow us to uncomment POJO.someMethod(String, int)
without getting an InvalidAdviceException
. Besides, any changes performed to the values contained in the args
array will be propagated to the joinpoint. This allows myAdvice
to edit the value of a joinpoint argument, as shown above.
Flexibility and Parameter Annotations
So what you are probably asking yourself is why do you have to use the annotations @Target
, @Arg
and @Args
to indicate the meaning of each of your advice's parameters.
The answer to this question is flexibility. Take a look at the two different advices below:
public void advice1(@Target Object target)
public void advice2(@Arg Object arg)
As you can see, you do not have to specify the exact type of the target of the joinpoint, as we did before. Instead of @Target POJO
, our example could have used @Target Object
as the advice parameter, which is useful when your advice is going to be applied to several different target types. This is what advice1
above does: it does not specify the type of the target. JBoss AOP will successfully apply this advice to any joinpoint, regardless of the type of the target.
The second advice, advice2
, also does not specify the type of the argument it wants to receive. So it will receive as parameter the first non-primitive argument of the intercepted joinpoint.
Now, if we remove the parameter annotations from advice1
and advice2
, we will not be able of differentiating between those advices, except for their names. Hence, JBoss AOP will not be able of knowing what value it should provide to the advice parameter. This gets more complex when you have more than one advice parameter, as the example below:
public void advice1(@Target Object target, @Arg Object arg)
public void advice2(@Arg Object arg, @Target Object target)
Both advices receive the joinpoint target and the first non-primitive joinpoint argument value. How could we differentiate between them and decide what value to pass to each of those advice parameters without the annotations?
This makes the parameter annotation usage essential to achieve total flexibility regarding what to receive as parameter value, and in which type and order. So, when writing your typed advices you must follow the rule below:
The parameters of a typed advice must be annotated.
Advice Return Type
All the typed advices we have seen so far had a void
return type.
This is not mandatory. After and finally advices are allowed to have a non-void return type. Take a look at the following example:
<aop>
<aspect class="MyAspect">
<bind pointcut="">*(..)">
<after aspect="MyAspect" name="myAdvice"/>
</bind>
</aop>
----
public MyAspect
{
public String myAdvice()
{
return "Hello world!";
}
}
The advice above intercepts all methods of POJO
that return a String
and it overwrites the method return value, by returning the string "Hello World"
.
In practice, this is useful for aspects that perform extra actions on the joinpoint return type. For example, an aspect that implements a remote call layer would want to deserialize the return value of a remote call before providing the result to the caller.
Throwing Exceptions
Your advice is allowed to declare to throw any exceptions you like. JBoss AOP will wrap that exception inside a RuntimeException
if this exception is not declared by the intercepted joinpoint. On the other hand, if the joinpoint declares to throw that exception type, JBoss AOP will just throw the exception as is. Either way, the basis application will get the exception as if the intercepted joinpoint had thrown it.
Part III Preview
In the next part, we will see the complete list of parameter annotations available. Besides, we will talk about overloaded advices. See you!
invokeNext()
of the class Invocation
.advice
was the only choice to declare an advice. This tag must be inside a bind
tag, like the example below:
<bind pointcut="execution(* *->(..))">
<advice aspect="Aspect" name="myAdvice"/>
</bind>
"Aspect"
must have been declared before the bind
tag, in an aspect
tag.advice
will be replaced by a tag with the name of the type of the advice you want to bind.
before
, after
, around
, throwing
, and finally
have the same attributes as the tag advice
.
<bind pointcut="execution(* *->(..))">
<before aspect="Aspect" name="myBeforeAdvice"/>
<around aspect="Aspect" name="myAdvice"/>
<after aspect="Aspect" name="myAfterAdvice"/>
<throwing aspect="Aspect" name="myThrowingAdvice"/>
<finally aspect="Aspect" name="myFinallyAdvice"/>
</bind>
advice
is also supported in the new JBoss AOP version. As this tag does not indicate the type of the advice being declared, the default type, around, will be used. This keeps compatibility with previous versions, garanteeing that no AOP code will be broken when upgrading to the new release.@Bind
:@Bind(pointcut="execution(* *->(..))", type=AdviceType.BEFORE)
type
is optional and its default value is AdviceType.AROUND
.@Aspect
in order to be recognized as an aspect by JBoss AOP.
public void myAdvice()
{
System.out.println("Hello world!");
}