通过AOP定义一个缺失的方法?

Define a missing method through AOP?

我所处的情况是我们正在使用的库的实现比我们的依赖项之一的实现更新。例如。依赖项使用 MyLibrary-1.0,我们使用 MyLibrary-2.0。

在较新的实现中,删除了一个已弃用的方法,这会导致 运行 次错误。

我正在尝试使用 AOP(具体来说是 Spring-AOP)来拦截对缺失方法的调用,并将它们代理到现有方法中...但我似乎做不到获得正确的方面。

感觉 Java 在我的方面有机会拦截之前引发了 'java.lang.NoSuchMethodError' 异常。是不是我遗漏了一些技巧,或者这是不可行的(例如,该方法必须存在才能实现它)?

@Before("execution(* com.mylibrary.SomeClass.*(..))") 
     Fails with java.lang.NoSuchMethodError

@Around("target(com.mylibrary.SomeClass) && execution(* missingMethod(..))")
     Fails with java.lang.NoSuchMethodError

当 JVM 加载 class 时,它会在 "linker" 阶段解析所有依赖项:外部 classes、属性和方法。你的情况不能通过这个阶段,因为缺少方法。

(Spring-)AOP有两种模式:Proxy和weaving。

  1. 代理创建...围绕 class 的代理:目标 class 必须存在并被加载
  2. 编织可以在加载 class 之前发生:当 classloader 加载 class 时,一个 byte[] 数组被传递给编织器,它可以操纵class 之前的 class 字节码确实被具体化了。这种类型的 aop 可以 适用于您的情况。然而,这并非易事。

假设您正在谈论独立于 Spring 的第 3 方库,您不能将 Spring AOP 与其基于代理的 "AOP lite" 方法一起使用,该方法仅适用于 public,Spring 组件的非静态方法。请改用更强大的 AspectJ。 Spring manual 解释了如何将带有加载时编织 (LTW) 的完整 AspectJ 集成到 Spring 应用程序中。如果你的应用程序到目前为止还没有基于 Spring,而你只是想使用框架因为 Spring AOP,你可以完全跳过整个 Spring 的东西并使用普通的 AspectJ。

您要使用的功能是类型间声明 (ITD),更具体地说是 AspectJ 为现有 classes 声明方法的能力。这是一些示例代码:

第三方库:

package org.library;

public class Utility {
    public String toHex(int number) {
        return Integer.toHexString(number);
    }

    // Let us assume that this method was removed from the new library version
    /*
    @Deprecated
    public String toOct(int number) {
        return Integer.toOctalString(number);
    }
    */
}

假设我注释掉的方法刚刚从你自己的项目依赖的最新版本中删除,但你知道如何重新实现它。

项目依赖依赖老版本的3rd party库:

package com.dependency;

import org.library.Utility;

public class MyDependency {
    public void doSomethingWith(int number) {
        System.out.println(number + " in octal = " + new Utility().toOct(number));
    }
}

因为之前弃用的方法Utility.toOct在你自己的项目使用的版本中已经不存在了,你会在运行时调用MyDependency.doSomethingWith.[=20=得到NoSuchMethodError ]

您自己的申请:

package de.scrum_master.app;

import org.library.Utility;

import com.dependency.MyDependency;

public class Application {
    public static void main(String[] args) {
        System.out.println("3333 in hexadecimal = " + new Utility().toHex(3333));
        new MyDependency().doSomethingWith(987);
    }
}

如您所见,该应用程序也使用相同的库,但在当前版本中仍然存在不同的方法。不幸的是,它还使用依赖于已删除方法的存在的依赖项。那么我们应该如何修复呢?

使用 ITD 的方面:

AspectJ 来拯救!我们只是将缺少的方法添加到 3rd 方库中。

package de.scrum_master.aspect;

import org.library.Utility;

public aspect DeprecatedMethodProvider {
    public String Utility.toOct(int number) {
        return Integer.toOctalString(number);
    }
}

如果您使用 AspectJ 编译器 Ajc 编译该项目,它就可以正常工作。在你的实际场景中,将切面编译成它自己的切面库,将编织代理 aspectjweaver.jar 放在 JVM 命令行中,以激活 LTW 并享受它如何编织class 在 class-loading.

期间通过字节码检测进入库 class 的方法

日志输出:

3333 in hexadecimal = d05
987 in octal = 1733

瞧瞧!享受。 :-)