微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

使用AspectJ

如何解决使用AspectJ

我正在编写一个非常通用的代码以使用around作为result = proceed();,然后是return result;来捕获返回类型。

某些方法的类型为void。例如

void doPrint() { 
   System.out.println("Doing something"); 
}

如何将这些返回类型为void方法与返回值或引发异常的方法一起以通用方式处理?

到目前为止,我的代码是:

import java.util.Arrays;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import org.aspectj.lang.softException;
import org.aspectj.lang.Signature;
import org.aspectj.lang.reflect.CodeSignature;
import org.aspectj.lang.reflect.sourceLocation;

public aspect LogInjector {
    private pointcut executionJoinPoints(): !within(LogInjector) && execution (* *.*(..));

    Object around(): executionJoinPoints(){
        SourceLocation loc;
        CodeSignature sig;
        
        Class<?> type;
        
        Logger logger;
        
        Object result;
        try {
            loc = thisJoinPointStaticPart.getSourceLocation();
            sig = (CodeSignature) thisJoinPointStaticPart.getSignature();
            
            type = loc.getWithinType();
            if (type == null) type = sig.getDeclaringType();
            
            logger = LogManager.getLogger(type);
            
            result = proceed();
            return result;
        } catch (RuntimeException rte) {
            result = rte;
            throw rte;
        } catch (Throwable t) {
            result = t;
            throw new SoftException(t);
        } finally {
            logger.trace("Source location: {} | Join Point: {} | Signature: {} | Args: {} | Result: {}",loc,thisJoinPointStaticPart,sig,Arrays.deepToString(thisJoinPoint.getArgs()),result);
        }
    }

}

this answerthis user改正的错误

解决方法

在此问题的第一个版本中,我评论了您在return result;之后忘记了result = proceed();,后来您对其进行了更正。那是一个很好的第一步。但是您的代码仍然无法编译,因为:

  • around()建议未声明返回类型。
  • 您不能从Throwable的建议中抛出around(),而只能抛出RuntimeException。因此,您需要将每个选中的ExceptionErrorThrowable包装在诸如AspectJ的SoftException之类的文件中,或者仅包装在RuntimeException中。

因此,在发布不可编译的代码时,请提及该事实,并发布您遇到的编译器错误。不要假装代码可以正常工作,而关于void方法处理的细节问题。

其他问题是:

  • 您将打印多余的签名,因为您还将打印已经包含相同签名的连接点。
  • 您确定声明类型,但从不打印。
  • 您导入org.aspectj.ajdt.internal.compiler.ast.Proceed,从而使方面依赖于非AspectJ类。更准确地说,该类来自于AspectJ的Eclipse插件AJDT(AspectJ开发工具)。

我也不太确定用捕获的异常覆盖结果并按此方式打印结果是一个好主意,但是在我接下来的MCVE(这是您提供的工作,而您再次没有这样做)就像上一个问题一样做)我保持不变。

驱动程序应用程序:

package de.scrum_master.app;

public class Application {
  public static void main(String[] args) {
    Application application = new Application();
    application.doSomething("foo");
    try {
      application.doSomethingSpecial("bar");
    } catch (Exception e) {
      e.printStackTrace();
    }
    application.doSomethingElse("zot");
  }

  public int doSomething(String string) {
    System.out.println("Doing something: " + string);
    return 42;
  }

  public void doSomethingSpecial(String string) throws Exception {
    throw new Exception("checked exception");
  }

  public void doSomethingElse(String string) {
    throw new IllegalArgumentException("runtime exception");
  }
}

方面:

请注意,我删除了Log4J并只是打印到控制台,以便将代码简化为重要部分。您可以轻松地再次添加库。我不想手动将其添加到示例程序中,然后尝试使用Log4J 1.x或2.x。

package de.scrum_master.aspect;

import static java.util.Arrays.deepToString;

import org.aspectj.lang.SoftException;
import org.aspectj.lang.reflect.CodeSignature;
import org.aspectj.lang.reflect.SourceLocation;

public aspect LogInjector {
  private pointcut executionJoinPoints() :
    !within(LogInjector) && execution (* *(..));

  Object around() : executionJoinPoints() {
    SourceLocation sourceLocation = thisJoinPointStaticPart.getSourceLocation();
    CodeSignature signature = (CodeSignature) thisJoinPointStaticPart.getSignature();
    Class<?> type = sourceLocation.getWithinType();
    if (type == null)
      type = signature.getDeclaringType();
    Object result = null;
    try {
      result = proceed();
      return result;
    } catch (RuntimeException rte) {
      result = rte;
      throw rte;
    } catch (Throwable t) {
      result = t;
      throw new SoftException(t);
    } finally {
      System.out.printf(
        "Source location: %s | Type: %s | Join Point: %s | Args: %s | Result: %s%n",sourceLocation,type,thisJoinPoint,deepToString(thisJoinPoint.getArgs()),result
      );
    }
  }

}

如您所见,该方面仅重新抛出运行时异常(无需将它们包装到另一个运行时异常中)并包装其他throwables。这也是必要的,因为否则通过调用层次结构抛出的异常将像俄罗斯套娃一样被多次包装,这是我们要避免的事情。

控制台日志:

Doing something: foo
Source location: Application.java:15 | Type: class de.scrum_master.app.Application | Join Point: execution(int de.scrum_master.app.Application.doSomething(String)) | Args: [foo] | Result: 42
Source location: Application.java:20 | Type: class de.scrum_master.app.Application | Join Point: execution(void de.scrum_master.app.Application.doSomethingSpecial(String)) | Args: [bar] | Result: java.lang.Exception: checked exception
org.aspectj.lang.SoftException
    at de.scrum_master.app.Application.doSomethingSpecial_aroundBody5$advice(Application.java:28)
    at de.scrum_master.app.Application.doSomethingSpecial(Application.java:1)
    at de.scrum_master.app.Application.main_aroundBody0(Application.java:8)
    at de.scrum_master.app.Application.main_aroundBody1$advice(Application.java:21)
    at de.scrum_master.app.Application.main(Application.java:1)
Caused by: java.lang.Exception: checked exception
    at de.scrum_master.app.Application.doSomethingSpecial_aroundBody4(Application.java:21)
    at de.scrum_master.app.Application.doSomethingSpecial_aroundBody5$advice(Application.java:21)
    ... 4 more
Source location: Application.java:24 | Type: class de.scrum_master.app.Application | Join Point: execution(void de.scrum_master.app.Application.doSomethingElse(String)) | Args: [zot] | Result: java.lang.IllegalArgumentException: runtime exception
Source location: Application.java:4 | Type: class de.scrum_master.app.Application | Join Point: execution(void de.scrum_master.app.Application.main(String[])) | Args: [[--command,--option=123]] | Result: java.lang.IllegalArgumentException: runtime exception
Exception in thread "main" java.lang.IllegalArgumentException: runtime exception
    at de.scrum_master.app.Application.doSomethingElse_aroundBody6(Application.java:25)
    at de.scrum_master.app.Application.doSomethingElse_aroundBody7$advice(Application.java:21)
    at de.scrum_master.app.Application.doSomethingElse(Application.java:1)
    at de.scrum_master.app.Application.main_aroundBody0(Application.java:12)
    at de.scrum_master.app.Application.main_aroundBody1$advice(Application.java:21)
    at de.scrum_master.app.Application.main(Application.java:1)

该日志显示void和non-void方法都可以很好地处理,甚至main()参数数组的内容-我使用命令行参数--command --option=123启动示例应用程序-都可以很好地打印因为我使用的是Arrays.deepToString(thisJoinPoint.getArgs())而不是Arrays.toString(thisJoinPoint.getArgs())


所以,就像我说的那样:我不明白为什么您认为自己在void方法上遇到了问题,而实际上却还有很多其他问题。您是否曾经阅读过AspectJ手册或教程,并使用示例代码作为开始?还是只是在使用“试验与错误”方法?

P.S .:我永远不会在一个方面中使用源位置,因为一个方面不是调试器,并且无论如何源代码都易于重构。如果代码是在没有调试信息的情况下编译的,则无论如何字节代码中都没有源位置信息,因此您不能依赖它。方面或日志通常不能替代调试器。


更新:与original question相比,您现在不在目标方法调用之前和之后都记录日志,而仅在之后。在这种情况下,after() returningafter() throwing通知的组合将更有效且更易于实现,因为您可以避免异常处理,重新抛出和finally块。在另一个问题中,我推荐了around()的建议是因为我看到您原来的before()after()的建议每个都必须确定相同的信息才能进行记录,即两次每种方法。但是,如果只需要一次,则around()实际上是不必要的。


更新2:您询问:

我遇到另一个问题。通过抛出SoftExceptionRuntimeExceptioncatch块无法捕获根据正常行为根据库中方法签名应该抛出和捕获的异常。

如果要使异常保持不变,请如上所述使用after() returning(打印结果)和after() throwing(不变的异常,无需包装它们)的组合。有关更多信息,请参见AspectJ manual

package de.scrum_master.aspect;

import static java.util.Arrays.deepToString;

import org.aspectj.lang.reflect.CodeSignature;
import org.aspectj.lang.reflect.SourceLocation;

public aspect LogInjector {
  private pointcut executionJoinPoints() :
    !within(LogInjector) && execution (* *(..));

  after() returning (Object result): executionJoinPoints() {
    SourceLocation sourceLocation = thisJoinPointStaticPart.getSourceLocation();
    CodeSignature signature = (CodeSignature) thisJoinPointStaticPart.getSignature();
    Class<?> type = sourceLocation.getWithinType();
    if (type == null)
      type = signature.getDeclaringType();
    System.out.printf("Source location: %s | Type: %s | Join Point: %s | Args: %s | Result: %s%n",result
    );
  }

  after() throwing (Throwable error): executionJoinPoints() {
    SourceLocation sourceLocation = thisJoinPointStaticPart.getSourceLocation();
    CodeSignature signature = (CodeSignature) thisJoinPointStaticPart.getSignature();
    Class<?> type = sourceLocation.getWithinType();
    if (type == null)
      type = signature.getDeclaringType();
    System.out.printf("Source location: %s | Type: %s | Join Point: %s | Args: %s | Error: %s%n",error
    );
  }

}

控制台日志将更改为:

Doing something: foo
Source location: Application.java:15 | Type: class de.scrum_master.app.Application | Join Point: execution(int de.scrum_master.app.Application.doSomething(String)) | Args: [foo] | Result: 42
Source location: Application.java:20 | Type: class de.scrum_master.app.Application | Join Point: execution(void de.scrum_master.app.Application.doSomethingSpecial(String)) | Args: [bar] | Error: java.lang.Exception: checked exception
java.lang.Exception: checked exception
    at de.scrum_master.app.Application.doSomethingSpecial(Application.java:21)
    at de.scrum_master.app.Application.main(Application.java:8)
Source location: Application.java:24 | Type: class de.scrum_master.app.Application | Join Point: execution(void de.scrum_master.app.Application.doSomethingElse(String)) | Args: [zot] | Error: java.lang.IllegalArgumentException: runtime exception
Source location: Application.java:4 | Type: class de.scrum_master.app.Application | Join Point: execution(void de.scrum_master.app.Application.main(String[])) | Args: [[--command,--option=123]] | Error: java.lang.IllegalArgumentException: runtime exception
Exception in thread "main" java.lang.IllegalArgumentException: runtime exception
    at de.scrum_master.app.Application.doSomethingElse(Application.java:25)
    at de.scrum_master.app.Application.main(Application.java:12)

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。