【问题标题】:Spring AOP pointcut not working with varargsSpring AOP 切入点不适用于可变参数
【发布时间】:2014-04-29 07:04:12
【问题描述】:

当参数之一是可变参数时,我无法让 spring-AOP 切入点工作。鉴于下面的代码,我预计切入点会在我的测试中的两个调用上被触发,但是在任何一种情况下都不会调用它。我在同一个类中处理其他方法的其他方面,所以我知道这不是我的测试或弹簧设置的问题 - 它似乎与此方法隔离,唯一独特的是可变参数。

方法声明:

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

切入点声明:

@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
{...}

测试调用:

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

bmcClmRestJsonClient.getRelation(contextFactory, objectId);

编辑:

我可以确认,如果我从切入点和方法定义中删除定义的可变参数部分(即删除所有“...”和“[]”),它会起作用,所以它看起来肯定与那些。如果我将 '...' 替换为 '[]',它也不起作用。

【问题讨论】:

  • 不用担心。我刚刚删除了它
  • 此问题仍列为未回答。如果它看起来合适,请您接受并支持我的回答吗?谢谢。
  • 我很欣赏你的评论,并赞成它(因为它很有帮助),但是因为它没有解决为什么它在 spring-aop 中不起作用的核心问题,我不能接受它作为一个完整的回答。我认为这个问题仍未得到解答。

标签: java spring aspectj spring-aop variadic-functions


【解决方案1】:

我不是 Spring 用户,但我在 AspectJ 中尝试了这一点,包括代码样式和注释样式语法。对我来说它有效,所以我认为如果你的切入点定义正确,它也应该适用于你。

更新:我还认为您的建议方法必须是公开的,而不是私有的。

驱动程序应用示例:

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;
    }
}

代码风格方面:

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);
    }
}

注解风格方面:

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 });
    }
}

两个方面都处于活动状态的示例输出:

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

如您所见,这两个方面基本上以相同的方式工作并产生相同的结果。

【讨论】:

  • 感谢您研究它,不幸的是它对我不起作用,即使签名是正确的。我目前倾向于它是 Spring-AOP 中的一个缺陷。鉴于您的发现,看起来另一种解决方法是使用原生 AspectJ。
  • 是的,原生 AspectJ 的使用在 Spring 中得到了很好的记录,并且有很多优点:没有动态代理(即更高效),更多的选项不仅仅是方法拦截(即构造函数或 setter/getter),有效对于任何类(也包括非 Spring Beans)。也许这也是一个版本的事情。我使用了 AspectJ 1.8.0,尽管我认为它也应该适用于 1.7.* 等旧版本。
【解决方案2】:

虽然我还没有找到解决方案,但有一种解决方法 - 您可以从连接点获取参数。以下是我决定的:

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

我从连接点提取参数如下:

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];
}

如果您需要指定切入点的参数以便切入正确的位置,这显然不够好 - 幸运的是我不是这样。

【讨论】:

    猜你喜欢
    • 2010-10-02
    • 1970-01-01
    • 1970-01-01
    • 2020-10-31
    • 2018-06-13
    • 1970-01-01
    • 1970-01-01
    • 2011-07-14
    • 1970-01-01
    相关资源
    最近更新 更多