AOP围绕外部库的重写方法? [英] AOP around overridden methods of external library?

查看:49
本文介绍了AOP围绕外部库的重写方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am searching for a practical solution for the following problem:

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

我正在寻找的是具有通用的AOP错误处理建议.它将包装所有方法的代码,这些方法是外部库中方法的直接替代或实现.基本上是尝试/捕获错误恢复.

What I am looking for is to have a generic AOP error handling advice. It would wrap the code of every method that is a direct override or implementation of a method from the external library. Basically doing a try/catch for error recovery.

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

I can search for them manually of course. However our code base is extensive. And so are the possibilities to override methods of the external library classes. Hence thinking about AOP to help me out.

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

Load time weaving is ruled out because it concerns a web application. It might not be possible to add a JVM agent with the application server. So it must be compile time weaving.

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

Does somebody know how to do this with AspectJ ?

推荐答案

假定您的外部库类/接口位于某个包中,例如 org.external.library 或其任何子包中,您可以使用AspectJ语法将这些类/接口表示为 org.external.library .. * + . .. 表示包括子包", * 表示所有类", + 表示"include子类".现在附加.*(..),您将获得所有方法,而不必考虑这些类的名称和参数数量.

Assuming that your external library classes/interfaces are in a certain package such as org.external.library or in any of its subpackages, you can express those classes/interfaces in AspectJ syntax as org.external.library..*+. The .. means "include subpackages", the * means "all classes", the + means "include subclasses". Now append .*(..) and you get all methods regardless of name and number of arguments for those classes.

现在是切入点

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

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

intercepts all non-static methods in any library subclasses regardless of return type.

坏消息是您不能将切入点限制为覆盖方法,因为 @Override 批注的保留类型为源",即,它不适用于纵横比匹配.但是也许您只是想将切入点缩小到公共方法,以便排除内部方法引发的错误.这取决于您的情况以及您要如何处理.假设非覆盖方法也可以破坏基本组件,那么切入点是很好的.

The bad news is that you cannot limit the pointcut to overriding methods because the @Override annotation has retention type "source", i.e. it is unavailable for aspect matching. But maybe you want to narrow down the pointcut to public methods only in order to exclude errors thrown by internal methods. It depends on your situation and how you want to deal with it. Assuming that also non-overriding methods can break base components, the pointcut is good, though.

这是一个自洽的示例:

库类/接口:

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();
}

一个类扩展任何库类:

A class not extending any library class:

如您所见,出于演示目的,此类在约50%的所有情况下随机引发运行时异常.我们希望他们 不是 由我们的方面来处理.

As you can see, this class throws runtime exceptions randomly in ~50% of all cases for demo purposes. We expect them not to be handled by our aspect.

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 + "!";
    }
}

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

如您所见,这些类还出于演示目的在所有情况的约50%中随机抛出运行时异常.我们希望它们可以由我们的方面来处理.

As you can see, these classes also throw runtime exceptions randomly in ~50% of all cases for demo purposes. We expect them to be handled by our aspect.

main 方法通过几次调用每个方法来演示整个设置,而不处理被覆盖的库类中的任何错误,而仅在我们自己的类中进行处理.

The main method demonstrates the whole setup by calling each method a few times, not handling any errors in overridden library classes, but only in our own class.

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;
        }
    }
}

控制台输出:

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

The output looks a bit different each time you run the application due to the randomisation in the code:

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

这篇关于AOP围绕外部库的重写方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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