我遇到了一個問題,試圖在我的應用程序中使用ehcache進行Spring緩存。由於我無法詳細說明的原因,我的應用程序使用BeanFactories而不是ApplicationContexts的圖形。只要我們手動註冊我們的BeanPostProcessors,這種方法運行良好,就像Spring文檔中所提到的那樣。Spring緩存支持需要ApplicationContext?
我們現在正在嚮應用程序添加緩存。當我們使用最簡單的註釋配置時,它就起作用了。
//這工作
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
}
}
我們配置此使用Spring XML爲這個bean工廠:
<?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 ...
我們正在使用Spring 4.1.9和2.9.2的Ehcache
上面的代碼工作得很好。我們的「用戶」ehcache實例開始填充,因爲我們得到緩存未命中,並返回緩存值的命中。
運行正常後,我們發現無法逐出特定用戶的所有條目,因爲緩存鍵是permissionid和Map :: toString結果的串聯。我們決定爲每個用戶創建一個緩存,這樣我們就可以更好地控制驅逐。要使用Spring,我們需要使用CacheResolver來完成此操作。
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");
}
}
我們加入了新的bean定義
<bean id="myCacheResolver" class="com.x.y.z.MyCacheResolver">
<constructor-arg index="0" ref="cacheManager"/>
</bean>
這樣組裝起來,並更改RoleManager註釋,以
@Cacheable(cacheResolver="myCacheResolver")
一旦我們這樣做,但是,我們可以得到以下異常當isAllowed方法被調用時:
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)
當我從堆棧跟蹤中查看CacheAspectSupport類時,發現它有一個成員applicationContext,它是null。
protected <T> T getBean(String beanName, Class<T> expectedType) {
return BeanFactoryAnnotationUtils.qualifiedBeanOfType(this.applicationContext, expectedType, beanName);
}
這似乎是在春天給我的錯誤,因爲我們直到我們需要使用CacheResolver不使用的ApplicationContexts,然而緩存的工作。我查看了文檔,並且沒有提到必須使用ApplicationContexts才能使用Spring緩存抽象。
我想我的問題是,有沒有人遇到過這個問題,如果是的話,你做了什麼來解決它?我們絕對不能在我們的應用程序中使用ApplicationContexts,我寧願不直接向ehcache(或JSR-107)API扔出完全可用的抽象和代碼。
在此先感謝!
看來Spring 4.3向CacheAspectSupport類添加了一個setBeanFactory()方法,並且不推薦使用setApplicationContext()方法。我無法在他們的JIRA中找到與該更改相對應的問題,但我確實驗證了我的代碼可用於Spring 4.3 – user2729944