В приведенном ниже примере я покажу вам, как использовать проект Spring Data — Redis в качестве поставщика кэширования для Spring Cache Abstraction, представленной в Spring 3.1. У меня много вопросов о том, как использовать конфигурацию Spring на Java, поэтому я предоставлю для вашего обзора конфигурации на основе XML и Java.
зависимости
В этом примере были использованы следующие зависимости:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
<? xml version = '1.0' encoding = 'UTF-8' ?> < modelVersion >4.0.0</ modelVersion > < groupId >com.joshuawhite.example</ groupId > < artifactId >spring-redis-example</ artifactId > < version >1.0</ version > < packaging >jar</ packaging > < name >Spring Redis Example</ name > < dependencies > < dependency > < groupId >org.springframework.data</ groupId > < artifactId >spring-data-redis</ artifactId > < version >1.0.2.RELEASE</ version > </ dependency > <!-- required for @Configuration annotation --> < dependency > < groupId >cglib</ groupId > < artifactId >cglib</ artifactId > < version >2.2.2</ version > </ dependency > < dependency > < groupId >redis.clients</ groupId > < artifactId >jedis</ artifactId > < version >2.0.0</ version > < type >jar</ type > < scope >compile</ scope > </ dependency > < dependency > < groupId >log4j</ groupId > < artifactId >log4j</ artifactId > < version >1.2.14</ version > </ dependency > </ dependencies > < build > < plugins > < plugin > < groupId >org.apache.maven.plugins</ groupId > < artifactId >maven-compiler-plugin</ artifactId > < configuration > < source >1.6</ source > < target >1.6</ target > </ configuration > </ plugin > </ plugins > </ build > </ project > |
Код и конфигурация
HelloService
ниже очень прост. Как вы увидите в реализации, он просто возвращает строку с «Hello», добавленной перед именем, которое передается.
1
2
3
4
5
6
7
|
package com.joshuawhite.example.service; public interface HelloService { String getMessage(String name); } |
Глядя на класс HelloServiceImpl
(ниже), вы видите, что я использую аннотацию Spring @Cacheable для добавления возможностей кэширования в метод getMessage
. Для получения более подробной информации о возможностях этой аннотации ознакомьтесь с документацией Cache Abstraction . Для развлечения я использую язык выражений Spring (SpEL) для определения условия. В этом примере ответ методов будет кэшироваться только в том случае, если передано имя «Иисус Навин».
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
|
package com.joshuawhite.example.service; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; @Service ( 'helloService' ) public class HelloServiceImpl implements HelloService { /** * Using SpEL for conditional caching - only cache method executions when * the name is equal to 'Joshua' */ @Cacheable (value= 'messageCache' , condition= '' Joshua '.equals(#name)' ) public String getMessage(String name) { System.out.println( 'Executing HelloServiceImpl' + '.getHelloMessage(\'' + name + '\')' ); return 'Hello ' + name + '!' ; } } |
Класс App
ниже содержит наш метод main
и используется для выбора конфигураций на основе XML и Java. Каждый из System.out.println
используется для демонстрации того, когда происходит кэширование. Напоминаем, что мы ожидаем, что только кэширование выполнений методов, переданных в «Joshua». Это будет более ясно, когда мы посмотрим на вывод программ позже.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
package com.joshuawhite.example; import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.support.GenericXmlApplicationContext; import com.joshuawhite.example.config.AppConfig; import com.joshuawhite.example.service.HelloService; public class App { public static void main(String[] args) { boolean useJavaConfig = true ; ApplicationContext ctx = null ; //Showing examples of both Xml and Java based configuration if (useJavaConfig ) { ctx = new AnnotationConfigApplicationContext(AppConfig. class ); } else { ctx = new GenericXmlApplicationContext( '/META-INF/spring/app-context.xml' ); } HelloService helloService = ctx.getBean( 'helloService' , HelloService. class ); //First method execution using key='Josh', not cached System.out.println( 'message: ' + helloService.getMessage( 'Josh' )); //Second method execution using key='Josh', still not cached System.out.println( 'message: ' + helloService.getMessage( 'Josh' )); //First method execution using key='Joshua', not cached System.out.println( 'message: ' + helloService.getMessage( 'Joshua' )); //Second method execution using key='Joshua', cached System.out.println( 'message: ' + helloService.getMessage( 'Joshua' )); System.out.println( 'Done.' ); } } |
Обратите внимание, что компонентное сканирование все еще используется при использовании конфигурации на основе XML. Вы можете видеть, что я использую аннотацию @Service
в строке 6 HelloServiceImpl.java
выше. Далее мы рассмотрим, как настроить jedisConnectionFactory
, redisTemplate
и cacheManager
.
Конфигурирование JedisConnectionFactory
В этом примере я выбрал Jedis в качестве нашего предпочтительного клиента Java, поскольку он указан на сайте Redis как «рекомендуемая» клиентская библиотека для Java. Как видите, настройка очень проста. Хотя я явно устанавливаю use-pool = true, исходный код указывает, что это значение по умолчанию. JedisConnectionFactory также предоставляет следующие значения по умолчанию, если они не установлены явно:
- Hostname =»локальный»
- Порт = 6379
- время ожидания = 2000 мс
- базы данных = 0
- usePool = TRUE
Примечание. Хотя индекс базы данных настраивается, JedisConnectionFactory
поддерживает подключение только к одной базе данных Redis одновременно. Поскольку Redis является однопоточным, рекомендуется настроить несколько экземпляров Redis вместо использования нескольких баз данных в одном процессе. Это позволяет вам лучше использовать процессор / ресурсы. Если вы планируете использовать redis-cluster, поддерживается только одна база данных. Для получения дополнительной информации о значениях по умолчанию, используемых в пуле соединений, посмотрите на реализацию JedisPoolConfig
или JedisPoolConfig
Apache Commons Pool org.apache.commons.pool.impl.GenericObjectPool.Config
и его приложения.
org.apache.commons.pool.impl.GenericObjectPool
класс.
Настройка RedisTemplate
Как и следовало ожидать от класса «template» Spring, RedisTemplate
заботится о сериализации и управлении соединениями и (при условии, что вы используете пул соединений) является поточно-ориентированным. По умолчанию RedisTemplate использует сериализацию Java ( JdkSerializationRedisSerializer
). Обратите внимание, что сериализация данных в Redis делает Redis «непрозрачным» кешем. В то время как другие сериализаторы позволяют отображать данные в Redis, я обнаружил, что сериализация, особенно при работе с графами объектов, быстрее и проще в использовании. При этом, если у вас есть требование, чтобы другие не Java-приложения могли иметь доступ к этим данным, сопоставление — ваш лучший из готовых вариантов. У меня был большой опыт использования Hessian и Google Protocol Buffers / protostuff . Я поделюсь некоторыми RedisSerializer
реализации RedisSerializer
в следующем посте.
Настройка RedisCacheManager
Настроить RedisCacheManager
просто. Напоминаем, что RedisCacheManager
зависит от RedisTemplate
который зависит от фабрики соединений, в нашем случае JedisConnectionFactory
, которая может одновременно подключаться только к одной базе данных. В качестве обходного пути RedisCacheManager имеет возможность установить префикс для ваших ключей кеша.
Предупреждение. При работе с другими решениями для кэширования Spring CacheManger обычно содержит карту реализаций Cache
(каждая реализует карту, аналогичную функциональности), которые поддерживаются отдельными кэшами. При RedisCacheManager
конфигурации RedisCacheManager
по умолчанию это не так. Основываясь на комментарии RedisCacheManager
к RedisCacheManager
, неясно, является ли это ошибкой или просто неполной документацией.
«… По умолчанию ключи сохраняются путем добавления префикса (который действует как пространство имен)».
Хотя DefaultRedisCachePrefix, настроенный в RedisCacheManager
безусловно, поддерживает это, он не включен по умолчанию. В результате, когда вы запрашиваете в RedisCacheManager
Cache
с заданным именем, он просто создает новый экземпляр Cache
который указывает на ту же базу данных. В результате экземпляры Cache
все одинаковы. Один и тот же ключ будет получать одинаковое значение во всех экземплярах Cache
.
Как упоминается в комментарии javadoc, префиксы можно использовать для настройки управляемых клиентом (Redis не поддерживает эту функцию изначально) пространств имен, которые по существу создают «виртуальные» кэши в одной и той же базе данных. Вы можете включить эту функцию, вызвав redisCacheManager.setUsePrefix(true)
используя конфигурацию Spring XML или Java.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
<? xml version = '1.0' encoding = 'UTF-8' ?> < beans xsi:schemaLocation=' http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd'> < context:component-scan base-package = 'com.joshuawhite.example.service' /> < context:property-placeholder location = 'classpath:/redis.properties' /> <!-- turn on declarative caching --> < cache:annotation-driven /> <!-- Jedis ConnectionFactory --> < bean id = 'jedisConnectionFactory' class = 'org.springframework.data.redis.connection.jedis.JedisConnectionFactory' p:host-name = '${redis.host-name}' p:port = '${redis.port}' p:use-pool = 'true' /> <!-- redis template definition --> < bean id = 'redisTemplate' class = 'org.springframework.data.redis.core.RedisTemplate' p:connection-factory-ref = 'jedisConnectionFactory' /> <!-- declare Redis Cache Manager --> < bean id = 'cacheManager' class = 'org.springframework.data.redis.cache.RedisCacheManager' c:template-ref = 'redisTemplate' /> </ beans > |
Конфигурация Java ниже эквивалентна конфигурации XML выше. Люди обычно зацикливаются на использовании PropertySourcesPlaceholderConfigurer
. Для этого необходимо использовать аннотацию @PropertySource
и определить bean-компонент PropertySourcesPlaceholderConfigurer
. PropertySourcesPlaceholderConfigurer
не будет достаточно сам по себе.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
package com.joshuawhite.example.config; import org.springframework.beans.factory.annotation.Value; import org.springframework.cache.CacheManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; import org.springframework.data.redis.cache.RedisCacheManager; import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; @Configuration @ComponentScan ( 'com.joshuawhite.example' ) @PropertySource ( 'classpath:/redis.properties' ) public class AppConfig { private @Value ( '${redis.host-name}' ) String redisHostName; private @Value ( '${redis.port}' ) int redisPort; @Bean public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() { return new PropertySourcesPlaceholderConfigurer(); } @Bean JedisConnectionFactory jedisConnectionFactory() { JedisConnectionFactory factory = new JedisConnectionFactory(); factory.setHostName(redisHostName); factory.setPort(redisPort); factory.setUsePool( true ); return factory; } @Bean RedisTemplate<Object, Object> redisTemplate() { RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<Object, Object>(); redisTemplate.setConnectionFactory(jedisConnectionFactory()); return redisTemplate; } @Bean CacheManager cacheManager() { return new RedisCacheManager(redisTemplate()); } } |
Вот файл свойств, который используется обеими конфигурациями. Замените приведенные ниже значения хостом и портом, которые вы используете.
1
2
|
redis.host-name=yourHostNameHere redis.port= 6379 |
Выход
Наконец, вот вывод из нашего краткого примера приложения. Обратите внимание, что независимо от того, сколько раз мы вызываем getHelloMessage('Josh')
, ответ методов не кэшируется. Это потому, что мы определили условие (см. HelloServiceImpl.java
, строка 13), в котором мы HelloServiceImpl.java
ответ методов только тогда, когда имя равно «Джошуа». Когда мы вызываем getHelloMessage('Joshua')
в первый раз, метод выполняется. Второй раз, однако, это не так.
1
2
3
4
5
6
7
8
9
|
Executing HelloServiceImpl.getHelloMessage( 'Josh' ) message: Hello Josh! Executing HelloServiceImpl.getHelloMessage( 'Josh' ) message: Hello Josh! Executing HelloServiceImpl.getHelloMessage( 'Joshua' ) message: Hello Joshua! Executing HelloServiceImpl.getHelloMessage( 'Joshua' ) message: Hello Joshua! Done. |
На этом мы завершаем краткий обзор кэширования с помощью Spring Data Redis.
Ссылка: Кэширование с помощью Spring Data Redis от нашего партнера JCG Джошуа Уайта в блоге