如何使用 Aspectj 捕获和抑制 Java 类抛出的异常

Posted

技术标签:

【中文标题】如何使用 Aspectj 捕获和抑制 Java 类抛出的异常【英文标题】:How to catch and suppress an Exception Thrown from a Java Class using Aspectj 【发布时间】:2020-12-18 14:18:59 【问题描述】:

我想使用 Aspectj 处理从 Circle.getArea() 方法抛出的异常。

Shape.java

    package Shapes;
    
    public class Circle 
        private double radius;
        public Circle(double radius) 
               this.radius = radius;
        
        public double getPerimeter()
               return 2 * Math.PI * this.radius;
        
        public double getArea()
            return Math.PI * this.radius * this.radius;
        
    

Rectangle.java


    package Shapes;
    
    public class Rectangle 
        private double width, height;
        public Rectangle(double width, double height) 
             this.width = width;
             this.height = height;
        
        public double getPerimeter() 
               return 2 * (this.width + this.height);
        
        public double getArea() 
               return this.width * this.height;
        
    

Circle.java


    package Shapes;
    
    public class Circle 
        private double radius;
        public Circle(double radius) 
            this.radius = radius;
        
        public double getPerimeter() 
            return 2 * Math.PI * this.radius;
        
        public double getArea() 
            throw new RuntimeException("Oops, I don't know how to calculate this :(");
        
    

Main.java


    package Shapes;
    
    public class Main 
    
        public static void main(String[] args) 
            try 
                Shape s;
                s = (Shape) new Rectangle(2, 10);
                System.out.println("The area of " + s + " is " + s.getArea());
                
                s = (Shape) new Rectangle(-2, 10);
                System.out.println("The perimeter of " + s +" is " + s.getPerimeter());
                
                s = (Shape) new Circle(-2);
                System.out.println("The perimeter of " + s +" is " + s.getPerimeter());
                
                s = (Shape) new Circle(2);
                System.out.println("The area of " + s + " is " + s.getArea());
                
                catch(Exception e) 
                    System.out.println("Error: " + e.getMessage());
                
        
    

解决.aj


    package Shapes;
    
    privileged public aspect Resolve 
        declare parents: Rectangle implements Shape;
        declare parents: Circle implements Shape;
        
        public String Rectangle.getName()
            return "Rectangle";
        
        
        public String Circle.getName()
            return "Circle";
        
        
        public String Rectangle.toString()
            return this.getName()+"("+this.width+","+this.height+")";
        
        public String Circle.toString()
            return this.getName()+"("+this.radius+")";
        
        
    
        after() throwing(RuntimeException e) : execution(* Circle.*())
            handleException();
           
    
        protected void handleException()
        
            System.out.println("Error detected");
        
    

当前输出为:

The area of Rectangle(2.0,10.0) is 20.0

The perimeter of Rectangle(-2.0,10.0) is 16.0

The perimeter of Circle(-2.0) is -12.566370614359172

Error detected

Error: Oops, I don't know how to calculate this :(

我想避免打印“错误:糟糕,我不知道如何计算这个:(”,我需要在最后得到圆形对象的实际面积。 但是,我无法更改任何 .java 文件。所有更改都应使用 Resolve.aj 文件。

【问题讨论】:

【参考方案1】:

你需要使用aroundadvice而不是after

     Object around () : execution(* Circle.*())
         try 
              return proceed();
          
         catch(Exception e) 
         
            handleException();
         
         return null;
       

代码输出:

The area of Rectangle(2.0,10.0) is 20.0
The perimeter of Rectangle(-2.0,10.0) is 16.0
The perimeter of Circle(-2.0) is -12.566370614359172
Error detected
The area of Circle(2.0) is 0.0

为什么我们使用 around 建议而不是 after?

非常非正式地,aroundadvice 拦截给定的 joinpoint,并且可以注入新的行为之前 , after, 而不是那个joinpointproceed 是一个特殊的功能,它允许around 建议继续执行joinpoint

根据 AspectJ 支持的 advice 类型( beforeafteraround),aroundadvice 是唯一允许返回 值和/或使用proceed 的方法。这使得around advice 可以多次执行相同的joinpoint,或者根本不执行它。此外,您甚至可以使用不同的上下文执行截获的 joinpoint例如,更改方法参数的值)。

有关建议和继续工作的更多信息,请访问this SO Thread。

我们的around advice 将拦截Circle 类中方法的所有执行joinpoint,并相应地处理这些方法抛出的异常。

【讨论】:

以上是关于如何使用 Aspectj 捕获和抑制 Java 类抛出的异常的主要内容,如果未能解决你的问题,请参考以下文章

AspectJ——切入点语法之捕获类和对象构造上的连接点

AspectJ——切入点语法之捕获属性上的连接点

AspectJ——切入点语法之捕获属性上的连接点

AspectJ——切入点语法之捕获异常处理上的连接点

如何抑制或捕获 subprocess.run() 的输出?

AspectJ——切入点语法之限制连接点的作用域