Spring缓存支持需要ApplicationContext吗?

3
我在使用Spring缓存与ehcache时遇到了问题。由于某些原因,我的应用程序使用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
    }
}

我们使用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和ehcache 2.10.2。以上代码运行良好。我们的“user” ehcache实例开始填充,因为我们得到了缓存失效,并返回击中的缓存值。一旦这个运行正确,我们发现不可能驱逐特定用户的所有条目,因为缓存键是权限ID和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,但该成员为空。

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

在我看来,这似乎是Spring的一个Bug,因为我们并没有使用ApplicationContexts,但是缓存可以正常工作,直到我们需要使用CacheResolver。我已经查看了文档,没有提到必须使用ApplicationContexts才能使用Spring缓存抽象。

我的问题是,是否有人遇到过这个问题,如果有,你是如何解决的?我们绝对不能在我们的应用程序中使用ApplicationContexts,而我不想放弃一个完全可用的抽象,并直接使用ehcache(或JSR-107)APIs的代码。

提前感谢!


似乎Spring 4.3在CacheAspectSupport类中添加了setBeanFactory()方法,并弃用了setApplicationContext()方法。我没有找到与此更改对应的JIRA问题,但我已经验证我的代码可以在Spring 4.3中正常工作。 - user2729944
1个回答

1

Spring 4.3通过添加setBeanFactory()方法并使用设置的BeanFactory来调用CacheResolvers解决了这个问题。不幸的是,我目前无法更新我们的Spring库代码到4.3,但在将来升级时它将起作用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接