2013-07-01 7 views
22

... se l'istanza deve essere costruita manualmente, forse da una classe di fabbrica di terze parti? In precedenza, (Jersey 1.x), si potrebbe fare qualcosa di simile:Utilizzo di Jersey 2.0, come si registra un'istanza associabile per richiesta?

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 classe locale anonimo è in grado di accedere a un'istanza di restituire entro un certo margine. Ciò è utile quando non lavori con classi che hanno costruttori predefiniti, ma devono essere costruiti su base di richiesta.

Jersey 2.0 passato a HK2 come framework di iniezione delle dipendenze, ma ahimè, la pagina di migrazione (https://jersey.java.net/documentation/latest/migration.html) non fornisce un esempio di questo tipo di associazione e la documentazione HK2 non fornisce esempi utilizzando un AbstractBinder.

Per elaborare solo un po 'di più, sto cercando di fornire istanze di JPA EntityManager locali, indipendenti dal contenitore e risorse alle mie risorse. Questi devono essere prelevati da una classe di fabbrica singleton e dovrebbero limitarsi a una sola "unità di lavoro", che è una richiesta nel mio caso. Sono consapevole che ci sono soluzioni alternative (Basta iniettare la fabbrica, o associare a un threadlocal), ma ho trovato la soluzione precedente elegante e vorrei ricrearla se possibile.

EDIT:
Dopo aver scavato attraverso le javadocs HK2 per un po ', ho scoperto che qualcosa di simile può essere raggiunto come segue:

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

E per registrarlo ...

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

Questo "sembra funzionare", ma sembra anche un po 'poco chiaro. dispose() non viene mai chiamato, ad esempio. Inoltre, questa associazione sembra comportarsi implicitamente come RequestScoped. La modifica della configurazione su bindFactory(this).to(MyInjectable.class).in(RequestScoped.class); non sembra in realtà modificare il comportamento. Mi manca qualcosa o è questa la soluzione prevista?

+0

Sto affrontando gli stessi identici problemi. Il tuo "EDIT" è stato molto utile. Grazie. Prima di leggere l'ultimo paragrafo del tuo post, anch'io ho scoperto che 'dispose' non viene mai chiamato dopo una richiesta. Sei riuscito a capire perché? – aioobe

+0

Ciao, quale framework DI stai usando per il tuo progetto? Perché posso fornirti un paio di esempi di come stiamo facendo le cose con guizzo e abbiamo anche fatto con la saldatura. Con guice, usa guice-persist, dove EntityManager è scope of Request. Con Weld abbiamo creato un provider di ambito di richiesta. Inseriamo i nostri repository nelle risorse JAX. Ogni repository, EM iniettato, per la risoluzione delle operazioni del database. –

+0

L'ambito predefinito è l'ambito della richiesta. Ovviamente, dichiararlo esplicitamente non lo cambia. Ad esempio, puoi dichiararlo singleton con "Singleton.class" o cosa non ti piace con questa soluzione? –

risposta

2

Al posto di Factory<T>.dispose(T), la registrazione con l'iniettabile CloseableService può fare la maggior parte di ciò che si desidera. Sarà richiesto un adattatore CloseableFactory. CloseableServicecloses() tutte le risorse registrate all'uscita dall'ambito della richiesta.

Per un esempio specifico, vedere ConnectionFactory di seguito.

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

Qui di seguito è una versione meno generale di 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); 
     } 
    } 
} 
Problemi correlati