Статьи

Spring 3.1 Caching и @CacheEvict

В моем последнем блоге было продемонстрировано применение аннотации @Cacheable в Spring 3.1, которая используется для маркировки методов, возвращаемые значения которых будут храниться в кэше. Тем не менее, @Cacheable — это только одна из пары аннотаций, которые разработчики Spring разработали для кэширования, а другая — @CacheEvict .

Как и @Cacheable , @CacheEvict имеет @CacheEvict value , key и condition . Они работают точно так же, как и те, которые поддерживаются @Cacheable , поэтому дополнительную информацию о них смотрите в моем предыдущем блоге: Spring 3.1 Caching и @Cacheable .

CacheEvict поддерживает два дополнительных атрибута: allEntries и beforeInvocation . Если бы я был азартным человеком, я бы положил деньги на самый популярный из них — allEntries . allEntries используется для полной очистки содержимого кэша, определенного @CacheEvict обязательного value @CacheEvict . Метод ниже демонстрирует, как применить allEntries :

1
2
3
4
@CacheEvict(value = "employee", allEntries = true)
public void resetAllEntries() {
  // Intentionally blank
}

resetAllEntries() устанавливает для @CacheEvict allEntries значение «true» и, предполагая, что метод findEmployee(...) выглядит следующим образом:

1
2
3
4
5
@Cacheable(value = "employee")
public Person findEmployee(String firstName, String surname, int age) {
 
  return new Person(firstName, surname, age);
}

… Затем в следующем коде resetAllEntries() очистит кэш «сотрудников». Это означает, что в приведенном ниже тесте JUnit employee1 не будет ссылаться на тот же объект, что и employee2 :

1
2
3
4
5
6
7
8
9
@Test
public void testCacheResetOfAllEntries() {
 
  Person employee1 = instance.findEmployee("John", "Smith", 22);
  instance.resetAllEntries();
  Person employee2 = instance.findEmployee("John", "Smith", 22);
 
  assertNotSame(employee1, employee2);
}

Второй атрибут — beforeInvocation . Это определяет, удаляются ли элемент (ы) данных из кэша до или после вызова вашего метода.

Код ниже довольно бессмысленный; тем не менее, он демонстрирует, что вы можете одновременно применять @CacheEvict и @Cacheable к методу.

1
2
3
4
5
6
@CacheEvict(value = "employee", beforeInvocation = true)
@Cacheable(value = "employee")
public Person evictAndFindEmployee(String firstName, String surname, int age) {
 
  return new Person(firstName, surname, age);
}

В приведенном выше коде @CacheEvict удаляет все записи в кэше с соответствующим ключом, прежде чем @Cacheable поиск в кэше. Поскольку @Cacheable не найдет никаких записей, он вызовет мой код, хранящий результат в кэше. Последующий вызов моего метода вызовет @CacheEvict который удалит все соответствующие записи, в результате чего в тесте JUnit ниже переменной employee1 никогда не будет ссылаться на тот же объект, что и employee2 :

1
2
3
4
5
6
7
8
@Test
public void testBeforeInvocation() {
 
  Person employee1 = instance.evictAndFindEmployee("John", "Smith", 22);
  Person employee2 = instance.evictAndFindEmployee("John", "Smith", 22);
 
  assertNotSame(employee1, employee2);
}

Как я уже говорил выше, evictAndFindEmployee(...) кажется несколько бессмысленным, поскольку я применяю и @Cacheable и @CacheEvict к @Cacheable и @CacheEvict же методу. Но более того, это делает код неясным и нарушает принцип единой ответственности; следовательно, я бы рекомендовал создать отдельные методы кеширования и кеширования. Например, если у вас есть метод кэширования, такой как:

1
2
3
4
5
@Cacheable(value = "employee", key = "#surname")
public Person findEmployeeBySurname(String firstName, String surname, int age) {
 
  return new Person(firstName, surname, age);
}

затем, предполагая, что вам нужно более точное управление кэшем, чем простое «прозрачное все», вы можете легко определить его аналог:

1
2
3
4
5
@CacheEvict(value = "employee", key = "#surname")
public void resetOnSurname(String surname) {
  // Intentionally blank
 
}

Это простой пустой маркерный метод, который использует то же выражение SpEL, которое применялось к @Cacheable чтобы @Cacheable все экземпляры Person из кэша, где ключ соответствует аргументу ‘фамилия’.

1
2
3
4
5
6
7
8
@Test
public void testCacheResetOnSurname() {
 
  Person employee1 = instance.findEmployeeBySurname("John", "Smith", 22);
  instance.resetOnSurname("Smith");
  Person employee2 = instance.findEmployeeBySurname("John", "Smith", 22);
  assertNotSame(employee1, employee2);
}

В приведенном выше коде первый вызов findEmployeeBySurname(...) создает объект Person , который Spring сохраняет в кэше «employee» с ключом, определенным как «Smith». Вызов resetOnSurname(...) очищает все записи из кэша «employee» с фамилией «Smith», и, наконец, второй вызов findEmployeeBySurname(...) создает новый объект Person , который Spring снова сохраняет в « сотрудник »кеш с ключом« кузнец ». Следовательно, переменные employee1 и employee2 не ссылаются на один и тот же объект.

Изучив аннотации кэширования Spring, следующая часть головоломки заключается в том, чтобы разобраться с настройкой практического кэширования: как включить кэширование Spring и какую реализацию кэширования следует использовать? Подробнее об этом позже …

Приятного кодирования и не забудьте поделиться!

Ссылка: Spring 3.1 Caching и @CacheEvict от нашего партнера по JCG Роджера Хьюза в блоге Captain Debug’s Blog .