如何使用AspectJ拦截处理自身异常的方法 [英] How to intercept method which handles its own exceptions using AspectJ

查看:1236
本文介绍了如何使用AspectJ拦截处理自身异常的方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在发生某些特定异常时添加一些监控。
例如,如果我有这样的方面:

  @Aspect 
public class LogAspect {

@AfterThrowing(value =execution(* *(..)),throwing =e)
public void log(JoinPoint joinPoint,Throwable e){
System .out.println(一些记录的东西);
}
}

测试类:

 公共类示例{


public void divideByZeroWithCatch(){
try {
int a = 5/0;
}
catch(ArithmeticException e){
System.out.println(不能除以零);
}
}

public void divideByZeroWithNoCatch(){
int b = 5/0;
}

public static void main(String [] args){
示例e = new Example();
System.out.println(*****使用catch块*****调用方法);
e.divideByZeroWithCatch();
System.out.println(*****没有catch块*****的调用方法);
e.divideByZeroWithNoCatch();
}
}

作为输出我会得到:

  *****使用catch块调用方法***** 
不能除以零
** ***没有捕获阻止的呼叫方法*****
一些记录内容

I我想知道是否有办法让我在抛出异常后拦截方法执行,在我的建议中做一些事情并继续在相应的catch块中执行代码?
所以如果我打电话给 divideByZeroWithCatch()我可以得到:

 一些记录的东西
不能除以零


解决方案

是的,你可以。你需要一个处理程序()切入点:

  package de.scrum_master.aspect; 

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
公共类LogAspect {
@AfterThrowing(value =execution(* *(..)),throwing =e)
public void log(JoinPoint thisJoinPoint,Throwable e){
System.out.println(thisJoinPoint + - >+ e);
}

@Before(handler(*)&& args(e))
public void logCaughtException(JoinPoint thisJoinPoint,Exception e){
System.out.println(thisJoinPoint + - >+ e);
}
}

日志输出,假设类示例在包 de.scrum_master.app 中:

  *****使用catch块调用方法***** 
handler(catch(ArithmeticException)) - > java.lang.ArithmeticException:/ by zero
不能除以零
*****没有catch块的调用方法*****
执行(void de.scrum_master.app。 Example.divideByZeroWithNoCatch()) - > java.lang.ArithmeticException:/ by zero
execution(void de.scrum_master.app.Example.main(String [])) - > java.lang.ArithmeticException:/ by zero
线程main中的异常java.lang.ArithmeticException:/ by $
at de.scrum_master.app.Example.divideByZeroWithNoCatch(Example.java:13)
at de.scrum_master.app.Example.main(Example.java:21)






更新:如果您想知道异常处理程序的位置,有一种简单的方法:使用封闭的连接点的静态部分。您还可以获取有关参数名称和类型等的信息。只需使用代码完成以查看可用的方法。

  @Before(handler(*)&& args(e))
public void logCaughtException(
JoinPoint thisJoinPoint,
JoinPoint.EnclosingStaticPart thisEnclosingJoinPointStaticPart,
异常e
){
//异常处理程序
System.out.println(thisJoinPoint.getSignature()+ - >+ e);

//方法签名+参数类型/名称
MethodSignature methodSignature =(MethodSignature)thisEnclosingJoinPointStaticPart.getSignature();
System.out.println(+ methodSignature);
Class<?> [] paramTypes = methodSignature.getParameterTypes();
String [] paramNames = methodSignature.getParameterNames();
for(int i = 0; i< paramNames.length; i ++)
System.out.println(+ paramTypes [i] .getName()++ paramNames [i]) ;

//方法注释 - 关注,反思!
方法method = methodSignature.getMethod();
for(Annotation annotation:method.getAnnotations())
System.out.println(+ annotation);
}

现在更新你的代码:

  package de.scrum_master.app; 

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
int id();
String name();
String remark();
}



  package de.scrum_master.app; 

公共类示例{
@MyAnnotation(id = 11,name =John,remark =我最好的朋友)
public void divideByZeroWithCatch(int dividend,String someText ){
try {
int a = 5/0;
} catch(ArithmeticException e){
System.out.println(不能除以零);
}
}

public void divideByZeroWithNoCatch(){
int b = 5/0;
}

public static void main(String [] args){
示例e = new Example();
System.out.println(*****使用catch块*****调用方法);
e.divideByZeroWithCatch(123,Hello world!);
System.out.println(*****没有catch块*****的调用方法);
e.divideByZeroWithNoCatch();
}
}

然后控制台日志显示:

  *****使用catch块调用方法***** 
catch(ArithmeticException) - > java.lang.ArithmeticException:/ by zero
void de.scrum_master.app.Example.divideByZeroWithCatch(int,String)
int dividend
java.lang.String someText
@de .scrum_master.app.MyAnnotation(id = 11,name = John,remark =我最好的朋友)
不能除以零
*****没有catch块的调用方法*****
执行(void de.scrum_master.app.Example.divideByZeroWithNoCatch()) - > java.lang.ArithmeticException:/ by zero
execution(void de.scrum_master.app.Example.main(String [])) - > java.lang.ArithmeticException:/ by zero
线程main中的异常java.lang.ArithmeticException:/ by $
at de.scrum_master.app.Example.divideByZeroWithNoCatch(Example.java:14)
at de.scrum_master.app.Example.main(Example.java:22)

如果这对你来说已经够好了,那你就没事了。但要注意,静态部分不是完整的连接点,因此您无法从那里访问参数值。为此,您必须进行手动记账。这可能很昂贵,可能会降低您的应用程序速度。但是为了它的价值,我告诉你如何做到这一点:

  package de.scrum_master 。方面; 

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;

@Aspect
公共类LogAspect {
private ThreadLocal< JoinPoint> enclosingJoinPoint;

@AfterThrowing(value =execution(* *(..)),throwing =e)
public void log(JoinPoint thisJoinPoint,Throwable e){
System.out.println(thisJoinPoint + - >+ e);
}

@Before(执行(* *(..))&& within(de.scrum_master.app .. *))
public void recordJoinPoint (JoinPoint thisJoinPoint){
if(enclosingJoinPoint == null)
enclosingJoinPoint = ThreadLocal.withInitial(() - > thisJoinPoint);
else
enclosingJoinPoint.set(thisJoinPoint);
}

@Before(handler(*)&& args(e))
public void logCaughtException(JoinPoint thisJoinPoint,Exception e){
//异常处理程序
System.out.println(thisJoinPoint + - >+ e);

//方法签名+参数类型/名称
JoinPoint enclosingJP = enclosingJoinPoint.get();
MethodSignature methodSignature =(MethodSignature)enclosingJP.getSignature();
System.out.println(+ methodSignature);
Class<?> [] paramTypes = methodSignature.getParameterTypes();
String [] paramNames = methodSignature.getParameterNames();
Object [] paramValues = enclosingJP.getArgs();
for(int i = 0; i< paramNames.length; i ++)
System.out.println(+ paramTypes [i] .getName()++ paramNames [i] + =+ paramValues [i]);

//执行方法的目标对象
System.out.println(+ enclosingJP.getTarget());

//方法注释 - 关注,反思!
方法method = methodSignature.getMethod();
for(Annotation annotation:method.getAnnotations())
System.out.println(+ annotation);
}
}

为什么我们需要 ThreadLocal 加入点簿记的成员?好吧,因为很明显我们会在多线程应用程序中遇到问题。



现在控制台日志说:



< pre class =lang-none prettyprint-override> *****使用catch块调用方法*****
handler(catch(ArithmeticException)) - > java.lang.ArithmeticException:/ by zero
void de.scrum_master.app.Example.divideByZeroWithCatch(int,String)
int dividend = 123
java.lang.String someText = Hello world!
de.scrum_master.app.Example@4783da3f
@ de.scrum_master.app.MyAnnotation(id = 11,name = John,remark = my best friend)
不能除以$
*****没有catch块的调用方法*****
执行(void de.scrum_master.app.Example.divideByZeroWithNoCatch()) - > java.lang.ArithmeticException:/ by zero
execution(void de.scrum_master.app.Example.main(String [])) - > java.lang.ArithmeticException:/ by zero
线程main中的异常java.lang.ArithmeticException:/ by $
at de.scrum_master.app.Example.divideByZeroWithNoCatch(Example.java:14)
at de.scrum_master.app.Example.main(Example.java:22)


I'm trying add some monitoring when some specific exception occurs. For example, if I have an aspect like this:

@Aspect
public class LogAspect {

  @AfterThrowing(value = "execution(* *(..))", throwing = "e")
  public void log(JoinPoint joinPoint, Throwable e){
    System.out.println("Some logging stuff");
  }
}

And test class:

 public class Example {


  public void divideByZeroWithCatch(){
    try{
      int a = 5/0;
    }
    catch (ArithmeticException e){
      System.out.println("Can not divide by zero");
    }
  }

  public void divideByZeroWithNoCatch(){
    int b = 5/0;
  }

  public static void main (String [] args){
    Example e = new Example();
    System.out.println("***** Calling method with catch block *****");
    e.divideByZeroWithCatch();
    System.out.println("***** Calling method without catch block *****");
    e.divideByZeroWithNoCatch();
  }
}

As an output i will get:

***** Calling method with catch block *****
Can not divide by zero
***** Calling method without catch block *****
Some logging stuff

I was wondering if there is way for me to intercept method execution just after throwing exception, do something in my advice and continue with executing code in corresponding catch block? So that if i call divideByZeroWithCatch()i can get:

Some logging stuff
Can not divide by zero 

解决方案

Yes, you can. You need a handler() pointcut:

package de.scrum_master.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class LogAspect {
    @AfterThrowing(value = "execution(* *(..))", throwing = "e")
    public void log(JoinPoint thisJoinPoint, Throwable e) {
        System.out.println(thisJoinPoint + " -> " + e);
    }

    @Before("handler(*) && args(e)")
    public void logCaughtException(JoinPoint thisJoinPoint, Exception e) {
        System.out.println(thisJoinPoint + " -> " + e);
    }
}

Log output, assuming class Example is in package de.scrum_master.app:

***** Calling method with catch block *****
handler(catch(ArithmeticException)) -> java.lang.ArithmeticException: / by zero
Can not divide by zero
***** Calling method without catch block *****
execution(void de.scrum_master.app.Example.divideByZeroWithNoCatch()) -> java.lang.ArithmeticException: / by zero
execution(void de.scrum_master.app.Example.main(String[])) -> java.lang.ArithmeticException: / by zero
Exception in thread "main" java.lang.ArithmeticException: / by zero
    at de.scrum_master.app.Example.divideByZeroWithNoCatch(Example.java:13)
    at de.scrum_master.app.Example.main(Example.java:21)


Update: If you want to know where the exception handler is located, there is a simple way: use the enclosing joinpoint's static part. You can also get information about parameter names and types etc. Just use code completion in order to see which methods are available.

@Before("handler(*) && args(e)")
public void logCaughtException(
    JoinPoint thisJoinPoint,
    JoinPoint.EnclosingStaticPart thisEnclosingJoinPointStaticPart,
    Exception e
) {
    // Exception handler
    System.out.println(thisJoinPoint.getSignature() + " -> " + e);

    // Method signature + parameter types/names
    MethodSignature methodSignature = (MethodSignature) thisEnclosingJoinPointStaticPart.getSignature();
    System.out.println("    " + methodSignature);
    Class<?>[] paramTypes = methodSignature.getParameterTypes();
    String[] paramNames = methodSignature.getParameterNames();
    for (int i = 0; i < paramNames.length; i++)
        System.out.println("      " + paramTypes[i].getName() + " " + paramNames[i]);

    // Method annotations - attention, reflection!
    Method method = methodSignature.getMethod();
    for (Annotation annotation: method.getAnnotations())
        System.out.println("    " + annotation);
}

Now update your code like this:

package de.scrum_master.app;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    int id();
    String name();
    String remark();
}

package de.scrum_master.app;

public class Example {
    @MyAnnotation(id = 11, name = "John", remark = "my best friend")
    public void divideByZeroWithCatch(int dividend, String someText) {
        try {
            int a = 5 / 0;
        } catch (ArithmeticException e) {
            System.out.println("Can not divide by zero");
        }
    }

    public void divideByZeroWithNoCatch() {
        int b = 5 / 0;
    }

    public static void main(String[] args) {
        Example e = new Example();
        System.out.println("***** Calling method with catch block *****");
        e.divideByZeroWithCatch(123, "Hello world!");
        System.out.println("***** Calling method without catch block *****");
        e.divideByZeroWithNoCatch();
    }
}

Then the console log says:

***** Calling method with catch block *****
catch(ArithmeticException) -> java.lang.ArithmeticException: / by zero
    void de.scrum_master.app.Example.divideByZeroWithCatch(int, String)
      int dividend
      java.lang.String someText
    @de.scrum_master.app.MyAnnotation(id=11, name=John, remark=my best friend)
Can not divide by zero
***** Calling method without catch block *****
execution(void de.scrum_master.app.Example.divideByZeroWithNoCatch()) -> java.lang.ArithmeticException: / by zero
execution(void de.scrum_master.app.Example.main(String[])) -> java.lang.ArithmeticException: / by zero
Exception in thread "main" java.lang.ArithmeticException: / by zero
    at de.scrum_master.app.Example.divideByZeroWithNoCatch(Example.java:14)
    at de.scrum_master.app.Example.main(Example.java:22)

If that is good enough for you, then you are fine. But beware, the static part is not the full joinpoint, so you cannot access parameter values from there. In order to do that you have to do manual bookkeeping. And this is possibly expensive and can slow down your application. But for what it is worth, I show you how to do it:

package de.scrum_master.aspect;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;

@Aspect
public class LogAspect {
    private ThreadLocal<JoinPoint> enclosingJoinPoint;

    @AfterThrowing(value = "execution(* *(..))", throwing = "e")
    public void log(JoinPoint thisJoinPoint, Throwable e) {
        System.out.println(thisJoinPoint + " -> " + e);
    }

    @Before("execution(* *(..)) && within(de.scrum_master.app..*)")
    public void recordJoinPoint(JoinPoint thisJoinPoint) {
        if (enclosingJoinPoint == null)
            enclosingJoinPoint = ThreadLocal.withInitial(() -> thisJoinPoint);
        else
            enclosingJoinPoint.set(thisJoinPoint);
    }

    @Before("handler(*) && args(e)")
    public void logCaughtException(JoinPoint thisJoinPoint, Exception e) {
        // Exception handler
        System.out.println(thisJoinPoint + " -> " + e);

        // Method signature + parameter types/names
        JoinPoint enclosingJP = enclosingJoinPoint.get();
        MethodSignature methodSignature = (MethodSignature) enclosingJP.getSignature();
        System.out.println("    " + methodSignature);
        Class<?>[] paramTypes = methodSignature.getParameterTypes();
        String[] paramNames = methodSignature.getParameterNames();
        Object[] paramValues = enclosingJP.getArgs();
        for (int i = 0; i < paramNames.length; i++)
            System.out.println("      " + paramTypes[i].getName() + " " + paramNames[i] + " = " + paramValues[i]);

        // Target object upon which method is executed
        System.out.println("    " + enclosingJP.getTarget());

        // Method annotations - attention, reflection!
        Method method = methodSignature.getMethod();
        for (Annotation annotation: method.getAnnotations())
            System.out.println("    " + annotation);
    }
}

Why do we need a ThreadLocal member for the joinpoint bookkeeping? Well, because obviously we would get into problems in multi-threaded applications otherwise.

Now the console log says:

***** Calling method with catch block *****
handler(catch(ArithmeticException)) -> java.lang.ArithmeticException: / by zero
    void de.scrum_master.app.Example.divideByZeroWithCatch(int, String)
      int dividend = 123
      java.lang.String someText = Hello world!
    de.scrum_master.app.Example@4783da3f
    @de.scrum_master.app.MyAnnotation(id=11, name=John, remark=my best friend)
Can not divide by zero
***** Calling method without catch block *****
execution(void de.scrum_master.app.Example.divideByZeroWithNoCatch()) -> java.lang.ArithmeticException: / by zero
execution(void de.scrum_master.app.Example.main(String[])) -> java.lang.ArithmeticException: / by zero
Exception in thread "main" java.lang.ArithmeticException: / by zero
    at de.scrum_master.app.Example.divideByZeroWithNoCatch(Example.java:14)
    at de.scrum_master.app.Example.main(Example.java:22)

这篇关于如何使用AspectJ拦截处理自身异常的方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆