AOP Configuration and Usage

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

Configuration

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>
      

Using Aspects

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.

AOP Lifecycle callbacks

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/undeployment
  • aop:lifecycle-create - Triggered when the target beans enter/leave the create lifecycle state upon deployment/undeployment
  • aop:lifecycle-describe - Triggered when the target beans enter/leave the describe lifecycle state upon deployment/undeployment
  • aop:lifecycle-install - Triggered when the target beans enter/leave the install lifecycle state upon deployment/undeployment
  • aop:lifecycle-instantiate - Triggered when the target beans enter/leave the instantiate lifecycle state upon deployment/undeployment

Aspect dependencies

Aspects 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.