Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Modifying parameters in more than one aspect providing around advice

Tags:

java

aop

aspectj

I have two aspects each of which modify method arguments. When both aspects are applied to the same method, I would expect execution of the aspects to be chained and I would expect that the arguments modified in the first aspect to be available to the second aspect via joinPoint.getArgs(); However, it appears that each aspect gets only the original arguments; the second aspect never sees the modified values. I've contrived an example:

The test class:

public class AspectTest extends TestCase {
    @Moo
    private void foo(String boo, String foo) {
        System.out.println(boo + foo);
    }

    public void testAspect() {
        foo("You should", " never see this");
    }
}

The method foo() is advised by two aspects:

@Aspect
public class MooImpl {

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

    @Around("methodPointcut()")
    public Object afterMethodInControllerClass(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("MooImpl is being called");
        Object[] args = joinPoint.getArgs();
        args[0] = "don't";
        return joinPoint.proceed(args);
    }
}

and...

@Aspect
public class DoubleMooImpl {

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

    @Around("methodPointcut()")
    public Object afterMethodInControllerClass(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("DoubleMooImpl is being called");
        Object[] args = joinPoint.getArgs();
        args[1] = " run and hide";
        return joinPoint.proceed(args);
    }
}

I would expect the output to be:

MooImpl is being called
DoubleMooImpl is being called
don't run and hide

...but is:

MooImpl is being called
DoubleMooImpl is being called
You should run and hide

Am I using the correct approach to modify arguments via around advice?

like image 380
Java Junky Avatar asked Oct 11 '12 16:10

Java Junky


People also ask

Which of the following interface can be used as parameter for around advices in AspectJ?

JoinPoint is an AspectJ interface that provides reflective access to the state available at a given join point, like method parameters, return value, or thrown exception. It also provides all static information about the method itself. We can use it with the @Before, @After, @AfterThrowing, and @AfterReturning advice.

Which AOP advice can access and modify arguments of a JoinPoint?

You can use Spring AOP, create point cut using @Around . Then you can use the below code to change the arguments of the method, based on the condition. int index = 0; Object[] modifiedArgs = proceedingJoinPoint.

Which interface need to be implemented for around advice?

Around advice must implement the AOP Alliance org. aopalliance. intercept. MethodInterceptor interface.

Which of the following advice type allow you to access arguments of JoinPoint in the advice?

We use @AfterThrowing annotation for this type of advice. Around Advice: This is the most important and powerful advice. This advice surrounds the join point method and we can also choose whether to execute the join point method or not.


2 Answers

After having been on the road for days, I have finally gotten around to thinking about this and answering your question. I hope it is okay if I use native AspectJ syntax because I do not feel comfortable with the @AspectJ notation.

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface Moo {}
public class AspectTest {
    @Moo
    private void foo(String boo, String foo) {
        System.out.println(boo + foo);
    }

    public static void main(String[] args) {
        new AspectTest().foo("You should", " never see this");
    }
}
public aspect MooImpl {
    pointcut methodPointcut() : execution(@Moo * *(String, ..));

    Object around(String firstArg) : methodPointcut() && args(firstArg, ..) {
        System.out.println("MooImpl is being called");
        return proceed("don't");
    }
}
public aspect DoubleMooImpl {
    pointcut methodPointcut() : execution(@Moo * *(*, String, ..));

    Object around(String secondArg) : methodPointcut() && args(*, secondArg, ..) {
        System.out.println("DoubleMooImpl is being called");
        return proceed(" run and hide");
    }
}

Your mistake was to assume that you could manipulate arguments retrieved via getArgs(), which is wrong. In order to pass arguments to proceed() you need to refer to them via args(), which I have demonstrated above. Please note the syntax for retrieving the first vs. second String argument in the two aspects.

This should solve your problem.

like image 87
kriegaex Avatar answered Sep 28 '22 07:09

kriegaex


This does not sound like a aspect ordering issue , it is more of how method arguments are handled in java - references to arguments are passed by value, since your first argument is a String, by modifying what the String reference is pointing to you are not really affecting the original String in any way and so gets passed as such.

You can try instead passing in a StringBuilder or some other mutable type and then modifying the state, the state change should get reflected correctly then.

Update:

I tested with a mutable type and it changes as expected:

@Moo
private void foo(StringBuilder boo, StringBuilder foo) {
    System.out.println(boo.toString() + foo.toString());
}

public void testAspect() {
    foo(new StringBuilder("You should"), new StringBuilder(" never see this"));
}

With MooImpl Aspect:

@Aspect
public class MooImpl {

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

    @Around("methodPointcut()")
    public Object afterMethodInControllerClass(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("MooImpl is being called");
        Object[] args = joinPoint.getArgs();
        ((StringBuilder)args[0]).append("****");
        return joinPoint.proceed(args);
    }
}

and DoubleMooImpl:

@Aspect
public class DoubleMooImpl {

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

    @Around("methodPointcut()")
    public Object afterMethodInControllerClass(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("DoubleMooImpl is being called");
        Object[] args = joinPoint.getArgs();
        ((StringBuilder)args[1]).append("****");
        return joinPoint.proceed(args);
    }
}

and getting this output:

MooImpl is being called
DoubleMooImpl is being called
You should**** never see this****
like image 34
Biju Kunjummen Avatar answered Sep 28 '22 06:09

Biju Kunjummen