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

排除AeyJ中带注释的方法

彭弘伟
2023-03-14

嗨,我想排除带注释的方法,这里是代码。

@Aspect
public class ExceptionHandlingAspect {
    private static final String TAG = ExceptionHandlingAspect.class.getName();

   @Pointcut("execution(* android.mobile.peakgames.net.aspectjandroid.AspectActivity.*(..)) " +
        "&& !@annotation(android.mobile.peakgames.net.aspectjandroid.exception.NoTryCatch)")
   public void exceptionEntryPoint() {
   }

    @AfterThrowing(pointcut = "exceptionEntryPoint()", throwing = "throwable")
    public void exceptionMethod(JoinPoint joinPoint, Throwable throwable) {
        Log.e(TAG, "Exception caught : " + throwable + " on method : " + joinPoint.getSignature());
        if (joinPoint.getTarget() instanceof Activity) {
            if (throwable instanceof AuthenticationException) {
                new AlertDialog.Builder((Context) joinPoint.getTarget())
                        .setTitle("Authentication Error")
                        .setMessage("You are not authenticated")
                        .show();
            } else {
                new AlertDialog.Builder((Context) joinPoint.getTarget())
                        .setTitle("Error")
                        .setMessage("Error occurred at : " + joinPoint.getSignature() + " " +
                                "Exception : " + throwable)
                        .show();
            }
        }
    }

    @Around(value = "exceptionEntryPoint()")
    public Object exceptionAroundMethod(ProceedingJoinPoint joinPoint) {
        try {
            return joinPoint.proceed();
        } catch (Throwable ignored) {
        }
        return null;
    }
}

排除任何用NoTryCatch注释的方法

上面的代码确实排除了用NoTryCatch注释的方法,但是当这个方法被异常调用时,它会停止下一个方法的执行。例如

@NoTryCatch
void test(){throws NullPointor..}

现在我按顺序调用方法

test()
test1()

test1()不运行。

如果我删除@注释(android.mobile.peakgames.net.aspectjandroid.exception.NoTryCatch)test1()运行

共有1个答案

姚丰羽
2023-03-14

当然,如果忽略在test()中引发的异常,即让它升级,则test1()不会运行。由于该未处理的异常,从未调用下一个方法。我认为这正是你的aspect设计的目的。你为什么期待不同的行为?如果你真的期望其他的东西,那么请在评论中描述它,我可以在编辑我的答案时告诉你如何做。

OP评论后更新:

好吧,这里有一个自制的问题:如果方法调用NoTryCatch void callee(),当然不会像设计的那样处理被调用方()中的异常。相反,它升级到未注释的调用方(),因此方面将在那里处理它。调用者如何知道被调用者中的某个方面忽略了异常?或者这个方面怎么知道呢?当将控制返回给调用方时,被调用方的控制流已经结束。

这种异常处理的概念至少很复杂。我甚至认为它有问题,因为调用链的最内部元素决定了所有外部元素都应该忽略一个异常。通常,异常处理的工作方式正好相反。调用方决定如何处理被调用方而不是被调用方本身引发的异常。因此,我建议您改变异常处理的想法和概念。

话虽如此,我将用一点MCVE向您展示我所说的在您的应用程序中真正发生的事情。因为我不是Android开发者,希望它能在任何Java SE机器上运行,所以我用实物模型模拟了AndroidAPI的相关部分:

Android API模型:

package android.content;

public class Context {}
package android.app;

import android.content.Context;

public class Activity extends Context {}

这一个只是通过登录控制台来模拟警报对话框。

package android.app;

import android.content.Context;

public class AlertDialog {
  public AlertDialog() {}

  public static class Builder {
    private String title;
    private String message;

    public Builder(Context target) {}

    public Builder setTitle(String title) {
      this.title = title;
      return this;
    }

    public Builder setMessage(String message) {
      this.message = message;
      return this;
    }

    public void show() {
      System.out.println("ALERT DIALOG: " + title + " -> " + message);
    }
  }
}
package org.apache.http.auth;

public class AuthenticationException extends Exception {
  private static final long serialVersionUID = 1L;

  public AuthenticationException(String message) {
    super(message);
  }
}

标记注释:

package android.mobile.peakgames.net.aspectjandroid.exception;

import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Retention;

@Retention(RUNTIME)
public @interface NoTryCatch {}

驱动程序应用:

package android.mobile.peakgames.net.aspectjandroid;

import org.apache.http.auth.AuthenticationException;

import android.app.Activity;
import android.mobile.peakgames.net.aspectjandroid.exception.NoTryCatch;

public class AspectActivity extends Activity {
  public String doSomething() {
    System.out.println("Doing something");
    return "something";
  }

  @NoTryCatch
  public String doSomethingElse() {
    System.out.println("Doing something else");
    throw new RuntimeException("oops");
  }

  public String doSomethingFancy() throws AuthenticationException {
    System.out.println("Doing something fancy");
    throw new AuthenticationException("uh-oh");
  }

  public void run() throws AuthenticationException {
    doSomething();
    doSomethingElse();
    doSomethingFancy();
  }

  public static void main(String[] args) throws AuthenticationException {
    new AspectActivity().run();
  }
}

OP的方面,略有优化:

基本上,这正是你的方面,有一些优化:

  • 您将错误处理逻辑分为两个建议,一个是“周围”建议,另一个是“抛出后”建议。这使得遵循实际的控制流有点困难,因为在一个建议中,您记录了错误,但后来却捕获并忽略了另一个建议中的相同错误。因此,我决定将日志拉入“around”建议的“catch”块,使其更清楚发生了什么
package de.scrum_master.aspect;

import org.apache.http.auth.AuthenticationException;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.mobile.peakgames.net.aspectjandroid.AspectActivity;
import android.util.Log;

@Aspect
public class ExceptionHandlingAspect {
  private static final String TAG = ExceptionHandlingAspect.class.getName();

  @Pointcut("execution(* *(..)) && target(activity)")
  public void methodsOfInterest(AspectActivity activity) {}

  @Pointcut("@annotation(android.mobile.peakgames.net.aspectjandroid.exception.NoTryCatch)")
  public void annotationNoTryCatch() {}

  @Around("methodsOfInterest(activity) && !annotationNoTryCatch()")
  public Object exceptionAroundMethod(ProceedingJoinPoint thisJoinPoint, AspectActivity activity) {
    try {
      return thisJoinPoint.proceed();
    } catch (Throwable throwable) {
      String errorMessage = "Error " + throwable + " in method " + thisJoinPoint.getSignature();
      Log.e(TAG, errorMessage);
      Builder builder = new AlertDialog.Builder(activity);
      if (throwable instanceof AuthenticationException)
        builder.setTitle("Authentication Error").setMessage("You are not authenticated").show();
      else
        builder.setTitle("Error").setMessage(errorMessage).show();
      return null;
    }
  }
}

控制台日志:

Doing something
Doing something else
[de.scrum_master.aspect.ExceptionHandlingAspect] Error java.lang.RuntimeException: oops in method void android.mobile.peakgames.net.aspectjandroid.AspectActivity.run()
ALERT DIALOG: Error -> Error java.lang.RuntimeException: oops in method void android.mobile.peakgames.net.aspectjandroid.AspectActivity.run()

日志清楚地显示

  • 执行带注释的方法doThingElse()并且没有在那里处理错误,
  • 但是调用方法run()反而触发了建议,因此错误在那里得到了处理。
  • 即使您还注释了run(),错误也会在main(...)中处理。

那么,为了避免注释整个调用链,您需要做什么?只有一种非常丑陋的方法可以做到这一点:手动簿记,即您的方面需要记住它以前忽略的异常实例,因为相应的错误处理建议从未针对该异常运行。

因此,您需要这样更改您的方面(忽略由手动try-catch创建的多线程和嵌套异常等问题,以免使其变得更复杂):

方面,迭代2:

package de.scrum_master.aspect;

import java.util.HashSet;
import java.util.Set;

import org.apache.http.auth.AuthenticationException;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.mobile.peakgames.net.aspectjandroid.AspectActivity;
import android.util.Log;

@Aspect
public class ExceptionHandlingAspect {
  private static final String TAG = ExceptionHandlingAspect.class.getName();

  private Set<Throwable> ignoredErrors = new HashSet<>();

  @Pointcut("execution(* *(..)) && target(activity)")
  public void methodsOfInterest(AspectActivity activity) {}

  @Pointcut("@annotation(android.mobile.peakgames.net.aspectjandroid.exception.NoTryCatch)")
  public void annotationNoTryCatch() {}

  @Around("methodsOfInterest(activity) && !annotationNoTryCatch()")
  public Object exceptionAroundMethod(ProceedingJoinPoint thisJoinPoint, AspectActivity activity) throws Throwable {
    try {
      return thisJoinPoint.proceed();
    } catch (Throwable throwable) {
      if (ignoredErrors.contains(throwable))
        throw throwable;
      String errorMessage = "Error " + throwable + " in method " + thisJoinPoint.getSignature();
      Log.e(TAG, errorMessage);
      Builder builder = new AlertDialog.Builder(activity);
      if (throwable instanceof AuthenticationException)
        builder.setTitle("Authentication Error").setMessage("You are not authenticated").show();
      else
        builder.setTitle("Error").setMessage(errorMessage).show();
      return null;
    }
  }

  @AfterThrowing(value = "methodsOfInterest(activity) && annotationNoTryCatch()", throwing = "throwable")
  public void ignoreExceptions(JoinPoint thisJoinPoint, AspectActivity activity, Throwable throwable) {
    ignoredErrors.add(throwable);
  }
}

控制台日志,迭代2:

Doing something
Doing something else
Exception in thread "main" java.lang.RuntimeException: oops
    at android.mobile.peakgames.net.aspectjandroid.AspectActivity.doSomethingElse(AspectActivity.java:17)
    at android.mobile.peakgames.net.aspectjandroid.AspectActivity.run_aroundBody4(AspectActivity.java:27)
    at android.mobile.peakgames.net.aspectjandroid.AspectActivity.run_aroundBody5$advice(AspectActivity.java:34)
    at android.mobile.peakgames.net.aspectjandroid.AspectActivity.run(AspectActivity.java:1)
    at android.mobile.peakgames.net.aspectjandroid.AspectActivity.main(AspectActivity.java:32)

如您所见,异常现在升级,按您所说的那样“崩溃”应用程序。

P、 S.:InheritableThreadLocal

P. P. S.:如果您将@NoTryCatch注释从doXXthingElse()向下移动到doXXTHingFancy,则日志更改如下:

Doing something
Doing something else
[de.scrum_master.aspect.ExceptionHandlingAspect] Error java.lang.RuntimeException: oops in method String android.mobile.peakgames.net.aspectjandroid.AspectActivity.doSomethingElse()
ALERT DIALOG: Error -> Error java.lang.RuntimeException: oops in method String android.mobile.peakgames.net.aspectjandroid.AspectActivity.doSomethingElse()
Doing something fancy
Exception in thread "main" org.apache.http.auth.AuthenticationException: uh-oh
    at android.mobile.peakgames.net.aspectjandroid.AspectActivity.doSomethingFancy(AspectActivity.java:22)
    at android.mobile.peakgames.net.aspectjandroid.AspectActivity.run_aroundBody4(AspectActivity.java:28)
    at android.mobile.peakgames.net.aspectjandroid.AspectActivity.run_aroundBody5$advice(AspectActivity.java:34)
    at android.mobile.peakgames.net.aspectjandroid.AspectActivity.run(AspectActivity.java:1)
    at android.mobile.peakgames.net.aspectjandroid.AspectActivity.main(AspectActivity.java:32)

 类似资料:
  • 我对AOP相当陌生。我正在尝试使用AspectJ在没有Spring的maven项目中创建注释。然而,我试图使用@方面调用的方法没有被调用。 这就是我的pom看起来的样子: 注释如下所示: 我为我的注释创建了一个注释处理器: } 我在这次通话中称之为: 我创建了一个aop。xml文件,并将其放置在与pom相同的文件夹中。xml。 当我调用createPurchase方法时,它在没有首先调用@Befo

  • 问题内容: 我需要 在类中使用@X注释的方法或使用@X注释的方法的切入点 。我还 需要注释对象 。如果同时注释了类和方法,则 我更喜欢将方法注释作为参数 。 我尝试了以下操作,这将创建“不一致的绑定”警告。(为什么不将它们设置为null?) 下面创建“跨’||’的参数x的模糊绑定 在切入点”警告。(我认为这并不一定有意义:为什么不绑定第一个短路评估?) 如果存在类和方法注释,则将先前的尝试自然地分

  • 我有一个用@Cacheable注释的方法。如果在方法内部捕获了异常,我希望清除缓存。但是,缓存似乎是在清除缓存的行之后执行的方面中加载的。因此,当在方法中捕获异常时,即使清除了缓存,空字符串结果仍保留在缓存中。 我应该从哪里清除缓存?

  • 我尝试在DAO中使用带有Spring注释的,但遇到了这样的问题: 工作正确: 例外: 如何使用parallelStream()注释的事务方法? 更新为什么会发生这种情况Spring事务管理器和多线程,但我希望支持java 8的Spring 4能够提供一些解决方案。有什么想法吗?

  • 问题内容: 我有一个带有注释的类(而不是对其所有方法进行标记)。 虽然我在该类中只有一个方法,但不应将其注释为。 我的问题是我可以在此方法中添加注释以将其标记为“非事务性”吗?或者我应该开始将此类中的每个方法都标记为“事务性”,但不包括此方法(很多工作) 谢谢。 问题答案: 有不同的事务传播策略可以使用。这些存在于枚举中。您可能要使用的是 因此,使用这两种方法在您的类中注释方法。 您可以在此处找到

  • 我有一个方法,每隔20分钟运行一次。我只是在Spring boot中使用了@scheduled注释,但是我需要一个在运行时需要延迟时间的调度器。例如。如果我希望能够在运行时改变方法执行的延迟时间/频率,而不停止应用程序,即改变以DB为单位的频率,那么代码应该适应它。 代码中的固定速率应该是可变的,并在运行时取值。有可能实现吗?