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

2024-01-09

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

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

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

我当然可以手动搜索它们。然而,我们的代码库非常广泛。重写外部库类的方法的可能性也是如此。因此考虑 AOP 来帮助我。

加载时编织被排除,因为它涉及 Web 应用程序。可能无法向应用程序服务器添加 JVM 代理。所以一定是编译时编织。

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


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

现在以切入点为例

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

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

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

这是一个自洽的示例:

库类/接口:

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扩展任何库类:

正如您所看到的,出于演示目的,此类在约 50% 的情况下会随机引发运行时异常。我们期待他们not由我们方面处理。

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% 的情况下随机抛出运行时异常。我们希望它们由我们方面处理。

The 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
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

AOP 围绕外部库的重写方法? 的相关文章

  • Grails 3.x bootRun 失败

    我正在尝试在 grails 3 1 11 中运行一个项目 但出现错误 失败 构建失败并出现异常 什么地方出了错 任务 bootRun 执行失败 进程 命令 C Program Files Java jdk1 8 0 111 bin java
  • 如何为最终用户方便地启动Java GUI程序

    用户想要从以下位置启动 Java GUI 应用程序Windows 以及一些额外的 JVM 参数 例如 javaw Djava util logging config file logging properties jar MyGUI jar
  • Java new Date() 打印

    刚刚学习 Java 我知道这可能听起来很愚蠢 但我不得不问 System out print new Date 我知道参数中的任何内容都会转换为字符串 最终值是 new Date 返回对 Date 对象的引用 那么它是如何打印这个的呢 Mo
  • 在 HTTPResponse Android 中跟踪重定向

    我需要遵循 HTTPost 给我的重定向 当我发出 HTTP post 并尝试读取响应时 我得到重定向页面 html 我怎样才能解决这个问题 代码 public void parseDoc final HttpParams params n
  • Android:捕获的图像未显示在图库中(媒体扫描仪意图不起作用)

    我遇到以下问题 我正在开发一个应用程序 用户可以在其中拍照 附加到帖子中 并将图片保存到外部存储中 我希望这张照片也显示在图片库中 并且我正在使用媒体扫描仪意图 但它似乎不起作用 我在编写代码时遵循官方的Android开发人员指南 所以我不
  • INSERT..RETURNING 在 JOOQ 中不起作用

    我有一个 MariaDB 数据库 我正在尝试在表中插入一行users 它有一个生成的id我想在插入后得到它 我见过this http www jooq org doc 3 8 manual sql building sql statemen
  • Spark 1.3.1 上的 Apache Phoenix(4.3.1 和 4.4.0-HBase-0.98)ClassNotFoundException

    我正在尝试通过 Spark 连接到 Phoenix 并且在通过 JDBC 驱动程序打开连接时不断收到以下异常 为简洁起见 下面是完整的堆栈跟踪 Caused by java lang ClassNotFoundException org a
  • 控制Android的前置LED灯

    我试图在用户按下某个按钮时在前面的 LED 上实现 1 秒红色闪烁 但我很难找到有关如何访问和使用前置 LED 的文档 教程甚至代码示例 我的意思是位于 自拍 相机和触摸屏附近的 LED 我已经看到了使用手电筒和相机类 已弃用 的示例 但我
  • 反射找不到对象子类型

    我试图通过使用反射来获取包中的所有类 当我使用具体类的代码 本例中为 A 时 它可以工作并打印子类信息 B 扩展 A 因此它打印 B 信息 但是当我将它与对象类一起使用时 它不起作用 我该如何修复它 这段代码的工作原理 Reflection
  • JavaMail 只获取新邮件

    我想知道是否有一种方法可以在javamail中只获取新消息 例如 在初始加载时 获取收件箱中的所有消息并存储它们 然后 每当应用程序再次加载时 仅获取新消息 而不是再次重新加载它们 javamail 可以做到这一点吗 它是如何工作的 一些背
  • Spring Data JPA 应用排序、分页以及 where 子句

    我目前正在使用 Spring JPA 并利用此处所述的排序和分页 如何通过Spring data JPA通过排序和可分页查询数据 https stackoverflow com questions 10527124 how to query
  • 磁模拟

    假设我在 n m 像素的 2D 表面上有 p 个节点 我希望这些节点相互吸引 使得它们相距越远吸引力就越强 但是 如果两个节点之间的距离 比如 d A B 小于某个阈值 比如 k 那么它们就会开始排斥 谁能让我开始编写一些关于如何随时间更新
  • 路径中 File.separator 和斜杠之间的区别

    使用有什么区别File separator和一个正常的 在 Java 路径字符串中 与双反斜杠相反 平台独立性似乎不是原因 因为两个版本都可以在 Windows 和 Unix 下运行 public class SlashTest Test
  • Java TestNG 与跨多个测试的数据驱动测试

    我正在电子商务平台中测试一系列商店 每个商店都有一系列属性 我正在考虑对其进行自动化测试 是否有可能有一个数据提供者在整个测试套件中提供数据 而不仅仅是 TestNG 中的测试 我尝试不使用 testNG xml 文件作为机制 因为这些属性
  • getResourceAsStream() 可以找到 jar 文件之外的文件吗?

    我正在开发一个应用程序 该应用程序使用一个加载配置文件的库 InputStream in getClass getResourceAsStream resource 然后我的应用程序打包在一个 jar文件 如果resource是在里面 ja
  • 仅将 char[] 的一部分复制到 String 中

    我有一个数组 char ch 我的问题如下 如何将 ch 2 到 ch 7 的值合并到字符串中 我想在不循环 char 数组的情况下实现这一点 有什么建议么 感谢您花时间回答我的问题 Use new String value offset
  • 无法捆绑适用于 Mac 的 Java 应用程序 1.8

    我正在尝试将我的 Java 应用程序导出到 Mac 该应用程序基于编译器合规级别 1 7 我尝试了不同的方法来捆绑应用程序 1 日食 我可以用来在 Eclipse 上导出的最新 JVM 版本是 1 6 2 马文 看来Maven上也存在同样的
  • 在 Scala 中,使用“_”和使用命名标识符有什么区别?

    为什么当我尝试使用时会出现错误 而不是使用命名标识符 scala gt res0 res25 List Int List 1 2 3 4 5 scala gt res0 map gt item toString
  • simpleframework,将空元素反序列化为空字符串而不是 null

    我使用简单框架 http simple sourceforge net http simple sourceforge net 在一个项目中满足我的序列化 反序列化需求 但在处理空 空字符串值时它不能按预期工作 好吧 至少不是我所期望的 如
  • 编译器抱怨“缺少返回语句”,即使不可能达到缺少返回语句的条件

    在下面的方法中 编译器抱怨缺少退货声明即使该方法只有一条路径 并且它包含一个return陈述 抑制错误需要另一个return陈述 public int foo if true return 5 鉴于Java编译器可以识别无限循环 https

随机推荐

  • 在 jooq 的 UPDATE 中使用原始值表达式

    这是我试图执行的查询 UPDATE TABLE users SET metadata metadata keyA keyB WHERE
  • 如何在 R 中获取当前种子

    我正在 R 中运行一个带有随机因子的函数 我意识到我忘记运行set seed 在运行这个函数之前 有什么方法可以找回seedR 正在使用的值 我想它将是任意数字 但并不重要 以便我可以重现执行 您可以使用以下命令获取当前的随机状态 Rand
  • 如何将地址转换为纬度/经度?

    我如何将地址或城市转换为纬度 经度 我可以从哪些商业机构 租用 这项服务 这将用于具有全时互联网访问的 Windows PC 上的商业桌面应用程序 谷歌有一个地理编码 API 似乎对于他们拥有谷歌地图数据的大多数位置都运行良好 http g
  • 为什么内部 TABLE 部分必须经过 THEAD TFOOT TBODY 才能验证?

    我经常使用 THEAD TBODY 和 TFOOT 元素将数据表划分为可以使用 CSS 单独寻址的部分 我也明白总是有一个隐式的 TBODY 标签 让我困惑的是这些必须进入验证的顺序 该表将验证
  • Hadoop 映射器从 2 个不同的源输入文件读取

    我有一个链接很多映射器和缩减器的工具 在某些时候我需要合并之前映射缩减步骤的结果 例如作为输入 我有两个包含数据的文件 input a txt apple 10 orange 20 input b txt apple 5 orange 40
  • 如何使用javas Process.waitFor()?

    我正在尝试从 Java 运行命令行命令 快速的健全性检查让我意识到我遇到麻烦的原因是我无法获取pr waitFor 请拨打以下电话进行工作 该程序在不到 30 秒内结束 并且在 foo 之后不打印任何内容 我预计它会花费 30 多秒并在 f
  • 如何从模块导入类而不导入整个模块

    我有一个非常大的 python 模块 超过 1 GB 我使用以下命令在另一个 python 脚本中从该模块导入一个类from module import class 问题是 当我第一次启动 python 脚本时 内存消耗非常高 并且脚本执行
  • 具有多个手柄的 JQuery UI 滑块:如何阻止手柄交叉?

    我正在开发一个快速解决方案 它使用具有多个手柄的滑块来定义动态布局的宽度 我尝试使用 ExtJS3 和最新的 JQuery UI 在 ExtJS 中 您可以限制句柄 这样它们就不会相互交叉 这是一种实现我需要的 UI 的非常直观的方法 但是
  • 初始化集合时,哈希集对内存有何作用?

    我偶然发现了以下问题 我想要一个包含从 1 到 100 000 000 的所有数字的哈希集 我尝试了以下代码 var mySet new HashSet
  • ServiceStack OrmLite 命令超时

    使用 IDbConnection ExecuteSql 时如何设置命令超时 IDbConnection db ConnectionFactory OpenDbConnection db ExecuteSql 如果我使用 IDbCommand
  • 在撰写中禁用横向模式

    如何禁用可组合函数的横向模式 我想始终以纵向模式显示可组合项 PS 无法在活动的清单文件中设置它 因为我只希望此行为适用于一个可组合项 而不适用于活动中的其他可组合项 你可以这样做DisposableEffect 活动requestedOr
  • 线程:PyQt 因“出队时队列中存在未知请求”而崩溃

    我正在开发的应用程序的一部分需要向一小群人发送一些电子邮件 由于连接到 SMTP 服务器并发送电子邮件可能需要一些时间 因此我想在此操作期间使用后台线程来提供一个进度条来完成这项工作 现在发生的情况是 我可以实现一个工作得很好的测试结构 但
  • PHP 中如何判断 value 是否为日期

    我正在使用 PHP 中的值数组 其中一些值可能包括各种字符串格式的日期 我需要将多种格式的日期转换为它们的等效数字 Unix 时间戳 问题是能够确定字符串是否是日期 Using if timestamp strtotime str fals
  • Android:加密密码[重复]

    这个问题在这里已经有答案了 可能的重复 存储密码 https stackoverflow com questions 5359399 storing a password 我正在使用共享首选项来存储密码 按原样保存密码数据是否安全 或者我必
  • Three.js 中的渐进式加载/LOD/流网格

    我正在使用 STL 加载器将 stl 文件加载到 Three js 场景中 这些 stl 文件的大小从 5mb 到 50mb 不等 有没有一种方法可以用来在模型加载时逐步加载 流式传输 提高细节级别 不确定术语是否正确 以便我的用户在出现任
  • R 按组总和总结给出 NA

    我有一个像这样的数据框 Observations 2 190 835 Variables 13 patientid
  • Angular 2 自定义复合控件

    我正在尝试为 Angular 2 创建一个自定义复合控件 我的要求是我需要创建一个允许用户选择文件的通用文件选择器控件either使用 html5 输入 类型 文件 or通过输入文件的 url 我决定创建通用表单控件 为两个子控件实现 Co
  • 何时对卷积层使用什么类型的填充?

    我知道当我们在神经网络中使用卷积层时 我们通常使用填充并且主要使用恒定填充 例如零填充 并且有不同类型的填充 例如对称 反射 恒定 但我不确定使用不同填充方法的优点和缺点以及何时使用哪一种 这实际上取决于神经网络的用途的情况 我不会告诉它的
  • Bootstrap 表 - 无法在 tr 上添加点击事件

    我正在使用 Bootstrap 表 http wenzhixin net cn p bootstrap table docs index html http wenzhixin net cn p bootstrap table docs i
  • AOP 围绕外部库的重写方法?

    我正在寻找以下问题的实用解决方案 外部库提供组件作为基类 自定义组件是通过扩展这些基类来创建的 当实现抛出未处理的异常时 基类就会中断 基类源代码不可用 只有一个二进制罐子 我正在寻找的是一个通用的 AOP 错误处理建议 它将包装每个方法的