Cosa è richiesto per un ambito in un'infrastruttura di iniezione?

4

Lavorando con librerie come Seam, Guice e Spring, mi sono abituato a gestire variabili all'interno di un ambito. Queste librerie ti offrono una miriade di ambiti e ti consentono di definire la tua. Questo è un modello molto utile per gestire i cicli di vita variabili e l'iniezione delle dipendenze.

Ho cercato di identificare dove scope è la soluzione corretta o dove un'altra soluzione è più appropriata (variabile di contesto, singleton, ecc.). Ho scoperto che se il ciclo di vita dell'oscilloscopio non è ben definito, è molto difficile e spesso incline a gestire iniezioni in questo modo.

Ho cercato su questo argomento ma ho trovato poche discussioni sul modello. Esistono alcuni buoni articoli che discutono su dove utilizzare l'ambito e quali sono i prerequisiti / i prerequisiti suggeriti per l'ambito?

Sono interessato sia alla discussione di riferimento sia alla tua opinione su ciò che è richiesto o suggerito per una corretta implementazione dell'ambito. Tieni presente che mi riferisco all'ambito dell'ambito come idea generale, questo include cose come i singleton a livello globale, la variabile web con scope di richiesta o sessione, gli ambiti di conversazione e altri.

Modifica

Alcuni semplici sfondi su ambiti personalizzati: link

Alcune definizioni pertinenti in precedenza:

"scoping" - Un insieme di requisiti che definiscono quali oggetti vengono iniettati a che ora. Un semplice esempio di questo è l'ambito della discussione, basato su un ThreadLocal. Questo ambito inietterebbe una variabile basata su quale thread ha istanziato la classe. Ecco un esempio di questo:

"variabile di contesto" - Un repository passato da un oggetto a un altro contenente variabili rilevanti. Proprio come lo scoping, questo è un modo più bruto di accedere alle variabili in base al codice chiamante.

Esempio:

methodOne(Context context){
    methodTwo(context);
}

methodTwo(Context context){
    ...
    //same context as method one, if called from method one
}

"singleton globally scope" - Seguendo lo schema singleton, c'è un oggetto per istanza dell'applicazione. Questo vale per gli ambiti perché esiste un ciclo di vita di base per questo oggetto: c'è solo uno di questi oggetti istanziati.

Ecco un esempio di oggetto con scope Singleton JSR330:

@Singleton
public void SingletonExample{
...
}

utilizzo:

public class One {
     @Inject
     SingeltonExample example1;
}

public class Two {
     @Inject
     SingeltonExample example2;
}

Dopo l'istanziazione:

one.example1 == two.example2 //true;
    
posta John Ericksen 21.03.2012 - 17:25
fonte

2 risposte

2

Limita l'uso degli ambiti o più concretamente Usa gli ambiti per il solo cablaggio .

Se usato correttamente, gli ambiti possono ridurre molta piastra della caldaia di fabbrica. Io uso gli ambiti per collegare tra loro sottoprocessi che potrebbero aver bisogno di accedere al suo nome e ai suoi argomenti. È simile al RequestScope s fornito da Guice e Spring.

Ma gli scope sono effettivamente una mappa locale filettata di una stringa all'oggetto. Questo è praticamente un deposito di variabili globali. Questo è il motivo per cui limito l'utilizzo dell'ambito.

Questo porta al corollario:

Nascondi gli ambiti o più in generale Nascondi il tuo framework DI

Poiché gli ambiti (e framework DI) sono essenzialmente Depositi di variabili globali , preferisco incapsulare il DIF in modo tale che l'unica cosa che sa che esiste un DIF sia il metodo principale.

Per fare questo, definisco la mia interfaccia di ambito e il mio ProcessFactory che definisco all'interno di un modulo Guice.

Per questo motivo, il mio gestore dei processi è privo di riferimenti a Guice. In realtà, è poco più di questo metodo:

void run(final ProcessContext context) {
    try {
        this.scope.enter(context.processArgs());
        this.factory.create(args.processName());
    } finally {
        this.scope.exit();
    }
}

Ecco il modulo Guice completo che lega e nasconde il mio uso di un Guice. Implementa le mie interfacce ProcessScope e ProcessFactory. Lega @ProcessParameters in modo che possano essere iniettati e anche alcuni oggetti di convenienza (@ProcessName String e ProcessConfig).

public class ProcessRunnerModule extends AbstractModule {
    private static final String PROCESS_RUN_SCOPE = "ProcessRunScope";

    /**
     * Objects of type Map<String, Object> parameterized by @ProcessParameters
     * are injected in the ProcessRunScope
     */
    static final Key<Map<String, Object>> PROCESS_PARAMETERS_KEY;
    static {
        final TypeLiteral<Map<String, Object>> MAP_TYPE = new TypeLiteral<Map<String, Object>>() {
        };
        PROCESS_PARAMETERS_KEY = Key.get(MAP_TYPE, ProcessParameters.class);
    }


    /**
     * Wraps Guice scope to ProcessScope. Injects the @ProcessParameters into guice
     */
    private static class GuiceScopeAdapter implements ProcessScope {

        private final SimpleScope scope;

        @Inject @SuppressWarnings("unused")
        public GuiceScopeAdapter(@Named(PROCESS_RUN_SCOPE) SimpleScope scope) {
            this.scope = scope;
        }

        @Override
        public void enterScope(Map<String, Object> parameters) {
            scope.enter();
            scope.seed(PROCESS_PARAMETERS_KEY, parameters);
        }

        @Override
        public void exitScope() {
            scope.exit();
        }
    }

    /**
     * Processes are run and bound in @ProcessRunScope.
     */
    protected void configure() {
        final SimpleScope processRunScope = new SimpleScope();
        bindScope(ProcessRunScope.class, processRunScope);
        bind(SimpleScope.class)
            .annotatedWith(Names.named(PROCESS_RUN_SCOPE))
            .to(processRunScope);
    }

    /**
     * This wraps Processes bound via MapBinder to a ProcessFactory
     */
    @Provides @Singleton
    ProcessFactory createProcessFactory(final Map<String, Provider<Process>> processFactories) {
        log.info("Instantiating process factory", "known-processes", processFactories.keySet());
        return new ProcessFactory() {
            @Override
            public Process create(final String name) {
                return processFactories.get(name).get();
            }
        };
    }

    /**
     * ProcessRunner does not know about Guice
     */
    @Provides @Singleton
    ProcessRunner createProcessRunner(
            final ProcessScope processScope,
            final ProcessFactory processFactory) {

        return new ProcessRunner(processScope, processFactory);
    }


    /**
     * Convienience: bind a @ProcessName extracted from the @ProcessParameters
     */
    @Provides @ProcessName @ProcessRunScope
    String bindProcessName(final @ProcessParameters Map<String, Object> params) {
        return params.get(ProcessRunner.PROCESS_NAME_KEY).toString();
    }

    /**
     * Convienience: bind a ProcessConfig wrapping the @ProcessParameters
     */
    @Provides @ProcessRunScope
    ProcessConfig createParamHelper(final @ProcessParameters Map<String, Object> params) {
        return new ProcessConfig(params);
    }
}
    
risposta data 19.04.2012 - 21:51
fonte
0

Non iniettare oggetti con un ambito più piccolo dell'oggetto in cui lo si inietta. In tal caso, iniettare una fabbrica in modo che il dipendente possa gestire l'ambito.

Tutti gli altri ambiti possono essere gestiti tramite il framework DI. Il framework DI dovrebbe essere in grado di interrogare qualche oggetto o metodo che fornisci per vedere se dovrebbe ricreare nuovamente una nuova istanza oppure no.

In altre parole, un framework DI dovrebbe delegare una gestione complessa dell'ambito all'utente.

    
risposta data 12.05.2012 - 02:59
fonte

Leggi altre domande sui tag