web-dev-qa-db-fra.com

La méthode Spring Cache @Cacheable est ignorée lorsqu'elle est appelée à partir de la même classe

J'essaie d'appeler un @Cacheable méthode de la même classe:

@Cacheable(value = "defaultCache", key = "#id")
public Person findPerson(int id) {
   return getSession().getPerson(id);
} 

public List<Person> findPersons(int[] ids) {
   List<Person> list = new ArrayList<Person>();
   for (int id : ids) {
      list.add(findPerson(id));
   }
   return list;
}

et en espérant que les résultats de findPersons sont également mis en cache, mais le @Cacheable l'annotation est ignorée et la méthode findPerson est exécutée à chaque fois.

Suis-je en train de faire quelque chose de mal ici, ou c'est prévu?

41
David Zhao

Cela est dû à la façon dont les proxys sont créés pour gérer la mise en cache, les fonctionnalités liées aux transactions dans Spring. Ceci est une très bonne référence sur la façon dont Spring le gère - Transactions, mise en cache et AOP: comprendre l'utilisation du proxy dans Spring

En bref, un appel automatique contourne le proxy dynamique et tout problème transversal comme la mise en cache, la transaction, etc. qui fait partie de la logique des proxy dynamiques est également contourné.

Le correctif consiste à utiliser le temps de compilation AspectJ ou le tissage du temps de chargement.

45
Biju Kunjummen

Voici ce que je fais pour les petits projets avec une utilisation marginale des appels de méthode dans la même classe. La documentation en code est fortement conseillée, car elle peut sembler étrange à ses collègues. Mais c'est facile à tester, simple, rapide à réaliser et m'épargne la pleine instrumentation AspectJ. Cependant, pour une utilisation plus intensive, je conseillerais la solution AspectJ.

@Service
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
class PersonDao {

    private final PersonDao _personDao;

    @Autowired
    public PersonDao(PersonDao personDao) {
        _personDao = personDao;
    }

    @Cacheable(value = "defaultCache", key = "#id")
    public Person findPerson(int id) {
        return getSession().getPerson(id);
    }

    public List<Person> findPersons(int[] ids) {
        List<Person> list = new ArrayList<Person>();
        for (int id : ids) {
            list.add(_personDao.findPerson(id));
        }
        return list;
    }
}
18
Mario Eis

Pour tous ceux qui utilisent le plug-in Grails Spring Cache , ne solution de contournement est décrite dans la documentation . J'ai eu ce problème sur une application Grails, mais malheureusement, la réponse acceptée semble inutilisable pour Grails. La solution est moche, à mon humble avis, mais cela fonctionne.

L'exemple de code le montre bien:

class ExampleService {
    def grailsApplication

    def nonCachedMethod() {
        grailsApplication.mainContext.exampleService.cachedMethod()
    }

    @Cacheable('cachedMethodCache')
    def cachedMethod() {
        // do some expensive stuff
    }
}

Remplacez simplement exampleService.cachedMethod () par votre propre service et méthode.

2
Dónal Boyle