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.