两个具有相同名称但不同参数的方法的两个切入点

时间:2012-08-10 16:50:38

标签: spring aop pointcut

平。

我正在编写@Aspect来记录我的持久层。

首先可能会向有经验的开发人员显示错误的代码;)

/** Interface of the class to observe. */
public interface PersistenceService {

    public Serializable save(Serializable serializable);

    public List<Serializable> save(List<Serializable> list)
}

/** Actual class to observe. */
@Service
public class PersistenceService {

    @Autowired
    private SomeJpaRepository rep;

    public Serializable save(Serializable serializable) {
        return rep.save(serializable);
    }

    public List<Serializable> save(List<Serializable> list) {
        return rep.save(list);
    }
}

这里的方面:

/** The Aspect. */
@Aspect
@Component
public class PersistenceService {

    /** A org.slf4j.Logger (using logback). */
    private final Logger logger = LoggerFactory.getLogger(getClass());

    /** Pointcut to define the classes to observe. */
    @Pointcut("within(de.mypckg.myproject.persistence.*.*)")
    public void inPersistanceLayer() {}

    /** Pointcut for the first save-method. */
    @Pointcut("execution(public * save(..)) && args(serializable)")
    public void saveOperation(Serializable serializable) {}

    /** Pointcut for the first save-method. */
    @Pointcut("execution(public * save(..)) && args(list)")
    public void saveOperation(List<Serializable> list) {}

    /** Method for the first save-method. */
    @Around("inPersistanceLayer() && saveOperation(serializable)")
    public List<Serializable> logSave(ProceedingJoinPoint joinPoint, Serializable serializable) throws Throwable {

        // log some stuff
        Object saved = joinPoint.proceed();
        // log somemore stuff
    }

    /** Method for the second save-method. */
    @Around("inPersistanceLayer() && saveOperation(list)")
    public List<Serializable> logSave(ProceedingJoinPoint joinPoint, List<Serializable> list) throws Throwable {

        // log some stuff
        Object saved = joinPoint.proceed();
        // log somemore stuff
    }
}

如果我只有一个切入点(以及它的方法),它可以工作,但如果我添加第二个,我会得到以下异常:

java.lang.IllegalArgumentException: warning no match for this type name: list [Xlint:invalidAbsoluteTypeName]

我改变了切入点的顺序,它总是排在第二位。关于如何解决这个问题的任何想法?

更新
一旦我发布了这个问题,我就有了一个想法。我改变了像这样的切入点:

/** The Aspect. */
@Aspect
@Component
public class PersistenceService {

    /** A org.slf4j.Logger (using logback). */
    private final Logger logger = LoggerFactory.getLogger(getClass());

    /** Pointcut to define the classes to observe. */
    @Pointcut("within(de.mypckg.myproject.persistence.*.*)")
    public void inPersistanceLayer() {}

    /** Pointcut for the save-method. */
    @Pointcut("execution(public * save(..))")
    public void saveOperation() {}

    /** Pointcut for the serializable argument. */
    @Pointcut("args(serializable)")
    public void serializableArgument(Serializable serializable) {}

    /** Pointcut for the list argument. */
    @Pointcut("args(list)")
    public void listArgument(List<Serializable> list) {}

    /** Method for the first save-method. */
    @Around("inPersistanceLayer() && saveOperation() && serializableArgument(serializable)")
    public Object logSave(ProceedingJoinPoint joinPoint, Serializable serializable) throws Throwable {

        // log some stuff
        Object saved = joinPoint.proceed();
        // log somemore stuff
        return saved;
    }

    /** Method for the second save-method. */
    @Around("inPersistanceLayer() && saveOperation(list) && listArgument(list)")
    public Object logSave(ProceedingJoinPoint joinPoint, List<Serializable> list) throws Throwable {

        // log some stuff
        Object saved = joinPoint.proceed();
        // log somemore stuff
        return saved;
    }
}

现在异常消失了,但仍然存在一个小问题(我猜这个问题要容易得多):由于ArrayList实现了Serializable,所以两个切入点都被执行,至少在我使用ArrayList的测试用例中。 /> 我将调查并发布我发现的内容,但也感谢帮助;)

更新2

更正了kriegaex声明的复制粘贴错误。谢谢!

方法logSave(..)的返回类型是Object。

更新3

我将代码更改为仅使用一个切入点和一个方法,并使用像kriegaex提议的instanceof进行检查。

/** The Aspect. */
@Aspect
@Component
public class PersistenceService {

    /** A org.slf4j.Logger (using logback). */
    private final Logger logger = LoggerFactory.getLogger(getClass());

    /** Pointcut to define the classes to observe. */
    @Pointcut("within(de.mypckg.myproject.persistence.*.*)")
    public void inPersistanceLayer() {}

    /** Pointcut for the save-method. */
    @Pointcut("execution(public * save(*)) && args(serializable)")
    public void saveOperation(Serializable serializable) {}

    /** Method for the first save-method. */
    @Around("inPersistanceLayer() && saveOperation() && serializableArgument(serializable)")
    public Serializable logSave(ProceedingJoinPoint joinPoint, Serializable serializable) throws Throwable {

        // log some stuff
        Serializable saved = (Serializable) joinPoint.proceed();

        if (saved instanceof List<?>) {
            List<?> savedList = (List<?>) saved;
            // log somemore stuff with a List
        } else {
            // log somemore stuff
        }
        return saved;
    }
}

我仍然想知道为什么它不起作用。

2 个答案:

答案 0 :(得分:1)

以下是两个选项:

申请类

package de.scrum_master.aspectj.sample;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

public class TestApp {
    public static void main(String[] args) {
        save(new HashSet<String>());
        List<Serializable> arg = new ArrayList<Serializable>();
        save(arg);
    }
    static Serializable save(Serializable arg) { return arg; }
    static List<Serializable> save(List<Serializable> arg) { return arg; }
}

<强>方面

package de.scrum_master.aspectj.sample;

import java.io.Serializable;
import java.util.List;

public aspect TestAspect {
    pointcut saveOperation(Object arg) : execution(* save(*)) && args(arg);
    pointcut serializableArgument(Serializable serializable) : execution(* save(Serializable)) && args(serializable);
    pointcut listArgument(List<Serializable> list) : execution(* save(List<Serializable>)) && args(list);

    Object around(Object arg) : saveOperation(arg) {
        if (arg instanceof List)
            System.out.println("Global advice   [List]:         " + thisJoinPointStaticPart.getSignature());
        else
            System.out.println("Global advice   [Serializable]: " + thisJoinPointStaticPart.getSignature());
        return proceed(arg);
    }

    List<Serializable> around(List<Serializable> list) : listArgument(list) {
        System.out.println("Specific advice [List]:         " + thisJoinPointStaticPart.getSignature());
        return proceed(list);
    }

    Serializable around(Serializable serializable) : serializableArgument(serializable) {
        System.out.println("Specific advice [Serializable]: " + thisJoinPointStaticPart.getSignature());
        return proceed(serializable);
    }
}

正如您所看到的,返回类型Object和使用简单切入点saveOperation(Object arg)的第一个建议是通用的一站式购物解决方案。另外两个建议是特定于参数类型的,每个都使用单独的切入点。如果编织并运行应用程序类,则会产生以下输出:

Global advice   [Serializable]: Serializable de.scrum_master.aspectj.sample.TestApp.save(Serializable)
Specific advice [Serializable]: Serializable de.scrum_master.aspectj.sample.TestApp.save(Serializable)
Global advice   [List]:         List de.scrum_master.aspectj.sample.TestApp.save(List)
Specific advice [List]:         List de.scrum_master.aspectj.sample.TestApp.save(List)

使用切入点serializableArgument(Serializable serializable)的建议只会触发一次,就像你想要的那样。

答案 1 :(得分:0)

如果将切入点写为

,则存在差异
args(java.io.Serializable)

execution(* *(java.io.Serializable))

如果参数在运行时中为Serializable,则前者匹配,后者仅与声明类型{{1}的单个参数的方法签名匹配}。在您的示例中,您使用Serializable作为..的参数,这意味着将匹配任意数量的方法参数。

查看Spring参考文档中的pointcut examples。特别是,我认为您会发现execution讨论很有趣。


编辑:

请注意,您无法将args变体用于绑定,但您可以轻松地使用execution将两者结合起来,例如。

&&

@Pointcut("execution(public * save(java.io.Serializable)) && args(serializable)")
public void saveOperation(Serializable serializable) {}