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

时间:2016-02-11 16:27:09

标签: web-services rest dependency-injection jersey jax-rs

我有一个JAX-RS应用程序(在Tomcat上运行Jersey 2),它依赖于与HBase的重量级连接。我希望在整个应用程序中初始化并重用该连接以获得多种资源。我已经设置了一个Binder,它将连接绑定为Singleton,并使用@Inject annotaton将该连接注入到我的资源中。但是,由于注入在第一次调用服务之前不会发生,因此在此之前不会初始化连接。

申请表:

public class MyApplication extends ResourceConfig {

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

The Binder:

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,以便它可以第一次调用该服务。这样做的正确方法是什么?

2 个答案:

答案 0 :(得分:1)

Thank you for all of the comments and answers. A combination of the above were required.

Part of the issue was that I was using the the @PostConstruct to call the HbaseConnection.getConnection() method had an unchecked exception. Once I got rid of that, and switch to the Immediate scope, the class seems to be loaded appropriately. Here's my final solution:

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
    }
}

Now my only problem is that it looks like some Threads/ThreadLocals are being left around on undeployment, but this has to be bugs in the libraries that I'm using as I have no control over their lifecycle.

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$1] (value [org.apache.hadoop.io.Text$1@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$1] (value [org.apache.hadoop.hdfs.DFSUtil$1@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$2]. 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$2]. 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$1.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"]

答案 1 :(得分:0)

您的HbaseConnection初始化两次。

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

因此,要使您的示例正常工作,您需要:

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

    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 {
            ...
        }
        ...
    }