我遇到了一个问题,试图在我的应用程序中使用带有ehcache的Spring缓存。由于我无法详细说明的原因,我的应用程序使用BeanFactories而不是ApplicationContexts的图表。只要我们手动注册我们的BeanPostProcessors,这种方法就可以正常工作,如Spring文档中所述。
我们现在正在为应用添加缓存。当我们使用最简单的注释配置时,它可以工作。
//这可行
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
}
}
我们将此配置为对此bean工厂使用spring xml:
<?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和ehcache 2.10.2
以上代码效果很好。我们的&#34;用户&#34;的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);
}
这似乎是Spring中的一个错误,因为我们不使用ApplicationContexts,但缓存仍然有效,直到我们需要使用CacheResolver。我查看了文档,但没有提到必须使用ApplicationContexts才能使用Spring缓存抽象。
我想我的问题是,有没有人遇到过这个问题,如果有的话,你做了什么来解决它?我们绝对不能在我们的应用程序中使用ApplicationContexts,而且我不想直接向ehcache(或JSR-107)API抛出一个完全可用的抽象和代码。
提前致谢!
答案 0 :(得分:1)
Spring 4.3通过添加setBeanFactory()方法并使用BeanFactory设置调用CacheResolvers来解决问题。不幸的是,我目前无法将我们的Spring库代码更新为4.3,但是当我们将来能够进行升级时它会起作用。