如何进行拦截()在另一个AspectJ的方面?(How to intercept proceed()

2019-09-21 08:33发布

我有一个情况如下:我有一个LoggingAspect几个切入点匹配我的主要应用特定的方法执行。 相应的咨询机构基本上看起来都差不多,造成大量重复的代码:

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

有一些变化,虽然。 有时切入点&建议有一个argthis参数也被输出到日志。 有时,“完成”,如果它只是不换其他很多呼叫的电话未成年人,像这样不打印消息:

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

恒定的事情是,我手动告诉记录器

  • 以增加被印刷在第一消息之后缩进层次,即,直接前proceed()被调用,并且
  • 降低缩进级别被印刷的最后消息之前(如果有的话被印刷),即后直接proceed()返回。

我的想法是,我还想写一元方面(或者称之为帮手方面)与拦截切入点proceed()的调用LoggingAspect ,从而自动调整相应的缩进级别。 但是,似乎没有切入点的匹配proceed() 我曾尝试call(SomeMethodInMyMainApp)甚至一个切入点匹配的记录方面的一切,但切入点匹配什么我并不需要,但从来没有在继续。

如果有人知道我怎么能做到这一点,我将不胜感激的提示或代码段。

这样做的一种间接的方式可能是拦截不建议本身,而是这些意见建议的方法调用(或执行)通过创建一个额外的切入点是这样的:

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

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

我宁愿另一种方式,虽然,有没有我记得更新额外catchAll()切入点,每次我更改日志方面的东西。

Answer 1:

建议包装在一个匿名类中进行()。 而写这本ADRESS执行方面(但不要忘了继续的潜在的异常())。

我的建议:

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

最好的问候马尔科



Answer 2:

请注意:我要在这里回答我的问题,增加了更多的信息和参数化的由loddar2012建议的解决方案的附加功能。 因为他的回答使我对正确的方向,我会接受它,即使在这里这个答案真的解决了从原来的问题我所有的需求,如(引用自己):

有一些变化,虽然。 有时切入点&建议具有Arg或该参数也被输出到日志。 有时,如果它只是不换其他很多呼叫的通话轻微不打印“完成”的消息

我们在这里处理的基本的东西是什么拉姆尼瓦斯·拉达称在他的书中工人对象模式 在行动的AspectJ 。 他(和loddar2012的)想法是,以纯散文

  • 包装一个呼叫到一个匿名类的实例(工作者对象),其中
    • 基类或实现接口提供意图做的工作的方法,
    • 工人对象提供了一个具体实现的辅助方法的具体要求proceed()在其中,
    • 工人方法能够创建对象后立即被调用(我们将在这里做的)或更高版本,甚至在自己的线程,
    • 工人对象可通过周围或添加到调度队列(其中没有我们需要在这里)。

如果你需要执行你的优雅的解决方案proceed()调用异步将创建匿名的情况下Runnable的类。 我们将用我们自己的抽象基类LogHelper ,不过,因为我们希望在我们的茶叶一些更多的糖,特别是选择通过日志消息,并影响日志输出到每个工人的一些其他参数。 所以这是我做了什么(包名和进口的代码示例未显示):

摘要工人的基类:

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");
        }
    }
    // (...)
}

正如你所看到的,记录咨询打电话之前做一些事情proceed(logHelper)即执行中的工人对象的log()方法)之后,有些东西,使用存储在工人对象内部的状态信息,如

  • 要记录的消息,
  • 日志级别(这里所说的“类型”),
  • 标志规定,如果缩进水平应在继续前提出,
  • 标记,用于规定当“完成”消息应工人执行之后被打印。

因为在我的使用情况下,所有登录方法返回void ,没有必要实施返回值的传球,但如果需要的话,这将是容易实现。 该建议的返回值会就做Object ,我们将通过的结果proceed()回到我们的调用,没什么大不了的。

有些意见捕捉joinpoints将被记录并利用parametrised工人对象来完成工作:

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作为与一种或多种构造参数的工人对象,设置其状态
  • 实现log()方法,任选地使用经由结合连接点状态this()args()
  • 调用/运行辅助对象(通话将被记录意见的切入点拦截和真实记录企业在那里进行)。


文章来源: How to intercept proceed() in another AspectJ aspect?