java - Utilizando Jersey 2.0, ¿cómo se registra una instancia enlazable por solicitud?
dependency-injection jersey-2.0 (1)
En lugar de Factory<T>.dispose(T)
, el registro con el Factory<T>.dispose(T)
puede hacer la mayoría de lo que desea. Se requerirá un adaptador CloseableFactory
. CloseableService
closes()
todos los recursos registrados al salir del alcance de la solicitud.
Para un ejemplo específico, vea la sección de ConnectionFactory
continuación.
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 continuación se muestra una versión menos general de un CloseableFactory
- un 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);
}
}
}
... si la instancia necesita construirse manualmente, tal vez por una clase de fábrica de terceros? Anteriormente, (Jersey 1.x), harías algo como esto:
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;
}
};
}
}
La clase local anónima puede acceder a una instancia para regresar dentro de algún alcance. Esto es útil cuando no está trabajando con clases que tienen constructores predeterminados, pero deben construirse para cada solicitud.
Jersey 2.0 cambió a HK2 como marco de inyección de dependencias, pero, por desgracia, la página de migración ( https://jersey.java.net/documentation/latest/migration.html ) no proporciona un ejemplo de este tipo de enlace, y La documentación HK2 no proporciona ejemplos utilizando un AbstractBinder.
Para elaborar un poco más, estoy tratando de proporcionar instancias JPA EntityManager de recursos locales e independientes del contenedor a mis recursos. Estos deben ser obtenidos de una clase de fábrica de singleton, y solo deben quedarse para una sola "unidad de trabajo", que es una solicitud en mi caso. Soy consciente de que existen soluciones alternativas (solo inyecte la fábrica o enlace a un threadlocal), pero la solución anterior me pareció elegante y me gustaría recrearla si es posible.
EDITAR:
Después de revisar un poco los javadocs HK2, descubrí que se puede lograr algo similar de la siguiente manera:
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) {}
}
Y para registrarlo ...
public class MyResourceConfig extends ResourceConfig {
public MyResourceConfig() {
register(new MyInjectableProvider());
}
}
Esto "parece funcionar", pero también parece poco claro. dispose () nunca se llama, por ejemplo. Además, este enlace parece comportarse implícitamente como RequestScoped. Modificación de la configuración a bindFactory(this).to(MyInjectable.class).in(RequestScoped.class);
no parece realmente cambiar el comportamiento ¿Me estoy perdiendo algo, o es esta la solución deseada?