从CDI代理获取真实对象

3

我正在寻找一种干净的CDI解决方案,而不是依赖于WELD的解决方案,但到目前为止还没有找到...

我需要测试通过@Inject @Any MyInterface bean获取的对象列表中的每个元素是否是代理对象,如果是,则需要获取真实对象进行内省,并获取对象的所有属性。

我的WELD实现:

MyInterface interf = obj;
if (isProxy(interf )) {
        interf = (Config) ((TargetInstanceProxy)interf ).getTargetInstance();
}

在这里,isProxy被定义为(CDI解决方案?):

public boolean isProxy(Object obj) {
    try{
        return Class.forName("org.jboss.weld.bean.proxy.ProxyObject").isInstance(obj);
    } catch (Exception e) {
        LOGGER.error("Unable to check if object is proxy", e);
    }
    return false;
}

有什么建议/提示吗?在官方文档中,我没有找到关于内省的提及(这里)。

然后,我想通过类似以下方式获取bean的所有属性:

Arrays.stream(interf.getClass().getDeclaredFields()).forEach(
                        field -> extractStuff(...)
                );

我们使用Wildfly和WELD,但不想将自己绑定在CDI的实现上。
编辑: 更准确地说,问题是:您是否知道一种干净的CDI解决方案,WELD已经使用TargetInstanceProxy实现了?而不是我需要回到学校或者我是否理解我所写的内容。感谢您花时间帮助!

有一件事突然出现在我脑海中:为什么你不能重构你的遗留代码,以正确的方式重新编写呢?而且为什么你需要这些测试呢?通常情况下,并不需要这样做。 - Roland
因为他们不想 :( 对你们来说,随心所欲地处理公司的代码总是这么容易吗? - Paolof76
我被踩了一下,是因为什么原因呢?如果人们对赞成票不满意,他们在这里就会踩票吗? - Paolof76
3个回答

9
CDI有意隐藏(或者说不暴露)内部细节,因为当面向接口编程时,这些细节对最终用户来说并不重要。此外,擅自更改可能会导致奇怪的错误,因为您应该始终通过代理调用方法,而不是实际实例。
所以简短的答案是 - 不,没有纯CDI的方法来做到这一点。(至少没有预期的方法。)
然而,看到您已经在使用Weld,还有其他方法。除了TomEE之外,几乎所有EE服务器都配备了Weld,因此依赖Weld API应该是相当安全的。 现在为什么我这么说 - 在Weld 3.x(WildFly 12+)中,API被扩展以包含WeldConstructWeldClientProxy,它们是由Weld子类(拦截器/装饰器)和/或客户端代理实现的接口 - 有关这些类的更多信息,请参见javadoc。
如果你必须这样做,那么你可以添加对Weld API的依赖,如下所示:
<dependency>
  <groupId>org.jboss.weld</groupId>
  <artifactId>weld-api</artifactId>
  <version>x.y.z</version>
</dependency>

然后,在您的代码中,您可以通过以下方式检查注入的对象是否为代理:

@Inject
Foo foo;

public void doSomething() {
  if (foo instanceof WeldClientProxy) {
    // foo is a proxy
  } else {
    // not a proxy
  }
}

如果您想获得实际实例,WeldClientProxy允许您获取Metadata, 从中您可以检索到底层上下文实例。这是我能为您提供的最接近您所需的内容。

谢谢,是的,我明白你的观点...他们不想直接使用焊接,但还是谢谢你给了我很好的理由。附言:你说获取包装在代理中的内部类并不重要。但也许并非如此...为什么Weld开发人员会花时间提供这个可能性呢?CDI缺乏反射工具,就像Introspector.getBeanInfo一样... - Paolof76
嗯,我们已经看到过一些情况,大多数集成商(例如WildFly、Payara等)或其他EE技术与我们集成(如RestEasy),他们确实需要获取上下文实例 - 因此这个API应运而生,以避免他们对真正的内部Weld类进行修改。对于最终用户来说,我们很少见到不需要这样做的情况,因为这样会更加“干净”。话虽如此,肯定总会有一些特殊情况 :) - Siliarus
1
哦,我刚想起来,很久以前有一个关于你所需的CDI要求,但从未实现,请参见https://issues.jboss.org/browse/CDI-10。 - Siliarus
嘿 @Siliarus,它重新开放了,也许他们会在2.1版本中实现它 :) 这是一个有理由暂时使用实现(weld)的另一个原因! - Paolof76

4

一个常见的选项是:

  1. 获取您想要解封的实例的Bean
  2. 获取它的作用域 (bean.getScope())
  3. 从bean管理器(可注入)中获取与作用域相关联的上下文
  4. 使用 context.get(bean) 从CDI上下文中获取已解封的实例(有一些情况下根本无法获取)。

0
// test is weld proxy
if (service.getClass().getSimpleName().endsWith("WeldClientProxy")) {
    service = getWeldTargetInstance(service);
}
private static Object getWeldTargetInstance(Object proxy) throws NoSuchMethodException, SecurityException,
        IllegalAccessException, IllegalArgumentException, InvocationTargetException, ClassNotFoundException {
    Class<?> targetInstanceProxy=Class.forName("org.jboss.weld.interceptor.util.proxy.TargetInstanceProxy"),
        clazz=proxy.getClass(); 
    boolean isAssignableFrom=targetInstanceProxy.isAssignableFrom(clazz);
    if(isAssignableFrom) { 
        Method method = clazz.getDeclaredMethod("getTargetInstance");
        Object instance = method.invoke(proxy);
        return instance;
    }else {
        throw new IllegalArgumentException();
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接