2013-08-29 1 views
9

Ho un'applicazione a riga di comando che utilizza un bean gestito Primavera-che è composto da un java ExecutorService creata con:Come arrestare correttamente i servizi di esecuzione con Spring?

ExecutorService service = Executors.newFixedThreadPool(4); 

Ora, voglio il mio servizio di arresto quando la mia applicazione si chiude, così ho fatto la mia fagioli implementare l'interfaccia DisposableBean ed avere un metodo di distruggere tali come:

public void destroy(){ 
    service.shutdown(); 
} 

allora potrei essere tentato di fare qualcosa di simile registrare un gancio di arresto sul contesto primavera. Tuttavia ho scoperto (nel modo più duro, cioè in una versione pre-produzione) che questo non funziona: il gancio di shutdown non viene chiamato prima che venga chiamato il metodo ExecutorService.shutdown(), causando un classico problema di catch 22 (si ottiene chiamato su interruzione, cioè, se premo Ctrl-C mentre l'applicazione è in esecuzione). Questo è sfuggito ai miei test unitari perché per qualche motivo sembra funzionare bene all'interno di JUnit, il che mi sta ancora sconcertando: cosa fa JUnit in modo diverso?

La soluzione che ho trovato finora è chiamare esplicitamente ApplicationContext.close() appena prima di uscire dalla mia funzione principale. Mi chiedevo se ci fosse una soluzione migliore a questo e quali sono le migliori pratiche per avere pool di thread flessibili gestiti da Spring. Inoltre, cosa succede se il mio bean è non gestito direttamente da Spring ma è creato da un bean gestito da Spring? Devo effettuare una cascata delle chiamate a destroy()? Non sarebbe molto incline agli errori?

Apprezzo qualsiasi commento, suggerimento, ulteriore lettura, RTFM, ricette magiche.

Grazie!

+0

PS: cosa succede se voglio spostare la mia applicazione da riga di comando su un server di app come Tomcat? Qualcosa cambia? –

+0

Includendo il titolo e il tuo PS, contiamo * sette * (7!) Punti interrogativi. :-) Potrebbe ottenere migliori risposte se chiedi solo una domanda specifica. – Keith

risposta

24

Sei consapevole che questo:

ExecutorService service = Executors.newFixedThreadPool(4); 

possono essere sostituiti con questo:

<bean id="service" class="java.util.concurrent.Executors" 
     factory-method="newFixedThreadPool" destroy-method="shutdown"> 
    <constructor-arg value="4"/> 
</bean> 

Il contesto primavera gestisce poi, in modo più diretto, l'arresto del servizio esecutore - e si può essere riutilizzato più facilmente.

+0

Ne sono consapevole. Tuttavia, in alcuni casi preferisco creare i miei bean a livello di codice piuttosto che attraverso la configurazione, ad esempio, se non saprò quanti bean avrò bisogno fino al runtime o la definizione di un determinato bean verrà da una fonte esterna, ad esempio, database o coda dei messaggi. –

+0

Conta il mio up-down, non valido. Questo non funziona ma restituisce "InvalidArgumentException" durante l'inizializzazione del contesto Spring. – Powerslave

1

Prendere in considerazione l'utilizzo di TaskExecutor di Spring, che può essere configurato con un pool di thread. http://static.springsource.org/spring/docs/3.0.x/reference/scheduling.html

+0

Non penso che si adatti al mio caso d'uso. Ho un sacco di sincronizzazione tra le attività che deve essere eseguita (ad esempio, barriere) e preferisco avere più controllo su di esso esplicitamente utilizzando gli esecutori e le attività personalizzate. –

1

Per documentazione ufficiale primavera, quando si utilizza la configurazione di annotazione a base, per destroyMethod campo della @Bean, il comportamento predefinito di primavera è quello di richiamare automaticamente, i metodi no-arg pubblici denominati close o shutdown quando il contesto applicazione viene chiusa.

Per comodità per l'utente, il contenitore tenterà di dedurre un metodo destroy contro un oggetto restituito dal metodo @Bean. Per l'esempio , dato un metodo @Bean che restituisce un DBDC BasicDataSource di Apache Commons, il contenitore noterà il metodo close() disponibile su quell'oggetto e lo registrerà automaticamente come destroyMethod. Questa "deduzione del metodo di distruzione" è attualmente limitata a che rileva solo metodi pubblici, senza argomenti denominati "chiudi" o "chiudi". Il metodo può essere dichiarato a qualsiasi livello della gerarchia di ereditarietà e verrà rilevato indipendentemente dal tipo di ritorno del metodo @Bean (ad es., il rilevamento si verifica in modo riflessivo rispetto all'istanza del bean stessa al momento della creazione).

Per ribadire, questo è il comportamento di default per annotazioni-driven configurazione quando un metodo distruggere non è impostato in modo esplicito. Se questo comportamento è indesiderato esplicitamente modificando distruggere metodo su una stringa vuota si disabilita questa "caratteristica":

Per disattivare distruggere metodo inferenza per un particolare @Bean, specificare un stringa vuota come valore, ad esempio @Bean (destroyMethod = ""). Si noti che le interfacce DisposableBean e Closeable/AutoCloseable saranno rilevate e verrà richiamato il corrispondente metodo di eliminazione/chiusura .

D'altra parte, quando si utilizza la configurazione XML, questo è non il comportamento di default ... per raggiungere la parità, destroy-method possono essere esplicitamente impostato (inferred). Fare riferimento alle sezioni Destruction callbacks e Default initialization and destroy methods nei documenti ufficiali per i dettagli.