java - Spring缓存支持需要ApplicationContext?

标签 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

上面的代码工作得很好。我们的“用户”的 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/

相关文章:

c# - HttpRuntime.Cache[] 与应用程序[]

java - JBoss接缝: How to Open jpa/hibernate session in view

java - 为什么它不打印任何东西?

java - 无法从 Spring 表单中获取图像

java - Spring WebServiceTemplate 的 marshalSendAndReceive 方法在发送 Request 时将 SOAP header 发送为 null

java - 使用 Spring MVC 设置输入文本的日期格式

caching - 添加、删除和缓存 SKNode

java - Java可以缓存一个特定的函数吗?

java - Kotlin 数据类打包

java - ResolvedModule、Module 和 Observable Module 有什么区别