当前位置: 首页 > 知识库问答 >
问题:

如何在另一个AspectJ方面拦截procedue()?

司英飙
2023-03-14

我的情况如下:我有一个loggingapect,其中有几个切入点与我的主应用程序中的特定方法执行相匹配。相应的建议机构基本上看起来都很相似,导致大量代码重复:

void around() : download() {
    String message = "Downloading, verifying (MD5) and unpacking";
    SimpleLogger.verbose(message, IndentMode.INDENT_AFTER);
    proceed();
    SimpleLogger.verbose(message + " - done", IndentMode.DEDENT_BEFORE);
}

不过也有一些变化。有时是切入点

void around(BasicFilter filter) : fixFaultyLinkTargets()  && this(filter) {
    String message = "TOC file: checking for faulty link targets";
    SimpleLogger.verbose(message, IndentMode.INDENT_AFTER);
    proceed(filter);
    SimpleLogger.dedent();
}

经常发生的事情是我手动告诉记录器

  • 在打印第一条消息后,即在调用procedue()之前,增加缩进级别,并且
  • 在打印最终消息之前(如果打印了任何消息),即直接在返回后降低缩进级别

我的想法是,我想用一个切入点编写一个元方面(或称其为帮助方面),该切入点拦截LoggingAspect中的进行()调用,以便相应地自动调整缩进级别。但是似乎没有切入点匹配进行()。我尝试过调用(某些方法InMyMainApp),甚至一个切入点匹配日志方面的所有内容,但是切入点匹配任何我不需要的东西,但从来没有继续。

如果有人知道我该如何做到这一点,我将非常感谢您的提示或代码片段。

实现这一点的间接方法可能不是拦截通知本身,而是通过创建以下额外切入点来拦截这些通知所建议的方法调用(或执行):

// ATTENTION: each new pointcut must also be added here
pointcut catchAll() : download() || fixFaultyLinkTargets() || ...;

void around() : catchAll() {
    SimpleLogger.indent();
    proceed();
    SimpleLogger.dedent();
}

不过,我更喜欢另一种方式,而不必每次在日志方面更改某些内容时都记得更新额外的catchAll()切入点。

共有2个答案

南门刚捷
2023-03-14

请注意:我将在这里回答我自己的问题,在loddar2012建议的解决方案中添加更多信息和参数化的附加功能。因为他的回答把我引向了正确的方向,所以我将接受这个答案,尽管这个答案确实解决了我从原始问题中的所有需求,例如(引用我自己的话):

不过也有一些变化。有时是切入点

我们在这里处理的基本问题是Ramnivas Laddad在其著作AspectJ in Action中所称的worker对象模式。他的(和loddar2012的)想法是,用平淡的散文

  • 将调用包装到匿名类(worker对象)的实例中,其中

如果需要异步执行调用,一个优雅的解决方案是创建匿名可运行类的实例。不过,我们将使用我们自己的抽象基类LogHelper,因为我们想要在我们的tea中添加更多的糖,特别是将日志消息和一些影响日志输出的其他参数传递给每个worker的选项。这就是我所做的(示例代码中未显示包名和导入):

抽象工作者基类:

abstract class LogHelper {
    // Object state needed for logging
    String message;
    boolean logDone;
    boolean indent;
    LogType type;

    // Main constructor
    LogHelper(String message, boolean logDone, boolean indent, LogType type) {
        this.message = message;
        this.logDone = logDone;
        this.indent = indent;
        this.type = type;
    }
    // Convenience constructors for frequent use cases
    LogHelper(String message, boolean logDone) {
        this(message, logDone, true, LogType.VERBOSE);
    }
    LogHelper(String message) {
        this(message, true);
    }

    // Worker method to be overridden by each anonymous subclass
    abstract void log();
}

记录捕获工作对象执行的建议:

aspect LoggingAspect
{
    void around(LogHelper logHelper) :
        execution(* LogHelper.log()) && this(logHelper)
    {
        try {
            SimpleLogger.log(logHelper.type, logHelper.message);
            if (logHelper.indent)
                SimpleLogger.indent();
            proceed(logHelper);
        } finally {
            if (logHelper.indent)
                SimpleLogger.dedent();
            if (logHelper.logDone)
                SimpleLogger.log(logHelper.type, logHelper.message + " - done");
        }
    }
    // (...)
}

如您所见,日志记录建议在调用containe(logHelper)之前做了一些事情(即执行worker对象的log()方法),然后使用存储在worker对象中的状态信息做了一些事情,例如

  • 要记录的消息,
  • 日志级别(此处称为“类型”),
  • 指定是否应在继续之前提高缩进级别的标志,
  • 指定是否应在工作程序执行后打印“完成”消息的标志

因为在我的用例中,所有记录的方法都返回void,所以不需要实现返回值传递,但如果必要的话,这很容易实现。然后,通知的返回值将只是对象,我们将把继续()的结果传递给调用方,这没什么大不了的。

一些建议捕获要记录的连接点并利用参数化的工作对象来完成工作:

aspect LoggingAspect
{
    // (...)

    pointcut processBook()     : execution(* OpenbookCleaner.downloadAndCleanBook(Book));
    pointcut download()        : execution(* Downloader.download());
    pointcut cleanBook()       : execution(* OpenbookCleaner.cleanBook(Book));
    pointcut cleanChapter()    : execution(* OpenbookCleaner.cleanChapter(Book, File));
    pointcut initialiseTitle() : execution(* *Filter.initialiseTitle(boolean));

    void around(final Book book) : processBook() && args(book) {
        new LogHelper("Book: " + book.unpackDirectory) {
            void log() { proceed(book); } }.log();
    }
    void around() : download() {
        new LogHelper("Downloading, verifying (MD5) and unpacking") {
            void log() { proceed(); } }.log();
    }
    void around() : cleanBook() {
        new LogHelper("Filtering") {
            void log() { proceed(); } }.log();
    }
    void around(final File origFile) : cleanChapter() && args(*, origFile) {
        new LogHelper("Chapter: " + origFile.getName()) {
            void log() { proceed(origFile); } }.log();
    }
    void around() : initialiseTitle() {
        new LogHelper("Initialising page title", false) {
            void log() { proceed(); } }.log();
    }
}

这些示例展示了如何

  • 使用一个或多个构造函数参数将匿名LogHelper实例化为工作对象,设置其状态
卢深
2023-03-14

建议将procedue()封装在匿名类中。编写一个方面来处理这个执行(但不要忘记procedure()的潜在异常)。

我的建议:

// AspectProceedCaller.java
public abstract class AspectProceedCaller { 
    public abstract Object doProceed(); 
};

// aspect ProceedCallerAspect.aj
aspect ProceedCallerAspect {
     pointcut execProceedCaller() : execution( * AspectProceedCaller+.doProceed() );

     Object around() : execProceedCaller() {
         try {
              SimpleLogger.indent();
              return proceed();
         }
         finally {
              SimpleLogger.dedent();
         }
     }
};


// Your application aspect 
aspect AnyAspect {
    pointcut anyPointcut() : ...;

    Object around() : anyPointcut() {
        AspectProceedCaller apc=new AspectProceedCaller() {
            public Object doProceed() {
                return proceed();
            }
        };  

        // DO Stuff before ....

        Object retval = apc.doProceed();

        // ... and after calling proceed.

        return retval;
    }
};

问候马尔科

 类似资料:
  • 有多篇关于如何在Spring AOP中拦截内部方法调用的帖子。但是找不到任何与使用AspectJ排除内部方法相关的帖子。我们希望使用AspectJ编译时编织来实现它promise的运行时性能改进。 如果另一个类的方法调用了下面类TestService中的任何公共方法,则应该拦截该调用。但是,不应该截取从method1()到method2()的内部调用。我们只希望拦截器对每个对象只拦截一次。 一个示

  • 使用bytepal拦截类Foo,有两种方法A、B 在A方法中,B方法被调用。如果我们在intercetpor类栏中同时删除A和B到C方法,它们在第一行调用 会发生什么?这些方法的执行顺序是什么?

  • 我正在尝试使用来做同样的事情,现在我使用哪个注释来捕获相关的对象,切入点表达式应该是什么?我尝试了,但这是一个void方法,如何捕获该方法的参数?我是AOP的初学者,所以如果这个问题太琐碎,请原谅。 解决方案:使用spring AOP获取方法参数?

  • 我试图让aspectj拦截带注释的方法: 我删除了!为了简洁起见,在(InterceptMeAspect)内,但它并没有拦截太多。如果我删除注释约束(在(@InterceptMe*)内),它可以工作,但会拦截所有内容,这会造成一个大问题。 输出字节码似乎有完整的注释,所以我希望注释标准匹配。我正在或试图进行编译时编织。这很重要,因为我有另一个方面确实使用上面相同的方法工作。我怀疑该方面正在搞乱这个

  • 我正在使用AeyJ拦截一个名为的方法。为此,我使用了我自己指定的(标记)注释。这就是类的样子: 截取截取注释的方面: 然而,我的方面是基于带注释的参数进行拦截。但我希望方面能够拦截参数t包含的特定值的方法请求。 例如,如果t==“t1”,则必须截取该方法,否则不能截取。 我想知道是否可以在AeyJ(与Spring AOP结合使用)中做到这一点。