2012-04-14 13 views
5

Esiste un modo semplice per utilizzare l'annotazione Spring @Cacheable con un bean non singleton (ad esempio con ambito sessione) e avere la cache con lo stesso ambito di detto bean?L'annotazione @Cacheable di Spring può avere lo stesso ambito del bean del metodo annotato?

Esempio:

import javax.inject.Inject; 
import javax.inject.Named; 

import org.springframework.cache.annotation.Cacheable; 
import org.springframework.context.annotation.Scope; 
import org.springframework.security.core.context.SecurityContextHolder; 

@Named 
@Scope("session") 
public class UserNameRetriever { 

    @Inject private UserDao userDao; 

    @Cacheable("userName") 
    public String getUserName() { 
     return userDao.getUserByLogin((String)SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getName(); 
    } 

} 

Idealmente, UserNameRetriever.getUserName() sarebbe prendere il nome utente dal UserDao una volta per sessione, ma questo codice in realtà memorizza nella cache di applicazione a livello.

risposta

2

non l'ho provato, ma secondo Spring reference penso che questo dovrebbe funzionare:

@Cacheable(value = "userName", key = "#root.target") 
+0

Questo funziona, ma ha una perdita di memoria, senza ( \t \ @CacheEvict (value = "username", key = "# root.target") \t \ @PreDestroy \t pubblico cleanup void() {} \t Inoltre, ho deciso di non utilizzare @Cacheable dopo tutto, poiché non garantisce che il valore verrà calcolato una sola volta. –

+0

Cosa intendi per perdita di memoria? Forse la tua cache è troppo grande. @Cacheable cercherà prima nella cache, quindi garantisce che il valore non verrà calcolato mentre è ancora nella cache. – sinuhepop

+0

Si tratta di una perdita di memoria perché una volta terminata la sessione, il valore è ancora nella cache, ma non è possibile accedervi. @PreDestroy garantisce che il valore memorizzato nella cache venga rimosso quando termina la sessione. –

2

Penso che ti stai avvicinando al problema dal lato sbagliato. Invece di disporre di "local" cache con ambito sessione che memorizzano un solo valore, una cache globale memorizza una mappa dal nome utente ai valori User.

Nel tuo caso cadere @Cacheable su getUserName() e metterlo sul UserDao:

public class UserDao { 
    @Cacheable("users") 
    public User getUserByLogin(String user) { 
    //... 
    } 
} 

Non solo questo è un uso più idiomatica di cache, ma anche che si rivelerà più facile da mantenere e più performante.

Torna alla tua domanda iniziale: no, questo non è possibile subito. Dovresti scrivere il tuo CacheManager - probabilmente un intorno all'implementazione esistente.

+0

RE: Penso che ti stai avvicinando al problema dal lato sbagliato: UserDao recupera l'utente da un sistema esterno, e voglio che lo username (usato solo esteticamente) sia coerente durante una sessione, ma aggiorni in una nuova sessione se è cambiato in detto sistema esterno. Il tuo approccio non mostrerebbe questo comportamento. –

3

Se si desidera una cache di sessione, utilizzare la sessione. Vale a dire, memorizzare l'utente in un campo privato nel bean con ambito sessione o accedere direttamente all'oggetto . @Cacheable è generalmente pensato per risorse a livello di applicazione.

Beh, è ​​possibile utilizzare key="#user.id", e di invalidare la cache (manualmente o con @CacheEvict) su un metodo @PreDestroy, ma sarebbe meglio se non si mischiano i due - di caching e di sessione di dati.

+0

Salve @Bozho, potresti per favore giustificare il motivo per cui pensi che sia meglio usare httpsession o Cacheable in un'applicazione web di primavera? –

+0

è a livello di sessione vs risorse a livello di applicazione. È uno sforzo aggiuntivo se si desidera utilizzare @Cacheable per le risorse di sessione – Bozho

Problemi correlati