带有key =“#id”的@CacheEvict抛出NullPointerException

时间:2015-10-12 14:05:02

标签: spring-cache google-guava-cache

我正在尝试将Spring Caching注释@Cacheable@CacheEvict与GuavaCacheManager一起使用。

我用这两个测试创建了一个测试用例:

  1. cachesById - 验证对使用@Cacheable注释的方法的两次调用是否返回同一对象
  2. evict - 如果在这两个调用之间调用了使用@CacheEvict注释的方法,则验证是否返回了两个不同的实例
  3. 当我没有为@CacheEvict指定密钥时,两者都工作正常,但是当我这样做时,我得到以下异常:

    java.lang.NullPointerException
        at com.google.common.base.Preconditions.checkNotNull(Preconditions.java:210)
        at com.google.common.cache.LocalCache$LocalManualCache.invalidate(LocalCache.java:4764)
        at org.springframework.cache.guava.GuavaCache.evict(GuavaCache.java:135)
        at org.springframework.cache.interceptor.AbstractCacheInvoker.doEvict(AbstractCacheInvoker.java:95)
        at org.springframework.cache.interceptor.CacheAspectSupport.performCacheEvict(CacheAspectSupport.java:409)
        at org.springframework.cache.interceptor.CacheAspectSupport.processCacheEvicts(CacheAspectSupport.java:392)
        at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:362)
        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.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:653)
        at com.myorg.caching.CacheTest$Repo$$EnhancerBySpringCGLIB$$eed50f3e.update(<generated>)
        at com.myorg.caching.CacheTest.evict(CacheTest.java:50)
    

    这可以通过执行以下测试来重现。

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(
            classes = { Repo.class, CacheTest.SpringConfig.class },
            loader = AnnotationConfigContextLoader.class)
    public class CacheTest {
    
        private static final String CACHE_NAME = "cacheName";
    
        @Inject
        private Repo repo;
    
        @Test
        public void cachesById() {
            Entity aResult1 = repo.getEntity(1);
            Entity aResult2 = repo.getEntity(1);
            assertEquals(aResult1.getId(), aResult2.getId());
            assertSame(aResult1, aResult2);
        }
    
        @Test
        public void evict() {
            Entity aResult1 = repo.getEntity(1);
            repo.update(aResult1);
            Entity aResult2 = repo.getEntity(1);
            assertEquals(aResult1.getId(), aResult2.getId());
            assertNotSame(aResult1, aResult2);
        }
    
        /** Mock repository/entity classes below. */
    
        @Component
        public static class Repo {
    
            @Cacheable(value = CACHE_NAME, key = "#id")
            public Entity getEntity(int id) {
                return new Entity(id);
            }
    
            @CacheEvict(value = CACHE_NAME, key = "#id")
            public void update(Entity e) {    
            }
        }
    
    
        public static class Entity {
            private int id;
    
            public Entity(int id) {
                super();
                this.id = id;
            }
    
            public int getId() {
                return id;
            }
    
            public void setId(int id) {
                this.id = id;
            }
        }
    
        /** Guava Cachemanager Spring configuration */
    
        @Configuration
        @EnableCaching
        public static class SpringConfig {
            @Bean
            public CacheManager cacheManager() {
                GuavaCacheManager manager = new GuavaCacheManager(CACHE_NAME);
                manager.setCacheBuilder(CacheBuilder.newBuilder().expireAfterWrite(
                        1, TimeUnit.MINUTES).recordStats());
                return manager;
            }
        }
    }
    

    但是,如果我改变

    ,测试通过
    @CacheEvict(value = CACHE_NAME, key = "#id")
    public void update(Entity e) {
    

    成:

    @CacheEvict(value = CACHE_NAME)
    public void update(Entity e) {
    

    ..但是我忽略了我需要为Entity指定缓存键的地方。有谁知道我错过了什么?

    谢谢!

1 个答案:

答案 0 :(得分:10)

您必须从

修复组件类
@Component
public static class Repo {

    @Cacheable(value = CACHE_NAME, key = "#id")
    public Entity getEntity(int id) {
        return new Entity(id);
    }

    @CacheEvict(value = CACHE_NAME, key = "#id")
    public void update(Entity e) {    
    }
}

@Component
public static class Repo {

    @Cacheable(value = CACHE_NAME, key = "#id")
    public Entity getEntity(int id) {
        return new Entity(id);
    }

    @CacheEvict(value = CACHE_NAME, key = "#e?.id")
    public void update(Entity e) {    
    }
}

为什么呢?在getEntity方法中,您使用Entity缓存int id对象,您必须将相同的int id传递到@CacheEvict带注释的方法。您不必更改方法的签名 - 通过使用SPEL,您可以“进入”实体并使用其id字段。

希望我帮助过。