【问题标题】:@CacheEvict with key="#id" throws NullPointerException@CacheEvict with key="#id" 抛出 NullPointerException
【发布时间】:2016-01-10 01:19:18
【问题描述】:

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

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

  1. cachesById - 验证对带有 @Cacheable 注释的方法的两次调用返回相同的对象
  2. evict - 如果在这两个调用之间调用了带有 @CacheEvict 注释的方法,则验证是否返回两个不同的实例

当我没有为 @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 指定缓存键的地方。有谁知道我错过了什么?

谢谢!

【问题讨论】:

  • 没有名为 id 的方法参数,所以它指的是什么,在 java 中没有又名 null。您有一个名为 e 的参数,其类型为 Entity,它可能具有 id 属性。将#id 更改为#e.id。我建议阅读 spring 缓存文档。

标签: spring-cache google-guava-cache


【解决方案1】:

你必须修复你的组件类

@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 方法中,您使用int id 缓存Entity 对象,您必须将相同的int id 传递给@CacheEvict 注释方法。您不必更改方法的签名 - 通过使用 SPEL,您可以“进入”实体并使用其 id 字段。

希望我能帮上忙。

【讨论】:

  • 为什么key = "#e?.id"中有?
  • @Thanga 有可能 Entity e (#e) 可能为 null,因此访问 id 属性将导致 NullPointerException? 符号表明该属性可能为空,并且只有当它不为空时,程序才应访问id。见:link
  • 感谢您的解释@Trynkiewicz Mariusz .. 赞成
  • 这对我不起作用。它只是将异常更改为 NPE
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-08-11
  • 2013-09-07
  • 2013-06-18
  • 2016-07-07
  • 2014-10-02
  • 2012-05-21
  • 2014-08-15
相关资源
最近更新 更多