Home > AOP

Print
AOP

Overview

NanoContainer lets you configure aspects on components in a PicoContainer.

You can configure aspects in Java, or using Groovy. Advice can be configured for all components in a container, using pointcuts, or advice can be applied to just one component. Advice objects may themselves have dependencies on other components, supplied by the container.

NanoContainer uses dynaop to apply the aspects, provide pointcuts, etc. NanoContainer's approach to AOP is heavily influenced by dynaop, so you might want to check out the dynaop documentation for a good introduction to AOP. (It is possible to plug in your own AOP 'backend', if you prefer to use something besides dynaop.)

Advice

Two kinds of advice are supported, interceptors and mixins. An interceptor is invoked around method calls.

NanoContainer AOP interceptors implement the org.aopalliance.intercept.MethodInterceptor interface (see AOPAlliance). This is the same interface that Spring and other frameworks use, so it should be possible to use interceptors written for other frameworks with NanoAop. Mixins allow you to add new methods and behavior to an existing object. You can think of them as a way of dynamically providing multiple inheritance in Java. Mixins can be any kind of object; they are not required to extend or implement anything.

Configuring Aspects

For the following examples, we'll use the following logging interceptor:

public class LoggingInterceptor implements org.aopalliance.intercept.MethodInterceptor {

    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("start");
        Object result = invocation.proceed();
        System.out.println("end");
        return result;
    }

}

 

We'll also assume that we have a Dao interface, with an implementation class DaoImpl.

Java Configuration

The primary interface for configuring aspects in Java is org.nanocontainer.aop.AspectablePicoContainer. To create one you need an org.nanocontainer.aop.AspectablePicoContainerFactory. The following example uses these objects to apply our logging interceptor to all methods of all instances of Dao in the container:

AspectablePicoContainerFactory containerFactory = new DynaopAspectablePicoContainerFactory();
AspectablePicoContainer pico = containerFactory.createContainer();
PointcutsFactory cuts = pico.getPointcutsFactory();

LoggingInterceptor logger = new LoggingInterceptor();
pico.registerComponentImplementation(Dao.class, DaoImpl.class);
pico.registerInterceptor(cuts.instancesOf(Dao.class), cuts.allMethods(), logger);

Dao dao = (Dao) pico.getComponentInstance(Dao.class);
dao.aMethod(); // will print 'startend'

 

If we wanted to apply our interceptor to just one component, we could do the following:

pico.registerComponentImplementation("myDao", DaoImpl.class);
pico.registerInterceptor(cuts.component("myDao"), cuts.allMethods(), logger);

 

Note that components and aspects can be registered in any order. An aspect can be registered before the component it applies to. In the example above, we could have reversed the two lines and registered the interceptor before the component.

Container Supplied Advice

Advice objects may themselves be components in the PicoContainer, with dependencies on other components. Let's revise our LoggingInterceptor to have a dependency on a log object of some sort. For simplicity, we'll just use a StringBuffer as our logger (in real life it might be a log4j or a commons logging object of some sort):

public class LoggingInterceptor {
    private final StringBuffer log;

    public LoggingInterceptor(StringBuffer log) {
        this.log = log;
    }

    public Object invoke(MethodInvocation invocation) throws Throwable {
        log.append("start");
        Object result = invocation.proceed();
        log.append("end");
        return result;
    }
}

 

With this in place, we could then register our LoggingInterceptor and apply it to a component as follows:

// register dependency of LoggingInterceptor:
pico.registerComponentInstance("log", StringBuffer.class);

pico.registerComponentImplementation("logger", LoggingInterceptor.class);
pico.registerComponentImplementation("myDao", DaoImpl.class);

// you can specify the component key of the advice object:
pico.registerInterceptor(cuts.component("myDao"), cuts.allMethods(), "logger");

 

Pico will supply the StringBuffer "log" component to the constructor of the LoggingInterceptor.

Mixins are a special case. While you can register a mixin as an explicit component in the container, you usually don't want to. Applying the same mixin instance as advice to multiple components would in effect mean that multiple components would share the same base class object - sharing the same instances variables, etc. This is usually not a good thing. However, you may specify a mixin class that has dependencies on components in the container. The container will instantiate a new mixin object for each component that it applies the mixin to, but will satisfy any dependencies the mixin has from components in the container, using constructor injection.

Pointcuts

Notice the use of the org.nanocontainer.aop.PointcutsFactory, cuts, in the first example. This interface provides methods for producing pointcuts that match one class or method, or that match the class, method or component name against a regular expression, pointcuts that pick all instances of a given class, or all classes in a given package, etc. You are not limited to the pointcuts produced by PointcutsFactory however. Any pointcut that implements one of the interfaces org.nanocontainer.aop.ComponentPointcut, org.nanocontainer.aop.ClassPointcut, org.nanocontainer.aop.MethodPointcut will work. Class pointcuts match against the component's class. Component pointcuts match against the component key. Method pointcuts match against the method being invoked. Method pointcuts only apply to interceptor advice; they are not used for mixins.

Groovy Configuration

To configure aspects using Groovy, use DynaopNanoContainerBuilder. This is probably the easiest and most expressive way to configure aspects. A Groovy configuration file might look something like this:

An error occurred: http://svn.codehaus.org/picocontainer/java/nanocontainer/trunk/container/src/test/org/nanocontainer/script/groovy/GroovyNodeBuilderAopScriptedTestCase.groovy. The system administrator has been notified.

 

The example above shows a mixture of component scoped and container scoped advice, both interceptors and mixins. Again, note that the order in which things are registered does not matter. We could have registered container scoped advice before registering the components they apply to, or we could have registered advice dependencies such as IdGenerator and 'transaction' after using them in aspects, etc.

For more information, see the NanoContainer documentation. Just substitute DynaopGroovyNodeBuilder for GroovyNodeBuilder.

Limitations

Components that have aspects applied to them must implement an interface. Advice is not applied to components registered via registerComponentInstance, or for components supplied via a custom ComponentAdapter. (Note however that if you wrap your custom ComponentAdapter with the decorator org.nanocontainer.aop.defaults.AspectsComponentAdapter, aspects will be applied.)

Powered by Atlassian Confluence