如下主要通去年无聊做的 "涂涂影院后台管理系统" 一个 demo,看 RedisTemplate 的使用。

体验地址:​​http://video.71xun.com:8080​​  账户:niceyoo 密码:123456

主要用到地方:视频首页轮播图的获取,以及搜索检索界面,如下图所示:

RedisTemplate在项目中的应用_分布式

RedisTemplate在项目中的应用_Redis_02

由于是非maven非springboot项目,故配置有所繁琐,但正所谓有繁才有简,在下面会带大家看一下springboot中配置redis是怎样简单。

好了,先回到 "涂涂影院" 来吧。

本项目环境

开发工具:Eclipse

JDK:1.8

Redis;

RedisTemplate在项目中的应用_分布式_03eclipse中截图

相信非springboot项目里的配置大家都应该很熟悉吧,主要就是配置繁琐的 xml,搭建一个 ssm 项目可参考之前的例子:​​SSM(Spring+SpringMVC+Mybatis)框架环境搭建​

1、spring-redis.xml

<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">

<!-- 缓存的层级-->
<context:component-scan base-package="com.jeenotes.common.cache" />

<!-- 配置 读取properties文件 jeenotes.properties -->
<context:property-placeholder location="classpath:resources/jeenotes.properties" ignore-unresolvable="true"/>

<!-- Redis 配置 -->
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<!-- 控制一个pool可分配多少个jedis实例 -->
<property name="maxTotal" value="${redis.pool.maxTotal}" /><!-- -->
<!-- 连接池中最多可空闲maxIdle个连接 ,这里取值为20,表示即使没有数据库连接时依然可以保持20空闲的连接,
而不被清除,随时处于待命状态。 -->
<property name="maxIdle" value="${redis.pool.maxIdle}" /><!-- -->
<!-- 最大等待时间:当没有可用连接时,连接池等待连接被归还的最大时间(以毫秒计数),超过时间则抛出异常 -->
<property name="maxWaitMillis" value="${redis.pool.maxWaitMillis}" /><!-- -->
<!-- 在获取连接的时候检查有效性 -->
<property name="testOnBorrow" value="${redis.pool.testOnBorrow}" /><!-- -->
</bean>


<!-- redis单节点数据库连接配置 -->
<!-- Spring-redis连接池管理工厂 -->
<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="hostName" value="${redis.ip}" /><!-- -->
<property name="port" value="${redis.port}" /><!-- -->
<property name="password" value="${redis.pass}" /><!-- -->
<property name="poolConfig" ref="jedisPoolConfig" />
</bean>

<!-- redisTemplate配置,redisTemplate是对Jedis的对redis操作的扩展,有更多的操作,
封装使操作更便捷 -->
<!-- SDR默认采用的序列化策略有两种,一种是String的序列化策略,一种是JDK的序列化策略。
StringRedisTemplate默认采用的是String的序列化策略,保存的key和value都是采用此策略序列化保存的。
RedisTemplate默认采用的是JDK的序列化策略,保存的key和value都是采用此策略序列化保存的。
就是因为序列化策略的不同,即使是同一个key用不同的Template去序列化,结果是不同的。
所以根据key去删除数据的时候就出现了删除失败的问题。
-->
<!-- redis 序列化策略 ,通常情况下key值采用String序列化策略, -->
<!-- 如果不指定序列化策略,StringRedisTemplate的key和value都将采用String序列化策略; -->
<!-- 但是RedisTemplate的key和value都将采用JDK序列化 这样就会出现采用不同template保存的数据不能用同一个template删除的问题 -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate">
<property name="connectionFactory" ref="jedisConnectionFactory" />
<!-- 我们通常所用的序列化操作:
JDK的序列化——类上implements Serializable接口
XML和Json
protocol buffer(简称protobuf)Google的、 本项目采用中-->
<!-- <property name="keySerializer" ref="stringRedisSerializer" />
<property name="hashKeySerializer" ref="stringRedisSerializer" />
<property name="valueSerializer" ref="stringRedisSerializer"/> -->

</bean>

</beans>


注释比较详细,简要概述,jedisPoolConfig 用来配置 redis 连接池的一些配置,JedisConnectionFactory 则作为连接池的工厂类;还记得上文中提到的 redis 序列化问题吗?上边配置中(StringRedisTemplate)正是用到了 String 序列化策略。

2、RedisCache.java

/**
* redis缓存
*
* @author niceyoo
*
*/
@Component
public class RedisCache {


public final static String CAHCENAME="cache";//缓存名
public final static int CAHCETIME=300;//默认缓存时间 以秒计算的

@Autowired
private RedisTemplate<String, String> redisTemplate;

public <T> boolean putCache(String key, T obj) {
final byte[] bkey = key.getBytes();
final byte[] bvalue = ProtoStuffSerializerUtil.serialize(obj);
boolean result = redisTemplate.execute(new RedisCallback<Boolean>() {
@Override
public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
return connection.setNX(bkey, bvalue);
}
});
return result;
}

public <T> void putCacheWithExpireTime(String key, T obj, final long expireTime) {
final byte[] bkey = key.getBytes();
final byte[] bvalue = ProtoStuffSerializerUtil.serialize(obj);
redisTemplate.execute(new RedisCallback<Boolean>() {
@Override
public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
connection.setEx(bkey, expireTime, bvalue);
return true;
}
});
}

public <T> boolean putListCache(String key, List<T> objList) {
final byte[] bkey = key.getBytes();
final byte[] bvalue = ProtoStuffSerializerUtil.serializeList(objList);
boolean result = redisTemplate.execute(new RedisCallback<Boolean>() {
@Override
public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
return connection.setNX(bkey, bvalue);
}
});
return result;
}

public <T> boolean putListCacheWithExpireTime(String key, List<T> objList, final long expireTime) {
final byte[] bkey = key.getBytes();
final byte[] bvalue = ProtoStuffSerializerUtil.serializeList(objList);
boolean result = redisTemplate.execute(new RedisCallback<Boolean>() {
@Override
public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
connection.setEx(bkey, expireTime, bvalue);
return true;
}
});
return result;
}

public <T> T getCache(final String key, Class<T> targetClass) {
byte[] result = redisTemplate.execute(new RedisCallback<byte[]>() {
@Override
public byte[] doInRedis(RedisConnection connection) throws DataAccessException {
return connection.get(key.getBytes());
}
});
if (result == null) {
return null;
}
return ProtoStuffSerializerUtil.deserialize(result, targetClass);
}

public <T> List<T> getListCache(final String key, Class<T> targetClass) {
byte[] result = redisTemplate.execute(new RedisCallback<byte[]>() {
@Override
public byte[] doInRedis(RedisConnection connection) throws DataAccessException {
return connection.get(key.getBytes());
}
});
if (result == null) {
return null;
}
return ProtoStuffSerializerUtil.deserializeList(result, targetClass);
}

/**
* 精确删除key
*
* @param key
*/
public void deleteCache(String key) {
redisTemplate.delete(key);
}

/**
* 模糊删除key
*
* @param pattern
*/
public void deleteCacheWithPattern(String pattern) {
Set<String> keys = redisTemplate.keys(pattern);
redisTemplate.delete(keys);
}

/**
* 清空所有缓存
*/
public void clearCache() {
deleteCacheWithPattern(RedisCache.CAHCENAME+"|*");
}
}


RedisCache.java 实现了对对数据增删改查的几种方法,如何使用呢?

我们以首页的轮播图为例:

在需要使用的类中注入该组件:

RedisTemplate在项目中的应用_分布式_04

看一下如下方法:

@ResponseBody
@RequestMapping("huandeng")
public ResultMobileBannerBean huandeng(Model model) {

String cache_key = RedisCache.CAHCENAME + "|getPcHomeHuanDengList";
ResultMobileBannerBean result_cache = cache.getCache(cache_key, ResultMobileBannerBean.class);

if(result_cache != null){
return result_cache;
}

ResultMobileBannerBean bannerBean = new ResultMobileBannerBean();
bannerBean.setCode("000000");
List<HomeBanner> homeBannerList = homeManagerService.findMobileHomeBannerList();
bannerBean.setBannerList(homeBannerList);
cache.putCacheWithExpireTime(cache_key, bannerBean, RedisCache.CAHCETIME);

return bannerBean;
}


主要是 ResultMobileBannerBean result_cache = cache.getCache(cache_key, ResultMobileBannerBean.class); 在去数据库请求数据之前,先去 redis 中读取缓存信息,如果返回的数据非空的话,则返回该数据,否则去数据库查询数据,查询后在存放在 redis 中。

项目源码:​​https://gitee.com/niceyoo/jeenotes-ssm.git​


springboot 中是如何引用 redis 的呢?

1、maven依赖

<!-- Redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-redis</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<!-- Gson 可暂时忽略-->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.5</version>
</dependency>


2、yml配置信息

spring:
# Redis
redis:
host: 127.0.0.1
password:
# 数据库索引 默认0
database: 0
port: 6379
# 超时时间 Duration类型 3秒
timeout: 3S


3、项目中引用

@Autowired
private StringRedisTemplate redisTemplate;

@RequestMapping(value = "/getByParentId/{parentId}", method = RequestMethod.GET)
@ApiOperation(value = "通过parentId获取")
public Result<List<Department>> getByParentId(@PathVariable String parentId,
@ApiParam("是否开始数据权限过滤") @RequestParam(required = false, defaultValue = "true") Boolean openDataFilter){

List<Department> list = new ArrayList<>();
User u = securityUtil.getCurrUser();
String key = "department::"+parentId+":"+u.getId()+"_"+openDataFilter;
String v = redisTemplate.opsForValue().get(key);
if(StrUtil.isNotBlank(v)){
list = new Gson().fromJson(v, new TypeToken<List<Department>>(){}.getType());
return new ResultUtil<List<Department>>().setData(list);
}
list = departmentService.findByParentIdOrderBySortOrder(parentId, openDataFilter);
list = setInfo(list);
redisTemplate.opsForValue().set(key,
new GsonBuilder().registerTypeAdapterFactory(HibernateProxyTypeAdapter.FACTORY).create().toJson(list));
return new ResultUtil<List<Department>>().setData(list);
}


RedisTemplate 定义了 5 种数据结构操作:

  1. redisTemplate.opsForValue();//操作字符串
  2. redisTemplate.opsForHash();//操作hash
  3. redisTemplate.opsForList();//操作list
  4. redisTemplate.opsForSet();//操作set
  5. redisTemplate.opsForZSet();//操作有序set

最优补充

从上文可看出 RedisTemplate 在 springboot 中应用尤为简单,所以赶快切到 springboot 中吧~

如果文章有错的地方欢迎指正,大家互相留言交流。习惯在微信看技术文章,想要获取更多的Java资源的同学,可以关注微信公众号:niceyoo

RedisTemplate在项目中的应用_Redis_05