Java Spring 4.2.5 Guava缓存无法添加其他@Cacheable批注

时间:2018-07-17 17:19:45

标签: java spring caching guava

我正在开发一个现有的Java 8 / Spring 4.2.5 Web应用程序,并尝试将其已经工作的缓存(使用Spring对Guava Cache的支持)扩展到更多功能,以期改善性能。该应用程序利用了其CacheConfig文件中设置的Spring 4的内置缓存:

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。我不明白为什么我无法将缓存扩展到新功能,因为它没有出现额外的声明或注释来支持代码中其他任何地方的缓存。

有人可以指出我在以这种方式扩展缓存时可能做错了什么,以及如何纠正它吗?

1 个答案:

答案 0 :(得分:0)

正如M. Deinum所说,自定义密钥生成器就是问题所在。当参数之一为null时,密钥生成器将触发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) {
                    if (param != null) {
                        sb.append(param.toString());
                    }
                }
                return sb.toString();
            }
        };
    }

它解决了到目前为止我测试过的所有情况的空指针异常。