0

嘿。

我正在写一个@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;
    }
}

我仍然想知道为什么它不能以另一种方式工作。

4

2 回答 2

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)只会触发一次,就像您喜欢的那样。

于 2012-08-11T11:29:40.843 回答
0

如果您将切入点编写为

args(java.io.Serializable)

或者

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

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

查看 Spring 参考文档中的切入点示例。特别是,我认为您会发现args讨论很有趣。


编辑:

请注意,您不能使用execution变体进行绑定,但您可以&&像尝试过的那样轻松地使用将两者结合起来,例如

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

@Pointcut("execution(public * save(java.util.List)) && args(list)")
public void saveOperation(List<Serializable> list) {}
于 2012-08-11T12:36:40.723 回答