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

时间:2016-02-26 20:30:15

标签: aop spring-aop

我正处于这样一种情况,即我们使用的库的实现比我们的一个依赖项被编码的实现更新。例如。依赖使用MyLibrary-1.0,我们正在使用MyLibrary-2.0。

在较新的实现中,已删除已弃用的方法,这会导致我们遇到运行时错误。

我正在尝试使用AOP(特定于Spring-AOP)拦截对丢失方法的调用,并将它们代理到现有方法中...但我似乎无法使Aspect正确。

在我的Aspect有机会拦截之前,感觉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

2 个答案:

答案 0 :(得分:2)

假设您正在讨论独立于Spring的第三方库,您不能将Spring AOP与其基于代理的“AOP lite”方法一起使用,该方法仅适用于Spring组件的公共非静态方法。请使用更强大的AspectJ。 Spring manual解释了如何将完整的AspectJ与加载时编织(LTW)集成到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);
    }
    */
}

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

依赖于旧版第三方库的项目依赖性:

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来救援!我们只是将缺少的方法添加到第三方库。

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

Etvoilà!请享用。 :-)

答案 1 :(得分:1)

当JVM加载一个类时,它会解析“链接器”阶段中的所有依赖项:外部类,属性和方法。你不能在你的情况下通过这个阶段,因为缺少方法。

(Spring-)AOP有两种模式:代理和编织。

  1. 代理创建...围绕类的代理:目标类必须存在并加载
  2. 编织可以在加载类之前发生:当类加载器加载一个类时,将一个byte []数组传递给编织器,编织器可以在类真正具体化之前操作类字节码。这种类型的aop 可以在您的情况下工作。但是,这不是一件容易的事。