When writting new version of Kernel, there was always a need for simple usage of advanced AOP features in the Kernel itself. With the version 2.0.0 of Microcontainer there is an elegant way of binding your aspect to wired POJOs, using all the advantages of full dependency state machine. Meaning that even aspect behave as installed services, having full lifecycle and dependency support in the Microcontainer. And it's all a matter of configuration if you want to use aspectized Microcontainer or just plain POJO behaviour. To find out more about JBoss AOP, please see the JBoss AOP documentation
To leverage the JBoss AOP integration in the Microcontainer you need to make sure
that jboss-aop-mc-int.jar
is available on the classpath. The
Microcontainer will decide how whether to use AOP depending on if classes
from this jar are present.
To use the AOP integration we must include the AspectManager
bean.
in our configuration. It is included as follows
<?xml version="1.0" encoding="UTF-8"?> <deployment xmlns="urn:jboss:bean-deployer:2.0"> <bean name="AspectManager" class="org.jboss.aop.AspectManager"> <constructor factoryClass="org.jboss.aop.AspectManager" factoryMethod="instance"/> </bean> <!-- declare beans and aspects --> </deployment>
We can apply aspects to any beans we like. This can either be done by using already woven classes (loadtime or compile-time weaving). If the bean class is not already woven, but should have aspects applied to it, a proxy will be generated. If the class is not woven, field level aspects will not get triggered, you will only get constructor and method-level interception.
<?xml version="1.0" encoding="UTF-8"?> <deployment xmlns="urn:jboss:bean-deployer:2.0"> <bean name="AspectManager" class="org.jboss.aop.AspectManager"> <constructor factoryClass="org.jboss.aop.AspectManager" factoryMethod="instance"/> </bean> <aop:aspect xmlns:aop="urn:jboss:aop-beans:1.0" name="FooAdvice" class="org.jboss.test.microcontainer.support.TestAspect" method="foo" pointcut="execution(* *.SimpleBeanImpl->someMethod(..))"> </aop:aspect> <bean name="Intercepted" class="org.jboss.test.microcontainer.support.SimpleBeanImpl"/> <!-- declare beans and aspects --> </deployment>
import org.jboss.aop.joinpoint.Invocation; public class TestAspect { public static boolean fooCalled; public static boolean barCalled; boolean shouldInvoke; public Object foo(Invocation inv) throws Throwable { System.out.println("--- foo"); return inv.invokeNext(); } }
In the above example, whenever we call the method someMethod()
in the Intercepted
bean, we will get intercepted by the
FooAdvice
bean's foo()
method. The aspect
methods must have the signature shown. If the method
attribute
is left out for the aop:aspect
advice, it will look for an advice
method called invoke()
. Please see the JBoss AOP documentation for
more information about advice methods, and the pointcut expressions used to pick out
methods/constructors/fields that should have aspects applied.
We can also aspectize the installs and uninstalls of a bean. The following
snippet shows a AOP lifecycle callback handler that gets triggered once
StartBean
enters the start
state upon
deployment, and when it leaves the start
state upon
undeployment.
<?xml version="1.0" encoding="UTF-8"?> <deployment xmlns="urn:jboss:bean-deployer:2.0"> <bean name="AspectManager" class="org.jboss.aop.AspectManager"> <constructor factoryClass="org.jboss.aop.AspectManager" factoryMethod="instance"/> </bean> <aop:lifecycle-start xmlns:aop="urn:jboss:aop-beans:1.0" name="InstallAdvice" class="org.jboss.test.microcontainer.support.LifecycleCallback" classes="@org.jboss.test.microcontainer.support.Start"> </aop:lifecycle-start> <bean name="StartBean" class="org.jboss.test.microcontainer.support.SimpleBeanImpl"> <annotation>@org.jboss.test.microcontainer.support.Start</annotation> </bean> </deployment>
import org.jboss.dependency.spi.ControllerContext; public class LifecycleCallback{ public void install(ControllerContext ctx){ System.out.println("Bean " + ctx.getName() + " is being installed"; } public void install(ControllerContext ctx){ System.out.println("Bean " + ctx.getName() + " is being uninstalled"; } }
The install
and uninstall
methods
are required, and must have the signature shown. The names of these methods can be
overridden by passing in the installMethod
and
uninstallMethod
attributes as part of the
aop:lifecycle-start
tag.
We can also intercept the install/uninstall upon entering/leaving other states, by
substituting aop:lifecycle-start
with one of the following.
aop:lifecycle-configure
-
Triggered when the target beans enter/leave the configure
lifecycle state upon deployment/undeploymentaop:lifecycle-create
-
Triggered when the target beans enter/leave the create
lifecycle state upon deployment/undeploymentaop:lifecycle-describe
-
Triggered when the target beans enter/leave the describe
lifecycle state upon deployment/undeploymentaop:lifecycle-install
-
Triggered when the target beans enter/leave the install
lifecycle state upon deployment/undeploymentaop:lifecycle-instantiate
-
Triggered when the target beans enter/leave the instantiate
lifecycle state upon deployment/undeploymentAspects and AOP lifecycle callbacks configured via the microcontainer can have dependencies just as normal beans can have. Beans which have these aspects applied inherit the aspect's dependencies.
<?xml version="1.0" encoding="UTF-8"?> <deployment xmlns="urn:jboss:bean-deployer:2.0"> <bean name="AspectManager" class="org.jboss.aop.AspectManager"> <constructor factoryClass="org.jboss.aop.AspectManager" factoryMethod="instance"/> </bean> <aop:lifecycle-configure xmlns:aop="urn:jboss:aop-beans:1.0" name="JMXAdvice" class="org.jboss.aop.microcontainer.aspects.jmx.JMXLifecycleCallback" classes="@org.jboss.aop.microcontainer.aspects.jmx.JMX"> <property name="mbeanServer"><inject bean="MBeanServer"/></property> </aop:lifecycle-configure> <aop:aspect xmlns:aop="urn:jboss:aop-beans:1.0" name="TxAdvice" class="org.acme.aspects.TxAspect" pointcut="execution(* *->@org.acme.Tx(..))"> <property name="txManager"><inject bean="TxManager"/></property> </aop:aspect> <bean name="BeanWithAspects" class="org.jboss.test.microcontainer.support.SimpleBeanImpl"> <annotation>@org.jboss.aop.microcontainer.aspects.jmx.JMX</annotation> <annotation>@org.acme.Tx</annotation> </bean> <bean name="PlainBean" class="org.jboss.test.microcontainer.support.SimpleBeanImpl"> </bean> </deployment>
JmxAdvice
depends on a
bean called MBeanServer
having been deployed, and
TxAdvice
depends on a bean called TxManager
having been deployed. BeanWithAspects
has been annotated with both
@JMX
and @Tx
. Using this configuration,
BeanWithAspects
gets both JMXAdvice
and
TxAdvice
applied. BeanWithAspects
inherits the dependencies
of all applied aspects and AOP lifecycle callbacks, so it cannot be started until
JMXAdvice
and TxAdvice
have their dependencies
satisfied. PlainBean
on the other hand, has no aspects applied, and
so has no extra dependencies from the AOP layer.