【问题标题】:Can't get @Singleton to do anything无法让@Singleton 做任何事情
【发布时间】:2012-11-29 10:06:42
【问题描述】:

在 JBoss AS7 上运行,我有这个:

import javax.inject.Singleton;

@Singleton
public class Connections {
    private final List<AtmosphereResource> connections = new ArrayList<AtmosphereResource>();

    public void add(AtmosphereResource event) {
        connections.add(event);
    }
}

还有这个:

import javax.inject.Inject;

public class PubSubAtmosphereHandler extends AbstractReflectorAtmosphereHandler {
    @Inject
    private Connections connections;

    @Override
    public void onRequest(AtmosphereResource event) throws IOException {
        [...]
        connections.add(event); // <---
}

NPE 在指定行上。在阅读了无数页和示例之后,这是重复数十次但不起作用的方法之一。我有空的 beans.xml,放在我的 WEB-INF 中。我在这里错过了什么?

【问题讨论】:

  • pubsubhandler 是否由容器管理?
  • 你是在问它是否用 @ManagedBean 装饰?如果是这样,我尝试了有和没有都无济于事。如果你的意思是别的,你必须详细说明一下。
  • 我不确定,这发生在 Atmosphere 库中。我只在大气.xml 中指定该类,然后请求进来。对于它的价值,原始请求是一个打开comet/websocket 连接的GET http 请求,而这个类是接受该请求和文件的类将其用于以后的服务器推送。
  • 问题很可能是处理程序不是容器管理的,所以不会发生注入。
  • 有没有办法解决这个问题?我不介意拥有某种代理类。还是处理程序的初始实例化形式就像毒树一样,我在那里所做的一切都会遇到同样的问题?

标签: java jakarta-ee singleton jboss7.x cdi


【解决方案1】:

经过一些研究,事实证明 Atmosphere 为这种功能提供了(目前已损坏的)钩子。这意味着 IS 可以简单地使用您的常规注释并使其与 Atmosphere 一起使用,尽管是“外部”实例化。

如果您知道您的代码将部署在哪里,您可以简单地覆盖 Atmosphere 中的默认 noop InjectorProvider 类,方法是在同一个包中拥有一个同名的类并让它提供正确的 Injector。

我在这个答案的末尾添加了 JBoss AS7 的代码,以及应该在 Google Guice 和 Spring 上工作的代码的链接,我自己没有测试过。

如果您需要您的代码在多个平台上工作,您可能需要弄清楚如何检测您正在运行的内容,然后返回适当的 Injector。由于我对 Guice 和 Spring 不是很熟悉,所以我将把这个练习留给读者。

JBoss AS7 的脏“初稿”代码(请记住,这必须进入声明的包中以覆盖默认的 noop 提供程序):

package org.atmosphere.di;

import java.util.NoSuchElementException;
import java.util.ServiceLoader;

import javax.enterprise.context.spi.CreationalContext;
import javax.enterprise.inject.spi.BeanManager;
import javax.enterprise.inject.spi.InjectionTarget;
import javax.naming.InitialContext;
import javax.naming.NamingException;

public class InjectorProvider {

    private InjectorProvider() {}

    public static Injector getInjector() {
        return LazyProvider.INJECTOR;
    }

    private static final class LazyProvider {
        private static final Injector   INJECTOR;

        static {
            Injector injector = new Injector() {
                @Override public void inject(final Object o) {
                    try {
                        final BeanManager bm = (BeanManager) new InitialContext().lookup("java:comp/BeanManager");
                        final CreationalContext cc = bm.createCreationalContext(null);
                        final InjectionTarget it = bm.createInjectionTarget(bm.createAnnotatedType(o.getClass()));
                        it.inject(o, cc);
                        cc.release();
                    } catch (final NamingException e) {
                        e.printStackTrace();
                    }
                }
            };
            try {
                injector = ServiceLoader.load(Injector.class).iterator().next();
            } catch (final NoSuchElementException e) {}
            INJECTOR = injector;
        }
    }
}

请注意,包装代码取自 Atmosphere 代码库,这是在 Java 中执行 Singletons 的一种非常糟糕的方式。您可能不想在生产中使用这种“原样”。

Guice 注射器,未经测试:https://github.com/Atmosphere/atmosphere/blob/atmosphere-1.0.x/extras/guice/src/main/java/org/atmosphere/guice/GuiceInjector.java

弹簧喷油器,未经测试:https://github.com/Atmosphere/atmosphere/blob/atmosphere-1.0.x/extras/spring/src/main/java/org/atmosphere/spring/SpringInjector.java

【讨论】:

  • 这个解决方案可能适用于特定的气氛版本?因为我查过,目前的大气没有 org.atmosphere.di 包。
  • 由于最初的问题发生在 2013 年,我无法告诉你 Atmosphere 从那时起的发展方向。在撰写本文时,对此的支持非常简单,并且如前所述,有一个错误使其无法开箱即用。由于 DI 框架(如果有的话)已经流行起来,我确信在这个时代,有更好的开箱即用的解决方案。
【解决方案2】:

也许可以使用 jndi 获取 BeanManager。然后你就可以从那里得到豆子了。

BeanManager bm = initialContext.lookup("java:comp/BeanManager");
Bean<Connections> bean = (Bean<Connections>) bm.getBeans(Connections.class).iterator().next();
CreationalContext<Connections> ctx = bm.createCreationalContext(bean);

Connections connections = (Connections) bm.getReference(bean, Connections.class, ctx);
connections.add(event);

【讨论】:

  • 你建议如何在这里获得initialContext?也许BeanManager 也可以使用此代码访问? BeanManager bm = CDI.current().getBeanManager();
【解决方案3】:

其他人想说的是,PubSubAtmosphereHandler 的生命周期必须由容器(又名 JBoss)控制。

换句话说,容器负责创建和初始化 PubSubAtmosphereHandler 的实例。

如果您或您的框架创建此对象,则不会进行注入。

【讨论】:

  • 那么这是否意味着 Atmosphere 框架根本无法用于注入?
猜你喜欢
  • 2011-04-26
  • 1970-01-01
  • 1970-01-01
  • 2019-06-18
  • 2014-02-09
  • 1970-01-01
  • 2020-10-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多