如何在 REST 调用之前初始化注入值?

How to initialize injected value before REST call?

我有一个依赖于与 HBase 的重量级连接的 JAX-RS 应用程序(运行 Jersey 2 on Tomcat)。我想在我的多个资源应用程序中初始化和重用该连接。我已经设置了一个将连接绑定为单例的活页夹,并使用@Inject 注释将该连接注入到我的资源中。但是,由于在第一次调用服务之前不会发生注入,因此直到那时才初始化连接。

应用程序:

public class MyApplication extends ResourceConfig {

    public MyApplication() {
        super(MyResource.class);
        register(new HbaseBinder());
    }
}

活页夹:

public class HbaseBinder extends AbstractBinder {

    @Override
    protected void configure() {
        bindAsContract(HbaseConnection.class).in(Singleton.class);
        bind(new HbaseConnection()).to(HbaseConnection.class);
    }
}

注入:

@Path("/myResource")
public class MyResource {

    @Inject
    private HbaseConnection hbaseConnection;

    ...
}

HBase 连接:

@Singleton
public class HbaseConnection {    
    public Connection getConnection() throws IOException {
        ...
    }
    ...
}

我想做的是在应用程序部署时初始化 Singleton,以便它准备好继续对服务的第一次调用。执行此操作的正确方法是什么?

您的 HbaseConnection 被初始化了两次。

  • 首先,它会在您的活页夹中初始化(假设您的 MyApplication class 在您的 web.xml 中注册为 init-param
  • 然后,当HK2检测到@Singleton注解(或执行bindAsContract指令时)再次初始化

因此,要使您的示例有效,您需要:

  1. 从您的 HbaseConnection class 中删除 @Singleton 注释。
  2. 从您的 HBaseBinder class
  3. 中删除行 bindAsContract(HbaseConnection.class).in(Singleton.class);

MyApplication 的初始化发生在 servlet 容器启动期间(因此 HbaseConnection 的构造),这正是您想要的,对吗?

提供实例(在活页夹中)的是您,而不是 HK2。这就是为什么您不想通过使用 @Singleton 来创建实例来指示它。

活页夹应该是:

public class HbaseBinder extends AbstractBinder {

    @Override
    protected void configure() {
        // just bind is ok
        bind(new HbaseConnection()).to(HbaseConnection.class);
    }
}

HBase 连接:

// do not annotate with `@Singleton`!
public class HbaseConnection {    
    public Connection getConnection() throws IOException {
        ...
    }
    ...
}

感谢您的所有评论和回答。需要以上各项的组合。

部分问题是我使用 @PostConstruct 调用 HbaseConnection.getConnection() 方法时出现未经检查的异常。一旦我摆脱了它,并切换到 Immediate 范围,class 似乎被适当地加载了。这是我的最终解决方案:

public class MyApplication extends ResourceConfig {

    @Inject
    public MyApplication(ServiceLocator locator) {
        super(MyResource.class);
        register(new HbaseBinder());
        ServiceLocatorUtilities.enableImmediateScope(locator);
    }
}

@Path("/myResource")
public class MyResource {    

    @Inject
    private HbaseConnection hbaseConnection;

    ...
}

public class HbaseBinder extends AbstractBinder {

    @Override
    protected void configure() {
        bindAsContract(HbaseConnection.class).in(Immediate.class);
    }
}

@Immediate
public class HbaseConnection {

    @PostConstruct
    public void postConstruct() {
        // Call getConnection(), wrapped in try/catch.
    }

    public Connection getConnection() throws IOException {
        // Get the connection.
    }

    @PreDestroy
    public void preDestroy() {
        // Call cleanup(), wrapped in try/catch.
    }

    public void cleanup() throws IOException {
        // Close/cleanup the connection
    }
}

现在我唯一的问题是,看起来有些 Threads/ThreadLocals 正在取消部署,但这一定是我正在使用的库中的错误,因为我无法控制它们的生命周期。

12-Feb-2016 14:01:45.054 INFO [ContainerBackgroundProcessor[StandardEngine[Catalina]]] org.apache.catalina.startup.HostConfig.undeploy Undeploying context [/my-resource]
12-Feb-2016 14:01:46.129 WARNING [ContainerBackgroundProcessor[StandardEngine[Catalina]]] org.apache.catalina.loader.WebappClassLoaderBase.clearReferencesThreads The web application [my-resource] appears to have started a thread named [ImmediateThread-1455303641406] but has failed to stop it. This is very likely to create a memory leak. Stack trace of thread:
 sun.misc.Unsafe.park(Native Method)
 java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)
 java.util.concurrent.SynchronousQueue$TransferQueue.awaitFulfill(SynchronousQueue.java:764)
 java.util.concurrent.SynchronousQueue$TransferQueue.transfer(SynchronousQueue.java:695)
 java.util.concurrent.SynchronousQueue.poll(SynchronousQueue.java:941)
 java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1066)
 java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1127)
 java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
 java.lang.Thread.run(Thread.java:745)
12-Feb-2016 14:01:46.130 WARNING [ContainerBackgroundProcessor[StandardEngine[Catalina]]] org.apache.catalina.loader.WebappClassLoaderBase.clearReferencesThreads The web application [my-resource] appears to have started a thread named [Thread-5] but has failed to stop it. This is very likely to create a memory leak. Stack trace of thread:
 sun.net.dns.ResolverConfigurationImpl.notifyAddrChange0(Native Method)
 sun.net.dns.ResolverConfigurationImpl$AddressChangeListener.run(ResolverConfigurationImpl.java:144)
12-Feb-2016 14:01:46.134 SEVERE [ContainerBackgroundProcessor[StandardEngine[Catalina]]] org.apache.catalina.loader.WebappClassLoaderBase.checkThreadLocalMapForLeaks The web application [my-resource] created a ThreadLocal with key of type [org.apache.htrace.core.Tracer.ThreadLocalContext] (value [org.apache.htrace.core.Tracer$ThreadLocalContext@2c25bbe0]) and a value of type [org.apache.htrace.core.Tracer.ThreadContext] (value [org.apache.htrace.core.Tracer$ThreadContext@787153ae]) but failed to remove it when the web application was stopped. Threads are going to be renewed over time to try and avoid a probable memory leak.
12-Feb-2016 14:01:46.135 SEVERE [ContainerBackgroundProcessor[StandardEngine[Catalina]]] org.apache.catalina.loader.WebappClassLoaderBase.checkThreadLocalMapForLeaks The web application [my-resource] created a ThreadLocal with key of type [org.apache.hadoop.io.Text] (value [org.apache.hadoop.io.Text@1badb836]) and a value of type [sun.nio.cs.UTF_8.Encoder] (value [sun.nio.cs.UTF_8$Encoder@13652d32]) but failed to remove it when the web application was stopped. Threads are going to be renewed over time to try and avoid a probable memory leak.
12-Feb-2016 14:01:46.136 SEVERE [ContainerBackgroundProcessor[StandardEngine[Catalina]]] org.apache.catalina.loader.WebappClassLoaderBase.checkThreadLocalMapForLeaks The web application [my-resource] created a ThreadLocal with key of type [org.apache.hadoop.hdfs.DFSUtil] (value [org.apache.hadoop.hdfs.DFSUtil@6080a3db]) and a value of type [java.util.Random] (value [java.util.Random@169acbf5]) but failed to remove it when the web application was stopped. Threads are going to be renewed over time to try and avoid a probable memory leak.
12-Feb-2016 14:03:02.383 INFO [Thread-7] org.apache.catalina.loader.WebappClassLoaderBase.checkStateForResourceLoading Illegal access: this web application instance has been stopped already. Could not load [org.apache.hadoop.util.ShutdownHookManager]. The following stack trace is thrown for debugging purposes as well as to attempt to terminate the thread which caused the illegal access.
 java.lang.IllegalStateException: Illegal access: this web application instance has been stopped already. Could not load [org.apache.hadoop.util.ShutdownHookManager]. The following stack trace is thrown for debugging purposes as well as to attempt to terminate the thread which caused the illegal access.
    at org.apache.catalina.loader.WebappClassLoaderBase.checkStateForResourceLoading(WebappClassLoaderBase.java:1353)
    at org.apache.catalina.loader.WebappClassLoaderBase.checkStateForClassLoading(WebappClassLoaderBase.java:1341)
    at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1206)
    at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1167)
    at org.apache.hadoop.util.ShutdownHookManager.getShutdownHooksInOrder(ShutdownHookManager.java:124)
    at org.apache.hadoop.util.ShutdownHookManager.run(ShutdownHookManager.java:52)

12-Feb-2016 14:03:02.383 INFO [Thread-4] org.apache.coyote.AbstractProtocol.pause Pausing ProtocolHandler ["http-apr-8080"]
12-Feb-2016 14:03:02.449 INFO [Thread-4] org.apache.coyote.AbstractProtocol.pause Pausing ProtocolHandler ["ajp-apr-8009"]
12-Feb-2016 14:03:02.500 INFO [Thread-4] org.apache.catalina.core.StandardService.stopInternal Stopping service Catalina
12-Feb-2016 14:03:02.530 INFO [Thread-4] org.apache.coyote.AbstractProtocol.stop Stopping ProtocolHandler ["http-apr-8080"]
12-Feb-2016 14:03:02.581 INFO [Thread-4] org.apache.coyote.AbstractProtocol.stop Stopping ProtocolHandler ["ajp-apr-8009"]