2015-10-12 1 views
0

J'essaie d'utiliser les annotations Spring Caching @Cacheable et @CacheEvict avec le GuavaCacheManager.@CacheEvict avec key = "# id" throws NullPointerException

J'ai créé un test avec ces deux tests:

  1. cachesById - vérifie que deux invocations à une méthode annotatted avec @Cacheable retourne le même objet
  2. evict - vérifie que deux cas différents sont renvoyés si une méthode annotée avec @CacheEvict est appelé entre ces deux invocations

les deux fonctionnent très bien quand je ne précise pas une clé pour @CacheEvict , cependant quand je fais je reçois l'exception suivante:

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) 

Ceci peut être reproduit en exécutant le test ci-dessous.

@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; 
     } 
    } 
} 

Cependant, le test passe si je change

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

dans:

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

..mais je me manque le point où je dois spécifier la clé du cache pour Entity . Est-ce que quelqu'un sait ce que je manque?

Merci!

+0

Il n'y a pas de paramètre de méthode nommé 'id', à quoi fait-il référence, rien AKA' null' dans java. Vous avez un paramètre nommé 'e' de type' Entity' qui a probablement une propriété 'id'. Changez "# id" en '# e.id'. Et je suggère une lecture sur la documentation de mise en cache de printemps. –

Répondre

6

Vous devez vous fixer la classe de composants

@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) {  
    } 
} 

Pourquoi? Dans la méthodevous mettez en cache un objet Entity en utilisant int id, vous devez passer le même int id dans la méthode annotée @CacheEvict. Vous n'avez pas besoin de changer la signature de la méthode - en utilisant SPEL vous pouvez entrer dans l'entité et utiliser son champ id.

J'espère avoir aidé.

+1

Pourquoi il y a un '?' Dans 'key =" #e? .id "' – Thanga

+4

@Thanga il y a chance que ** Entity e ** ('# e') soit nul et, par conséquent, accédant' id' La propriété provoquerait 'NullPointerException'. Le signe '?' indique que cette propriété pourrait être nulle et seulement si ce n'est pas un programme nul devrait accéder 'id'. Voir: [lien] (http://docs.spring.io/spring/docs/current/spring-framework-reference/html/expressions.html#expressions-operator-safe-navigation) –

+0

Merci pour l'explication @Trynkiewicz Mariusz .. Mis en colère – Thanga