我在尝试在我的应用程序中使用带有 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
上面的代码工作得很好。我们的“用户”的 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 抛出一个完美可用的抽象和代码。
提前致谢!
最佳答案
Spring 4.3 通过添加 setBeanFactory() 方法并使用 BeanFactory 从而设置为调用 CacheResolvers 解决了这个问题。不幸的是,我目前无法将我们的 Spring 库代码更新到 4.3,但是当我们将来能够升级时它会起作用。
关于java - Spring缓存支持需要ApplicationContext?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38128426/