org.nanocontainer.aop |
Lets you configure aspects on components in a {@link
org.picocontainer.PicoContainer}. Two kinds of advice are supported,
interceptors and mixins. An interceptor is invoked around method
calls. Interceptors implement the
org.aopalliance.interceptor.MethodInterceptor
interface. 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. 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. Currently, dynaop is used to apply the aspects.
Other backends may be supported in the future.
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 {@link
org.nanocontainer.aop.AspectablePicoContainer}. To create one you
need a {@link
org.nanocontainer.aop.AspectablePicoContainerFactory}. The
following example applies 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.log);
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 {@link 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 expressionm, 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 {@link
org.nanocontainer.aop.ComponentPointcut}, {@link
org.nanocontainer.aop.ClassPointcut}, {@link
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 {@link org.nanocontainer.aop.script.groovy.DynaopNanoContainerBuilder}.
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 .
|