spring-by-pivotal

logo ehcache

Je suis récemment tombé sur le problème suivant dans l’intégration de Spring et Ehcache : Le cache ne fonctionne pas quand une méthode publique d’un service appelle une autre méthode publique du même service et soumise au cache.

Voici l’exemple en question :

@Service
public class DateService {

 @Cacheable("Date")
 public Date findCurrentDate(){
  ...
 }

 public Date findNextDate(){
  Date currentDate = findCurrentDate();
  ...
 }

}

Ici, à chaque fois qu’on appellera findCurrentDate() depuis findNextDate() la Date ne sera jamais mise en cache et jamais lue depuis le cache.

Voici un extrait de la documentation Spring qui explique le pourquoi du comment :

In proxy mode (which is the default), only external method calls coming in through the proxy are intercepted. This means that self-invocation, in effect, a method within the target object calling another method of the target object, will not lead to an actual caching at runtime even if the invoked method is marked with @Cacheable – considering using the aspectj mode in this case.

Method visibility and @Cacheable/@CachePut/@CacheEvict
When using proxies, you should apply the @Cache* annotations only to methods with public visibility. If you do annotate protected, private or package-visible methods with these annotations, no error is raised, but the annotated method does not exhibit the configured caching settings. Consider the use of AspectJ (see below) if you need to annotate non-public methods as it changes the bytecode itself.

Deux solutions possibles :

  • Soit on change le design et on descend la méthode findCurrentDate() d’un niveau.
  • Soit on applique un contournement, qui charge le service lui même, afin de forcer un nouvel appel au proxy (voir ci-dessous).
@Service
public class DateService {

 @Autowired
 private ApplicationContext applicationContext;

 private DateService self;

 @PostConstruct
 private void init() {
  self = applicationContext.getBean(DateService);
 }

 @Cacheable("Date")
 public Date findCurrentDate(){
  ...
 }

 public Date findNextDate(){
  Date currentDate = self.findCurrentDate();
  ...
 }

}
Publicités