Hot questions for Using AspectJ in java 8

Question:

I'm migrating my project from java 7 to java 8 and the problem I have is related to aspectj weaving using aspectj-maven-plugin.

I could configure successfuly the weaving using this plugin running on Java 6 and 7 according to Haus documentation. But the problem is that I haven't found any way to use (and find) plugin version 7 that supports java 8. I saw here that plugin 7 adds java 8 support but couldn't find a way to use it.

This is the configuration plugin I need:

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>aspectj-maven-plugin</artifactId>
    <version>1.7</version> <!-- AspectJ weaver plugin 7 is for java 8 (version 1.6 is for java 7) -->
          <configuration>
              <complianceLevel>1.8</complianceLevel>
              <source>1.8</source>
              <target>1.8</target>
          </configuration>
    <executions>
        <execution>
            <goals>
                <goal>compile</goal>
                <goal>test-compile</goal>
            </goals>
        </execution>
    </executions>
</plugin>

I confirmed that above code using version 1.6 works fine for Java 7, but had no luck trying to use version 1.7.

Do you know how to run the weaver for spring+aspectj running on Java 8?


Answer:

Solution before the official release prior to Sep 2015

After many headaches and many hours struggling against this, fortunately I could solve this problem. Here is what I did:

To use aspectj-maven-plugin with Java 8 I could configure version aspectj-maven-plugin 1.7 (Note that aspectj-maven-plugin 1.6 works for Java 7).

So, the maven plugin configuration needs to be:

<!-- AspectJ configuration -->
<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>aspectj-maven-plugin</artifactId>
    <version>1.7-SNAPSHOT</version>
    <configuration>
        <complianceLevel>1.8</complianceLevel>
        <source>1.8</source>
        <target>1.8</target>
    </configuration>
    <executions>
        <execution>
            <goals>
                <goal>compile</goal>
                <goal>test-compile</goal>
            </goals>
        </execution>
    </executions>
</plugin>

By the way, the aspectJ jars needed are:

<!-- Spring AOP + AspectJ -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>1.8.1</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>4.0.4.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.8.1</version>
</dependency>

The most important thing I've struggled was that to install the aspectj-maven-plugin 1.7 jar I had to do it manually since these jar/pom files aren't on maven repo yet.

Update: So, the jar file can be downloaded from Haus Jira link (look at the Attachment section). If Haus is not available anymore you can download it from my github:

https://github.com/fedepia/aspectj-maven-plugin-1.7

After download it and copy it to my local repo I needed to create my own aspectj-maven-plugin-1.7-SNAPSHOT.pom file within the directory:

.m2\repository\org\codehaus\mojo\aspectj-maven-plugin\1.7-SNAPSHOT\aspectj-maven-plugin-1.7-SNAPSHOT.pom

I based on a copy from version 1.6 but had to modify the following content:

<version>1.7-SNAPSHOT</version>

<properties>
    <aspectjVersion>1.8.1</aspectjVersion>
    <mavenVersion>2.2.1</mavenVersion>
    <changesPluginVersion>2.9</changesPluginVersion>
</properties>

That's all here you go, hope to help.

Update: (adding more details as Xtreme Biker asked in the comments)

In my context configuration I have:

<aop:aspectj-autoproxy /> 

<bean id="notificationAspect" class="com.integration.core.aspect.NotificationAspect" factory-method="aspectOf" scope="singleton"></bean>

For my java aspect I use:

@Aspect
public class NotificationAspect
{
   ...
   @AfterThrowing(pointcut="@annotation(com.integration.core.meta.NotifyOnFailure)", throwing="ex")
   public void executeOnException(JoinPoint joinPoint, ExternalApiExecutionException ex) throws Throwable
    {
    ...



Finally official plugin released since Sep 2015

This is an update to the answer with the official plugin release. In order to use Java 8 with AspectJ, the official aspectj maven plugin can be found on this link:

http://www.mojohaus.org/aspectj-maven-plugin/usage.html

Here is the link to maven repository:

http://mvnrepository.com/artifact/org.codehaus.mojo/aspectj-maven-plugin/1.8

As the documentation stated the code to use it is:

<project>
  ...
  <dependencies>
    ...
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjrt</artifactId>
      <version>1.8.7</version>
    </dependency>
    ...
  </dependencies>
  ...
  <build>
    <plugins>
      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>aspectj-maven-plugin</artifactId>
        <version>1.8</version>
        <executions>
          <execution>
            <goals>
              <goal>compile</goal>       <!-- use this goal to weave all your main classes -->
              <goal>test-compile</goal>  <!-- use this goal to weave all your test classes -->
            </goals>
          </execution>
        </executions>
      </plugin>
      ...
    </plugins>
  <build>
  ...
</project>

Question:

I'm trying to use the metrics-aspectj library (https://github.com/astefanutti/metrics-aspectj) to get annotated metrics working in my Dropwizard app, but I am seeing the following exception on startup:

[ERROR] Failed to execute goal org.codehaus.mojo:aspectj-maven-plugin:1.8:compile (default) on project app: AJC compiler errors:
[ERROR] error Missing message: configure.incompatibleComplianceForSource in: org.aspectj.ajdt.ajc.messages
[ERROR] error no sources specified
[ERROR] abort AspectJ Compiler 1.8.7

This is what's in my pom file:

<dependency>
  <groupId>io.astefanutti.metrics.aspectj</groupId>
  <artifactId>metrics-aspectj</artifactId>
  <version>1.2.0</version>
</dependency>
<dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjrt</artifactId>
  <version>1.8.10</version>
</dependency>

  <plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>aspectj-maven-plugin</artifactId>
    <version>1.8</version>
    <configuration>
      <source>1.8</source>
      <target>1.8</target>
      <aspectLibraries>
        <aspectLibrary>
          <groupId>io.astefanutti.metrics.aspectj</groupId>
          <artifactId>metrics-aspectj</artifactId>
        </aspectLibrary>
      </aspectLibraries>
    </configuration>
    <executions>
      <execution>
        <goals>
          <goal>compile</goal>
        </goals>
      </execution>
    </executions>
  </plugin>

App Details

  • Java 8
  • Dropwizard
  • Multi Module Setup

Answer:

[ERROR] error Missing message: configure.incompatibleComplianceForSource in: org.aspectj.ajdt.ajc.messages

Will be fixed by adding

<complianceLevel>1.8</complianceLevel>

in addition to the <source/> & <target/>

<complianceLevel/> defaults to 1.5 and <source/> 1.8 is not backwards compatible to 1.5.

Question:

I have a working AOP (when using inside the project it is written in) but when I build this project (maven install), and use that JAR in another project, and try to use the @TimedLog annotation, nothing happens. I try to breakpoint into it, but it doesn't reach there.

It looks like this:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface TimedLog {
    boolean shouldAttachMethodArgs() default false;
    boolean shouldAttachReturnValue() default false;
    String message() default "";
}

This is the actual Aspect:

@Aspect
@Configuration
@Slf4j
public class MethodExecutionAspect {

    @Pointcut("@annotation(timedLogVar)")
    public void annotationPointCutDefinition(TimedLog timedLogVar) {}

    @Pointcut("execution(* *(..))")
    public void atExecution() {}

    @Around(value = "annotationPointCutDefinition(timedLogVar) && atExecution()", argNames = "joinPoint,timedLogVar")
    public Object around(ProceedingJoinPoint joinPoint, TimedLog timedLogVar) throws Throwable {
        Stopwatch stopwatch = Stopwatch.createStarted();
        Object returnValue = joinPoint.proceed();
        stopwatch.stop();

        log.info(String.format("test message %s", stopwatch.elapsed(TimeUnit.MILLISECONDS)));

        return returnValue;
    }
}

An implementation of it would be:

@TimedLog
void testDefaultValues() throws InterruptedException {
    int sleepTimeInMillis = 200;
    log.info("Resting for {} millis", value("sleepTimeInMillis", sleepTimeInMillis));
    Thread.sleep(sleepTimeInMillis);
}

pom.xml

<!-- AOP -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>5.0.2.RELEASE</version>
    <scope>compile</scope>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.8.13</version>
    <scope>compile</scope>
</dependency>

From what you can see here, this is an AOP that decorates a method and logs its runtime.

I've been struggling with it for a while now, and would really appreciate your help.

Thanks

EDIT: As requested, the full pom.xml of the project that's supposed to use that AOP (it lives foo.bar.utils:utils-common)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>backend</artifactId>
        <groupId>foo.bar.backend</groupId>
        <version>2.0.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>backend-logic</artifactId>
    <repositories>
        <repository>
            <id>maven-s3-release-repo</id>
            <name>S3 Release Repository</name>
            <url>s3://repository.foobar.com/releases</url>
        </repository>
        <repository>
            <id>maven-s3-snapshot-repo</id>
            <name>S3 Snapshot Repository</name>
            <url>s3://repository.foobar.com/snapshots</url>
        </repository>
    </repositories>

    <dependencies>
        <dependency>
            <groupId>foo.bar.backend</groupId>
            <artifactId>backend-contract</artifactId>
            <version>2.0.0-SNAPSHOT</version>
        </dependency>

        <!-- Spring boot actuator to expose metrics endpoint -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!-- Micormeter core dependecy  -->
        <dependency>
            <groupId>io.micrometer</groupId>
            <artifactId>micrometer-core</artifactId>
        </dependency>
        <!-- Micrometer Prometheus registry  -->
        <dependency>
            <groupId>io.micrometer</groupId>
            <artifactId>micrometer-registry-prometheus</artifactId>
        </dependency>

        <!-- Common -->
        <dependency>
            <groupId>foo.bar.utils</groupId>
            <artifactId>utils-common</artifactId>
            <version>1.0.0-SNAPSHOT</version>
        </dependency>

        <!-- Auth -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-security</artifactId>
            <version>2.0.0.M1</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

        <!-- Utils -->
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>18.0</version>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.4</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.datatype</groupId>
            <artifactId>jackson-datatype-joda</artifactId>
        </dependency>
        <dependency>
            <groupId>joda-time</groupId>
            <artifactId>joda-time</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.4</version>
        </dependency>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-io</artifactId>
            <version>1.3.2</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

    </dependencies>
    <build>
        <extensions>
            <extension>
                <groupId>org.springframework.build</groupId>
                <artifactId>aws-maven</artifactId>
                <version>5.0.0.RELEASE</version>
            </extension>
        </extensions>
    </build>

</project>

EDIT2: The implementation that doesn't work (in the different project that's supposed to use the AOP)

@Slf4j
@Configuration
@EnableAspectJAutoProxy
public class TestingSomething {

    @TimedLog(message = "test something")
    public void testingSomething() {
        log.info("ololol");
    }

}

And the test, testing it:

@RunWith(SpringRunner.class)
@SpringBootTest(classes = SomeSpringConfiguration.class,
        properties = {"server.port=0", "enable.security=true"})
@WebAppConfiguration
public class testingSomethingTest {
    @Autowired
    TestingSomething testingSomething;

    @Test
    public void testingLol() {
        testingSomething.testingSomething();
    }
}

Answer:

For the aspects to work you need to enable them. To enable you need to either configure them through xml or through annotation:

@Configuration
@EnableAspectJAutoProxy

Through xml:

<beans …>
      <!– Enable AspectJ auto-wiring –>
      <aop:aspectj-autoproxy />
</beans>

When you include your jar into another application, this other application has its own configuration and context. Even if you have enabled the aspect autowiring for your original context you still need to do the same for your new application by one of the two ways pointed above.

If you use the annotation make sure it is within the component scan scope and that it is actualy included into your context.

UPDATE: do @Import (MethodExecutionAspect.class) on your testingSomethingTest to ensure it is component scanned.

Question:

I am in the process of upgrading a project from Java 7 to Java 8 (and with it Spring 3 to Spring 4).

I am getting the following compilation error:

DomainSecurityAspect.java:[88,39] error: cannot access ApplicationEventPublisherAware

The code it is complaining about is:

@Around("target(com.mycompany.automation.domain.framework.DomainEntityImpl+) && !execution(* equals(..)) && !execution(* hashCode()) "
        + "&& !execution(* toString()) && !execution(* get*(..)) && !execution(* is*(..)) && execution(public * *(..)) "
        + "&& !within(com.mycompany.iecc.data.automation.domain.aop.DomainSecurityAspect)"
        + "&& !execution(* com.mycompany.iecc.data.automation.domain.framework.BaseObject.*(..))"
        + "&& !execution(* com.mycompany.iecc.data.automation.domain.framework.DomainObjectImpl.*(..))")

public Object domainObjectInstanceExecution(final ProceedingJoinPoint thisJoinPoint) throws Throwable    
{
    if (this.securityInterceptor == null)
    {
        return thisJoinPoint.proceed();
    }

    final AspectJCallback callback = new AspectJCallback()
    {
        @Override
        public Object proceedWithObject()
        {
            try
            {
                return thisJoinPoint.proceed();
            }
            catch (Error e) 
            {
                throw e;
            }
            catch (RuntimeException re) 
            {
                throw re;
            }
            catch (Throwable th)
            {
                throw new RuntimeException(th);
            }
        }
    };

    // Compiler complains about this line
    return this.securityInterceptor.invoke(thisJoinPoint, callback);

}
Software Versions I am using:

Java 8 (jdk1.8.0_66)

Spring 4.2.4.RELEASE

Aspectj version 1.8.8

cglib-nodep version 3.2.0

Note: This compiles OK when it uses Java 7 with Spring 4, but when compiled with Java 8 it gives that compilation error.


Answer:

It turns out the error was in my POM - to fix this I had to change the scope from runtime to compile

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <scope>compile</scope>
    </dependency>