Domanda

I am having trouble getting a spring-AOP pointcut to work when one of the parameters is a vararg. Given the below code I would have expected the pointcut to be triggered on both calls from my test, however it is not called in either case. I have other aspects working on other methods in the same class, so I know it is not a problem with my testing or spring setups - it appears isolated to this method and the only thing unique about it is the vararg.

Method declaration:

SearchResults getRelation(final ClmUserContextFactory contextFactory, final CloudObjectId objectId,
            final Class<? extends CloudClass>... relationClasses) throws BmcClientException;

Pointcut declaration:

@Around(value = "execution(com.a.b.SearchResults "
        + "com.a.b.BmcClmRestJsonClient.getRelation(com.a.b.ClmUserContextFactory, com.a.b.CloudObjectId, Class<? extends com.a.b.CloudClass>...))"
        + " && args(contextFactory, objectId, relationClasses)",
        argNames = "jp,contextFactory,objectId,relationClasses")
private SearchResults getRelationVarargs(ProceedingJoinPoint jp, ClmUserContextFactory contextFactory,
        CloudObjectId objectId,
        Class<? extends CloudClass>[] relationClasses)
        throws Throwable
{...}

Calls from test:

bmcClmRestJsonClient.getRelation(contextFactory, objectId, new Class[] { CloudClass.class });

bmcClmRestJsonClient.getRelation(contextFactory, objectId);

EDIT:

I can confirm that if I remove the vararg part of the definition (ie remove all '...' and '[]') from the pointcut and method definitions it works, so it definitely appears to be something to do with those. It also does not work if I replace the '...' with '[]'.

È stato utile?

Soluzione 2

Whilst I am yet to find a solution, there is a work-around - you can get the arguments from the join point. The following is what I have decided on:

@Around(value = "execution(com.a.b.SearchResults "
        + "com.a.b.BmcClmRestJsonClient.getRelation(*, *, ..))")
public SearchResults getRelationVarargs(ProceedingJoinPoint jp)

And I extract the arguments from the join point as follows:

Object[] args = jp.getArgs();

ClmUserContextFactory contextFactory = (ClmUserContextFactory) args[0];
CloudObjectId objectId = (CloudObjectId) args[1];
Class<? extends CloudClass>[] relationClasses = null;
if (args.length == 3)
{
    relationClasses = (Class<? extends CloudClass>[]) args[2];
}

This obviously would not be good enough if you needed to specify the arguments for the pointcut in order for it to cut in the right places - luckily for me that is not the case.

Altri suggerimenti

I am not a Spring user, but I tried this with AspectJ, both with code-style and annotation-style syntax. For me it works, so I assume it should also work for you if your pointcut definition is correct.

Update: I also think your advice method must be public, not private.

Sample driver application:

package de.scrum_master.app;

public class Application {
    public static void main(String[] args) {
        System.out.println("Starting application");
        doSomething(11, String.class, String.class);
        System.out.println("Stopping application");
    }

    public static boolean doSomething(final int number, final Class<? extends String>... classes) {
        System.out.println("Doing something");
        return true;
    }
}

Code-style aspect:

package de.scrum_master.aspect;

import de.scrum_master.app.Application;

public aspect CodeStyleAspect {
    boolean around(int number, Class<? extends String>[] classes) :
        execution(boolean Application.doSomething(int, Class<? extends String>...)) &&
        args(number, classes)
    {
        System.out.println(this.getClass().getName());
        System.out.println(thisJoinPointStaticPart);
        System.out.print("number = " + number + ", classes = { ");
        for (Class<? extends String> clazz : classes)
            System.out.print(clazz.getName() + ", ");
        System.out.println("}");
        return proceed(number, classes);
    }
}

Annotation-style 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 AnnotationStyleAspect {
    @Around(
        "execution(boolean de.scrum_master.app.Application.doSomething(int, Class<? extends String>...)) &&" +
        "args(number, classes)"
    )
    public Object myAdvice(ProceedingJoinPoint thisJoinPoint, int number, Class<? extends String>[] classes) throws Throwable {
        System.out.println(this.getClass().getName());
        System.out.println(thisJoinPoint.getStaticPart());
        System.out.print("number = " + number + ", classes = { ");
        for (Class<? extends String> clazz : classes)
            System.out.print(clazz.getName() + ", ");
        System.out.println("}");
        return thisJoinPoint.proceed(new Object[] { number, classes });
    }
}

Sample output with both aspects active:

Starting application
de.scrum_master.aspect.CodeStyleAspect
execution(boolean de.scrum_master.app.Application.doSomething(int, Class[]))
number = 11, classes = { java.lang.String, java.lang.String, }
de.scrum_master.aspect.AnnotationStyleAspect
execution(boolean de.scrum_master.app.Application.doSomething(int, Class[]))
number = 11, classes = { java.lang.String, java.lang.String, }
Doing something
Stopping application

As you can see, both aspects basically work the same way and yield equivalent results.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top