./lib/aspectwerkz-jdk5-2.0.jar
on the classpath instead of ./lib/aspectwerkz-jdk14-2.0.jar
. Same if you want to run it inside an IDE, or use an Ant script.
This jar contains the Java 5 implementations of the AspectWerkz annotations.
perThread
deployment model -
perThread
deployment model for both aspects and mixins.
It should have never been added in the first place. We are working on supporting the
much more fine-grained and expressive
percflow
and
percflowbelow
.
perJVM
deployment model for mixins -
@Introduce
and
<introduce ...>
-
@Mixin
and
<mixin ...>
(child of <system> XML element)
@Implements
and
<introduce ...>
-
@Introduce
and
<introduce ...>
(child of <aspect> XML element)
PATH
- unless using Java 5.
PATH
.
These can be found
here.
The
AspectWerkz 2
architecture is a complete rewrite of the core weaver and container.
When we where designing the new architecture we had four different objectives:
The AspectWerkz weaver is now completely based on static compilation (no reflection at all), which means that the weaved code is very fast. We have released a simple microbenchmark that compares AspectWerkz with all the other major AOP implementations.
We will publish a small paper in which where we compare the performance of all major AOP frameworks on some days.
The new container is very open and extensible. It allows plugging in different Aspect Model Extensions that allows running other AOP framework's aspect inside the AspectWerkz Extensible Aspect Container.
Read more here
In
AspectWerkz 2
we allow the user to both hot-deploy and undeploy his aspect
at runtime. This implementation is based on
HotSwap and guarantees that the performance
of the target application will be the same when an aspect has been undeployed as it was before
it was deployed (either hot-deployed or at startup time).
Read more here
We have put a lot of effort into implementing the full semantics of AOP as defined by AspectJ.
The last couple of years the Java AOP landscape has flourished. It has gone from one single implementation (AspectJ), to a whole bunch of frameworks, each one with its own way of defining and instantiating its aspects and with its own weaver, runtime environment and tools. Even though having many ways of doing things is not necessary a bad thing, especially not in such early days of a new technology, it is not making it easier for new users of AOP and does not speed up its adoption.
When you look at most frameworks out there you can see that they have a lot in common, most of them share same semantics, life-cycle options etc. Most of them have re-implemented concepts introduced by AspectJ and in some cases introduced their own.
This is something that we have had in mind when we were designing the new AspectWerkz container and weaver and led us to an open architecture that was easy to extend, an architecture that formed the foundation for the AspectWerkz Extensible Aspect Container.
Basically it allows you to plug in extensions that can handle the framework specific details, while the common things are shared. What this brings you is a weaver and a container that can weave, deploy and run any aspect no matter how it is implemented and defined.
This introduces many advantages and opens for standardization on tools (development and runtime manageability).
Read more about this in the TSS article
AspectWerkz
supports both hot deployment and hot undeployment of aspects.
It utilizes HotSwap (Java 1.4) or JVMTI (Java 5) to redefine your application at runtime.
New aspects can be added to the running system and old ones can be redefined or removed at runtime.
All these services are accessed from the
org.codehaus.aspectwerkz.transform.inlining.deployer.Deployer
class, which has a rich
set of services.
Deployment of aspects is done using one of the
Deployer.deploy(..)
methods.
Here is the API for deployment of aspects (see details below):
DeploymentHandle deploy(Class aspect) DeploymentHandle deploy(Class aspect, ClassLoader deployLoader) DeploymentHandle deploy(Class aspect, DeploymentScope scope, ClassLoader deployLoader) DeploymentHandle deploy(Class aspect, String xmlDef) DeploymentHandle deploy(Class aspect, String xmlDef, ClassLoader deployLoader) DeploymentHandle deploy(Class aspect, String xmlDef, DeploymentScope scope) DeploymentHandle deploy(Class aspect, String xmlDef, DeploymentScope scope, ClassLoader deployLoader)
Details on the deployment API:
DeploymentHandle
, read more about
that in the section about deployment handles below.
DeploymentScope
to the
deploy
method if you want predictable and safe deployment. For details, see the section on deployment
scopes below.
Undeployment of aspects is done using one of the
Deployer.undeploy(..)
methods.
Here is the API for undeployment of aspects (see details below):
void undeploy(Class aspect) void undeploy(Class aspect, ClassLoader loader) void undeploy(DeploymentHandle deploymentHandle)
Details on the deployment API:
DeploymentHandle
then all join points that where affected by
the deployment event defined by the handle will be reverted to the state they where in
before the deployment occured. This means that
you need to keep track of order and
dependencies etc. e.g. f.e. rollback all changes in the correct order etc.
The use of deployment scopes give you more predictable and safer deployment.
They are needed due to the fact that no JVMs today support schema redefinition when redefining your classes.
This means that you have to define a special kind of pointcut that we call deployment scope, which will prepare you application and advise the points that you are interested in doing hot deployment on later.
You can then retrieve a handle to this deployment scope by getting the actual instance of the abstraction and then use this to narrow down the scope of the deployment so you are sure that you will not try to deploy the aspect at points in your code that will not be affected. Hence you are garantueed that your aspect will be deployed at valid points in your code.
Definition
You define the deployment scope just as regular pointcuts, in its own aspect or in the same aspect as the rest of your code:
@Expression("execution(String *.toString())") DeploymentScope toString;
<deployment-scope name="toString" expression="execution(String *.toString())"/>
Runtime retrieval
You can then retrieve the instance of the
DeploymentScope
like this:
DeploymentScope scope = SystemDefinition.getDefinitionFor(loader, systemId).getDeploymentScope("toString");
All
deploy(..)
methods returns a
DeploymentHandle
which is a handle to
the specific deployment event. You can use this handle to revert the changes made by the deployment,
In other words, it allows you to undeploy the aspect you deployed and be sure that it will be
undeployed exactly the way it was deployed, same class loader, same deployment scope etc.
You retrieve it from one of the
deploy(..)
methods and can later use it when
undeploying the aspect:
// deploy aspect DeploymentHandle handle = Deployer.deploy(..); // store the handle somewhere ... // retrieve the handle from storage DeploymentHandle handle = ... // undeploy using handle Deployer.undeploy(handle);
In previous releases of
AspectWerkz
we only supported one single type of
after advice. The sematics of this type was that it was always executed, regardless
of wheter an exception had been throw or if the method returned successfully.
In the new 2.x architecture we have enhanced the semantics for this type of advice and have
borrow the semantics from
AspectJ
. So now we support three different types
of
after advice:
@AfterFinally
(same as
@After
)
@AfterReturning [TYPE]
-
@AfterReturing(pointcut)
or
@AfterReturning(type=onReturnedType, pointcut=pointcut)
@AfterThrowing [TYPE]
-
@AfterThrowing(pointcut)
or
@AfterThrowing(type=onThrownedType, pointcut=pointcut))
An
after finally
advice declaration has the same semantics as an
after
advice declaration, you can use any syntax you like.
after finally
advice are always executed, they work the same as a finally block.
Meaning that they will be invoked regardless of wheter an exception has been thrown or if the
method has returned successfully.
Java 5 annotation definition
@AfterFinally("execution(@TransactionAttribute * *.*(..))") public void logTxMethods(StaticJoinPoint joinPoint) {..}
JavaDoc annotation definition
/** * @AfterFinally("execution(@TransactionAttribute * *.*(..))") */ public void logTxMethods(StaticJoinPoint joinPoint) {..}
XML definition
<advice type="after finally" bind-to="execution(@TransactionAttribute * *.*(..))" name="logTxMethods"/>
after returning [TYPE]
advice is executed if the method returns normally (without
throwing an exception)
and the actual type that is returned is of the type that is specified
in the advice declaration.
If no return type is specified then it applies to all invocations that return normally, e.g. not when throwing an exception.
Java 5 annotation definition
@AfterReturning( type="@Service *..*", pointcut="execution(@TransactionAttribute * *.*(..)" ) public void txCommitReturningService(StaticJoinPoint joinPoint) {..}
JavaDoc annotation definition
/** * @AfterReturning( * type="@Service *..*", * pointcut="execution(@TransactionAttribute * *.*(..)" * ) */ public void txCommitReturningService(StaticJoinPoint joinPoint) {..}
XML definition
<advice type="after returning(@Service *..*)" bind-to="execution(@TransactionAttribute * *.*(..))" name="txCommitReturningService"/>
after throwing [TYPE]
advice is executed after the execution completes
abruptly because of an exception
and the actual type of the exception that is thrown is of the type that is specified
in the advice declaration.
If no exception type is specified then it applies to all invocations that returns with an exception.
Java 5 annotation definition
@AfterThrowing( type="RuntimeException", pointcut="execution(@TransactionAttribute * *.*(..)" ) public void txRollbackOnRuntimeException(StaticJoinPoint joinPoint) {..}
JavaDoc annotation definition
/** * @AfterThrowing( * type="RuntimeException", * pointcut="execution(@TransactionAttribute * *.*(..)" * ) */ public void txRollbackOnRuntimeException(StaticJoinPoint joinPoint) {..}
XML definition
<advice type="after throwing(RuntimeException)" bind-to="execution(@TransactionAttribute * *.*(..))" name="txRollbackOnRuntimeException"/>
Advice are methods in aspect class. The method signature must conform to some rules, depending on the pointcut where the advice is bound to, when args(..), this(..) and/or target(..) are used to gain access to join point argument / caller / callee instance.
Aside from this pointcut bounded arguments, the advice can have extra arguments of type "JoinPoint" or "StaticJoinPoint", which do not appear in the pointcut. First version of AspectWerkz were supporting only "JoinPoint" as advice argument, and use of "getRtti()" RTTI API was mandatory.
In AspectWerkz 2.x, use of "StaticJoinPoint" and/or "JoinPoint" as advice argument is optional and use of it (or no use of it at all) depends on aspect developer decision. Note that for "around" advice, in order to invoke "proceed()" properly, you will need access to either a "StaticJoinPoint" or "JoinPoint" instance).
When only "StaticJoinPoint" is used, specific optimizations are used to maximize runtime performance
since RTTI access is then guaranteed to not happen.
The following demonstrates legal syntax:
@Before("execution(* foo.Bar.method(..))") void before() {} @Before("execution(* foo.Bar.method(..))") void before(StaticJoinPoint sjp) {} @Before("execution(* foo.Bar.method(..))") void before(JoinPoint jp) {} @Around("execution(* foo.Bar.method(..))") Object around() { // possible but rather useless since then all following advices // in the chain are skipped since proceed() is not called return null; } @Around("execution(* foo.Bar.method(..))") Object around(StaticJoinPoint sjp) throws Throwable { return sjp.proceed(); } @Around("execution(* foo.Bar.method(..))") Object around(JoinPoint jp) throws Throwable { return jp.proceed(); } @Around("call(* foo.Bar.method(..)) && args(i, j) && target(bar) && this(caller)") Object aroundCallWithBindings(int i, int j, Bar bar, Object caller, StaticJoinPoint sjp) throws Throwable { // no use of RTTI and tedious casting // much better performance bar.method(); int localInt = j + i; ... return sjp.proceed(); } // use of pointcut composition with argument bindings @Expression("execution(* foo.Bar.method(..)) && args(i)") void pointcut_1(int i) {} @Expression("execution(* foo.Bar.method(..)) && target(bar)") void pointcut_2(Bar bar) {} @Around("pointcut_1(myI) && pointcut_2(myBar)") Object around(JoinPoint jp, int myI, Bar myBar) throws Throwable { myBar.method(); return jp.proceed(); }
The use of EnclosingStaticJoinPoint
does not interfere on advice signature. EnclosingStaticJoinPoint
information is accessible from a StaticJoinPoint or JoinPoint instances.
AspectWerkz 2.x introduces this(..) and target(..) pointcut designators. They can be used to narrow a pointcut expression based on caller and calle types (note: they will implicitly filter out callee side static methods, callee constructors on constructor call pointcut (since the callee is not available yet), and caller side static method for call pointcuts and field pointcuts).
They can be used with type patterns: this(foo.Bar+), target(foo.*) etc.
They can be used to gain direct access to callee and caller instance in advice by using advice argument binding, when combined with pointcut with signature or advice with signature (see previous part).
// this pointcut is not declared as a field since it exposes parameter binding // note use of "bar" variable name @Expression("target(bar)") void pointcut(Bar bar) {} // use the pointcut with respect to its signature // note use of "myBar" variable name @Before("execution(* *..method(..)) && pointcut(myBar)") void before(Bar myBar) { myBar.callback(); }
In AspectWerkz 2.x, the former "TypedAnnotationProxy" and "UntypedAnnotationProxy" classes are gone. To define a strongly typed custom annotation with Java 1.3/1.4, you need to write an interface. This interface should only have method whose return type is the annotation element type (see restriction below) and with no parameters. This means that Java 1.3/1.4 annotation are identical to Java 5 annotations except that the annotation is an "interface" and not a "@interface" (see samples below).
If the annotation is exposing an anonymous element, the special "[type] value();" method should be used (as in Java 5 annotations)
Restrictions: In Java 5, Annotations defined with "@interface" have restrictions. Type of exposed elements cannot be multi-dimensional arrays, etc. The same restriction applies for Java 1.3/1.4 AspectWerkz annotations.
Note: Java 5 "default" value is not available in Java 1.3/1.4 AspectWerkz annotations, hence the default
value of "non Java 5" annotation elements is "null" (or 0 / false for primitives types).
Note: Java 5 does not support having several annotation of the same type on the same member, while this is
possible with AspectWerkz 1.3/1.4 Annotations (though not recommanded for upward compatibility)
Note: Java 1.3/1.4 annotation will implement both the given interface and the org...Annotation interface which
is equivalent to the java.lang.annotation.Annotation interface (f.e. annotationType() will return the type of the
annotation).
Note: for Java 5, the annotation that appears in the source must be part of the imports or fully qualified
(@foo.bar.AnnotateMe("some texte")). For AspectWerkz equivalent with 1.3/1.4, use the custom annotation.properties
file at compilation time to set annotation nickname (f.e. @Hello("some text") could be of type foo.bar.AnnotateMe this way..)
Samples: Java 5 Annotations and their Java 1.4 equivalent with AspectWerkz 2.x
// your Java 5 code ------------------- // ... specific annotation @Target and @Retention to control retention and bindings public @interface AnnotateMe { String value() default null; } // your source code for an annotated method (same for field, class etc) @AnnotateMe public void someMethod() {} @AnnotateMe("my text") public void someOtherMethod() {} // your Java 1.3/1.4 code //----------------------- // use of AspectWerkz Annotations.getAnnotation(...) allow runtime visibily // binding is not limited by the annotation public interface AnnotateMe { String value(); } // your source code for an annotated method (same for field, class etc) /** * @AnnotateMe */ public void someMethod() {} /** * @AnnotateMe("my text") */ public void someOtherMethod() {} // Note for such an annotation that has a single element "value()", the following doclet style syntax is allowed: // and the annotation value() will return "my text" as for @AnnotateMe("my text") /** * @AnnotateMe my text */ public void someOtherMethod() {}
Samples with more complex annotation
// your Java 5 code ------------------- @interface Complex { Class someClass(); int[] numbers() } @Complex(someClass=String.class, numbers={1, 2}) public void someMetod() {} // your Java 1.3/1.4 code ------------------------- // just remove the "@" sign interface Complex { Class someClass(); int[] numbers() } // just write it as a doclet and use fully qualified name when an element is a class type /** @Complex(someClass=java.lang.String.class, numbers={1, 2}) */ public void someMetod() {}
We have replaced the old mixin implementation with a completely new and better one. The new implementation mainly gives you two things you did not have before:
transient
(e.g. not serialized with
when the target class is serialized), by setting the
transient=true
flag.
org.codehaus.aspectwerkz.aspect.MixinFactory
interface.
The mixins can be any regular class, which means that it can be a standalone class, an inner class of a regular class or an inner class in your aspect class.
Three different
deployment models
are supported,
perJVM
,
perClass
and
perInstance
.
perJVM
- means that there will be only one mixin instance - like a singleton.
perClass
- means that there will be one mixin instance per
target class.
perInstance
- means that there will be one mixin instance
per target instance.
perThread
deployment model has been left to die in peace. The
perJVM
deployment model is currently not supported but might come back.
If you are using the default factory implementation you have to either provide an no argument constructor or a constructor that takes the:
perClass
deployment model.
perInstance
deployment model.
perJVM
deployment model.
To define the mixin using annotations you have to use the
org.codehaus.aspectwerkz.annotation.Mixin
annotation.
If using Java 5 this is an annotation interface (e.g.
@interface
) else
(for JavaDoc annotations) it is a regular interface.
This annotation has three different parameters:
expression
(can be used as the default anonymous value) - the pointcut expression
that is used to pick out the set of classes to apply this mixin to.
deploymentModel
(optional) - has to me one of
perJVM
,
perClass
or
perInstance
. If not specified then
perInstance
is used.
isTransient
(optional) - defines the mixin as transient or not. If not
specified then the default is
false
, e.g. serializable.
Definition using Java 5 annotations
@Mixin("within(*..domain.*)") public class MyMixin { ... } @Mixin( pointcut="within(*..domain.*)", deploymentModel="perClass", isTransient=true ) public class MyOtherMixin { ... }
Definition using JavaDoc annotations
/** @Mixin("within(*..domain.*)") */ public class MyMixin { ... } /** * @Mixin( * pointcut="within(*..domain.*)", * deploymentModel="perClass", * isTransient=true * ) */ public class MyOtherMixin { ... }
The XML definition (in the
META-INF/aop.xml
file) can be used for
three different purposes:
class
attribute, e.g.
<mixin class="foo.bar.MyMixinImpl"/>
. See below for details.
To define the mixin using the XML definition, use the
mixin
element. It has
five different attributes that can be used:
class
(mandatory) - defines which class to use as the implementation class.
Has to be the fully qualified name of the class.
deployment-model
(optional) - defines the deployment model. Can be one of
perJVM
,
perClass
or
perInstance
.
transient
(optional) - defines the mixin as
transient
or not.
factory
(optional) - defines the custom mixin factory implementation to use
with the specific mixin.
bind-to
(optional) - defines to which class set the mixin should be applied to.
Example:
<mixin class="foo.bar.MyMixinImpl"/> <mixin class="foo.bar.MyOtherMixinImpl" deployment-model="perClass" transient="true" factory="factories.MyMixinFactory" bind-to="within(*..domain.*)"/>
If you need to be in control of how the aspects are instantiated and/or need to pass in additional dependencies or configuration using an IoC (dependency injection) framework or similar. Then you can create custom mixin factory.
The custom factories needs to implement the
org.codehaus.aspectwerkz.aspect.MixinFactory
:
public interface MixinFactory { /** * Creates a new perClass mixin instance, if it already exists then return it. * * @param the target class * @return the mixin instance */ Object mixinOf(Class klass); /** * Creates a new perInstance mixin instance, if it already exists then return it. * * @param the target instance * @return the mixin instance */ Object mixinOf(Object instance); }
The custom mixin factory can only be defined in the
mixin
element in the
META-INF/aop.xml
file.
You can get a hold of the mixin instances for a specific instance or class through the
org.codehaus.aspectwerkz.aspect.management.Mixins
class.
Example:
// get per class deplolyed mixin Object perclassMixin = Mixins.mixinOf(MyMixin.class, Target.class); // get per instance deplolyed mixin Object perinstanceMixin = Mixins.mixinOf(MyMixin.class, targetInstance);
You can get a hold of specific aspect instances using the
org.codehaus.aspectwerkz.aspect.management.Aspects
class.
Example:
// get per jvm deployed aspect Object singletonAspect = Aspects.aspectOf(MyAspect.class); ... = Aspects.aspectOf("uniqueNameOfAspect"); // get per class deployed aspect Object perclassAspect = Aspects.aspectOf(MyAspect.class, Target.class); ... = Aspects.aspectOf("uniqueNameOfAspect", Target.class); // get per instance deployed aspect Object perinstanceAspect = Aspects.aspectOf(MyAspect.class, targetInstance); ... = Aspects.aspectOf("uniqueNameOfAspect", targetInstance);
When using around, it can be usefull to modify advised target argument before proceeding
to the next advice or target invocation.
It is now possible to do it using a strongly typed user defined sub-interface of JoinPoint
or StaticJoinPoint
.
When using this feature, all you have to write is a public interface
with a single method
whose signature is Object proceed(< any parameters you need >)
.
Then when writing an around advice, you simply declare this interface to be the first parameter of your
advice method, instead of using the regular JoinPoint
or StaticJoinPoint
interface as described here.
Example:
public class MyAspect { // we could inherit StaticJoinPoint instead public static interface MyCustomJoinPoint extends JoinPoint { Object proceed(String arg0, int[] arg1); } // see here the use of MyCustomJoinPoint as first argument, and the // advice signature be in the order of the MyCustomJoinPoint.proceed(..) method @Around("somePointcut(arg0, arg1)") // we could write @Around("execution(* Foo.m(..)) && args(arg0, arg1)") as well public Object around(MyCustomJoinPoint jp, String arg0, int[] arg1) throws Throwable { ... // lets change the args : return jp.proceed("new arg0", new int[]{1,2,3}); } }
Since 2.0.RC3, staticinitialization(<type pattern>)
pointcut is supported. It can also be used
in conjunction with withincode in the form of withincode(staticinitialization(<type pattern>))
Aspects can be deployed as perInstance
as described here.
In such a case, every single instance will be advised, even though each one will have a distinct aspect instance.
Per instance interception allow to programmatically add a new interceptor to a given instance only.
We name it interceptor since to allow this dynamicity, the semantics are limited and the interceptor
must conform to a given interface, hence the regular semantics of advice (like parameter binging with args()
)
cannot apply.
For this to work, the META-INF/aop.xml
definition has to declare which classes should be
opened for per instance interception. Fine grained control is provided to only open to a specific
set of join points f.e. only execution
, only call
, get
, set
or any composition of those get|set|execution
or all
.
Note that the <advisable... >
directive controls the caller side. Only the caller side is
made advisable, and will expose the API to add and remove advices.
For finer control on the callee side, you should consider using perTarget deployment model for regular aspects.
Then at runtime, you simply cast the instance to org.codehaus.aspectwerkz.intercept.Advisable
and use the API provided thru it.
The interceptors must implements one of the interface among:
org.codehaus.aspectwerkz.intercept.BeforeAdvice
org.codehaus.aspectwerkz.intercept.AroundAdvice
org.codehaus.aspectwerkz.intercept.AfterReturningAdvice
org.codehaus.aspectwerkz.intercept.AfterThrowingAdvice
org.codehaus.aspectwerkz.intercept.AfterAdvice
The API allows to programmatically bind an interceptor to a new pointcut, which will be implicitly narrowed to the advisable instance. It is possible to use anonymous inner interceptors: Example for the definition:
<aspectwerkz> <system id="tests"> <advisable pointcut-type="execution" expression="within(test.intercept.execution.InterceptTest)"/> <advisable pointcut-type="call" expression="within(test.intercept.call.Caller) && target(test.intercept.call.Callee)"/> <advisable pointcut-type="set" expression="within(test.intercept.set.InterceptTest)"/> <advisable pointcut-type="get" expression="within(test.intercept.get.InterceptTest)"/> <advisable pointcut-type="call|execution|set|get" expression="within(test.aspect.MemberMethodTestAspect)"/> <advisable pointcut-type="all" expression="within(test.aspect.AllMemberMethodTestAspect)"/> ... ... regular aspect / mixin definition </system> </aspectwerkz>
// Caller has been defined as Advisable in the aop.xml public class Caller { public void demo() { Callee callee = new Callee(); // Callee was declared as a possible target for "call" pointcuts in Caller // hence any call of Callee by a Caller instance is able to handle interceptor addition / removal // this call will occur with regular aspects bounded - if any callee.adviseWithAround(); callee.adviseWithAround(1); // lets add an around interceptor to the "this" Caller instance // and for the the call to Callee as expressed in the given pointcut // Note that the change will depend on the advisable definition // Here it was <advisable pointcut-type="call|execution" ...> and thus // any aw_addAdvice(..) on f.e. get / set pointcut will be useless ((Advisable) this).aw_addAdvice( "call(* test.intercept.call.Callee.adviseWithAround(..))", // we use an anonymous inner class here new AroundAdvice() { // notice the generic invoke() method defined in the // org.codehaus.aspectwerkz.intercept.AroundAdvice interface public Object invoke(JoinPoint jp) throws Throwable { InterceptTest.log("around1_pre_call "); Object result = jp.proceed(); InterceptTest.log("around1_post_call "); return result; } } ); // this call will occur with regular aspects bounded - if any // and with the new around advice (with lower precedence) callee.adviseWithAround(); callee.adviseWithAround(1); // this call on another instance of Caller will not be affected by the new around advice Caller caller = new Caller(); caller.callAdviseWithAroundOnTheGivenCalleeInstance(callee); // we can now remove the advice // it will remove the first advice found of the given class type // thus cannot really be used when an anonymous class has been used, // but using the interface enables us to remove ALL around advice. // Again we narrow the pointcut ((Advisable) this).aw_removeAdvice("call(* test.intercept.call.Callee.adviseWithAround(int))", AroundAdvice.class); // will still have the interceptor since it is another instance of Caller caller.callAdviseWithAroundOnTheGivenCalleeInstance(callee); // will not have the interceptor any more : adviseWithAround(int) signature callee.adviseWithAround(2);
Note: constructors are not made advisable ie open for this programmatic interception for now (2.0)
Sometimes it is not possible to use the weaver be it with offline mode (post compilation)
or online mode (load time weaving).
In such a case, you may still want to make use of a subset (at least) of AOP concepts.
AspectWerkz
provides its own proxy implementation to make use of the aspects (unchanged)
with proxies. The implementation relies on subclassing hence it is not needed to use interface based proxies.
For a tutorial on AWProxy, you can read this blog article that explains some more details about the API.
The distribution includes out of the box support for org.codehaus.aspectwerkz.proxy.Proxy
.
Just include the aspectwerkz.jar, aspectwerkz-core.jar and aspectwerkz-jdkXXX.jar in your classpath,
have your aspects declared in (one or more) META-INF/aop.xml available in the classpath
(defined thru annotations or XML - the usual documentation applies), and start up the application
as usual (no JVM option for weaving - though it is compatible as well).
Then to obtain a proxy of a class that is aware of the aspects available in the application classpath
(following the regular deployment hierarchy rules according to classloader delegation described
here) invoke the org.codehaus.aspectwerkz.proxy.Proxy
API.
You will get a proxy of the given class that will be aware of the aspects.
Note that only execution pointcuts will be applied (for both methods and constructors), though field get / set , call and handler pointcuts may have been defined within your aspects. This is the tradeoff of using proxy.
The advantages of using this approach is that you stick to one sole model to write your aspects
and immediately obtain proxy aware of them without any changes.
All pointcuts semantics like args(), this(), targe()
apply, as well as composition with
within()
, and cflow()
.
The performance is as good as for regular weaved class, and actually better than CGLIB proxies. You can read more about that in our AWBench project (see AW Proxy).
Example (assuming you have some aspects in the classpath):
public class TraceMe { public TraceMe(String name) { } public void step1() { step2(); } // this method is final hence won't be exposed in the proxy public final void step2() { } public static void main(String[] args) { // obtain a proxy that would be aware of the aspect(s) on "TraceMe" // TraceMe traceMe = new TraceMe("foo") is replaced by (regular reflective syntax) TraceMe traceMe = (TraceMe) Proxy.newInstance( TraceMe.class, new Class[]{String.class}, new String[]{"foo"} ); traceMe.step1(); }
Note that it is perfectly possible to use the per instance interception feature with proxies. Read more about it here.
AWProxy also provides a modified version of CGLIB so that CGLIB proxy are made aware of the AspectWerkz aspects. This version is not part of the distribution and you have to checkout it from the CVS, build it and include it first in your classpath so that your regular CGLIB becomes aware of it. This may not work if your CGLIB version has been repackaged - hence refer to the source code to rename the package as well.
CGLIB patch for AspectWerkz integration is available here To build a jar from this patch, checkout the CVS and run the following:
// in root folder of the distribution // adapt for unix / mac set ASPECTWERKZ_HOME=. // build AW ant clean dist // build the CGLIB extension cd src cd cglib-ext ant clean dist // the jar is in src/cglib-ext/target/