Кэширование Spring не синхронизировано

У меня есть одноэлементный класс (@Service с аннотацией). Этот класс имеет метод, выполнение которого занимает 200/300 мс.

Этот метод помечен @Cacheable и синхронизирован.

@Cacheable(value="nextPlaying", key = "#startingFrom.getYear() + #startingFrom.getMonth() + #startingFrom.getDay() + #startingFrom.getHours() + #startingFrom.getMinutes() + #locale.getLanguage()")
public synchronized List<Match> getNextPlaying(Date startingFrom, Locale locale)

Запуская несколько потоков, вызывающих этот метод, я вижу, что в течение этих 200/300 мс, пока результат не будет кэширован, он снова и снова выполняет метод, пока не будет кэширован. Похоже, что аннотация @Cacheable не учитывает synchronized... Это ошибка?


person ianaz    schedule 05.03.2015    source источник
comment
Поддержка синхронизированных кешей добавлена ​​в Spring 4.3: spring.io/blog/2016/03/04/   -  person Thomas    schedule 07.04.2016


Ответы (2)


Когда вы используете аннотацию @Cacheable, код, реализующий поиск в кеше, находится за пределами вашего метода. Поэтому модификатор synchronized на него не влияет.

Если вы хотите, чтобы все потоки использовали кешированный результат, вы должны создать синхронизированный метод, который переносит вызов в кешируемый метод getNextPlaying. Что-то вроде этого:

public synchronized List<Match> getNextPlayingSynchronized(Date startingFrom, Locale locale){
     return getNextPlaying(Date startingFrom, Locale locale);
}
...
@Cacheable(value="nextPlaying", key = "#startingFrom.getYear() + #startingFrom.getMonth() + #startingFrom.getDay() + #startingFrom.getHours() + #startingFrom.getMinutes() + #locale.getLanguage()")
public List<Match> getNextPlaying(Date startingFrom, Locale locale){
...//your old method without the synchronized modifier
}

Важно, чтобы эти методы находились в разных классах. В противном случае аспекты не работают.

person Andres    schedule 05.03.2015
comment
Хорошее решение, но тогда оно ничего не кэширует, никогда: S Я не могу понять, почему - person ianaz; 06.03.2015
comment
@аназ. Поместите методы в разные классы. В противном случае аспекты не работают. - person Andres; 06.03.2015
comment
Это не работает, потому что spring создал прокси-класс, поскольку вы вызываете @Cachable из того же класса, он не передает прокси и поэтому не будет кэшироваться. a) поместите его в другой класс b) используйте cglib c) используйте ссылку на себя (импортируйте свой собственный класс \\@Autowired MyClass self; затем вызовите self.getNextPlaying) - person R.S; 16.01.2020

Хорошие новости: Spring Framework 4.3 предоставил способ удовлетворить ваши потребности, добавив sync=true в @Cacheable.

person Clement.Xu    schedule 12.09.2016
comment
здесь › docs.spring.io/spring-framework/docs/current/ также говорит, что ваш поставщик кеша должен поддерживать реализацию синхронизированного кеша... - person Dzmitry Prakapenka; 11.07.2018