如何使用注释参数向Spring AOP通知传递参数?

9
我正在使用Spring 3.1.2.RELEASE和cglib加载时织入,我尝试让建议与具有自定义注释和注释参数的方法一起使用。
建议:
@Aspect
public class MyAdvice
{
   @Around("execution(@com.mycompany.locking.Lock * *(@com.mycompany.locking.LockVal(*), ..)) " +
   "&& args(batch) && @args(propertyToLock)"
   public Object lockAndProceed(ProceedingJoinPoint pjp, Object batch, LockVal propertyToLock) throws Throwable { 
        //Do stuff.... 
        pjp.proceed();
   }
}

这是我正在测试的类:

public interface UpdateManager
{
   public void processUpdate(MyBatchObject batch);
}


public class UpdateManagerImpl implements UpdateManager
{
   @Lock
   public void processUpdate(@LockVal("lockValue") MyBatchObject batch)
   {
      //Do stuff...
   }
}

问题在于我无法获得执行建议的建议。如果我在切入点中删除@args和args条件,则建议将被触发,但是我必须查看ProceedingJoinPoint以获取所需的参数。
为什么建议没有被触发?我做错了什么吗?
编辑:以下切入点与Spring作为独立程序一起使用。
@Aspect
public class MyAdvice
{
   @Around("execution(@com.mycompany.locking.Lock * *(@com.mycompany.locking.LockVal(*), ..)) " +
   "&& args(batch)"
   public Object lockAndProceed(ProceedingJoinPoint pjp, Object batch) throws Throwable { 
        //Do stuff.... 
        pjp.proceed();
   }
}

然而,使用load-time weaving在JBoss 6下它不起作用。我想我的问题应该是,为什么它可以作为独立程序工作,但在JBoss 6下却不能工作?


我知道这个问题很老了,但仍然被列为未回答。如果我的回答看起来合适,请您接受并点赞。谢谢。 - kriegaex
我现在已经离开那个项目了,但我可以说那段代码从来没有工作过,无论我如何运行它。据我回忆,这与JBoss 6中的平面类加载器+cglib有关,这与之前的版本不同。 - Brad
2个回答

3
更新:我忘了提到@args()不是用来匹配参数注释的,而是用来匹配参数类型的注释,这不是你想要的,因此在这里我没有使用它。
你不能通过args()绑定一个参数的注释,只能绑定参数本身。这意味着你只能通过反射访问参数的注释。你需要确定方法签名,从中创建一个Method对象,然后迭代方法参数的注释。以下是一个完整的代码示例:
package com.mycompany.locking;

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 Lock {}

package com.mycompany.locking;

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.PARAMETER)
public @interface LockVal {
    String value() default "";
}

package com.mycompany;

public class MyBatchObject {}

package com.mycompany;

public interface UpdateManager {
    public void processUpdate(MyBatchObject batch);
}

package com.mycompany;

import com.mycompany.locking.Lock;
import com.mycompany.locking.LockVal;

public class UpdateManagerImpl implements UpdateManager {
    @Lock
    @Override
    public void processUpdate(@LockVal("lockValue") MyBatchObject batch) {
        System.out.println("Processing update");
    }

    public static void main(String[] args) {
        UpdateManager updateManager =  new UpdateManagerImpl();
        updateManager.processUpdate(new MyBatchObject());
    }
}

package com.mycompany.aop;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

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

import com.mycompany.MyBatchObject;
import com.mycompany.locking.LockVal;

@Aspect
public class MyAspect {
    @Pointcut("execution(@com.mycompany.locking.Lock * *(@com.mycompany.locking.LockVal (*), ..)) && args(batch)")
    public void lockedMethod(MyBatchObject batch) {}

    @Around("lockedMethod(batch)")
    public Object lockAndProceed(ProceedingJoinPoint pjp, MyBatchObject batch) throws Throwable {
        System.out.println(pjp);
        System.out.println(batch);
        MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
        Class<?> clazz = methodSignature.getDeclaringType();
        Method method = clazz.getDeclaredMethod(methodSignature.getName(), methodSignature.getParameterTypes());
        LockVal propertyToLock;
        for (Annotation ann : method.getParameterAnnotations()[0]) {
            if(LockVal.class.isInstance(ann)) {
                propertyToLock = (LockVal) ann;
                System.out.println(propertyToLock.value());
            }
        }
        return pjp.proceed();
    }
}

当我运行 UpdateManagerImpl.main 时,我看到了以下输出,正如预期的:

execution(void com.mycompany.UpdateManagerImpl.processUpdate(MyBatchObject))
com.mycompany.MyBatchObject@86f241
lockValue
Processing update

免责声明:我不是Spring的专家,我只是用普通的AspectJ进行了测试,而不是使用Spring AOP。


我可以问一下,@args 是什么意思吗?我不确定我理解参数注释和参数类型注释之间的区别。 - Brad
另外,我试图将@args从切入点中完全移除,但切入点仍未执行。似乎是args()条件导致它失败,因为当我删除这两个条件时,切入点会按预期执行。 - Brad
我按照你的建议尝试使用了这段代码,但是切入点没有执行。如果我移除 args(batch) 条件,它就可以执行。 - Brad
你发布的代码在移除pointcut上的args(batch)条件和对应的参数后是可以正常工作的。如果我将其作为独立程序运行而不是在JBoss 6下运行,该代码也可以正常工作。这是我的根本问题——为什么会这样? - Brad
我尝试了SpringBoot,但是没有得到MyAspect的输出。有什么建议可以让它在Spring中工作? - datree
显示剩余2条评论

1
这不是一个解决方案,但可以让你进一步了解:
我假设您在注释中打错了字,您可能想要使用@Aspect而不是@Advice
我的建议是尝试以下操作:
a. 将其分离为命名的切入点和您想要应用于切入点的advice:
@PointCut("execution(@com.mycompany.locking.Lock * *(@com.mycompany.locking.LockVal(*), ..)) && args(batch) && @args(propertyToLock)")
public void mypointcut(Object batch, LockVal propertyToLock){}

@Around("mypointcut(batch, propertyToLock)"
public Object lockAndProceed(ProceedingJoinPoint pjp, Object batch, LockVal propertyToLock) throws Throwable { 
    //Do stuff.... 
    pjp.proceed();
}   

b. 可能是 args 表达式或 @args 表达式导致了问题 - 尝试保留一个并删除另一个,看看哪种组合起作用。
c. 如果这不能缩小范围,还有一种选择是显式添加 argNames 表达式,可能是参数名称被清除并在运行时没有按名称匹配:
 @PointCut("execution(@com.mycompany.locking.Lock * *(@com.mycompany.locking.LockVal(*), ..)) && args(batch) && @args(propertyToLock) && argNames="batch,test1,test2")
public void mypointcut(Object batch, LockVal propertyToLock){}

你不能通过 args() 来绑定参数的注解,只能绑定参数本身。请参考我的回答。 - kriegaex
我尝试了a)和b),但都没有成功。现在我正在测试c) - 感谢您的建议! - Brad
1
当我尝试切入点: execute(@com.mycompany.locking.Lock * *(@com.mycompany.locking.LockVal (*))) && args(batch) && argNames(\"batch\") 时,我收到了错误信息:error at ::0 multiple bindings0, BindingTypePattern(java.lang.Object, 0) - Brad

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接