确保一种方法';s调用堆栈在Java中始终包含另一个方法

确保一种方法';s调用堆栈在Java中始终包含另一个方法,java,static-analysis,call-hierarchy,Java,Static Analysis,Call Hierarchy,我在Java项目中使用的一个公共实用程序中有一个设计问题,我想确保特定方法a的所有调用者都被另一个方法B包装起来。正如我今天所写的代码的一般形式是: x.B(new Runnable() { y.A(); }); B正在执行的runnable可以有任意代码,并且可能多次调用A,因此我无法通过将对A的调用直接添加到B中来摆脱代码中的runnable。此外,A是第三方代码,因此我们无法修改它。runnable可能会再次调用B,并对A进行另一个嵌套调用,但今天这种情况从未发生过,所以我现在可

我在Java项目中使用的一个公共实用程序中有一个设计问题,我想确保特定方法a的所有调用者都被另一个方法B包装起来。正如我今天所写的代码的一般形式是:

x.B(new Runnable() {
    y.A();
});
B正在执行的runnable可以有任意代码,并且可能多次调用A,因此我无法通过将对A的调用直接添加到B中来摆脱代码中的runnable。此外,A是第三方代码,因此我们无法修改它。runnable可能会再次调用B,并对A进行另一个嵌套调用,但今天这种情况从未发生过,所以我现在可以忽略这种情况

我看到一些选择:

  • 声明
    A()抛出BlahException
    ,并使其成为该异常的唯一捕获者。这很难看,因为没有任何应该真正抛出的异常,但这很好,因为编译器将为我确保调用层次结构
  • 写一些静态分析工具来为我确保这个规则。我还没有对这个案例进行过太多的调查,因为这听起来比其他任何事情都需要更多的工作(但也许有一个预先存在的工具可以做到这一点?)
  • 在“A的开头”中添加一个断言(实际上,这段代码必须存在于Runnble的自定义版本中,因为我不能直接修改A),我们在对B的调用中运行。这可能会使用一些额外的线程/对象局部状态,也可能会遍历调用堆栈本身,两者都有点难看
  • 还有其他我没有考虑的选项吗?

    您考虑过使用面向方面编程(AOP)工具吗?然后您可以拦截方法
    A
    的每次调用,检查stacktrace中的方法
    B
    。如果它不在那里,您可以抛出异常来阻止执行
    A
    ,或者将错误写入日志,或者执行任何您喜欢的操作。大概是这样的:

    @Aspect
    public class CheckInsideMethodBAspect {
    
        @Around("execution(* com.example.AClass.A(..))")
        public void checkNestedMethod(ProceedingJoinPoint joinPoint) {
    
            // checking for method B in the call stack
            boolean isInsideB = false;
            StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
            for (StackTraceElement element: stackTraceElements) {
                if (element.getClassName().equals("ClassB") && element.getMethodName().equals("B")) {
                    isInsideB = true;
                    break;
                }
            }
    
            // if not inside B, throwing exception
            if (!isInsideB) {
                throw new NotInsideBException();
            }
    
            // if inside B, then proceeding with method A execution
            joinPoint.proceed();
        }
    
    }
    

    这在很大程度上是一种反模式。方法
    A
    不应该关心是谁调用它。为什么您认为需要这样做?您是否尝试使用代理模式?@KevinKrumwiede-可能
    A
    不知道其调用方,项目希望所有对
    A
    的调用都被包装。@gerty3000-其他选项:1)记录需求并执行代码审查,以及2)面向方面的编程。前者重量轻,但不能保证。后者似乎相当重。@KevinKrumwiede:同意;这是为了解决第三方代码中的一个bug(具体地说,是从本机代码回调到Java的JNA),该bug最终导致本机堆耗尽,我们的包装器将防止这种情况。如果抽象没有泄漏,我们就不在乎是谁在调用它。无论调用堆栈中方法
    B
    的要求背后的原因是什么,也就是说,无论方法
    B
    做了什么,这都是非常重要的,都可以通过这种方法直接实现,将
    B
    排除在外。