2015-05-26 12 views
5

Ich habe ein Spring Boot-Projekt mit einem benutzerdefinierten CacheResolver, da ich zur Laufzeit entscheiden muss, welchen Cache ich verwenden möchte, ich habe keine Kompilierungsfehler, aber wenn ich einige Tests mache und eine Pause mache Punkt auf meine benutzerdefinierte CacheResolver es tritt nie in es.Benutzerdefinierte CacheResolver funktioniert nicht

Dies ist meine Konfigurationsklasse für den 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); 
     } 
    } 

Hier ist meine 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; 
    } 
} 

Und hier meine DAO, wo ich den Cache verwenden:

@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 
     } 
. 
. 
. 
} 

Ich habe versucht, CacheResolver zu implementieren, anstatt AbstractCacheResolver zu erweitern, aber es hat nicht mak Irgendein Unterschied.

Vielen Dank.

+0

Nun, Sie haben keinen Cache-Namen dort, so wenn Caching funktioniert, muss es den Cache-Namen auflösen, sonst wird es eine Ausnahme werfen. Meine beste Vermutung ist, dass Caching überhaupt nicht funktioniert. Ihre Konfiguration ist auch sehr _weird_. Sie verwenden 'CachingConfiguration', um den _default_' CacheResolver' bereitzustellen, so dass Sie ihn gar nicht angeben müssen. Wenn Sie es pro Vorgang (mit einem speziellen Namen) angeben möchten, definieren Sie es anderswo (nicht als Standard). –

+0

Danke @ stéphane-nicoll, du hast mich in die richtige Richtung gelenkt, ich habe das Problem behoben, indem ich die Cache-Namen mit der 'CacheConfig'-Annotation eingefügt habe, meine Tests durchgeführt und Fehler gemacht habe und nun funktioniert der'CacheResolver' wie erwartet. Ich nahm an, dass Cache-Namen nicht enthalten sein müssten, da der 'CacheResolver' dort war. – tommylii

+0

Gern geschehen, aber die Cache-Namen werden nicht benötigt. Wie gesagt, wenn Ihr Cache-Resolver nicht aufgerufen wurde und Sie keine Ausnahme hatten, hatten Sie wahrscheinlich überhaupt kein Caching. Die folgende Antwort ist falsch (es sei denn, es gibt einen Fehler) –

Antwort

1

Cache-Namen müssen an einem bestimmten Punkt aufgenommen werden, sondern nur die CacheResolver Angabe ist nicht genug, um zu verwenden, die @Cacheable Klasse bewusst sein, den verfügbaren Cache-Namen benötigt, so dass ich eingeschlossen sie mit der @CacheConfig Anmerkung:

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

eine Sache, die ich mag nicht wissen, ist, dass ich ein null CacheManager, auch zur Verfügung stellen muß, wenn ich es nicht verwenden, sonst bekomme ich folgende Fehlermeldung:

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. 

So ließ ich es so , ein d es funktioniert:

@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 meine Tests, Schreiten durch meine benutzerdefinierte CacheResolver und es verhält sich wie auf den richtigen Cache erwartete Lösung (en)

Meine Konfigurationsklasse nicht CachingConfigurerSupport mehr erweitert.

+0

Wie können Sie mit einem leeren 'CacheManager' in' WgstCacheResolver kommen 'Wenn die Oberklasse' AbstractCacheResolver' in der 'afterPropertiesSet'-Methode explizit darauf überprüft wird? 'public void afterPropertiesSet() {' 'Assert.notNull (dieser.cacheManager," CacheManager darf nicht null sein ");' '}' –

-1

Nach ein bisschen hin und her (Sorry!), Es ist in der Tat ein Fehler in Spring Framework.

Ich habe SPR-13081 erstellt. Erwarten Sie eine Reparatur für die nächste Wartungsversion (4.1.7.RELEASE). Danke für das Beispielprojekt!

+0

Kein Problem :) vielen Dank dafür – tommylii

+0

Hi, I ' m versuche, einen globalen Cache zu konfigurieren, ohne für jede Methode oder Klasse einen Namen angeben zu müssen. Ich verstehe, dass ein benutzerdefinierter 'CacheResolver' anstelle eines Namens zur Verfügung gestellt werden könnte. Was aus den Dokumenten nicht klar ist, ob die Methode/Klasse immer noch auf sie in der Annotation verweisen muss, oder suchen Sie nach Typ, wenn keiner angegeben ist? –

+0

Wenn Sie einen benutzerdefinierten Cache-Resolver angeben, werden wir ihn aufrufen, wenn wir die Cache-Namen auflösen müssen. Wie die entsprechenden Cache (s) gelöst werden, hängt vollständig von Ihrer Implementierung ab. Nachdem ich das gesagt habe, bin ich mir nicht sicher, ob ich Ihre Frage oder die Verwirrung verstehe, auf die Sie sich beziehen. Sie müssen natürlich noch "@ Cacheable" angeben, aber Sie müssen keine Cache-Namen angeben. –

Verwandte Themen