2015-05-26 14 views
5

Ho un progetto Spring Boot con un custom CacheResolver in quanto ho bisogno di decidere sul runtime quale cache voglio usare, non ho errori di compilazione ma, quando faccio qualche test e posto una pausa punto al mio custom CacheResolver non ci entra mai.Custom CacheResolver non funziona

Questa è la mia classe di configurazione per il cache:

@Configuration 
@EnableCaching(proxyTargetClass = true) 
@PropertySource(CacheConfig.CLASSPATH_DEPLOY_CACHE_PROPERTIES_PROPERTIES) 
public class CacheConfig extends CachingConfigurerSupport{ 

     public static final String CLASSPATH_DEPLOY_CACHE_PROPERTIES_PROPERTIES = "classpath:/deploy/cache-properties.properties"; 

     public static final String CACHEABLE_DOCUMENTS_PROPERTY = "cacheable.documents"; 
     public static final String TTL_CACHEABLE_DOCUMENTS_PROPERTY = "ttl.cacheable.documents"; 
     public static final String SIZED_CACHEABLE_DOCUMENTS_PROPERTY = "sized.cacheable.documents"; 
     public static final String CACHE_NAME = "permanentCache"; 
     public static final String TTL_CACHE = "ttlCache"; 
     public static final String SIZED_CACHE = "sizedCache"; 
     public static final String CACHEABLE_DOCUMENTS = "cacheableDocuments"; 
     public static final String SIZED_CACHEABLE_DOCUMENTS = "sizedCacheableDocuments"; 
     public static final int WEIGHT = 1000000; 
     public static final int TO_KBYTES = 1000; 

     @Inject 
     protected Environment environment; 

     //@Bean 
     @Override 
     public CacheManager cacheManager() { 
     SimpleCacheManager cacheManager = new SimpleCacheManager(); 
     GuavaCache sizedCache = new GuavaCache(SIZED_CACHE, CacheBuilder.newBuilder().maximumWeight(WEIGHT).weigher(
       (key, storable) -> { 
        String json = ((Storable) storable).toJson(); 
        return json.getBytes().length/TO_KBYTES; 
       } 
     ).build()); 
     GuavaCache permanentCache = new GuavaCache(CACHE_NAME,CacheBuilder.newBuilder().build()); 
     //GuavaCache ttlCache = new GuavaCache(TTL_CACHE, CacheBuilder.newBuilder().expireAfterWrite(30, TimeUnit.MINUTES).build()); 
     cacheManager.setCaches(Arrays.asList(permanentCache,sizedCache)); 
     return cacheManager; 
     } 

     @Bean(name = "wgstCacheResolver") 
     @Override 
     public CacheResolver cacheResolver(){ 
     CacheResolver cacheResolver = new WgstCacheResolver(cacheManager(),cacheableDocuments(),sizedCacheableDocuments()); 
     return cacheResolver; 
     } 


     @Bean(name = CACHEABLE_DOCUMENTS) 
     public List<String> cacheableDocuments(){ 
     String[] cacheableDocuments = StringUtils.commaDelimitedListToStringArray(environment.getProperty(CACHEABLE_DOCUMENTS_PROPERTY)); 
     return Arrays.asList(cacheableDocuments); 
     } 

     @Bean(name = SIZED_CACHEABLE_DOCUMENTS) 
     public List<String> sizedCacheableDocuments(){ 
     String[] sizedCacheableDocuments = StringUtils.commaDelimitedListToStringArray(environment.getProperty(SIZED_CACHEABLE_DOCUMENTS_PROPERTY)); 
     return Arrays.asList(sizedCacheableDocuments); 
     } 
    } 

Ecco il mio CacheResolver

public class WgstCacheResolver extends AbstractCacheResolver { 

    private final List<String> cacheableDocuments; 
    private final List<String> sizedCacheableDocuments; 

    public WgstCacheResolver(final CacheManager cacheManager,final List<String> cacheableDocuments, final List<String> sizedCacheableDocuments) { 
    super(cacheManager); 
    this.cacheableDocuments = cacheableDocuments; 
    this.sizedCacheableDocuments = sizedCacheableDocuments; 
    } 

    /** 
    * Resolves the cache(s) to be updated on runtime 
    * @param context 
    * @return*/ 
    @Override 
    protected Collection<String> getCacheNames(final CacheOperationInvocationContext<?> context) { 

    final Collection<String> cacheNames = new ArrayList<>(); 
    final AbstractDao dao = (AbstractDao)context.getTarget(); 
    final String documentType = dao.getDocumentType().toString(); 
    if (cacheableDocuments.contains(documentType)){ 
     cacheNames.add("permanentCache"); 
    } 
    if (sizedCacheableDocuments.contains(documentType)){ 
     cacheNames.add("sizedCache"); 
    } 
    return cacheNames; 
    } 
} 

E qui il mio DAO in cui io uso la cache:

@Component 
    @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE, proxyMode = ScopedProxyMode.DEFAULT) 
    @CacheConfig(cacheResolver = "wgstCacheResolver") 
    public class CacheableDao<T extends Storable> extends AbstractDao<T> { 

     private final Logger logger = LoggerFactory.getLogger(CacheableDao.class); 

     public CacheableDao(final Bucket bucket, final Class<T> typeParameterClass, final DocumentType documentType) { 
     super(bucket, typeParameterClass, documentType); 
     } 

     @Cacheable(key = "{#root.methodName, #root.target.generateFullKey(#key)}") 
     public T get(final String key) throws DatastoreAccessException, ObjectMappingException { 
     //do something 
     } 
. 
. 
. 
} 

ho provato a implementare CacheResolver invece di estendere AbstractCacheResolver ma non ha fatto e qualsiasi differenza.

Grazie.

+0

Beh, non si dispone di alcun nome di cache, quindi se la cache funziona, è necessario risolvere il nome della cache altrimenti genererà un'eccezione. La mia ipotesi migliore è che il caching non funzioni affatto. Anche la tua configurazione è molto _weird_. Si usa 'CachingConfiguration' per fornire _default_' CacheResolver' quindi non è necessario specificarlo. Se si desidera specificarlo per operazione (con un nome speciale), definirlo altrove (non come predefinito). –

+0

Grazie @ stéphane-nicoll, mi hai indirizzato nella giusta direzione, ho risolto il problema includendo i nomi di cache disponibili con l'annotazione 'CacheConfig', eseguito i miei test e il debug e ora il' CacheResolver' personalizzato funziona come previsto. Supponevo che i nomi di cache non avessero bisogno di essere inclusi poiché c'era il 'CacheResolver'. – tommylii

+0

Prego, ma i nomi della cache non sono necessari. Come ho detto, se il tuo resolver cache non è stato chiamato e non hai fatto eccezioni, probabilmente non hai avuto alcun caching. Quella risposta qui sotto non è corretta (a meno che non ci sia un bug) –

risposta

1

nomi cache devono essere inclusi ad un certo punto, basta specificare il CacheResolver utilizzare non abbastanza è, la classe @Cacheable ha bisogno di essere a conoscenza dei nomi cache disponibili, così li ho inclusi con il @CacheConfig della nota:

@CacheConfig(cacheNames = {WgstCacheConfig.PERMANENT_CACHE, WgstCacheConfig.SIZED_CACHE}, 
    cacheResolver = WgstCacheConfig.WGST_CACHE_RESOLVER) 
public class CacheableDao<T extends Storable> extends AbstractDao<T> { 

una cosa che non mi piace è che ho bisogno di fornire un NULL CacheManager, anche se non sto usando, altrimenti ottengo il seguente errore:

Caused by: java.lang.IllegalStateException: No CacheResolver specified, and no bean of type CacheManager found. Register a CacheManager bean or remove the @EnableCaching annotation from your configuration. 

Così ho lasciato in questo modo , un d funziona:

@Bean 
    public CacheManager cacheManager() { 
    return null; 
    } 

    @Bean(name = WGST_CACHE_RESOLVER) 
    public CacheResolver cacheResolver(){ 
    CacheResolver cacheResolver = new WgstCacheResolver(cacheableDocuments(),sizedCacheableDocuments(),getPermanentCache(), 
                 getSizedCache()); 
    return cacheResolver; 
    } 

reran mio test, passando attraverso il mio personalizzato CacheResolver e si comporta come previsto risolvere alla cache corretto (s)

La mia classe di configurazione non sta estendendo più CachingConfigurerSupport.

+0

Come si può ottenere con un 'CacheManager' nullo in' WgstCacheResolver 'quando la superclasse' AbstractCacheResolver' ha un controllo esplicito per questo nel metodo 'afterPropertiesSet'? 'public void afterPropertiesSet() {' 'Assert.notNull (this.cacheManager," CacheManager non deve essere nullo ");' '}' –

-1

Dopo un po 'di avanti e indietro (mi dispiace per quello!) Si scopre che questo è davvero un bug in Spring Framework.

Ho creato SPR-13081. Aspettatevi una correzione per la prossima versione di manutenzione (4.1.7.RELEASE). Grazie per il progetto di esempio!

+0

Nessun problema :) grazie mille per averlo consultato – tommylii

+0

Salve, I ' Sto cercando di configurare una cache globale senza dover specificare un nome su ogni metodo o classe. Capisco che potrebbe essere fornito un 'CacheResolver' personalizzato al posto di un nome. Ciò che non è chiaro dai documenti è se il metodo/classe deve ancora fare riferimento ad esso nell'annotazione o lo si cerca per tipo se non ne viene specificato nessuno? –

+0

Se si specifica un risolutore cache personalizzato, è ciò che invocheremo quando sarà necessario risolvere i nomi della cache. Il modo in cui le cache appropriate vengono risolte dipende completamente dalla tua implementazione. Detto questo, non sono sicuro di capire la tua domanda o la confusione a cui ti riferisci. Devi comunque specificare '@ Cacheable ', ma non devi fornire alcun nome di cache. –