Spring缓存支持需要ApplicationContext吗?

时间:2016-06-30 16:12:28

标签: java spring caching

我遇到了一个问题,试图在我的应用程序中使用带有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抛出一个完全可用的抽象和代码。

提前致谢!

1 个答案:

答案 0 :(得分:1)

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