2016-06-30 45 views
2

我遇到了一個問題,試圖在我的應用程序中使用ehcache進行Spring緩存。由於我無法詳細說明的原因,我的應用程序使用BeanFactories而不是ApplicationContexts的圖形。只要我們手動註冊我們的BeanPostProcessors,這種方法運行良好,就像Spring文檔中所提到的那樣。Spring緩存支持需要ApplicationContext?

我們現在正在嚮應用程序添加緩存。當我們使用最簡單的註釋配置時,它就起作用了。

//這工作

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和2.9.2的Ehcache

上面的代碼工作得很好。我們的「用戶」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); 
    } 

這似乎是在春天給我的錯誤,因爲我們直到我們需要使用CacheResolver不使用的ApplicationContexts,然而緩存的工作。我查看了文檔,並且沒有提到必須使用ApplicationContexts才能使用Spring緩存抽象。

我想我的問題是,有沒有人遇到過這個問題,如果是的話,你做了什麼來解決它?我們絕對不能在我們的應用程序中使用ApplicationContexts,我寧願不直接向ehcache(或JSR-107)API扔出完全可用的抽象和代碼。

在此先感謝!

+0

看來Spring 4.3向CacheAspectSupport類添加了一個setBeanFactory()方法,並且不推薦使用setApplicationContext()方法。我無法在他們的JIRA中找到與該更改相對應的問題,但我確實驗證了我的代碼可用於Spring 4.3 – user2729944

回答

1

Spring 4.3通過添加一個setBeanFactory()方法並使用BeanFactory設置來調用CacheResolvers來解決了這個問題。不幸的是,我現在無法將我們的Spring庫代碼更新到4.3,但是在我們未來能夠升級時它將工作。