我所处的情况是,我们正在使用的库的实现比我们的依赖项之一的实现更新。例如。依赖项使用 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
假设您正在谈论独立于 Spring 的第 3 方库,您不能将 Spring AOP 与其基于代理的“AOP lite”方法一起使用,该方法仅适用于 Spring 组件的公共非静态方法。请改用更强大的 AspectJ。 Spring 手册 解释了如何将带有加载时编织 (LTW) 的完整 AspectJ 集成到 Spring 应用程序中。如果你的应用程序到目前为止还没有基于 Spring,而你只是想使用 Spring AOP 的框架,你可以完全跳过整个 Spring 的东西,使用普通的 AspectJ。
您要使用的功能是类型间声明 (ITD),更具体地说是 AspectJ 为现有类声明方法的能力。这是一些示例代码:
第三方库:
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 方库:
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
在你自己的项目使用的版本中已经不存在了,你会在运行时调用NoSuchMethodError
时得到MyDependency.doSomethingWith
。
您自己的申请:
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 并享受它如何通过字节码检测将方法编织到库类中类加载。
日志输出:
3333 in hexadecimal = d05
987 in octal = 1733
瞧瞧!享受。
:-)
当 JVM 加载一个类时,它会在“链接器”阶段解析所有依赖项:外部类、属性和方法。你不能在你的案例中通过这个阶段,因为缺少方法。
(Spring-)AOP有两种模式:代理和编织。