© Copyright 2018 Contributors. All rights reserved.

The full list of resolved issues in 1.9.0 is available here.

AspectJ 1.9.0

Improved runtime interface

New factory methods have been added to the AspectJ runtime. This is an attempt to more optimally create thisJoinPoint and thisEnclosingJoinPoint objects. The generated code that invokes these now also uses the ability for the LDC bytecode instruction to load class constants directly (this replaces what was happening previously where generated code referenced string classnames and classloading was being done from the aspectj runtime as the woven application was starting).

This is turned on by using -Xajruntimetarget:1.9. This option was used previously to enable users to target an old runtime if they knew that old runtime is all that was available at some deployed target. The new generation mechanism is not the default, not until it has had a bit more testing out in the wild.

The changes to generated code have a couple of potential side effects:

In anticipation of not all build plugins supporting that -Xajruntimetarget option, you can now specify these kinds of option in the ASPECTJ_OPTS environment variable. Set that in your environment:


export ASPECTJ_OPTS="-Xajruntimetarget:1.9"

And it should get picked up by AspectJ when it runs.

AspectJ 1.9.0.RC4

Primary changes in RC4 are to add support for <compilerArg> in the Ant task. This enables users of the Ant task to pass in options supported by the underlying AspectJ but not yet surfaced elsewhere. Particularly useful with Java9 which includes a number of module related commands. For example, here is an iajc usage with compilerArg that is passing --add-modules java.xml.bind:


  <iajc destdir="bin" failonerror="true"
     showWeaveInfo="true" source="1.9" target="1.9"
     debug="true"  fork="true"  maxmem="256m">
     <compilerArg value="--add-modules"/>
     <compilerArg value="java.xml.bind"/>
    <src path="src" />
    <classpath>
            <pathelement location="${aspectj.home}/lib/aspectjrt.jar"/>
    </classpath>
 </iajc>
  • 1.9.0.RC4 available 21-Feb-2018
  • AspectJ 1.9.0.RC3

    Primary changes in RC3 are to upgrade JDT and pickup all the fixes for Java9 that have gone into it over the last few months.

  • 1.9.0.RC3 available 5-Feb-2018
  • AspectJ 1.9.0.RC2

    Key change in 1.9.0.RC2 is actually to be more tolerant of JDK10. The version handling has been somewhat overhauled so AspectJ 9 will behave better on Java 10 and future JDKs. This should put AspectJ in a better place if new JDK versions are going to arrive thick and fast.

    AspectJ 1.9.0.RC1

    This is the first release candidate of AspectJ 1.9.0 - the version of AspectJ to be based on Java9. It includes a recent version of the Eclipse Java9 compiler (from jdt core, commit #062ac5d7a6bf9).

    Automatic Modules

    AspectJ can now be used with the new module system available in Java9. The key jars in AspectJ have been given automatic module names. The automatic module name is org.aspectj.runtime for the aspectjrt module:

    
    $ java --module-path <pathto>/lib/aspectjrt.jar --list-modules | grep aspectj
    
    org.aspectj.runtime file:///<pathto>/lib/aspectjrt.jar automatic
    
    

    And similarly org.aspectj.weaver and org.aspectj.tools for aspectjweaver and aspectjtools respectively:

    
    $ java --module-path <pathto>/lib/aspectjweaver.jar --describe-module org.aspectj.weaver
    
    org.aspectj.weaver file:///<pathto>/lib/aspectjweaver.jar automatic
    requires java.base mandated
    contains aj.org.objectweb.asm
    contains aj.org.objectweb.asm.signature
    contains org.aspectj.apache.bcel
    contains org.aspectj.apache.bcel.classfile
    contains org.aspectj.apache.bcel.classfile.annotation
    contains org.aspectj.apache.bcel.generic
    contains org.aspectj.apache.bcel.util
    contains org.aspectj.asm
    contains org.aspectj.asm.internal
    ...
    



    Building woven modules

    AspectJ understands module-info.java source files and building modules that include aspects. Here is an example:

    
    module-info.java
    
    module demo {
    	exports pkg;
    	requires org.aspectj.runtime;
    }
    
    
    pkg/Demo.java
    
    package pkg;
    
    public class Demo {
    	public static void main(String[] argv) {
    		System.out.println("Demo running");
    	}
    }
    
    
    otherpkg/Azpect.java
    
    package otherpkg;
    
    public aspect Azpect {
    	before(): execution(* *(..)) && !within(Azpect) {
    		System.out.println("Azpect running");
    	}
    }
    
    

    We can now build those into a module:

    
    $ ajc -1.9 module-info.java otherpkg/Azpect.java pkg/Demo.java -outjar demo.jar
    
    ...
    module-info.java:3 [error] org.aspectj.runtime cannot be resolved to a module
    ...
    

    Wait, that failed! Yes, aspectjrt.jar (which includes the required org.aspectj.weaver module) wasn't supplied. We need to pass it on the module-path:

    
    $ ajc -1.9 --module-path <pathto>/aspectjrt.jar module-info.java otherpkg/Azpect.java pkg/Demo.java -outjar demo.jar
    
    

    Now we have a demo module we can run:

    
    $ java --module-path <pathto>/aspectjrt.jar:demo.jar --module demo/pkg.Demo
    
    Azpect running
    Demo running
    

    That's it!



    Binary weaving with modules

    A module is really just a jar with a module-info descriptor. As such you can simply pass a module on the inpath and binary weave it with other aspects. Take the module we built above, let's weave into it again:

    extra/AnotherAzpect.java
    
    package extra;
    
    public aspect AnotherAzpect {
    	before(): execution(* *(..)) && !within(*Azpect) {
    		System.out.println("AnotherAzpect running");
    	}
    }
    
    
    $ ajc -inpath demo.jar AnotherAzpect.java -outjar newdemo.jar

    Notice how there was no complaint here that the org.aspectj.runtime module hadn't been passed in. That is because inpath was being used which doesn't treat specified jars as modules (and so does not check dependencies). There is no module-inpath right now.

    Because the new jar produced includes the compiled aspect, the module-info specification inside is still correct, so we can run it exactly as before:

    $ java --module-path ~/installs/aspectj190rc1/lib/aspectjrt.jar:newdemo.jar --module demo/pkg.Demo
    
    Azpect running
    AnotherAzpect running
    Demo running
    


    Faster Spring AOP

    Dave Syer recently created a series of benchmarks for checking the speed of Spring-AspectJ: https://github.com/dsyer/spring-boot-aspectj

    Here we can see the numbers for AspectJ 1.8.11 (on an older Macbook Pro):

    
    Benchmark                 (scale)  Mode  Cnt   Score   Error  Units
    StartupBenchmark.ltw          N/A  avgt   10   2.553 ~ 0.030   s/op
    StartupBenchmark.ltw_100      N/A  avgt   10   2.608 ~ 0.046   s/op
    StartupBenchmark.spring     v0_10  avgt   10   2.120 ~ 0.148   s/op
    StartupBenchmark.spring     v1_10  avgt   10   2.219 ~ 0.066   s/op
    StartupBenchmark.spring    v1_100  avgt   10   2.244 ~ 0.030   s/op
    StartupBenchmark.spring    v10_50  avgt   10   2.950 ~ 0.026   s/op
    StartupBenchmark.spring    v20_50  avgt   10   3.854 ~ 0.090   s/op
    StartupBenchmark.spring   v20_100  avgt   10   4.003 ~ 0.038   s/op
    StartupBenchmark.spring     a0_10  avgt   10   2.067 ~ 0.019   s/op
    StartupBenchmark.spring     a1_10  avgt   10   2.724 ~ 0.023   s/op
    StartupBenchmark.spring    a1_100  avgt   10   2.778 ~ 0.057   s/op
    StartupBenchmark.spring    a10_50  avgt   10   7.191 ~ 0.134   s/op
    StartupBenchmark.spring   a10_100  avgt   10   7.191 ~ 0.168   s/op
    StartupBenchmark.spring    a20_50  avgt   10  11.541 ~ 0.158   s/op
    StartupBenchmark.spring   a20_100  avgt   10  11.464 ~ 0.157   s/op
    

    So this is the average startup of an app affected by aspects applying to the beans involved. Where numbers are referenced the first is the number of aspects/pointcuts and the second is the number of beans. The 'a' indicates an annotation based pointcut vs a non-annotation based pointcut ('v'). Notice things are much worse for annotation based pointcuts. At 20 pointcuts and 50 beans the app is 9 seconds slower to startup.

    In AspectJ 1.8.12 and 1.9.0.RC1 some work has been done here. The key change is to recognize that the use of annotations with runtime retention is much more likely than annotations with class level retention. Retrieving annotations with class retention is costly because we must open the bytes for the class file and dig around in there (vs runtime retention which are immediately accessible by reflection on the types). In 1.8.11 the actual type of the annotation involved in the matching is ignored and the code will fetch *all* the annotations on the type/method/field being matched against. So even if the match is looking for a runtime retention annotation, we were doing the costly thing of fetching any class retention annotations. In 1.8.12/1.9.0.RC1 we take the type of the match annotation into account - allowing us to skip opening the classfiles in many cases. There is also some deeper work on activating caches that were not previously being used correctly but the primary change is factoring in the annotation type.

    What difference does that make? AspectJ 1.9.0.RC1:

    
    Benchmark                 (scale)  Mode  Cnt  Score   Error  Units
    StartupBenchmark.ltw          N/A  avgt   10  2.568 ~ 0.035   s/op
    StartupBenchmark.ltw_100      N/A  avgt   10  2.622 ~ 0.075   s/op
    StartupBenchmark.spring     v0_10  avgt   10  2.096 ~ 0.054   s/op
    StartupBenchmark.spring     v1_10  avgt   10  2.206 ~ 0.031   s/op
    StartupBenchmark.spring    v1_100  avgt   10  2.252 ~ 0.025   s/op
    StartupBenchmark.spring    v10_50  avgt   10  2.979 ~ 0.071   s/op
    StartupBenchmark.spring    v20_50  avgt   10  3.851 ~ 0.058   s/op
    StartupBenchmark.spring   v20_100  avgt   10  4.000 ~ 0.046   s/op
    StartupBenchmark.spring     a0_10  avgt   10  2.071 ~ 0.026   s/op
    StartupBenchmark.spring     a1_10  avgt   10  2.182 ~ 0.032   s/op
    StartupBenchmark.spring    a1_100  avgt   10  2.272 ~ 0.024   s/op
    StartupBenchmark.spring    a10_50  avgt   10  2.557 ~ 0.027   s/op
    StartupBenchmark.spring   a10_100  avgt   10  2.598 ~ 0.040   s/op
    StartupBenchmark.spring    a20_50  avgt   10  2.961 ~ 0.043   s/op
    StartupBenchmark.spring   a20_100  avgt   10  3.093 ~ 0.098   s/op
    

    Look at the a20_100 case - instead of impacting start time by 9 seconds, it impacts it by 1 second.

    More to come...