A very brief intro to JBoss AOP.
This tutorial is a follow-up to the JBoss Microcontainer intro – read that first if you haven't yet.
AOP (Aspect Oriented Programming) is a concept of moving a „programatic envelopes“ away from the methods. What I call a programatic envelope is that kind of annoying code you have to repeat at the end or/and the beginning of many methods.
Usual textbook examples of AOP usage are:
However, AOP is particulary useful for frameworks for their need of generalization. It can help it to put the envelope code around method which it still does not know.
First, you mark some Java constructs (methods, fields, constructors, …) to
be AOP'ed (encapsulated with your envelope code).
Then you need so-called „aspect“ – that is the official name for envelope
code.
Then you need some tool which will bind the aspect to those methods. This is
done by advanced techniques like byte-code instrumentation and class
reflection.
JBoss AOP offers much more then the other AOP frameworks (like Spring AOP).
@Aspect
,
@Interceptor
, @Bind
etc. See Chapter
6. Annotation Bindings.Copied from JBoss AOP reference docs.
We want to catch all calls to methods that affect the fuel in the
Car
. To make this easily achievable with AOP, let's follow a
convention that all changing methods are setters and all fuel-related properties
names end with „Fuel“.
For the purposes of this example, we've changed the Car
class to
have two fuel-related properties:
01.
public
class
Car {
02.
public
int
litresOfFuel;
03.
public
int
getLitresOfFuel() {
return
litresOfFuel; }
04.
public
void
setLitresOfFuel(
int
litresOfFuel ) {
this
.litresOfFuel = litresOfFuel; }
05.
06.
public
int
reserveFuel;
07.
public
int
getReserveFuel() {
return
reserveFuel; }
08.
public
void
setReserveFuel(
int
reserveFuel ) {
this
.reserveFuel = reserveFuel; }
09.
10.
public
String toString(){
11.
return
"Car \""
+
this
.name+
"\" with "
+(
this
.litresOfFuel +
this
.reserveFuel)+
" litres of fuel."
;
12.
}
13.
...
14.
}
Interceptor
(„envelope“)This is a simple interceptor that will just notify us when triggered.
01.
package
cz.zizka.ondra.jbmctest;
02.
03.
import
java.util.logging.Logger;
04.
import
org.jboss.aop.advice.Interceptor;
05.
import
org.jboss.aop.instrument.Untransformable;
06.
import
org.jboss.aop.joinpoint.Invocation;
07.
08.
public
class
FuelInterceptor
implements
Interceptor, Untransformable {
09.
10.
private
static
final
Logger log = Logger.getLogger( FuelInterceptor.
class
.getName() );
11.
12.
public
Object invoke(Invocation invocation)
throws
Throwable
13.
{
14.
Object target = invocation.getTargetObject();
15.
if
( target
instanceof
Car ){
16.
log.info(
"The fuel is being changed for the car '"
+((Car)target).getName()+
"'."
);
17.
}
18.
return
invocation.invokeNext();
19.
}
20.
21.
public
String getName() {
22.
return
FuelInterceptor.
class
.getName();
23.
}
24.
}
// class FuelInterceptor
For the syntax of AOP pointcuts, see JBoss AOP documentation.
The syntax for our purpose stated above would be:
1.
execution(* cz.zizka.ondra.jbmctest.Car->set*Fuel(..)
The AOP settings go to a special XML file having <aop>
as
it's root element, and conforming to the JBoss AOP XSD.
From what I've found, this file must be either in
META-INF/jboss-aop.xml
of your .jar
, or anywhere on
the filesystem, but you'll have to point to it by the
-Djboss.aop.path
JVM argument – see 10.2.1. Precompiled
instrumentation and 5.2. Resolving
XML.
To let maven put this file to your .jar
, store it in
src/main/resources
(the default path for resources), giving the
resulting path src/main/resources/META-INF/jboss-aop.xml
.
So let's bind this interceptor to some method calls. In this case, we're
binding it to all methods of the Car
class whose name has the
pattern set*Fuel
.
1.
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
?>
2.
<
aop
>
3.
<
bind
pointcut="execution(* cz.zizka.ondra.*->set*Fuel(..))">
4.
<
interceptor
class
=
"cz.zizka.ondra.jbmctest.FuelInterceptor"
/>
5.
</
bind
>
6.
</
aop
>
Alternatively, it should be possible to put the AOP settings to the JBoss Microcontainer XML (but I wasn't able to get it working):
01.
<
deployment
xmlns:xsi
=
"http://www.w3.org/2001/XMLSchema-instance"
02.
xsi:schemaLocation
=
"urn:jboss:bean-deployer:2.0 bean-deployer_2_0.xsd"
03.
xmlns
=
"urn:jboss:bean-deployer:2.0"
04.
xmlns:aop
=
"urn:jboss:aop-beans:1.0"
>
05.
06.
<
bean
... />
<!-- Car and garage beans -->
07.
08.
<
aop:interceptor
name
=
"FuelInterceptor"
class
=
"cz.zizka.ondra.jbmctest.FuelInterceptor"
/>
09.
10.
<
aop:bind
pointcut="execution(* cz.zizka.ondra.jbmctest.Car->set*Fuel(..)">
11.
<
aop:interceptor-ref
name
=
"FuelInterceptor"
/>
12.
</
aop:bind
>
13.
14.
</
deployment
>
After setting Maven to use JBoss repositories (see the JBoss Microcontainer how-to), add these dependencies to your project:
01.
<
dependencies
>
02.
<
dependency
>
03.
<
groupId
>org.jboss.microcontainer</
groupId
>
04.
<
artifactId
>jboss-kernel</
artifactId
>
05.
<
version
>2.0.9.GA</
version
>
06.
</
dependency
>
07.
<
dependency
>
08.
<
groupId
>org.jboss.aop</
groupId
>
09.
<
artifactId
>jboss-aop</
artifactId
>
10.
<
version
>2.1.6.GA</
version
>
11.
</
dependency
>
12.
<
dependency
>
13.
<
groupId
>org.jboss.microcontainer</
groupId
>
14.
<
artifactId
>jboss-aop-mc-int</
artifactId
>
15.
<
version
>2.0.9.GA</
version
>
16.
<
scope
>runtime</
scope
>
17.
</
dependency
>
18.
</
dependencies
>
Also, add the JBoss AOP Maven plugin. It binds automatically to the
compile
phase. This will change the classes code to call your
interceptors when a method is being called.
01.
<
plugins
>
02.
<
plugin
>
03.
<
groupId
>org.jboss.maven.plugins</
groupId
>
04.
<
artifactId
>maven-jbossaop-plugin</
artifactId
>
05.
<
version
>2.1.3.GA</
version
>
06.
<
executions
>
07.
<
execution
>
08.
<
id
>compile</
id
> <
goals
> <
goal
>compile</
goal
> </
goals
>
09.
<
configuration
>
10.
<
includeProjectDependency
>true</
includeProjectDependency
>
11.
<
aoppaths
>
12.
<
aoppath
>src/main/resources/META-INF/jboss-aop.xml</
aoppath
>
13.
</
aoppaths
>
14.
</
configuration
>
15.
</
execution
>
16.
</
executions
>
17.
</
plugin
>
18.
</
plugins
>
01.
public
class
JBossAopSampleApp {
02.
03.
public
static
void
main( String[] args )
throws
Throwable
04.
{
05.
Car myCar =
new
Car(
"Red Devil"
);
06.
System.out.println(
"I have a garage: "
+car);
07.
08.
// Put some fuel in.
09.
myCar.setLitresOfFuel( myCar.getLitresOfFuel() +
1
);
10.
myCar.setReserveFuel( myCar.getReserveFuel() +
1
);
11.
12.
System.out.println(
"I have a garage: "
+car);
13.
}
14.
15.
}
Now run the application, and you'll see that the interceptor is really being invoked.
1.
I have a garage: Garage with a car: Car "Red Devil" with 0 litres of fuel.
2.
16.11.2009 11:42:47 FuelInterceptor invoke INFO: The fuel is being changed for the car 'Red Devil'.
3.
16.11.2009 11:42:47 FuelInterceptor invoke INFO: The fuel is being changed for the car 'Red Devil'.
4.
I have a garage: Garage with a car: Car "Red Devil" with 2 litres of fuel.
The application created in the examples above can be downloaded here: JBossAOPsample.zip