使用Jersey 2.0,如何为每个请求注册一个可绑定实例?

时间:2013-07-01 00:41:26

标签: java dependency-injection jersey jersey-2.0

...如果需要手动构建实例,可能是第三方工厂类?以前,(泽西岛1.x),你会做这样的事情:

public class MyInjectableProvider extends PerRequestTypeInjectableProvider<Context, MyInjectable> {
    public MyInjectableProvider() {
        super(MyInjectable.class);
    }

    @Override
    public Injectable<MyInjectable> getInjectable(ComponentContext ic, Context context) {
        MyInjectable myInjectableInstance = //...

        return new Injectable<MyInjectable>() {
            @Override
            public MyInjectable getValue() {
                return myInjectableInstance;
            }
        };
    }
}

匿名本地类能够访问要在某个范围内返回的实例。当您不使用具有默认构造函数的类时,这很有用,但它们需要基于每个请求构建。

Jersey 2.0切换到HK2作为依赖注入框架,但是,迁移页面(https://jersey.java.net/documentation/latest/migration.html)没有提供这种绑定的示例,HK2文档没有提供使用示例一个AbstractBinder。

为了进一步阐述,我正在尝试向我的资源提供资源本地的,与容器无关的JPA EntityManager实例。这些必须从单件工厂类中获取,并且应该只针对单个“工作单元”,这在我的情况下是一个请求。我知道有一些解决方法(只是注入工厂,或绑定到threadlocal),但我发现以前的解决方案很优雅,如果可能的话我想重新创建它。

修改
在仔细研究了HK2 javadocs后,我发现类似的东西可以实现如下:

public class MyInjectableProvider extends AbstractBinder 
        implements Factory<MyInjectable> {
    @Override
    protected void configure() {
        bindFactory(this).to(MyInjectable.class);
    }

    @Override
    public MyInjectable provide() {
        return getMyInjectable();
    }

    @Override
    public void dispose(MyInjectable instance) {}
}

注册它......

public class MyResourceConfig extends ResourceConfig {
    public MyResourceConfig() {
        register(new MyInjectableProvider());
    }
}

这“似乎有效”,但似乎有点不清楚。例如,从不调用dispose()。此外,此绑定似乎隐含地表现为RequestScoped。将配置修改为bindFactory(this).to(MyInjectable.class).in(RequestScoped.class);似乎并未实际更改行为。我错过了什么,或者这是预期的解决方案?

1 个答案:

答案 0 :(得分:2)

代替Factory<T>.dispose(T),注册注射CloseableService可能会完成您想要的大部分工作。将需要CloseableFactory适配器。退出请求范围时,CloseableService closes()所有注册资源。

有关具体示例,请参阅下面的ConnectionFactory

import org.glassfish.hk2.api.Factory;
import org.glassfish.jersey.server.CloseableService;

import javax.inject.Inject;
import javax.ws.rs.InternalServerErrorException;
import java.sql.Connection;
import java.sql.SQLException;

import static com.google.common.base.Preconditions.checkNotNull;

public class ConnectionFactory implements Factory<Connection> {
    private final CloseableService closeableService;

    @Inject
    public ConnectionFactory(CloseableService closeableService) {
        this.closeableService = checkNotNull(closeableService);
    }

    public Connection provide() {
        final Connection connection;
        try {
            connection = acquireConnection();
        } catch (SQLException e) {
            throw new InternalServerErrorException(e);
        }
        try {
            closeableService.add(new CloseableConnection(connection));
        } catch (Throwable t) {
            closeQuietly(connection);
            throw runtime(t);
        }
        return connection;
    }

    public void dispose(Connection connection) {
        closeQuietly(connection);
    }

    private static RuntimeException runtime(Throwable t) {
        throw ConnectionFactory.<RuntimeException>unchecked(t);
    }

    private static <T extends Throwable> T unchecked(Throwable t) throws T {
        throw (T) t;
    }

    private static void closeQuietly(Connection connection) {
        try {
            connection.close();
        } catch (SQLException ignore) {}
    }
}

以下是CloseableFactory的一般性较低版本 - CloseableConnection

import java.io.Closeable;
import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;

import static com.google.common.base.Preconditions.checkNotNull;

public final class CloseableConnection implements Closeable {
    private final Connection connection;

    public CloseableConnection(Connection connection) {
        this.connection = checkNotNull(connection);
    }

    public void close() throws IOException {
        try {
            connection.close();
        } catch (SQLException e) {
            throw new IOException(e);
        }
    }
}