Hot questions for Using AspectJ in aspects

Question:

Say Service calls Dao class on which logging aspect(annotational) needs to be applied. I am wondering how aspects actually gets applied.

As per my understanding at the time of DAO injection under Service object, spring finds out that there is some aspect(in this case logging) is configured for DAO, so it injects the proxy object instead of actual target object. Now when actual call is made to any method inside DAO, proxy applies the aspects and then call the actual target object. Is that correct ? Also i believe this is called Run time weaving.

On the other hand same can be done with load time weaving(with javaagent configuration) where byte code manipulation is done for classes on which aspects needs to be applied. So proxy does not come into picture here.

Please correct me if i am wrong as this is the foundation for all spring modules?


Answer:

Your understanding is right. Spring AOP is proxy-based. Spring uses either JDK proxies (preferred wheneven the proxied target implements at least one interface) or CGLIB proxies (if the target object does not implement any interfaces) to create the proxy for a given target bean.

Unless configured to do otherwise, Spring AOP performs run-time weaving. You can however set up Spring to do load-time weaving through AspectJ. Check the documentation link for more details.

Reference for Spring AOP proxying internals

Question:

I am trying to use AspectJ in sample project in IntelliJ IDEA. I have an experience with Spring AOP, but this is first time I am using AspectJ, and cannot make it work.

I am trying to do as described here: https://www.jetbrains.com/help/idea/2017.1/aspectj.html

My build.gradle:

apply plugin: 'java'

repositories
{
    mavenCentral()
}

dependencies
{
    compile "org.projectlombok:lombok:+"

    compile "org.aspectj:aspectjrt:+"
    compile "org.aspectj:aspectjweaver:+"
    compile "org.aspectj:aspectjtools:+"
}

buildscript
{
    repositories
    {
        maven
        {
            url "https://maven.eveoh.nl/content/repositories/releases"
        }
    }

    dependencies
    {
        classpath "nl.eveoh:gradle-aspectj:+"
    }
}

project.ext
{
    aspectjVersion = '+'
}

apply plugin: 'aspectj'

My aspect:

package aspects;

import lombok.SneakyThrows;
import lombok.experimental.FieldDefaults;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

import java.util.Arrays;

@Aspect
@FieldDefaults(makeFinal = true)
public aspect LoggingAspect
{
    Journal journal = Journal.INSTANCE;

    pointcut all(ProceedingJoinPoint proceedingJoinPoint) : execution(* * set*(..) );

    around() : all(ProceedingJoinPoint proceedingJoinPoint)
    {
        System.out.println("asd");
    }

    @SneakyThrows
    @Around("execution(* * repository.*.*(..))")
    public Object log(ProceedingJoinPoint proceedingJoinPoint)
    {
        journal.write(proceedingJoinPoint.getThis().getClass().getCanonicalName());
        journal.write("\n");

        String arguments = Arrays
            .stream(proceedingJoinPoint.getArgs())
            .map(o -> o.getClass().getCanonicalName() + " " + o.toString())
            .reduce(new StringBuilder(), StringBuilder::append, StringBuilder::append)
            .toString();

        journal.write(arguments);
        journal.write("\n");

        long start = System.currentTimeMillis();

        Object result = proceedingJoinPoint.proceed();

        journal.write(result.toString());
        journal.write("\n");

        journal.write(String.valueOf(System.currentTimeMillis() - start));
        journal.write("\n\n");
        journal.flush();

        return result;
    }
}

Sample class:

package repository;

import java.io.Serializable;

public class Task implements Serializable
{
    private static final long serialVersionUID = 1L;

    enum Status {NEW, IN_PROGRESS, FINISHED};

    private Integer id;
    private String description;
    private Employee assignee;
    private Employee reporter;
    private Status status;

    public Task(final Integer id, String description, final Employee assignee, final Employee reporter) {
        this.id = id;
        this.assignee = assignee;
        this.reporter = reporter;
        this.description = description;
        this.status = Status.NEW;
    }

    public Employee getAssignee() {
        return assignee;
    }

    public void setAssignee(Employee assignee) {
        this.assignee = assignee;
    }

...
}

I have IntelliJ IDEA Ultimate 2017.2 and it hints correctly that all methods (getters and setters) are my point-cuts. It also hints me that for advice "log" in my aspect there are multiple point-cuts. However, it does not hint about "all" advice.

I have installed (in newest version) plugins like: - AspectJ Support - AspectJ weaver - Spring AOP/@AspectJ

Gradle dependencies automatically created libraries for this project so I did not duplicate it.

I have enabled: Build > AspectJ weaving.

Settings > Build, Execution, Deployment > Compiler > Java Compiler:

Use compiler: ajc

Path to Ajc compiles: /home/me/.gradle/caches/modules-2/files-2.1/org.aspectj/aspectjtools/1.8.10/5c5957ee4615bde33b54ce965f5b85b8827b97d5/aspectjtools-1.8.10.jar

Command line parameters: empty

Generate debug info: checked

Delegate to javac: chcecked

Enable annotation processing options: checked

Project builds and compiles without problem. It runs like there was never any aspect, particularity nothing from aspect is printed out and no temporary files are created, and all methods runs fine (although in my "all" advice that is around I have not processed joint point).

When I create breakpoints in first lines of any advice and run project with debugger nothing is caught.

What am I missing?


Answer:

After wasting even more hours, I found out that I have mixed two distinct AspectJ sytnax together.

One that is just about aspects (like "public aspect LoggingAspect"), and second one that are regular Java annotations (like "@Aspect" or "@Around").

When I have replaced "public aspect LoggingAspect" with "public class LoggingAspect" it worked. It was very hard to find out, because everything still compiles without problem.

Hope it helps somebody someday or at least in future version of ajc they will add more verbose compiler output.

Question:

Background

Performing post-compile weaving of projects using:

  • AspectJ 1.9.4
  • io.freefair.aspectj.post-compile-weaving 4.1.1
  • Java 11.0.3
  • Gradle 5.6.2 (Groovy 2.5.4, Kotlin 1.3.41)

This project does not use Maven or Spring.

Layout

The projects include:

  • app.aspects - Contains a single LogAspect class annotated with @Aspect.
  • app.aspects.weaver - No source files, only dependencies to declare aspects and project to weave.
  • app.common - Defines @Log annotation referenced by pointcuts described in LogAspect.
  • app.program.main - Files to be woven with jointpoints described in LogAspect.
Gradle

Build files that relate to aspects are defined here. The idea is that weaving is independent from the application so neither the application's common classes nor the main program need know about weaving. Rather, the main program need only reference @Log from the common package and AJC will take care of the weaving.

app.aspects
apply plugin: "io.freefair.aspectj.post-compile-weaving"

dependencies {
    // For the @Log annotation
    compileOnly project(':app.common')

    // The LogAspect's joinpoint references the Main Program
    compileOnly project(':app.program.main')

    // Logging dependency is also compiled, but not shown here
}
app.aspects.weaver
apply plugin: "io.freefair.aspectj.post-compile-weaving"

dependencies {
    compileOnly "org.aspectj:aspectjrt:1.9.4"

    // This should set the -aspectpath ?
    aspect project(":app.aspects")

    // This should set the -inpath ?
    inpath(project(":app.program.main")) {
        // Only weave within the project
        transitive = false
    }
}
Classes
Log

The Log annotation is straightforward:

package com.app.common.aspects;

@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE, ElementType.CONSTRUCTOR })
public @interface Log {
    boolean secure() default false;
}
Main Program

The main program resembles:

package com.app.program.main;

import com.app.common.aspects.Log;

@Log
public class Program {

  /** This is the method to weave. */
  protected void run() throws InterruptedException, TimeoutException {
  }
}
Logging Aspect

The logging aspect resembles (see the code from a related question):

@Aspect
public class LogAspect {

    // In the future this will target points annotated with @Log
    @Pointcut("execution(* com.app.program.main.Program.run(..))")
    public void loggedClass() {
    }

    @Around("loggedClass()")
    public Object log(final ProceedingJoinPoint joinPoint) throws Throwable {
      return log(joinPoint, false);
    }

    private Object log(final ProceedingJoinPoint joinPoint, boolean secure) throws Throwable {
      // See last year's code for the full listing
      log.info("\u21B7| {}{}#{}({})", indent, className, memberName, params);
    }
}
Problem

It appears weaving is taking place, but the advice cannot be found:

.../app.aspects/build/classes/java/main!com/app/aspects/LogAspect.class [warning] advice defined in com.app.aspects.LogAspect has not been applied [Xlint:adviceDidNotMatch]

Question

What needs to change so that weaving of the LogAspect into Program's run() method works using Gradle?

Options File

The ajc.options file shows:

-inpath
.../app.aspects/build/classes/java/main
-classpath
.../.gradle/caches/modules-2/files-2.1/org.aspectj/...
-d
.../app.aspects/build/classes/java/main
-target
11
-source
11

It is disconcerting that -aspectpath isn't shown and -inpath is listing app.aspects instead of app.program.main.


Answer:

Merging apps.aspects and apps.aspects.weaver into the same project has produced:

Join point 'method-execution(void com.app.program.main.Program.run())' in Type 'com.app.program.main.Program' (Program.java:396) advised by around advice from 'com.app.aspects.LogAspect' (LogAspect.class(from LogAspect.java))

While this solves the problem, I don't understand why LogAspect needs to be in the same project that performs the weaving. The Gradle file becomes:

apply plugin: "io.freefair.aspectj.post-compile-weaving"

dependencies {
    compileOnly "org.aspectj:aspectjrt:1.9.4"
    compileOnly project(':app.common')
    compileOnly project(':app.program.main')

    compileOnly org_apache_logging_log4j__log4j_api

    inpath(project(":app.program.main")) {
        transitive = false
    }
}

compileJava.ajc.options.compilerArgs += "-showWeaveInfo"
compileJava.ajc.options.compilerArgs += "-verbose"

Question:

 class Test {

@override
public String a(){
b();
d();
}


private String b() {
c();
}

private String c(){
d();
}
private String d(){}

}

I want to intercept each methods of class Test that is been called from overridden method A() and want to know how much time each method like b(), c() took while processing some business logic separately.

How can I achieve it using Spring AOP or Aspectj?


Answer:

In order to

  • weave into private methods,
  • handle self-invocation within one class,
  • dynamically determine control flow and limit interception to only methods called directly or indirectly by your interface method

you need to switch from Spring AOP (proxy-based, many limitations, slow) to AspectJ using LTW (load-time weaving) as described in the Spring manual.

Here is an example in pure AspectJ (no Spring, Just Java SE) which you can easily adapt to your needs:

Sample interface

package de.scrum_master.app;

public interface TextTransformer {
  String transform(String text);
}

Class implementing interface incl. main method:

As you can see, I made up an example like yours and also made the methods spend time in order to have something to measure in the aspect later:

package de.scrum_master.app;

public class Application implements TextTransformer {
  @Override
  public String transform(String text) {
    String geekSpelling;
    try {
      geekSpelling = toGeekSpelling(text);
      return toUpperCase(geekSpelling);
    } catch (InterruptedException e) {
      throw new RuntimeException(e);
    }

  }

  private String toGeekSpelling(String text) throws InterruptedException {
    Thread.sleep(100);
    return replaceVovels(text).replaceAll("[lL]", "1");
  }

  private String replaceVovels(String text) throws InterruptedException {
    Thread.sleep(75);
    return text.replaceAll("[oO]", "0").replaceAll("[eE]", "Ɛ");
  }

  private String toUpperCase(String text) throws InterruptedException {
    Thread.sleep(50);
    return text.toUpperCase();
  }

  public static void main(String[] args) throws InterruptedException {
    System.out.println(new Application().transform("Hello world!"));
  }
}

Aspect:

package de.scrum_master.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import static java.lang.System.currentTimeMillis;

@Aspect
public class TimingAspect {
  @Around("execution(* *(..)) && cflow(execution(* de.scrum_master.app.TextTransformer.*(..)))")
  public Object measureExecutionTime(ProceedingJoinPoint thisJoinPoint) throws Throwable {
    long startTime = currentTimeMillis();
    Object result = thisJoinPoint.proceed();
    System.out.println(thisJoinPoint + " -> " + (currentTimeMillis() - startTime) + " ms");
    return result;
  }
}

Console log:

execution(String de.scrum_master.app.Application.replaceVovels(String)) -> 75 ms
execution(String de.scrum_master.app.Application.toGeekSpelling(String)) -> 189 ms
execution(String de.scrum_master.app.Application.toUpperCase(String)) -> 63 ms
execution(String de.scrum_master.app.Application.transform(String)) -> 252 ms
HƐ110 W0R1D!

You can also exclude the transform(..) method by just changing the pointcut from cflow() to cflowbelow():

@Around("execution(* *(..)) && cflowbelow(execution(* de.scrum_master.app.TextTransformer.*(..)))")

Then the console log is just:

execution(String de.scrum_master.app.Application.replaceVovels(String)) -> 77 ms
execution(String de.scrum_master.app.Application.toGeekSpelling(String)) -> 179 ms
execution(String de.scrum_master.app.Application.toUpperCase(String)) -> 62 ms
HƐ110 W0R1D!

I hope this is what you wanted. It is hard to tell because your description is somewhat ambiguous. BTW, please do read an AspectJ and/or Spring AOP manual.

Question:

I have a pretty short question in relation to AspectJ.

Do aspects execute from a separate thread or do they execute on the existing thread (i.e. the one your main method executes from)?


Answer:

They execute in the same thread where the advised code was executing. AspectJ is not concerned about threading in any way. Of course, nothing stops you from developing an aspect that, when triggered, would go on and create new threads, or schedule work to an executor service, or whatever else you might come to think of.

Question:

I am using AspectJ in Java to log the calls to some methods. I've looked online but couldn't manage to find an answer to this:

What happens when two @Around advices match for a method?

Specifically, I am using two @Around advices, like this:

@Around("condition1() && condition2() && condition3()")
public Object around(ProceedingJoinPoint point) {
    return around(point, null);
}

@Around("condition1() && condition2() && condition3() && args(request)")
public Object around(ProceedingJoinPoint point, Object request) {
    ...
    result = (Result) point.proceed();
    ...
}

Will this result in point.proceed() being called twice (having the actual method called twice) if both of these advices match?


Answer:

Your approach is highly problematic because you manually call one advice from another one. This is not how AOP should be applied. Please let AspectJ decide which advices to execute based on their respective pointcuts. The way you delegate from one advice to another you could even call an advice which would not match by itself. Example in plain AspectJ without Spring (works the same in Spring AOP, though):

Java driver application:

package de.scrum_master.app;

public class Application {
    private static void doSomething() {
        System.out.println("Doing something");
    }

    public static void main(String[] args) {
        doSomething();
    }
}

Aspect:

package de.scrum_master.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

@Aspect
public class MyBogusAspect {
    @Around("execution(* doSomething(..))")
    public Object matchingAdvice(ProceedingJoinPoint thisJoinPoint) {
        System.out.println("matching advice called on joinpoint " + thisJoinPoint);
        return nonMatchingAdvice(thisJoinPoint);
    }

    @Around("execution(* doSomethingElse(..))")
    public Object nonMatchingAdvice(ProceedingJoinPoint thisJoinPoint) {
        System.out.println("non-matching advice called on joinpoint " + thisJoinPoint);
        return thisJoinPoint.proceed();
    }
}

Console log:

matching advice called on joinpoint execution(void de.scrum_master.app.Application.doSomething())
non-matching advice called on joinpoint execution(void de.scrum_master.app.Application.doSomething())
Doing something

Can you see how unhealthy your approach is? An advice which otherwise would not match is called by a matching one. This yields some really unexpected behaviour IMO. Please don't do it!!!

Now as for your original question about multiple matching advice, this is how you should do it:

Modified aspect:

package de.scrum_master.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

@Aspect
public class MyBetterAspect {
    @Around("execution(* doSomething(..))")
    public Object matchingAdvice(ProceedingJoinPoint thisJoinPoint) {
        System.out.println(">>> matching advice on " + thisJoinPoint);
        Object result = thisJoinPoint.proceed();
        System.out.println("<<< matching advice on " + thisJoinPoint);
        return result;
    }

    @Around("execution(* doSomething(..))")
    public Object anotherMatchingAdvice(ProceedingJoinPoint thisJoinPoint) {
        System.out.println(">>> another matching advice on " + thisJoinPoint);
        Object result = thisJoinPoint.proceed();
        System.out.println("<<< another matching advice on " + thisJoinPoint);
        return result;
    }
}

New console log:

>>> matching advice on execution(void de.scrum_master.app.Application.doSomething())
>>> another matching advice on execution(void de.scrum_master.app.Application.doSomething())
Doing something
<<< another matching advice on execution(void de.scrum_master.app.Application.doSomething())
<<< matching advice on execution(void de.scrum_master.app.Application.doSomething())

As you can see, AspectJ or Spring AOP wrap multiple matching advice like onion skins around joinpoints and only the innermost proceed() calls the actual joinpoint while the outer layers call the inner ones, making sure that each joinpoint is executed only once. There is no need for you trying to be smarter than the AOP framework, possibly causing damage (see my first example).

One more thing: If multiple aspects have matching pointcuts, you can influence their order of execution via @DeclarePrecedence in AspectJ, but within a single aspect you have no influence on the execution order or at least you should not rely on it. In Spring AOP you can use the @Order annotation in order to determine aspect precedence, but the order is also undefined for multiple advice from the same aspect, see also the Spring manual.


Update 2016-02-28, 18:30 CET, after some discussion in comments:

Okay, we extend the driver class a little bit so we can test some more:

package de.scrum_master.app;

public class Application {
    private static void doSomething() {
        System.out.println("Doing something");
    }

    private static String doSomethingElse(String text) {
        System.out.println("Doing something else");
        return text;
    }

    private static int doAnotherThing(int i, int j, int k) {
        System.out.println("Doing another thing");
        return (i + j) * k;
    }

    public static void main(String[] args) {
        doSomething();
        doSomethingElse("foo");
        doAnotherThing(11, 22, 33);
    }
}

Now, binding the first parameter in AspectJ is as easy as args(request, ..) which works for one or more parameters. The only exception is zero parameters, in which case the pointcut would not fire. So either I end up with something similar to what you did:

package de.scrum_master.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class BoundFirstParameterAspect {
    @Pointcut("execution(* do*(..))")
    public static void myPointcut() {}

    @Around("myPointcut()")
    public Object matchingAdvice(ProceedingJoinPoint thisJoinPoint) {
        return anotherMatchingAdvice(thisJoinPoint, null);
    }

    @Around("myPointcut() && args(request, ..)")
    public Object anotherMatchingAdvice(ProceedingJoinPoint thisJoinPoint, Object request) {
        System.out.println(">>> another matching advice on " + thisJoinPoint);
        Object result = thisJoinPoint.proceed();
        System.out.println("<<< another matching advice on " + thisJoinPoint);
        return result;
    }
}

Which is makes the same advice fire twice and thus causes an overhead, even though the original method is only called once, but you can see the overhead in the log:

>>> another matching advice on execution(void de.scrum_master.app.Application.doSomething())
Doing something
<<< another matching advice on execution(void de.scrum_master.app.Application.doSomething())
>>> another matching advice on execution(String de.scrum_master.app.Application.doSomethingElse(String))
>>> another matching advice on execution(String de.scrum_master.app.Application.doSomethingElse(String))
Doing something else
<<< another matching advice on execution(String de.scrum_master.app.Application.doSomethingElse(String))
<<< another matching advice on execution(String de.scrum_master.app.Application.doSomethingElse(String))
>>> another matching advice on execution(int de.scrum_master.app.Application.doAnotherThing(int, int, int))
>>> another matching advice on execution(int de.scrum_master.app.Application.doAnotherThing(int, int, int))
Doing another thing
<<< another matching advice on execution(int de.scrum_master.app.Application.doAnotherThing(int, int, int))
<<< another matching advice on execution(int de.scrum_master.app.Application.doAnotherThing(int, int, int))

You can easily recognise how double advices are fired for each joinpoint.

Alternatively, you can bind the parameter during runtime, which is not very elegant and incurs a little runtime penalty, but works perfectly well:

package de.scrum_master.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class BoundFirstParameterAspect {
    @Pointcut("execution(* do*(..))")
    public static void myPointcut() {}

    @Around("myPointcut()")
    public Object matchingAdvice(ProceedingJoinPoint thisJoinPoint) {
        System.out.println(">>> matching advice on " + thisJoinPoint);
        Object[] args = thisJoinPoint.getArgs();
        Object request =  args.length > 0 ? args[0] : null;
        System.out.println("First parameter = " + request);
        Object result = thisJoinPoint.proceed();
        System.out.println("<<< matching advice on " + thisJoinPoint);
        return result;
    }
}

This avoids double advice execution as well as code duplication and yields the following console output:

>>> matching advice on execution(void de.scrum_master.app.Application.doSomething())
First parameter = null
Doing something
<<< matching advice on execution(void de.scrum_master.app.Application.doSomething())
>>> matching advice on execution(String de.scrum_master.app.Application.doSomethingElse(String))
First parameter = foo
Doing something else
<<< matching advice on execution(String de.scrum_master.app.Application.doSomethingElse(String))
>>> matching advice on execution(int de.scrum_master.app.Application.doAnotherThing(int, int, int))
First parameter = 11
Doing another thing
<<< matching advice on execution(int de.scrum_master.app.Application.doAnotherThing(int, int, int))

Last, but not least, you can have two slightly different pointcuts - one with empty args() and one with args(request, ..) - both of which can delegate parameter handling, logging and exception handling to a helper method in order to avoid duplication, as I said in one of my comments:

package de.scrum_master.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class BoundFirstParameterAspect {
    @Pointcut("execution(* do*(..))")
    public static void myPointcut() {}

    @Around("myPointcut() && args()")
    public Object myAdvice(ProceedingJoinPoint thisJoinPoint) {
        return myAdviceHelper(thisJoinPoint, null);
    }

    @Around("myPointcut() && args(request, ..)")
    public Object myAdviceWithParams(ProceedingJoinPoint thisJoinPoint, Object request) {
        return myAdviceHelper(thisJoinPoint, request);
    }

    private Object myAdviceHelper(ProceedingJoinPoint thisJoinPoint, Object request) {
        System.out.println(">>> matching advice on " + thisJoinPoint);
        System.out.println("First parameter = " + request);
        Object result = thisJoinPoint.proceed();
        System.out.println("<<< matching advice on " + thisJoinPoint);
        return result;
    }
}

The console log should be exactly the same as the previous one.


Update 2:

Well, I just realised that the empty args() trick would also apply to your original idea and avoid double execution as well as the helper method:

package de.scrum_master.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class BoundFirstParameterAspect {
    @Pointcut("execution(* do*(..))")
    public static void myPointcut() {}

    @Around("myPointcut() && args()")
    public Object myAdvice(ProceedingJoinPoint thisJoinPoint) {
        return myAdviceWithParams(thisJoinPoint, null);
    }

    @Around("myPointcut() && args(request, ..)")
    public Object myAdviceWithParams(ProceedingJoinPoint thisJoinPoint, Object request) {
        System.out.println(">>> matching advice on " + thisJoinPoint);
        System.out.println("First parameter = " + request);
        Object result = thisJoinPoint.proceed();
        System.out.println("<<< matching advice on " + thisJoinPoint);
        return result;
    }
}

This is acceptable as well as elegant because it does not generate byte code twice per joinpoint. The two pointcuts are mutually exclusive, so this is a good thing to do. I recommend this solution.

Question:

When creating aspects using the Aspect annotation as described here, is it possible to use this annotation with a class that contains a state (e.g. member variables that change as soon as a pointcut is hit)? Or in other words: is an aspect class a singleton? The source code of the annotation suggests something like this, but I cannot find more details in the documentation.

An example: here is an aspect that counts how often a method has been called. The aspect stores the number of invocations in a map and prints out the map:

@Aspect
public class CountAspect {

    private Map<String, Integer> counter = new HashMap<>(); 

    @Before("execution(* *.*(..))")
    public void countMethodCalls(JoinPoint jp) {
        String methodName = jp.getSignature().getName();
        counter.merge(methodName, 1, Integer::sum);
        System.out.println(counter);
    }
}

When testing this aspect everthing works as expected. When calling methods a, b and c several times, the output is

{main=1}
{a=1, main=1}
{a=1, b=1, main=1}
{a=1, b=1, c=1, main=1}
{a=2, b=1, c=1, main=1}
{a=3, b=1, c=1, main=1}

Is this result reliable? Or does my example only work because I have a rather simple example here?

Could it happen that in a more complex scenario, a second instance of CountAspect would be created by the AspectJ runtime and thus I would lose data collected in the counter map until then?

I am using Load-Time Weaving if that matters.


Answer:

AspectJ knows multiple instantiation models, one of which (and the default) is singleton. You find more information in the AspectJ manual. There you will see the following instatiation models:

  • perthis: one aspect instance per caller instance of each matched pointcut
  • pertarget: one aspect instance per callee instance of each matched pointcut
  • percflow: one aspect instance per entering a certain control flow
  • percflowbelow: one aspect instance per entering a certain control flow below the intercepted one (everything called by the intercepted method)

There is one more instantiation model described here:

  • pertypewithin: one aspect instance per type (class) as specified by the pointcut

So yes, your aspect as shown in the sample code is a singleton, you can rely on it. Whether you are using compile time weaving, binary weaving or load time weaving does not matter, except maybe if in a container like an application server you deliberately create different weaver instances via class-loader isolation or so.

Question:

I have this aspect:

public privileged aspect Teste {
private ISupermarket supermarket;

@AfterReturning(pointcut = "execution(* ca1.business.SupermarketFactory.createSupermarket(..))", returning = "result")
    public void afterCreateSupermarket(JoinPoint joinPoint, Object result) {
        supermarket = (ISupermarket) result;
    }
}

The thing is that I want to code it in native AspectJ notation.

I searched but the closest I got was to this:

void after() returning(result) : pointcut(* ca1.business.SupermarketFactory.createSupermarket(..)) {
    supermarket = (ISupermarket) result;
}

But this gives me some errors because it is not well coded.

Can anyone help me with this?


Answer:

I managed to find the answer:

pointcut afterCreateSupermarket():
    call(ISupermarket ca1.business.SupermarketFactory.createSupermarket(..));

after() returning(Object result): afterCreateSupermarket() {
    supermarket = (ISupermarket) result;
}

Question:

I've tried to add logging via Spring Aspects to my project, but the aspects never run.

This is the file where I define the aspects: LoggingInfo.java

package org.synyx.sybil.config;

import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;    

@Aspect
public class LoggingInfo {

    private static final Logger LOG = LoggerFactory.getLogger(LoggingInfo.class);

    @Pointcut("execution(* com.tinkerforge.IPConnection.connect(String, int)) && args(host, port)")
    public void connect(String host, int port) {
    }


    @Before("connect(host, port)")
    public void CreateIPConnection(String host, int port) {

        LOG.info("Creating IPConnection to {}:{}", host, port);
    }`enter code here`


    @AfterReturning("connect(host, port)")
    public void FinishedCreatingIPConnection(String host, int port) {

        LOG.info("Successfully created IPConnection to {}:{}", host, port);
    }

    @AfterThrowing("connect(host, port)")
    public void FailedCreatingIPConnection(String host, int port) {

        LOG.info("Failed creating IPConnection to {}:{}", host, port);
    }
}

This is my Spring config: SpringConfigDev.java

package org.synyx.sybil.config;

import com.tinkerforge.AlreadyConnectedException;
import com.tinkerforge.BrickletLEDStrip;
import com.tinkerforge.IPConnection;
import com.tinkerforge.NotConnectedException;
import com.tinkerforge.TimeoutException;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.annotation.Profile;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.annotation.Scope;

import org.springframework.core.env.Environment;

import org.synyx.sybil.out.OutputLEDStrip;
import org.synyx.sybil.out.SingleStatusOnLEDStrip;
import org.synyx.sybil.out.SingleStatusOutput;

import java.io.IOException;


@Profile("dev")
@Configuration
@EnableAspectJAutoProxy
@PropertySource("classpath:SpringConfigDev.properties")
public class SpringConfigDev {

//    private static final Logger LOG = LoggerFactory.getLogger(SpringConfigDev.class);

    @Autowired
    Environment env;

    @Bean(destroyMethod = "disconnect")
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public IPConnection ipConnection() throws AlreadyConnectedException, IOException {

        String hostname = env.getRequiredProperty("ipconnection.hostname");
        int port = env.getProperty("ipconnection.port", Integer.class, 4223); // 4223 is the standard port

//        LOG.info("Creating IPConnection to {}:{}", hostname, port);

        IPConnection ipConnection = new IPConnection();
        ipConnection.connect(hostname, port);

//        LOG.info("Successfully connected to {}:{}", hostname, port);

        return ipConnection;
    }


    @Bean
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public BrickletLEDStrip brickletLEDStrip(IPConnection ipConnection) throws TimeoutException, NotConnectedException {

        BrickletLEDStrip brickletLEDStrip = new BrickletLEDStrip(env.getRequiredProperty("brickletledstrip.uid"),
                ipConnection);
        brickletLEDStrip.setFrameDuration(10);
        brickletLEDStrip.setChipType(2812);

        return brickletLEDStrip;
    }


    @Bean
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public OutputLEDStrip outputLEDStrip(BrickletLEDStrip brickletLEDStrip) {

        return new OutputLEDStrip(brickletLEDStrip, env.getRequiredProperty("outputledstrip.length", Integer.class));
    }


    @Bean
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public SingleStatusOutput singleStatusOutput(OutputLEDStrip outputLEDStrip) {

        return new SingleStatusOnLEDStrip(outputLEDStrip);
    }
}

Everything in here works, execpt - apparently - for the @EnableAspectJAutoProxy.

This is the test I'm using to run it: OutputLEDStripTest.java

package org.synyx.sybil.out;

import org.junit.After;
import org.junit.Test;

import org.junit.runner.RunWith;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import org.synyx.sybil.config.SpringConfigDev;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;


@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { SpringConfigDev.class })
public class OutputLEDStripTest {

    @Autowired
    private OutputLEDStrip outputLEDStrip;

    @After
    public void close() { // throws NotConnectedException {

        outputLEDStrip.setBrightness(1.0);
        outputLEDStrip.setColor(Color.BLACK);
        outputLEDStrip.updateDisplay();
    }


    @Test
    public void testSetColor() throws Exception {

        outputLEDStrip.setColor(new Color(16, 32, 8));
        outputLEDStrip.updateDisplay();

        Color pixel = outputLEDStrip.getPixel(0);
        assertEquals("Pixel 0.red should be 16", 16, pixel.getRed());
        assertEquals("Pixel 0.green should be 32", 32, pixel.getGreen());
        assertEquals("Pixel 0.blue should be 8", 8, pixel.getBlue());
    }


    @Test
    public void testSetPixel() throws Exception {

        Color color = new Color(16, 35, 77);

        outputLEDStrip.setPixel(1, color);
        outputLEDStrip.updateDisplay();

        Color pixel0 = outputLEDStrip.getPixel(0);
        Color pixel1 = outputLEDStrip.getPixel(1);

        assertEquals("Pixel 0.red should be 0", 0, pixel0.getRed());
        assertEquals("Pixel 0.green should be 0", 0, pixel0.getGreen());
        assertEquals("Pixel 0.blue should be 0", 0, pixel0.getBlue());
        assertEquals("Pixel 1.red should be 16", 16, pixel1.getRed());
        assertEquals("Pixel 1.green should be 35", 35, pixel1.getGreen());
        assertEquals("Pixel 1.blue should be 77", 77, pixel1.getBlue());
    }


    @Test
    public void testSetBrightnessHalf() throws Exception {

        outputLEDStrip.setColor(Color.WHITE);

        outputLEDStrip.setBrightness(.5);
        outputLEDStrip.updateDisplay();

        Color pixel = outputLEDStrip.getPixel(0);
        assertTrue("Pixel 0 should be half as bright as a full white (127, 127, 127).",
            pixel.getRed() == (short) (127 * .5) && pixel.getGreen() == (short) (127 * .5)
            && pixel.getBlue() == (short) (127 * .5));
    }


    @Test
    public void testSetBrightnessFull() throws Exception {

        outputLEDStrip.setColor(Color.WHITE);

        outputLEDStrip.setBrightness(1);
        outputLEDStrip.updateDisplay();

        Color pixel = outputLEDStrip.getPixel(0);
        assertTrue("Pixel 0 should be full white (127, 127, 127).",
            pixel.getRed() == 127 && pixel.getGreen() == 127 && pixel.getBlue() == 127);
    }


    @Test
    public void testSetBrightnessDouble() throws Exception {

        outputLEDStrip.setColor(Color.WHITE);

        outputLEDStrip.setBrightness(2);
        outputLEDStrip.updateDisplay();

        Color pixel = outputLEDStrip.getPixel(0);
        assertTrue("Pixel 0 should be double as bright as a full white (127, 127, 127).",
            pixel.getRed() == (short) (127 * 2) && pixel.getGreen() == (short) (127 * 2)
            && pixel.getBlue() == (short) (127 * 2));
    }
}

You won't be able to sucessfully run this project unless you install the Tinkerforge brickd damon and have a Tinkerforge brick with a LED Strip bricklet connected somewhere and the SpringConfigDev.properties file set up for it. But, if logging worked, you should see the error messages in the log file.


Answer:

Spring configuration hints are all okay, but I guess they will not help because the pointcut says:

execution(* com.tinkerforge.IPConnection.connect(String, int))

The thing is: I assume that the Tinkerforge classes are not Spring components, i.e. they cannot be targeted by Spring AOP execution() pointcuts. So either you switch to full AspectJ and use a call() pointcut (unavailable in Spring AOP) or as a workaround you change your pointcut to target one of your Spring components' methods which has the same information. ;-)

Question:

I was experimenting with AspectJ. I tried to apply aspect on String class. I created Spring configuration file as:

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">

    <!-- Enable @AspectJ annotation support -->
    <aop:aspectj-autoproxy />

    <!-- Employee manager -->
    <bean id="employeeManager" class="com.test.advice.EmployeeManager" />

    <!-- Logging Aspect -->
    <bean id="loggingAspect" class="com.test.advice.LoggingAspect" />

    <bean id="bean1" class="java.lang.String">
        <constructor-arg value="abx" />
    </bean>

</beans>

Then an Aspect class like,

package com.test.advice;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

@Aspect
public class LoggingAspect
{

    @Around("execution(* java.lang.String.*(..))")
    public void logAroundGetEmployee(ProceedingJoinPoint joinPoint) throws Throwable
    {
        System.out.println("works");
    }
}

After that created a class with a main method like:

package com.test.advice;

package com.test.advice;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AspectJAutoProxyTest
{
    public static void main(String[] args)
    {
        ApplicationContext context = new ClassPathXmlApplicationContext("Spring-Customer.xml");

        String pqr = (String) context.getBean("bean1");

        pqr.trim();

    }
}

On running it should output "works" to console. But it fails saying,

Exception in thread "main" java.lang.ClassCastException: com.sun.proxy.$Proxy5 cannot be cast to java.lang.String
    at com.test.advice.AspectJAutoProxyTest.main(AspectJAutoProxyTest.java:13)

What is the issue? Can't we apply proxy to java.lang objects? Please help.


Answer:

To use a proxy object as a replacement for the real object, the proxy object must be of a subclass of the real object. String being final, the JVM does not permit creating such a subclass.

(Note that spring has two proxy modes; one creates an actual subclass and the other just implements all public interfaces. You're probably using the latter, but if you changed to the former, you'd see an exception at proxy creation time)

Question:

I'm having some difficulties importing a utility jar file containing some custom aspects into another project. It should be noted, that I'm not using Spring for this project, as my client is somewhat averse to Spring.

I have created a proof of concept (full code example below). When I run a test runner in the utility jar, any method annotated with my AspectJ annotation gets their aspects executed just fine. When I use the self-same jar in another project, the aspects are ignore.

When I run the main class in the utility I get:

$> java -cp aspectjrt-1.8.2.jar;aop-util-1.0-SNAPSHOT.jar TestOne
AspectOne's aroundAdvice's body is now executed Before aspectTestMethod is called. 
Executing TestOne.aspectTestMethod() 
AspectOne's aroundAdvice's body is now executed After aspectTestMethod is called.

If I run the main class of the consumer class, I get:

$>java -cp aspectjrt-1.8.2.jar;aop-util-1.0-SNAPSHOT.jar;aop-consumer-1.0-SNAPSHOT.jar Test
Test.testAspectOne
AspectTwo's aroundAdvice's body is now executed Before aspectTestMethod is called.
Test.testAspectTwo
AspectTwo's aroundAdvice's body is now executed After aspectTestMethod is called.

As I'm fairly new to aspect oriented programming, I'd really appreciate a pointer as to what I'm missing :)

utility jar
pom.xml
<?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">
        <modelVersion>4.0.0</modelVersion>

        <groupId>sandbox.aop</groupId>
        <artifactId>aop-util</artifactId>
        <version>1.0-SNAPSHOT</version>
        <packaging>jar</packaging>

        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
            <java.version>1.8</java.version>
        </properties>

        <dependencies>
            <dependency>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjrt</artifactId>
                <version>1.8.2</version>
            </dependency>
        </dependencies>

        <build>
            <plugins>
                <plugin>
                    <groupId>org.codehaus.mojo</groupId>
                    <artifactId>aspectj-maven-plugin</artifactId>
                    <version>1.7</version>
                    <configuration>
                        <complianceLevel>1.8</complianceLevel>
                        <source>1.8</source>
                        <target>1.8</target>
                    </configuration>
                    <executions>
                        <execution>
                            <goals>
                                <goal>compile</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    </project>
Annotation
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AnnotationOne { }
Aspect
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.JoinPoint;

@Aspect
public class AspectOne {

    @Pointcut("@annotation(AnnotationOne)")
    public void annotationPointCutDefinition(){
    }

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

    @Around("@annotation(AnnotationOne) && execution(* *(..))")
    public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
        Object returnObject = null;
        try {
            System.out.println("AspectOne's aroundAdvice's body is now executed Before aspectTestMethod is called.");
            returnObject = joinPoint.proceed();
        } catch (Throwable throwable) {
            throw throwable;
        }
        finally {
            System.out.println("AspectOne's aroundAdvice's body is now executed After aspectTestMethod is called.");
        }
        return returnObject;
    }

    @After("annotationPointCutDefinition() && atExecution()")
    public void printNewLine(JoinPoint pointcut){
        System.out.print("\n\r");
    }
}
Main class
public class TestOne {

    public static void main(String[] args) {
        TestOne testOne = new TestOne();
        testOne.aspectTestMethod();
    }

    @AnnotationOne
    public void aspectTestMethod(){
        System.out.println("Executing TestOne.aspectTestMethod()");
    }
}
AOP consumer
pom.xml
<?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">
    <modelVersion>4.0.0</modelVersion>

    <groupId>sandbox.aop</groupId>
    <artifactId>aop-consumer</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>sandbox.aop</groupId>
            <artifactId>aop-util</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>aspectj-maven-plugin</artifactId>
                <version>1.7</version>
                <configuration>
                    <complianceLevel>1.8</complianceLevel>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
Annotation
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AnnotationTwo {}
Aspect
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class AspectTwo {

    @Pointcut("@annotation(AnnotationTwo)")
    public void annotationPointCutDefinition(){
    }

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

    @Around("@annotation(AnnotationTwo) && execution(* *(..))")
    public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
        Object returnObject = null;
        try {
            System.out.println("AspectTwo's aroundAdvice's body is now executed Before aspectTestMethod is called.");
            returnObject = joinPoint.proceed();
        } catch (Throwable throwable) {
            throw throwable;
        }
        finally {
            System.out.println("AspectTwo's aroundAdvice's body is now executed After aspectTestMethod is called.");
        }
        return returnObject;
    }

    @After("annotationPointCutDefinition() && atExecution()")
    public void printNewLine(JoinPoint pointcut){
        System.out.print("\n\r");
    }
}
Main class
public class Test {

    public static void main(String[] args) {
        Test test = new Test();
        test.testAspectOne();
        test.testAspectTwo();
    }

    @AnnotationOne
    public void testAspectOne() {
        System.out.println(Test.class.getName() + ".testAspectOne");
    }

    @AnnotationTwo
    public void testAspectTwo() {
        System.out.println(Test.class.getName() + ".testAspectTwo");
    }
}

Answer:

You need to tell the aspectj weaver to weave aspect defined in your library using aspectLibraries:

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>aspectj-maven-plugin</artifactId>
    <version>1.7</version>
    <configuration>
        <complianceLevel>1.8</complianceLevel>
        <source>1.8</source>
        <target>1.8</target>
       <aspectLibraries>
            <aspectLibrary>
                <groupId>sandbox.aop</groupId>
                <artifactId>aop-util</artifactId>
            </aspectLibrary>
       </aspectLibraries>
    </configuration>
    <executions>
        <execution>
            <goals>
                <goal>compile</goal>
            </goals>
        </execution>
    </executions>
</plugin>

Question:

The background

I try to use LightAdmin in my Spring application. The root problem I have is, that it only supports some base plain types - I use java.util.UUID for example, which is not supported (see here).

Since these are static methods and there is no really any POI to bind own types, I thought about using aspects for that.

The problem

But that araised another problem - I can't make my aspect to hijack this static call. LightAdmin works as a separate servlet, but in the same application and my aspect is loaded in applicationContext.xml.

TypeHandler.java (the aspect code):

@Aspect
public class TypeHandler
{
    @Before("execution(static * org.lightadmin.core.persistence.metamodel.DomainTypeAttributeType.forType(..))")
    public void myBefore()
    {
        System.out.println("HIJACKED!");
    }
}

applicationContext.xml fragments:

<aop:aspectj-autoproxy/>

<context:component-scan
    base-package="my.website.web.backend"
    use-default-filters="true"
>
    <context:include-filter type="annotation" expression="org.aspectj.lang.annotation.Aspect"/>
</context:component-scan>
<context:load-time-weaver/>

I use Tomcat7 with org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader class loader.

In log I can see:

...
11:02:55.198 [localhost-startStop-1] INFO  org.springframework.context.weaving.DefaultContextLoadTimeWeaver - Using a reflective load-time weaver for class loader: org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader
...
INFO: Initializing Spring FrameworkServlet 'backend'
11:03:11.677 [localhost-startStop-1] INFO  org.springframework.web.servlet.DispatcherServlet - FrameworkServlet 'backend': initialization started
11:03:11.687 [localhost-startStop-1] INFO  org.springframework.web.context.support.XmlWebApplicationContext - Refreshing WebApplicationContext for namespace 'backend-servlet': startup date [Fri Aug 08 11:03:11 CEST 2014]; parent: Root WebApplicationContext
11:03:11.689 [localhost-startStop-1] INFO  org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loading XML bean definitions from ServletContext resource [/WEB-INF/backend-servlet.xml]
11:03:11.770 [localhost-startStop-1] INFO  org.springframework.web.servlet.DispatcherServlet - FrameworkServlet 'backend': initialization completed in 93 ms
sie 08, 2014 11:03:11 AM org.apache.catalina.core.ApplicationContext log
INFO: Initializing Spring FrameworkServlet 'lightadmin-dispatcher'
11:03:11.772 [localhost-startStop-1] INFO  org.springframework.web.servlet.DispatcherServlet - FrameworkServlet 'lightadmin-dispatcher': initialization started
11:03:11.773 [localhost-startStop-1] INFO  org.springframework.web.context.support.AnnotationConfigWebApplicationContext - Refreshing WebApplicationContext for namespace 'lightadmin-dispatcher-servlet': startup date [Fri Aug 08 11:03:11 CEST 2014]; parent: Root WebApplicationContext
...

(/WEB-INF/backend-servlet.xml is empty)

And application fails after calling org.lightadmin.core.persistence.metamodel.DomainTypeAttributeType.forType() for my UUID field without being hijacked by my aspect.

I already tried adding @Component annotation, registering the bean by hand in applicationContext.xml etc. - makes no effect. At all I think that the aspect itself is being loaded by beans factory, because if I configure an invalid pointcut, I have an exception about that.

The question(s)

So my two questions are:

  1. Is there other way to make this working (without LightAdmin source code modification, I want to avoid that at all costs)?
  2. If no, then how to make this aspect working?

Answer:

Please use the latest redesigned LightAdmin version with an extended set of supported types (UUID, Joda-Time). It's upgraded to Spring 4.0.X, the newest Spring DATA/REST and contains a bunch of defect fixes.

BTW, you don't need to use LightAdmin Nexus repositories anymore for releases. It's available directly from Maven Central now.

Question:

I am trying to use AspectJ with an MapReduce example, although I am not understanding one thing. But first, let me give you the code that I have.

[1] Wordcount example

package org.apache.hadoop.mapred.examples;

import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapred.*;

import java.io.IOException;
import java.util.Iterator;
import java.util.StringTokenizer;


/**
 * Common Wordcount example
 */
public class WordCount {


    public static class Map extends MapReduceBase implements Mapper<LongWritable, Text, Text, IntWritable> {
    private final static IntWritable one = new IntWritable(1);
    private Text word = new Text();

    public void map(LongWritable key, Text value, OutputCollector<Text, IntWritable> output, Reporter reporter) throws IOException {
        String line = value.toString();
        StringTokenizer tokenizer = new StringTokenizer(line);
        while (tokenizer.hasMoreTokens()) {
            word.set(tokenizer.nextToken());
            output.collect(word, one);
        }
    }
}

public static class Reduce extends MapReduceBase implements Reducer<Text, IntWritable, Text, IntWritable> {
    public void reduce(Text key, Iterator<IntWritable> values, OutputCollector<Text, IntWritable> output, Reporter reporter) throws IOException {
        int sum = 0;
        while (values.hasNext()) {
            sum += values.next().get();
        }
        output.collect(key, new IntWritable(sum));
    }
}

public static void main(String[] args) throws Exception {
    JobConf conf = new JobConf(WordCount.class);
    conf.setJobName("wordcount");

    conf.setOutputKeyClass(Text.class);
    conf.setOutputValueClass(IntWritable.class);

    conf.setMapperClass(Map.class);
    conf.setCombinerClass(Reduce.class);
    conf.setReducerClass(Reduce.class);
    conf.setNumReduceTasks(2);

    conf.setInputFormat(TextInputFormat.class);
    conf.setOutputFormat(TextOutputFormat.class);

    FileInputFormat.setInputPaths(conf, new Path(args[0]));
    FileOutputFormat.setOutputPath(conf, new Path(args[1]));

    JobClient.runJob(conf);
    }
}

[2] My mapreduce aspects

package org.apache.hadoop.mapred.aspects;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class MapReduceAspects {
    @Before("execution(* map(..))")
    public void mymap(JoinPoint joinPoint) {
        System.out.println("My Map Execution: " + joinPoint.getArgs() +   ":" + joinPoint.getTarget());
        Object[] obj = joinPoint.getArgs();
        for (Object o : obj){
           System.out.println(o.toString());
        }
    }

@Before("execution(* reduce(..))")
  public void myreduce() { System.out.println("My Reduce Execution"); }


  @Before("execution(* collect(..))")
  public void updatehash(JoinPoint joinPoint) {
      System.out.println("Output collect: Args: " + joinPoint.getArgs());

  }
}

```

[3] bean-aspects.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
   xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context"
   xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"
   xmlns:task="http://www.springframework.org/schema/task"
   xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.2.xsd">

<aop:aspectj-autoproxy proxy-target-class="true">
    <aop:include name="mapreduceAspect"/>
</aop:aspectj-autoproxy>

<bean id="mapreduceAspect" class="org.apache.hadoop.mapred.aspects.MapReduceAspects"/></beans>

[4] OutputCollector interface

package org.apache.hadoop.mapred;

import java.io.IOException;
import org.apache.hadoop.classification.InterfaceAudience.Public;
import org.apache.hadoop.classification.InterfaceStability.Stable;

@Public
@Stable
public interface OutputCollector<K, V> {
   void collect(K var1, V var2) throws IOException;
}

In [1], I have an wordcount example with map and reduce function. When I launch my application in the MapReduce framework, the framework will create a job that will execute the map and reduce functions. The map function accepts an input dir, and the reduce function outputs the result.

I can intercept the map and reduce function calls with AspectJ, but I can't intercept the collect call in the instruction output.collect(word, one) that is in the map function. Why this happens? Is it because the collect method in the interface does not have annotation[4]? Or didn`t I configure the Aspects correctly?

I would love if anyone could explain me why AspectJ is behaving like this.

Thanks,


Answer:

The answer is pretty simple:

  • The map and reduce methods are within your own code, i.e. they are subject to aspect weaving for execution() pointcuts.
  • The collect method is in a 3rd party library which is not subject to aspect weaving under normal circumstances. Thus, you cannot intercept it with an execution() pointcut, only with a call() pointcut.
  • Try something like call(* collect(..)) and it will work.
  • Caveat: Spring AOP does not support call() pointcuts, you have to use full-blown AspectJ in order to use it. See Spring manual, chapter 10.8 Using AspectJ with Spring applications for more information about how to activate AspectJ LTW (load-time weaving).
  • If you use full AspectJ via LTW or binary CTW (compile-time weaving), it is also possible to weave 3rd party code and use execution() pointcuts. You just need to make sure that the weaving agent is loaded before any targeted 3rd party code, which is usually the case because that is what Java agents were invented for.

Question:

I am working on an existing web application that uses EJB3. What I need to do is to weave some aspects into this application, in order to log some information after matching some specific methods (before and after persisting beans).

I am deploying my web application in a Jboss server in an ear format. the ear contains a war (servlets) and a jar (session beans and entity beans).

Any idea on how is this possible ? Do I have to put my aspects inside the jar or do I have to create a new jar (exclusively for aspects) ?

By the way I am using eclipse for this project. So my jar corresponds to an EJB project and my war corresponds to a dynamic web project.

Thank you for your answers


Answer:

As far as I know for web-apps it's only possible to do so with load-time weaving (shouldn't be mixed with compile-time one, they are not compatible with each other). To achieve that you should do the following:

  • Create aop.xml (link to the documentation how to do that) and put it into the META-INF folder of your project.

  • add aspectjweaver/tools/rt jars to your dependencies.

  • disable compile-time weaving (That's a configuration in your pom.xml. Read more there)

  • add to your app-server's JVM the following argument -javaagent:pathto/aspectjweaver.jar (documentation)

  • add the aspectjweaver.jar to your server's resources

That's it.

You should disable compile-time weaving im your pom.xml (if you use maven, of course). Unfortunately I didn't try to do that without maven (solely with Eclipse AspectJ plugin). Here is how it will probably look like.

<build>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>aspectj-maven-plugin</artifactId>
                <version>1.7</version>
                <configuration>
                    <!-- disabling -->
                    <outxml>true</outxml>
                    <XterminateAfterCompilation>true</XterminateAfterCompilation>
                    <!-- disabling -->
                </configuration>
                <executions>
                    <execution>
                        <id>compile</id>
                        <goals>
                            <goal>compile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
  </build>

Question:

I am trying to enable aspect weaving for private methods on a large spring boot project. I already have aspects targeting public methods working just fine but my research led me to the notion that Spring AOP aspects can only interact with public methods.

I came across this project which I have incorporated into my code (literally copy-pasting the usage example) :

https://github.com/subes/invesdwin-instrument

This has the side effect of HUNDREDS of aspects being targeted at startup (spring stuff mostly). The console messages look like this :

 [Xlint:cantFindType]
[AppClassLoader@277050dc] error can't determine implemented interfaces of missing type javax.servlet.Filter
when processing declare parents org.springframework.web.filter.GenericFilterBean
when processing type mungers 
when weaving

How do i configure aspectJ to only work with the aspects I am creating and ignore the rest?

******UPDATE******

I followed the recommendation in the comments and now i have the following exception on startup :

 java.lang.IllegalStateException: ClassLoader [jdk.internal.loader.ClassLoaders$AppClassLoader] does NOT provide an 'addTransformer(ClassFileTransformer)' method. Specify a custom LoadTimeWeaver or start your Java virtual machine with Spring's agent: -javaagent:org.springframework.instrument.jar

Do i need to add both aspectjweaver and spring-instrument java agents?


Answer:

First thing first, the project you linked relies on you running the Oracle JVM. Infact, it uses the sun.misc.unsafe class, which is also more difficult to use starting from JDK 9 (JPMS).

Anyway, that project will discover a META-INF/aop.xml file, which points to the classes containing your aspects. An example of that file content might be

<!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "http://www.eclipse.org/aspectj/dtd/aspectj.dtd">
<aspectj>
    <weaver>
        <include within="your.package.aspects.*"/>
        <include within="your.package.one.*"/>
    </weaver>
    <aspects>
        <aspect name="your.package.aspects.YourClassAspect"/>
    </aspects>
</aspectj>

Inside the <weaver> tag you're defining which classes will be considered for weaving. Your pointcuts then tells AspectJ which specific methods to weave.

But keep reading.


I don't recommend using that project, especially if you're running the latest releases of Spring.

Just

  • attach the AspectJ aspectjweaver.jar(and maybe also spring-instrument.jar, see docs) agent
  • define an aop.xml file under META-INF
  • define your pointcuts using annotations
  • if necessary (see docs) add the @EnableLoadTimeWeaving annotation

and you're good to go.

For further reading see AOP docs, and a couple of questions I asked some time ago, which produced a documentation update (see diff).

AspectJ LTW (weaving) not working with Spring Boot Spring AOP with AspectJ - Load time weaving doubts

Question:

I have a class MakeRedAspect in my project which makes red my messages:

import java.awt.Color;

import javax.swing.JLabel;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class MakeRedAspect {
    @Pointcut("execution(static javax.swing.JLabel[] createMultiLabel(..))")
    public void multilabelCreation() {}

    @Around("multilabelCreation()")
    public JLabel[] changeLabelColours(ProceedingJoinPoint thisJoinPoint) throws Throwable {
        JLabel[] labels = (JLabel[]) thisJoinPoint.proceed();
        for (JLabel label : labels)
            label.setForeground(Color.RED);
        return labels;
    }
}

I have added required libraries to use aspectj in my project too, and I have added the name of class in META-INF/aop.xml class too(I thought these two are enough to use an aspect class):

<aspectj>
    <aspects>

        <aspect name="mehad.aspect.general.I18N"/>
         <aspect name="mehad.aspect.general.MakeRedAspect"/>
        <aspect name="mehad.aspect.general.AspSwingWorkerExecution"/>
        <aspect name="mehad.aspect.general.RetryAspect"/>
        <aspect name="mehad.aspect.general.AspWatchScriptExecution"/>

        <!-- <aspect name="mehad.aspect.simulink.AspWatchPort"/>-->
        <!--<aspect name="mehad.aspect.simulink.AspScannerManager"/>
        <aspect name="mehad.aspect.simulink.AspComportInterface"/>
        <aspect name="mehad.aspect.simulink.AspHeaderIcon"/>
        <aspect name="mehad.aspect.simulink.AspFileUtil"/>-->

</aspects>

<!--
    <weaver options="-verbose">
    </weaver>
-->
</aspectj>

But again it doesn't make my messages red and it looks like my aspects doesn't work. I remember previously it worked fine and I guess that time I had added something to java build path,What's less in my steps that my aspects doesn't make effect?


Answer:

It looks like you want to use load-time weaving (LTW). In order to enable LTW, you need to have the AspectJ weaving agent on the Java command line:

java -javaagent:pathto/aspectjweaver.jar ...

See AspectJ documentation.

Question:

Here is my configuration of aspectj maven plugin for java 8:

<plugin>
       <groupId>org.codehaus.mojo</groupId>
       <artifactId>aspectj-maven-plugin</artifactId>
       <version>1.8</version>
       <configuration>
                <showWeaveInfo>true</showWeaveInfo>
                <source>1.8</source>
                <target>1.8</target>
                <Xlint>ignore</Xlint> 
                <complianceLevel>1.8</complianceLevel>
                <encoding>UTF-8</encoding>
                <verbose>false</verbose>
                <aspectLibraries>
                    <aspectLibrary>
                        <groupId>external project containing aspects</groupId>
                        <artifactId>external project containing aspects</artifactId>
                    </aspectLibrary>
                </aspectLibraries>
       </configuration>
       <executions>
         <execution>    
           <goals>
             <goal>compile</goal>
             <goal>test-compile</goal>
           </goals>
         </execution>
       </executions>
       <dependencies>
        <dependency>
           <groupId>org.aspectj</groupId>
           <artifactId>aspectjtools</artifactId>
           <version>1.8.7</version>
         </dependency>
       </dependencies> 
  </plugin>

When I add a surefire plugin like below, I am able to weave the aspects on load-time.

<plugin>
   <groupId>org.apache.maven.plugins</groupId>
   <artifactId>maven-surefire-plugin</artifactId>
   <configuration>
<argLine>javaagent:${user.home}/.m2/repository/org/aspectj/aspectjweaver/1.8.7/aspectjweaver-1.8.7.jar</argLine>
    </configuration>
 </plugin>

To make sure that my aspect is hit, I have added println statements in it. But they are not getting shownup on the console when trying to do compile time weaving. Please help me. I dont know what I am missing in my configuration. Not able to figure out why the aspects are not getting hit.


Answer:

No issue with the configuration. The problem was, my aspects were defined as abstract with their concrete implementation in the aop.xml file. That is the reason they were not loaded at compile time. So I made them concrete aspects and it worked.