使用 Jersey CDI 自动关闭数据库连接?

Close database connections automatically with Jersey CDI?

我想将实现 AutoClosable 的对象注入到 jersey 资源,并在请求结束时由框架自动关闭它。
基本上这是一个数据库连接提供程序,打开的连接应该在请求结束时关闭。
使用 @inject 注释时,对象不会关闭。
这可能吗?如何实现?

我认为您正在寻找 @Disposes 注释。

@Disposes 注释标识 disposer 方法 的处置参数,允许应用程序对 producer 返回的对象执行自定义清理方法生产者领域。作用域结束时自动调用 disposer 方法。


我不知道你的数据库连接提供者是什么样的,但这个例子会让你大致了解如何使用 @Disposes 注释:

public class Resources {

    @Produces
    @UserDatabase
    @RequestScoped
    @PersistenceContext
    private EntityManager em;

    public void close(@Disposes @UserDatabase EntityManager em) {
        em.close();
    }
}

您可以使用 CloseableService

The service may be used within the scope of a request to add instances of Closeable that are to be closed when the request goes out of scope, more specifically after the request has been processed and the response has been returned.

您可以将此服务注入到 Factory 中,您可以在其中创建您的提供商。只需要在Factory里面的CloseableService添加provider,请求结束就会关闭

public class ConnectionProviderFactory implements Factory<ConnectionProvider> {

    @Inject
    private CloseableService closeableService;

    @Override
    public ConnectionProvider provider() {
        ConnectionProvider provider = new ConnectionProvider();
        closeableService.add(provider);
        return provider;
    }

    @Override
    public void dispose(ConnectionProvider provider) {
    }
}

然后只需在您的活页夹中注册工厂

bindFactory(ConnectionProviderFactory.class)
        .to(ConnectionProvider.class)
        .in(RequestScoped.class);

请注意 Factory class 有一个 dispose 方法。我不是 100% 确定它是如何工作的细节,因为我有过这样的经历,有时 调用,有时不被调用。我发现 CloseableService 更可靠。也许有一天我会四处挖掘,尝试为自己解开 dispose 方法的谜团。


更新

不太确定您在做什么不同,但下面是使用 Jersey 测试框架的完整测试。 运行 它与任何其他 JUnit 测试一样。唯一的区别是我使用 Closeable

而不是 AutoCloseable
import java.io.Closeable;
import java.util.logging.Logger;
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.Response;
import org.glassfish.hk2.api.Factory;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.filter.LoggingFilter;
import org.glassfish.jersey.process.internal.RequestScoped;
import org.glassfish.jersey.server.CloseableService;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import static org.junit.Assert.assertEquals;
import org.junit.Test;

public class InjectCloseableTest extends JerseyTest {
    
    public static final String CONNECTION_MSG = "Connection";
    
    public static class ConnectionProvider implements Closeable {
        private static final Logger LOGGER
                = Logger.getLogger(ConnectionProvider.class.getName());
        
        public String getConnection() {
            return CONNECTION_MSG;
        }

        @Override
        public void close() {
            LOGGER.info("---- Closing Connection ----");
        }  
    }
    
    @Path("connection")
    public static class ConnectionResource {
        
        @Inject
        ConnectionProvider connectionProvider;
        
        @GET
        public String get() {
            return connectionProvider.getConnection();
        }
    }
    
    public static class ConnectionProviderFactory implements Factory<ConnectionProvider> {
        
        @Inject
        CloseableService closeableService;

        @Override
        public ConnectionProvider provide() {
            ConnectionProvider provider = new ConnectionProvider();
            closeableService.add(provider);
            return provider;
        }

        @Override
        public void dispose(ConnectionProvider t) {} 
    }
    
    @Override
    public ResourceConfig configure() {
        return new ResourceConfig(ConnectionResource.class)
                .register(new LoggingFilter(Logger.getAnonymousLogger(), true))
                .register(new AbstractBinder() {
                    @Override
                    protected void configure() {
                        bindFactory(ConnectionProviderFactory.class)
                                .to(ConnectionProvider.class)
                                .in(RequestScoped.class);
                    }
                });
    }
    
    @Test
    public void doit() {
        Response response = target("connection").request().get();
        assertEquals(200, response.getStatus());
        assertEquals(CONNECTION_MSG, response.readEntity(String.class));
        response.close();
    }
}

这是您需要 运行 它的唯一依赖项。

<dependency>
    <groupId>org.glassfish.jersey.test-framework.providers</groupId>
    <artifactId>jersey-test-framework-provider-grizzly2</artifactId>
    <version>${jersey2.version}</version>
    <scope>test</scope>
</dependency>