在下面的示例中,我将向您展示如何使用Spring Data – Redis项目作为Spring 3.1中引入的Spring Cache Abstraction的缓存提供程序。 我对如何使用Spring的基于Java的配置有很多疑问,因此我将同时提供基于XML和Java的配置供您查看。
依存关系
在此示例中使用了以下依赖关系:
<?xml version='1.0' encoding='UTF-8'?>
<project xmlns='http://maven.apache.org/POM/4.0.0'
xsi:schemaLocation='http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd'
xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'>
<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”,该字符串在传入的名称之前。
package com.joshuawhite.example.service;
public interface HelloService {
String getMessage(String name);
}
查看一下HelloServiceImpl
类(如下),您可以看到我正在利用Spring的@Cacheable批注为getMessage
方法添加缓存功能。 有关此批注功能的更多详细信息,请参阅Cache Abstraction文档 。 为了娱乐,我使用Spring Expression Language(SpEL)定义条件。 在此示例中,仅当传入的名称为“ Joshua”时才缓存方法响应。
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”的方法执行将被缓存。 当我们稍后查看程序输出时,这将更加清楚。
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的配置时,仍使用组件扫描。 您可以看到我在上面的HelloServiceImpl.java
第6行上使用@Service
批注。 接下来,我们将看看如何配置jedisConnectionFactory
, redisTemplate
和cacheManager
。
配置JedisConnectionFactory
在此示例中,我选择使用Jedis作为我们的Java客户端,因为它在Redis站点上被列为Java的“推荐”客户端库。 如您所见,设置非常简单。 当我显式设置use-pool = true时,它的源代码指示这是默认设置。 如果未显式设置,则JedisConnectionFactory还提供以下默认值:
- hostName =” localhost”
- 端口= 6379
- 超时= 2000毫秒
- 数据库= 0
- usePool = true
注意:尽管数据库索引是可配置的,但是JedisConnectionFactory
仅支持一次连接到一个Redis数据库。 由于Redis是单线程的,因此建议您设置Redis的多个实例,而不是在单个进程中使用多个数据库。 这使您可以获得更好的CPU /资源利用率。 如果计划使用redis-cluster,则仅支持单个数据库。 有关连接池中使用的默认值的更多信息,请查看JedisPoolConfig
或Apache Commons Pool org.apache.commons.pool.impl.GenericObjectPool.Config
以及其中的实现。
org.apache.commons.pool.impl.GenericObjectPool
类。
配置RedisTemplate
正如您从Spring“模板”类中所期望的那样, RedisTemplate
负责序列化和连接管理,并且( RedisTemplate
您正在使用连接池)是线程安全的。 默认情况下,RedisTemplate使用Java序列化( JdkSerializationRedisSerializer
)。 请注意,将数据序列化为Redis实质上使Redis成为“不透明”缓存。 虽然其他序列化程序允许您将数据映射到Redis,但我发现序列化(尤其是在处理对象图时)使用起来更快,更简单。 就是说,如果您要求其他非Java应用程序能够访问此数据,则映射是您最好的即用型选择。 我在使用Hessian和Google Protocol Buffers / proststuff方面有丰富的经验。 我将在以后的文章中分享RedisSerializer
一些示例实现。
配置RedisCacheManager
配置RedisCacheManager
很简单。 提醒一下, RedisCacheManager
依赖于RedisTemplate
,后者依赖于连接工厂(在我们的情况下为JedisConnectionFactory
,该连接工厂一次只能连接到一个数据库。 解决方法是,RedisCacheManager可以为您的缓存键设置前缀。
警告:在处理其他缓存解决方案时,Spring的CacheManger通常包含一个由单独的缓存支持的Cache
映射(每个实现类似功能的映射)的实现。 使用默认的RedisCacheManager
配置,情况并非如此。 根据RedisCacheManager
上的javadoc注释,不清楚这是错误还是仅是不完整的文档。
“ ...默认情况下,通过添加前缀(用作名称空间)来保存密钥。”
虽然RedisCacheManager
配置的DefaultRedisCachePrefix当然支持此功能,但默认情况下未启用它。 结果,当您向RedisCacheManager
询问给定名称的Cache
时,它只是创建一个指向相同数据库的新Cache
实例。 结果, Cache
实例完全相同。 相同的键将在所有Cache
实例中检索相同的值。
正如javadoc注释所暗示的那样,前缀可用于设置客户端管理的名称空间(Redis本身不支持此功能),这些名称空间实际上在同一数据库内创建“虚拟”缓存。 您可以通过使用Spring XML或Java配置调用redisCacheManager.setUsePrefix(true)
来redisCacheManager.setUsePrefix(true)
此功能。
<?xml version='1.0' encoding='UTF-8'?>
<beans
xmlns='http://www.springframework.org/schema/beans'
xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
xmlns:context='http://www.springframework.org/schema/context'
xmlns:c='http://www.springframework.org/schema/c'
xmlns:p='http://www.springframework.org/schema/p'
xmlns:cache='http://www.springframework.org/schema/cache'
xsi:schemaLocation='
http://www.springframework.org/schema/beansvhttp://www.springframework.org/schema/beans/spring-beans.xsd
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
注释和定义PropertySourcesPlaceholderConfigurer
豆。 PropertySourcesPlaceholderConfigurer
是不够的。
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());
}
}
这是两种配置都使用的属性文件。 将下面的值替换为您使用的主机和端口。
redis.host-name=yourHostNameHere
redis.port=6379
输出量
最后,这是我们简短的示例应用程序的输出。 请注意,无论我们调用getHelloMessage('Josh')
多少次,方法响应都不会被缓存。 这是因为我们定义了一个条件(请参见HelloServiceImpl.java
,第13行),其中仅当名称等于“ Joshua”时才缓存方法响应。 当我们第一次调用getHelloMessage('Joshua')
时,将执行该方法。 但是,第二次不是。
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进行缓存的过程。
参考: Joshua White博客博客中来自JCG合作伙伴 Joshua White的Spring Data Redis缓存 。
翻译自: https://www.javacodegeeks.com/2013/02/caching-with-spring-data-redis.html