java – 围绕外部库重写方法的AOP?

我正在寻找以下问题的实用解决方案:

>外部库提供组件作为基类.
>通过扩展这些基类来制作自定义组件.
>当实现抛出未处理的异常时,基类会中断.
>基类源代码不可用.只有一个二进制jar.

我正在寻找的是有一个通用的AOP错误处理建议.它将包装从外部库直接覆盖或实现方法的每个方法的代码.基本上做一个try / catch来进行错误恢复.

我当然可以手动搜索它们.但是我们的代码库很广泛.覆盖外部库类的方法的可能性也是如此.因此,考虑AOP来帮助我.

加载时间编织被排除,因为它涉及Web应用程序.可能无法在应用程序服务器中添加JVM代理程序.所以它必须是编译时编织.

有人知道如何使用AspectJ做到这一点?

解决方法:

假设您的外部库类/接口位于某个包中,例如org.external.library或其任何子包中,您可以将这些类/接口在AspectJ语法中表示为org.external.library .. *. ..表示“包括子包”,*表示“所有类”,表示“包括子类”.现在追加.*(..)并获得所有方法,无论这些类的名称和参数数量如何.

现在例如切入点

execution(!static * org.external.library..*+.*(..))

无论返回类型如何,都会拦截任何库子类中的所有非静态方法.

坏消息是您不能将切入点限制为覆盖方法,因为@Override注释具有保留类型“source”,即它不可用于方面匹配.但是,您可能只想将切入点缩小到公共方法,以排除内部方法引发的错误.这取决于您的情况以及您希望如何处理它.假设非重写方法也可以破坏基本组件,但切入点很好.

这是一个自洽的样本:

库类/接口:

package org.external.library;

public abstract class AbstractBase {
    public abstract String getText();
    public abstract int getNumber();
    public abstract void doSomething();
}
package org.external.library;

public interface Service {
    void start();
    void stop();
    boolean isRunning();
}

不扩展任何库类的类:

正如您所看到的,此类在所有情况下随机抛出运行时异常,用于演示目的.我们希望它们不会被我们的方面处理.

package de.scrum_master.app;

import java.util.Random;

public class MyOwnClass {
    private static final Random RANDOM = new Random();

    public String getGreeting(String recipient) {
        if (RANDOM.nextBoolean())
            throw new RuntimeException("cannot get greeting for '" + recipient + "'");
        return "Hello " + recipient + "!";
    }
}

扩展/实现库类/接口的类:

正如您所看到的,这些类还会在所有情况下随机抛出运行时异常,以用于演示目的.我们希望它们能够由我们的方面来处理.

main方法通过调用每个方法几次来演示整个设置,而不是在重写的库类中处理任何错误,而只是在我们自己的类中.

package de.scrum_master.app;

import java.util.Random;

import org.external.library.Service;

public class FooService implements Service {
    private static final Random RANDOM = new Random();
    private boolean isRunning;

    @Override
    public void start() {
        if (RANDOM.nextBoolean())
            throw new RuntimeException("cannot start");
        isRunning = true;
    }

    @Override
    public void stop() {
        if (RANDOM.nextBoolean())
            throw new RuntimeException("cannot stop");
        isRunning = false;
    }

    @Override
    public boolean isRunning() {
        return isRunning;
    }

    public void pause() {
        if (RANDOM.nextBoolean())
            throw new RuntimeException("cannot pause");
        isRunning = false;
    }

    public void resume() {
        if (RANDOM.nextBoolean())
            throw new RuntimeException("cannot resume");
        isRunning = true;
    }
}
package de.scrum_master.app;

import java.util.Random;

import org.external.library.AbstractBase;

public class Application extends AbstractBase {
    private static final Random RANDOM = new Random();

    @Override
    public String getText() {
        if (RANDOM.nextBoolean())
            throw new RuntimeException("cannot get text");
        return "Hello world!";
    }

    @Override
    public int getNumber() {
        if (RANDOM.nextBoolean())
            throw new RuntimeException("cannot get number");
        return RANDOM.nextInt(10);
    }

    @Override
    public void doSomething() {
        if (RANDOM.nextBoolean())
            throw new RuntimeException("cannot do something");
    }

    public void doSomethingElse() {
        if (RANDOM.nextBoolean())
            throw new RuntimeException("cannot do something else");
    }

    public static void main(String[] args) {
        Application application = new Application();
        FooService fooService = new FooService();
        MyOwnClass myOwnClass = new MyOwnClass();
        for (int i = 0; i < 5; i++) {
            application.getText();
            application.getNumber();
            application.doSomething();
            application.doSomethingElse();
            fooService.start();
            fooService.pause();
            fooService.isRunning();
            fooService.resume();
            fooService.isRunning();
            fooService.stop();
            try {
                myOwnClass.getGreeting("world");
                myOwnClass.getGreeting("guys");
            }
            catch (Exception e) {
                System.out.println("Uncaught by aspect: " + e);
            }
        }
    }
}

错误处理方面:

package de.scrum_master.aspect;

public aspect ErrorHandler {
    Object around() : execution(!static * org.external.library..*+.*(..)) {
        try {
            return proceed();
        }
        catch (Exception e) {
            System.out.println(thisJoinPoint + " -> " + e);
            return null;
        }
    }
}

控制台输出:

由于代码中的随机化,每次运行应用程序时输出看起来都有点不同:

execution(int de.scrum_master.app.Application.getNumber()) -> java.lang.RuntimeException: cannot get number
execution(void de.scrum_master.app.Application.doSomething()) -> java.lang.RuntimeException: cannot do something
execution(void de.scrum_master.app.Application.doSomethingElse()) -> java.lang.RuntimeException: cannot do something else
execution(void de.scrum_master.app.FooService.start()) -> java.lang.RuntimeException: cannot start
execution(void de.scrum_master.app.FooService.pause()) -> java.lang.RuntimeException: cannot pause
execution(void de.scrum_master.app.FooService.resume()) -> java.lang.RuntimeException: cannot resume
execution(void de.scrum_master.app.FooService.stop()) -> java.lang.RuntimeException: cannot stop
Uncaught by aspect: java.lang.RuntimeException: cannot get greeting for 'world'
execution(String de.scrum_master.app.Application.getText()) -> java.lang.RuntimeException: cannot get text
execution(int de.scrum_master.app.Application.getNumber()) -> java.lang.RuntimeException: cannot get number
execution(void de.scrum_master.app.Application.doSomething()) -> java.lang.RuntimeException: cannot do something
execution(void de.scrum_master.app.FooService.start()) -> java.lang.RuntimeException: cannot start
execution(void de.scrum_master.app.FooService.pause()) -> java.lang.RuntimeException: cannot pause
execution(void de.scrum_master.app.FooService.stop()) -> java.lang.RuntimeException: cannot stop
Uncaught by aspect: java.lang.RuntimeException: cannot get greeting for 'guys'
execution(void de.scrum_master.app.Application.doSomething()) -> java.lang.RuntimeException: cannot do something
execution(void de.scrum_master.app.FooService.start()) -> java.lang.RuntimeException: cannot start
execution(void de.scrum_master.app.FooService.pause()) -> java.lang.RuntimeException: cannot pause
Uncaught by aspect: java.lang.RuntimeException: cannot get greeting for 'guys'
execution(void de.scrum_master.app.Application.doSomethingElse()) -> java.lang.RuntimeException: cannot do something else
execution(void de.scrum_master.app.FooService.resume()) -> java.lang.RuntimeException: cannot resume
execution(void de.scrum_master.app.FooService.stop()) -> java.lang.RuntimeException: cannot stop
Uncaught by aspect: java.lang.RuntimeException: cannot get greeting for 'world'
execution(String de.scrum_master.app.Application.getText()) -> java.lang.RuntimeException: cannot get text
execution(int de.scrum_master.app.Application.getNumber()) -> java.lang.RuntimeException: cannot get number
execution(void de.scrum_master.app.Application.doSomething()) -> java.lang.RuntimeException: cannot do something
execution(void de.scrum_master.app.Application.doSomethingElse()) -> java.lang.RuntimeException: cannot do something else
execution(void de.scrum_master.app.FooService.start()) -> java.lang.RuntimeException: cannot start
execution(void de.scrum_master.app.FooService.pause()) -> java.lang.RuntimeException: cannot pause
execution(void de.scrum_master.app.FooService.stop()) -> java.lang.RuntimeException: cannot stop
上一篇:Spring包作用


下一篇:java – 方法中间的横切关注点