1. Обзор
Давайте рассмотрим пример использования Ehcache с Spring Boot. Мы будем использовать Ehcache версии 3, так как она обеспечивает реализацию менеджера кэша JSR-107 .
Примером является простая служба REST, которая производит квадрат числа.
2. Зависимости
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.6.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
<version>2.6.1</version></dependency>
<dependency>
<groupId>javax.cache</groupId>
<artifactId>cache-api</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>3.8.1</version>
</dependency>
весна-загрузка-стартер-сеть
весна-загрузка-стартер-кэш
javax.cache: кеш-API
org.ehcache: ehcache
3. Пример
Давайте создадим простой контроллер REST, который вызывает службу для возведения числа в квадрат и возвращает результат в виде строки JSON:
@RestController
@RequestMapping("/number", MediaType.APPLICATION_JSON_UTF8_VALUE)
public class NumberController {
// ...
@Autowired
private NumberService numberService;
@GetMapping(path = "/square/{number}")
public String getSquare(@PathVariable Long number) {
log.info("call numberService to square {}", number);
return String.format("{\"square\": %s}", numberService.square(number));
}
}
Теперь давайте создадим сервис.
Мы аннотируем метод @Cacheable
, чтобы Spring обрабатывал кэширование. В результате этой аннотации Spring создаст прокси NumberService
для перехвата вызовов квадратного
метода и вызова Ehcache.
Нам нужно указать имя используемого кеша и, возможно, ключ. Мы также можем добавить условие для ограничения того, что кешируется:
@Service
public class NumberService {
// ...
@Cacheable(
value = "squareCache",
key = "#number",
condition = "#number>10")
public BigDecimal square(Long number) {
BigDecimal square = BigDecimal.valueOf(number)
.multiply(BigDecimal.valueOf(number));
log.info("square of {} is {}", number, square);
return square;
}
}
Наконец, давайте создадим наше основное приложение Spring Boot:
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
4. Конфигурация кэша
Нам нужно добавить аннотацию Spring @EnableCaching к bean-компоненту Spring, чтобы включить управление кэшем Spring на основе аннотаций.
Давайте создадим класс CacheConfig
:
@Configuration
@EnableCaching
public class CacheConfig {
}
Автоконфигурация Spring находит реализацию Ehcache JSR-107. Однако по умолчанию кэши не создаются.
Потому что ни Spring, ни Ehcache не ищут файл ehcache.xml по умолчанию
. Мы добавляем следующее свойство, чтобы сообщить Spring, где его найти:
spring.cache.jcache.config=classpath:ehcache.xml
Давайте создадим файл ehcache.xml
с кешем, который называется SquareCache
:
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.ehcache.org/v3"
xmlns:jsr107="http://www.ehcache.org/v3/jsr107"
xsi:schemaLocation="
http://www.ehcache.org/v3 http://www.ehcache.org/schema/ehcache-core-3.0.xsd
http://www.ehcache.org/v3/jsr107 http://www.ehcache.org/schema/ehcache-107-ext-3.0.xsd">
<cache alias="squareCache">
<key-type>java.lang.Long</key-type>
<value-type>java.math.BigDecimal</value-type>
<expiry>
<ttl unit="seconds">30</ttl>
</expiry>
<listeners>
<listener>
<class>com.foreach.cachetest.config.CacheEventLogger</class>
<event-firing-mode>ASYNCHRONOUS</event-firing-mode>
<event-ordering-mode>UNORDERED</event-ordering-mode>
<events-to-fire-on>CREATED</events-to-fire-on>
<events-to-fire-on>EXPIRED</events-to-fire-on>
</listener>
</listeners>
<resources>
<heap unit="entries">2</heap>
<offheap unit="MB">10</offheap>
</resources>
</cache>
</config>
И давайте также добавим прослушиватель событий кеша, который регистрирует как CREATED
, так и EXPIRED
события кеша:
public class CacheEventLogger
implements CacheEventListener<Object, Object> {
// ...
@Override
public void onEvent(
CacheEvent<? extends Object, ? extends Object> cacheEvent) {
log.info(/* message */,
cacheEvent.getKey(), cacheEvent.getOldValue(), cacheEvent.getNewValue());
}
}
5. В действии
Мы можем использовать Maven для запуска этого приложения, запустив mvn spring-boot:run
.
Затем откройте браузер и получите доступ к службе REST через порт 8080.
Если мы перейдем на http://localhost:8080/number/square/12 ,
то получим обратно {“square”:144}
, а в логе увидим:
INFO [nio-8080-exec-1] c.b.cachetest.rest.NumberController : call numberService to square 12
INFO [nio-8080-exec-1] c.b.cachetest.service.NumberService : square of 12 is 144
INFO [e [_default_]-0] c.b.cachetest.config.CacheEventLogger : Cache event CREATED for item with key 12. Old value = null, New value = 144
Мы можем увидеть сообщение журнала из квадратного
метода NumberService
и событие CREATED
из EventLogger. Если мы затем обновим браузер, мы увидим только следующее, добавленное в журнал:
INFO [nio-8080-exec-2] c.b.cachetest.rest.NumberController : call numberService to square 12
Сообщение журнала в квадратном
методе NumberService
не вызывается. Это показывает нам, что кэшированное значение используется.
Если мы подождем 30 секунд до истечения срока действия кэшированного элемента и обновим браузер, мы увидим событие EXPIRED
, а значение будет добавлено обратно в кеш:
INFO [nio-8080-exec-1] (...) NumberController : call numberService to square 12
INFO [e [_default_]-1] (...) CacheEventLogger : Cache event EXPIRED for item with key 12. Old value = 144,New value = null
INFO [nio-8080-exec-1] (... )NumberService : square of 12 is 144
INFO [e [_default_]-1] (...) CacheEventLogger : Cache event CREATED for item with key 12. Old value = null, New value = 144
Если мы введем http://localhost:8080/number/square/3
в браузере, мы получим правильный ответ 9, но значение не кэшируется.
Это связано с условием, которое мы использовали в аннотации @Cacheable
, чтобы кэшировать только значения для чисел выше 10.
6. Заключение
В этом кратком руководстве мы показали, как настроить Ehcache с помощью Spring Boot.
Как всегда, код можно найти на GitHub .