有没有办法在不触及它们定义的情况下拦截 CDI 中所有 bean 的所有方法?

is there a way to intercept all methods of all beans in CDI without touching their definition?

我想编写一个工具,能够拦截所有在使用 CDI(焊接)的应用程序期间调用的方法。

我已经尝试使用 CDI 拦截器,但是,由于我想让 bean 不知道拦截器,所以我不想使用拦截器绑定来观察方法调用。

有没有办法拦截满足此约束条件的调用?

奖金问题:考虑到这一愿景,是否有任何方法可以跟踪来自方法的级联调用?自上而下(检索从特定方法主体调用的所有其他方法)和自下而上策略(检索特定方法的调用方方法)都很好。

在不接触代码的情况下在任何(或所有)方法上添加拦截器的方式(我知道)是 CDI 可移植扩展。起初这听起来令人生畏,但实际上并非如此。您会在网上找到大量资源,但 high-level 概述 步骤是:

  • 创建扩展 class,一个实现 标记接口的简单 class javax.enterprise.inject.spi.Extension
  • /META-INF/services/javax.enterprise.inject.spi.Extension 下添加一个文件,其中包含实施扩展 class 的名称

完成设置!

现在有趣的部分是:扩展只是 CDI bean,其中包含 观察者方法,用于 CDI 本身在其初始化的各个阶段触发的一组事件。看here。其中许多事件提供了调整容器或 CDI 对 bean 的了解的方法。这个想法是你的方法 @Observes 这些事件并向所有 bean 添加拦截器注释 classes.

所以下一步,具体到你的问题是用你想要的逻辑和拦截器注释创建拦截器,我们称之为 @MyInterceptor。扩展 class 的一个非常简单的实现是:

// There is a little ceremony for the annotation...
import javax.enterprise.util.AnnotationLiteral;

public class MyInterceptorLiteral extends AnnotationLiteral<MyInterceptor>
  implements MyInterceptor {
  // nothing more needed
}
// WARNING DEMO CODE, UNTESTED & SEE BELOW FOR POSSIBLE GOTCHAS
public class MyInterceptorExtension implements Extension {
  public void onProcessAnnotatedType(@Observes ProcessAnnotatedType<?> event) {
    event.configureAnnotatedType().add(new MyInterceptorLiteral());
  }
}

这可能需要一些调整,但原则是在 CDI 发现的所有 bean classes 上添加拦截器注释。我不知道这是否捕获生产者 methods/fields 生成的 bean 实例,这些实例可能更难处理。

至于跟踪调用级联,这是您的拦截器的工作。不是微不足道的,但有很多方法(例如 ThreadLocal 上下文对象)。

您所要求的与 Weld 对其监控所有 bean 的 Probe 扩展所做的非常相似 - 在一定程度上是可能的,但并非完全如此。 我的意思是,您可以为任何基于 class 的 bean 执行此操作,但不能为生产者 (method/field) 执行此操作,因为根据 CDI 定义,它们不能以标准方式被拦截或修饰。实现这一目标的唯一方法是通过 interception factory,这需要一些特殊的调整。

至于如何做到这一点,这里是您需要的几个基本步骤:

  1. 创建具有所需逻辑的已启用拦截器class
  2. 创建一个拦截器绑定(例如,MyBinding 为了这个答案)并将其放在拦截器上
  3. 创建一个 CDI 扩展,将此拦截器绑定添加到您要拦截的所有 bean。这可以在扩展生命周期的几个点上完成,但我建议查看 ProcessBeanAttributes<?>,您可以在其中配置属性并将 MyBinding 拦截器绑定添加到每个 bean。
  4. 不要忘记通过 META-INF 条目启用扩展。

您可以查看前面提到的 Weld's Probe 扩展,但请注意它做的事情更多,因此它会比您在此问题中要求的更复杂。以下是一些可能有帮助的链接:

  • Probe extension class
  • Interceptor class,请注意 class 是 @Vetoed 而你的不应该是!由于集成商的可选启用,Weld 已被否决。
  • MonitoredComponent,在这种情况下,它是一个构造型(然后包含绑定),它通过扩展添加到所有 bean。但是你应该可以直接添加绑定。

最后但同样重要的是,您需要注意一个大问题。向 all bean 添加拦截意味着您可能会尝试将其添加到某些不可代理的 bean(这是拦截要求)。这会导致错误。简而言之,final 方法和 final classes 会导致问题,但请参阅 this specification part 了解更多信息。 这也是 ProbeExtension 进行一些特殊检查以避免这种情况的原因。查看 this code 寻找灵感。

however, since I want to keep the beans unawares of the interceptor

我应该注意,使用这种方法或任何其他方法,bean 元数据(Bean<?> 对象)将包含有关添加的绑定的信息,因此“可见”bean 已被调整。