当前位置: 首页 > 知识库问答 >
问题:

Spring缓存支持需要ApplicationContext?

隆礼骞
2023-03-14

我在应用程序中尝试将Spring缓存与ehcache结合使用时遇到了一个问题。由于无法详细说明的原因,我的应用程序使用BeanFactory图而不是ApplicationContext。正如Spring文档中所述,只要我们手动注册BeanPostProcessor,这种方法就可以很好地工作。

我们现在正在为应用程序添加缓存。当我们使用最简单的注释配置时,它可以工作。

//这很有效

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

上面的代码工作得很好。当我们得到缓存未命中时,我们的“user”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)

当我从堆栈跟踪中查看CacheAeySupport类时,我看到它有一个成员,应用上下文,它为null。

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

在我看来,这似乎是Spring中的一个bug,因为我们不使用ApplicationContext,但缓存在需要使用CacheResolver之前一直有效。我已经查看了文档,没有提到必须使用ApplicationContext才能使用Spring缓存抽象。

我想我的问题是,有没有人遇到过这个问题,如果有,你是怎么解决的?我们绝对不能在我们的应用程序中使用ApplicationContexts,我也不想把一个完全可用的抽象和代码直接丢给ehcache(或JSR-107)API。

提前感谢!

共有1个答案

云昊阳
2023-03-14

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

 类似资料:
  • 一个好的HTTP缓存策略可以极大地提高一个web应用的性能及客户端的体验。谈到HTTP缓存,它主要是与HTTP的响应头'Cache-Control'相关,其次另外的一些响应头比如'Last-Modified'和'ETag'等也会起一定的作用。 HTTP的响应头'Cache-Control'主要帮助私有缓存(比如浏览器端缓存)和公共缓存(比如代理端缓存)了解它们应该如果缓存HTTP响应,以便后用。

  • 问题内容: 我最近有一个关于如何在另一个类的主方法中打开登录面板的问题。因为我还没有在Swing中上过任何课(仅是基本的Java编程),所以我已经偶然发现了另一个问题。 我如何检测用户是否按下了JPanel中的按钮并使它执行某项操作。 例如:用户按下了登录-> if(textfield1.getText()==“ user”){打开另一个JFrame}->等等。 这是我的主要代码: 这是Login

  • 一个轻量级的缓存实现,目前已支持 Redis Memcache Memcached File 四种储存模式 仓库地址: Github 安装 composer require easyswoole/cache 注意: 请确保框架已经引入了 composer 的 autoload.php 文件,否则报类不存在的错误 快速入门 如果不做任何设置,默认使用File驱动,开箱即用 use easySwool

  • 问题内容: 我正在尝试让我的python程序将数据插入MySQL,我遵循了指南,但是我不断收到以下错误。 “不支持身份验证插件’{0}’”。格式(plugin_name))mysql.connector.errors.NotSupportedError:不支持身份验证插件’caching_sha2_password’。 我错过了mysql服务器中的设置还是python不支持此设置? 我认为我可以更

  • 我想知道如何将缓存配置添加到Vertx http web客户端。 使用Apache http客户端,我可以轻松地设置setCacheConfig 有什么想法吗?

  • 我想问一个关于spring缓存注释和存储库创建的问题。 我正在尝试使用Spring Boot在redis中缓存我的值。(我有一个值,如果它不在缓存里,我需要缓存它,如果它在缓存里,我需要从那里获取它) null 我的问题是:如果我使用了上面的注释(@Cacheable、@CachePut和@CacheEvict),我是否应该通过扩展CrudRepository来为我感兴趣存储的类对象创建一个存储库