22

...如果需要手动构建实例,也许是由第 3 方工厂类?以前,(Jersey 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 实例。这些必须从单例工厂类中获取,并且应该只保留一个“工作单元”,这在我的情况下是一个请求。我知道有一些解决方法(只需注入工厂,或绑定到线程本地),但我发现以前的解决方案很优雅,如果可能的话想重新创建它。

编辑:
通过 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);似乎并没有真正改变行为。我错过了什么,还是这是预期的解决方案?

4

1 回答 1

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

下面是 a CloseableFactory- a的一个不太通用的版本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);
        }
    }
}
于 2013-11-25T18:32:25.953 回答