【发布时间】:2018-07-28 07:00:46
【问题描述】:
我正在开发一个现有的 Java 8/Spring 4.2.5 Web 应用程序,并尝试将其使用 Spring 对 Guava Cache 的支持的已经工作的缓存扩展到更多功能,以期提高性能。该应用程序使用 Spring 4 的内置缓存,在其 CacheConfig 文件中设置:
CacheConfig.java
import com.google.common.cache.CacheBuilder;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurer;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.guava.GuavaCache;
import org.springframework.cache.interceptor.CacheErrorHandler;
import org.springframework.cache.interceptor.CacheResolver;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.cache.support.SimpleCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableCaching
public class CacheConfig implements CachingConfigurer {
@Bean
@Override
public CacheManager cacheManager() {
SimpleCacheManager simpleCacheManager = new SimpleCacheManager();
GuavaCache cache1 = new GuavaCache("hourCache", CacheBuilder.newBuilder()
.expireAfterWrite(60, TimeUnit.MINUTES)
.build());
simpleCacheManager.setCaches(Arrays.asList(cache1));
return simpleCacheManager;
}
@Override
public CacheResolver cacheResolver() {
return null;
}
@Bean
@Override
public KeyGenerator keyGenerator() {
return new KeyGenerator() {
@Override
public Object generate(Object o, Method method, Object... params) {
StringBuilder sb = new StringBuilder();
sb.append(o.getClass().getName());
sb.append(method.getName());
for (Object param : params) {
sb.append(param.toString());
}
return sb.toString();
}
};
}
@Override
public CacheErrorHandler errorHandler() {
return null;
}
}
之前已经通过注解声明了缓存内容:
ScheduleResponseService.java
@Cacheable(value = "hourCache", unless="#result == null")
public ScheduleResponse getYearsTypeSummary(HttpServletRequest request) {
ScheduleResponse scheduleResponse = new ScheduleResponse();
YearsType yearsType = null;
String exceptionMsg ="Could not retrieve list of summary years for Schedule.";
scheduleResponse.setMessageText(exceptionMsg);
try {
yearsType = scheduleApiClient.getYears(authenticator.getAuthorizationToken(request), SUMMARY);
} catch (ClientResponseFailure ex) {
handleClientResponseFailure(scheduleResponse, ex.getResponse(), exceptionMsg);
} catch (PrivilegedActionException | GSSException ex) {
handleGenericException(scheduleResponse, ex.getMessage(), exceptionMsg);
} catch (Exception ex) {
handleGenericResponseException(scheduleResponse, ex.getMessage(), exceptionMsg, request);
}
scheduleResponse.setYearsType(yearsType);
return scheduleResponse;
}
但是,当我尝试为以前未缓存的同一个 Java 文件中的另一个函数扩展相同的基于注释的缓存声明时,即:
@Cacheable(value = "hourCache")
public ScheduleResponse getTermTypeSummary(HttpServletRequest request, String year, String term, String sess) {
ScheduleResponse scheduleResponse = new ScheduleResponse();
TermType termType = null;
String exceptionMsg = "Sorry! Could not retrieve the " + term.toUpperCase() + " " + year + " term.";
scheduleResponse.setMessageText(exceptionMsg);
try {
termType = scheduleApiClient.getTerm(authenticator.getAuthorizationToken(request), year, term, SUMMARY, sess);
} catch (ClientResponseFailure ex) {
handleClientResponseFailure(scheduleResponse, ex.getResponse(), exceptionMsg);
} catch (PrivilegedActionException | GSSException ex) {
handleGenericException(scheduleResponse, ex.getMessage(), exceptionMsg);
} catch (Exception ex) {
handleGenericResponseException(scheduleResponse, ex.getMessage(), exceptionMsg, request);
}
scheduleResponse.setTermType(termType);
return scheduleResponse;
}
我收到 NullPointerException 错误:
webmvc.config.CacheConfig$1.generate(CacheConfig.java:48) org.springframework.cache.interceptor.CacheAspectSupport$CacheOperationContext.generateKey(CacheAspectSupport.java:637)
org.springframework.cache.interceptor.CacheAspectSupport.generateKey(CacheAspectSupport.java:490)
org.springframework.cache.interceptor.CacheAspectSupport.findCachedItem(CacheAspectSupport.java:434)
org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:336)
org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:302)
org.springframework.cache.interceptor.CacheInterceptor.invoke(CacheInterceptor.java:61)
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:655)
webmvc.response.ScheduleResponseService$$EnhancerBySpringCGLIB$$952eed68.getTermTypeSummary()
webmvc.courses.controller.ScheduleController.displayScheduleTerm(ScheduleController.java:79)
我可以将@Cacheable... 注释添加到任何函数以接收NullPointerException。我不明白为什么我无法将缓存扩展到新函数,因为它似乎没有进行额外的声明或注释来支持代码中其他任何地方的缓存。
有人可以指出我在以这种方式扩展缓存时可能做错了什么,以及如何纠正它?
【问题讨论】:
-
之前的配置是什么?
-
getTermTypeSummary之前的配置?那个函数是完全一样的,我只是在它的顶部添加了@Cacheable(value = "hourCache") 注释。 CacheConfig 配置也没有改变。据我了解,它不需要进行更改以允许发生额外的缓存。
-
它们不一样,缓存也不会真正起作用。缓存键是方法参数,HttpServletRequest 永远不会相同,因此渲染缓存无用,因为它永远不会导致缓存命中。
-
真正的问题是您想要尝试的自定义密钥生成器。它假定所有参数都存在,当其中一个为空时,它将中断。
-
你能解释一下“他们不一样”是什么意思吗?在我编译的代码中,我实际上只添加了 @Cacheable... 注释,所以我不确定我理解那里的意思。