2016-06-30 11 views
2

Ich stoße auf ein Problem, Spring Spring Caching mit ehcache in meiner Anwendung zu verwenden. Aus Gründen, die ich nicht näher ausführen kann, verwendet meine Anwendung ein Diagramm von BeanFactories anstelle von ApplicationContexts. Dieser Ansatz hat gut funktioniert, solange wir unsere BeanPostProcessors manuell registrieren, wie in der Spring-Dokumentation erwähnt.Spring-Cache-Unterstützung erfordert ApplicationContext?

Wir fügen jetzt Caching zur App hinzu. Wenn wir die einfachste Annotationskonfiguration verwendet haben, funktioniert es.

// Das funktioniert

package com.x.y.z; 

public class RoleManager { 
    private String user; 

    public RoleManager(String user) { 
     this.user = user; 
    } 

    public String getName() { 
     return user; 
    } 

    @Cacheable("user") 
    public boolean isAllowed(String permissionId, Map<String,?> params) 
    { 
     ... lengthy and expensive operation to determine if user is permitted to do something 
    } 
} 

Wir konfigurieren in diesem Frühjahr xml für diese BeanFactory zu verwenden:

<?xml version="1.0" encoding="UTF-8"?> 
<beans xmlns="http://www.springframework.org/schema/beans" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util" 
    xmlns:cache="http://www.springframework.org/schema/cache" xmlns:p="http://www.springframework.org/schema/p" 
    xsi:schemaLocation= 
     "http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd 
     http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd 
     http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd"> 

    <cache:annotation-driven/> 
    <bean id="roleManager" class="com.x.y.z.RoleManager" scope="prototype"/> 
    <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager"> 
     <property name="cacheManager" ref="ehcacheManager"/> 
    </bean> 
    <bean id="ehcacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"> 
     <property name="configLocation" value="file:${conf.dir}/ehcache.xml"/> 
     <property name="shared" value="true"/> 
    </bean> 
</beans> 
... unrelated business beans elided ... 

Wir verwenden Frühling 4.1.9 und 2.10.2 ehcache

Der obige Code funktioniert sehr gut. Unsere ehcache-Instanz für "user" beginnt sich zu füllen, wenn Cache-Fehler auftreten, und gibt zwischengespeicherte Werte für Treffer zurück.

Sobald dies korrekt ausgeführt wurde, haben wir festgestellt, dass es nicht möglich ist, alle Einträge für einen bestimmten Benutzer zu entfernen, da der Cacheschlüssel eine Verkettung der Berechtigungs-ID und des Map :: toString-Ergebnisses ist. Wir haben uns entschieden, einen Cache pro Benutzer zu erstellen, damit wir mehr Kontrolle über die Räumung haben. Um Spring zu verwenden, müssen wir einen CacheResolver verwenden, um dies zu erreichen.

package com.x.y.z; 

import org.springframework.cache.CacheManager; 
import org.springframework.cache.interceptor.AbstractCacheResolver; 
import org.springframework.cache.interceptor.CacheOperationInvocationContext; 

import java.util.Collection; 
import java.util.Collections; 

public class MyCacheResolver extends AbstractCacheResolver { 
    public MyCacheResolver() { 
    } 

    public MyCacheResolver(CacheManager cacheManager) { 
     super(cacheManager); 
    } 

    @Override 
    protected Collection<String> getCacheNames(CacheOperationInvocationContext<?> cacheOperationInvocationContext) { 
     if(cacheOperationInvocationContext.getTarget() instanceof RoleManager) { 
      return Collections.singleton(((RoleManager) cacheOperationInvocationContext.getTarget()).getName()); 
     } 
     return Collections.singleton("user"); 
    } 
} 

Wir diese verdrahten durch eine neue Bean Definition Hinzufügen

<bean id="myCacheResolver" class="com.x.y.z.MyCacheResolver"> 
    <constructor-arg index="0" ref="cacheManager"/> 
</bean> 

Und die Anmerkung in Rolemanager ändern

@Cacheable(cacheResolver="myCacheResolver") 

Sobald wir das tun, aber wir die folgende Ausnahme erhalten Wenn die isAllowed-Methode aufgerufen wird:

java.lang.NullPointerException 
    at org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils.qualifiedBeanOfType(BeanFactoryAnnotationUtils.java:57) 
    at org.springframework.cache.interceptor.CacheAspectSupport.getBean(CacheAspectSupport.java:282) 
    at org.springframework.cache.interceptor.CacheAspectSupport.getCacheOperationMetadata(CacheAspectSupport.java:254) 
    at org.springframework.cache.interceptor.CacheAspectSupport.getOperationContext(CacheAspectSupport.java:226) 
    at org.springframework.cache.interceptor.CacheAspectSupport$CacheOperationContexts.<init>(CacheAspectSupport.java:500) 
    at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:299) 
    at org.springframework.cache.interceptor.CacheInterceptor.invoke(CacheInterceptor.java:61) 
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) 
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207) 
    at com.sun.proxy.$Proxy61.isAllowed(Unknown Source) 
    at com.x.y.z.RoleManager.isAllowed(CompositeRoleManager.java:61) 

Wenn ich die CacheAspectSupport-Klasse von der Stack-Ablaufverfolgung aus anschaue, sehe ich, dass sie ein Mitglied, applicationContext, hat, das null ist.

protected <T> T getBean(String beanName, Class<T> expectedType) { 
     return BeanFactoryAnnotationUtils.qualifiedBeanOfType(this.applicationContext, expectedType, beanName); 
    } 

Dies scheint ein Fehler im Frühjahr zu mir, da wir nicht ApplicationContexts verwenden und dennoch funktioniert das Caching, bis wir eine CacheResolver verwenden müssen. Ich habe mir die Dokumentation angeschaut und sehe keine Erwähnung, dass man ApplicationContexts verwenden muss, um die Spring-Caching-Abstraktion zu verwenden.

Ich denke, meine Frage ist, hat jemand dieses Problem erlebt, und wenn ja, was haben Sie getan, um es zu lösen? Wir können ApplicationContexte in unserer Anwendung absolut nicht verwenden, und ich würde lieber nicht eine perfekt nutzbare Abstraktion und einen Code direkt zu den ehcache (oder JSR-107) APIs werfen.

Vielen Dank im Voraus!

+0

Es scheint, dass Spring 4.3 eine Methode "setBeanFactory()" zur CacheAspectSupport-Klasse hinzugefügt und die Methode "setApplicationContext()" veraltet hat.Ich war nicht in der Lage, ein Problem in ihrem JIRA entsprechend der Änderung zu finden, aber ich habe überprüft, dass mein Code mit Spring 4.3 funktioniert – user2729944

Antwort

1

In Spring 4.3 wurde das Problem behoben, indem eine setBeanFactory() -Methode hinzugefügt und die BeanFactory verwendet wurde, um den CacheResolver aufzurufen. Leider kann ich den Spring-Bibliothekscode derzeit nicht auf 4.3 aktualisieren, aber es wird funktionieren, wenn wir in der Zukunft ein Upgrade durchführen können.

Verwandte Themen