Spring Boot整合Redis的完整步骤
前言
实际 开发 中 缓存 处理是必须的,不可能我们每次客户端去请求一次 服务器 ,服务器每次都要去 数据库 中进行查找,为什么要使用缓存?说到底是为了提高系统的运行速度。将用户频繁访问的内容存放在离用户最近,访问速度最 快的 地方,提高用户的响 应速度,今天先来讲下在 springboot 中整合 redis 的详细步骤。
一、spring boot对redis的支持
spring对redis的支持是使用spring data redis来实现的,一般使用jedis或者lettuce(默认),java客户端在 org.springframework.boot.autoconfigure.data.redis(spring boot 2.x) 中redis的自动配置 autoconfiguredataredis
redisautoconfiguration提供了redistemplate与stringredistemplate(只针对键值都是字符型的数据)模板,其中注解 @conditionalonmissingbean 是关键,表明该bean如果在spring中已经存在,则忽略,如果没有存在则在此处注册由spring管理,也就是说我们可以“重写”该bean,实现自己的redistemplate与stringredistemplate,事实上,是要需要重写的,理由如下:
- 没有实现我们所需要的序列化;
- 泛型总是<object, object>,大部分场景我们更需要<string, object>。
@bean @conditionalonmissingbean( name = {"redistemplate"} ) public redistemplate<object, object> redistemplate(redisconnectionfactory redisconnectionfactory) throws unknownhostexception { redistemplate<object, object> template = new redistemplate(); template.setconnectionfactory(redisconnectionfactory); return template; } @bean @conditionalonmissingbean public stringredistemplate stringredistemplate(redisconnectionfactory redisconnectionfactory) throws unknownhostexception { stringredistemplate template = new stringredistemplate(); template.setconnectionfactory(redisconnectionfactory); return template; }
二、实战
1、添加依赖
1)需要spring-boot-starter-cache依赖,管理缓存
<!-- spring boot cache --> <dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-cache</artifactid> </dependency>
2)需要spring-boot-starter-data-redis依赖(注:spring boot 2.x改为在data下),支持redis:主要以为jedis客户端为主,排除默认的lettuce作为客户端的依赖
<!-- redis cache --> <dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-data-redis</artifactid> <!-- 排除lettuce包,使用jedis代替--> <exclusions> <exclusion> <groupid>io.lettuce</groupid> <artifactid>lettuce-core</artifactid> </exclusion> </exclusions> </dependency>
3)需要jedis-client依赖(注:redis client 3版本以上会报错与spring-boot-starter-data-redis冲突,最好使用2.9.x),使用jedis作为客户端
<!-- redis client 3版本以上会报错与spring-boot-starter-data-redis冲突 --> <dependency> <groupid>redis.clients</groupid> <artifactid>jedis</artifactid> <version>2.9.0</version> </dependency>
2、redis配置
创建redisconfig配置类,增加@configuration注解,同时开启缓存管理支持(添加注解@enablecaching),继承cachingconfigurersupport重写key生成策略
@configuration @enablecaching public class redisconfig extends cachingconfigurersupport { /** * 生成key的策略:根据类名+方法名+所有参数的值生成唯一的一个key * @return */ @bean @override public keygenerator keygenerator() { return (object target, method method, object... params) -> { stringbuilder sb = new stringbuilder(); sb.append(target.getclass().getname()); sb.append(method.getname()); for (object obj : params) { sb.append(obj.tostring()); } return sb.tostring(); }; } }
之后使用的application.yml配置文件,其中这里已经选择jedis作为客户端。
# redis 配置 redis: port: 6379 # redis服务器连接密码(默认为空) password: host: xxx.xxx.xxx.xxx database: 0 jedis: pool: #连接池最大连接数(使用负值表示没有限制) max-active: 300 # 连接池中的最小空闲连接 max-idle: 100 # 连接池最大阻塞等待时间(使用负值表示没有限制) max-wait: 10000 # 连接超时时间(毫秒) timeout: 5000
同时读取配置属性,注入jedispoolconfig
/** * redis配置属性读取 */ @value("${spring.redis.host}") private string host; @value("${spring.redis.port}") private int port; @value("${spring.redis.database}") private int database; @value("${spring.redis.jedis.pool.max-idle}") private int maxidle; @value("${spring.redis.jedis.pool.max-wait}") private long maxwaitmillis; @value("${spring.redis.jedis.pool.max-active}") private int maxactive; /** * jedispoolconfig配置 * @return */ @bean public jedispoolconfig jedispoolconfig() { log.info("初始化jedispoolconfig"); jedispoolconfig jedispoolconfig = new jedispoolconfig(); jedispoolconfig.setmaxtotal(maxactive); jedispoolconfig.setmaxwaitmillis(maxwaitmillis); jedispoolconfig.setmaxidle(maxidle); return jedispoolconfig; }
3、实现序列化
针对redistemplate或stringredistemplate进行序列化,同时重写注册bean
redistemplate默认使用jdkserializationredisserializer,stringredistmeplate默认使用的是stringredisserializer。但都是不符合实际要求的
/** * 重新实现redistemplate:解决序列化问题 * @param redisconnectionfactory * @return */ @bean @suppresswarnings({"rawtype", "unchecked"}) public redistemplate<string, object> redistemplate(redisconnectionfactory redisconnectionfactory){ redistemplate<string, object> template = new redistemplate(); template.setconnectionfactory(redisconnectionfactory); jackson2jsonredisserializer jackson2jsonredisserializer = new jackson2jsonredisserializer(object.class); objectmapper om = new objectmapper(); // 设置任何字段可见 om.setvisibility(propertyaccessor.all, jsonautodetect.visibility.any); // 设置不是final的属性可以转换 om.enabledefaulttyping(objectmapper.defaulttyping.non_final); log.info("objectmapper: {}", om); jackson2jsonredisserializer.setobjectmapper(om); stringredisserializer stringredisserializer = new stringredisserializer(); // key采用string的序列化方式 template.setkeyserializer(stringredisserializer); // hash的key采用string的序列化方式 template.sethashkeyserializer(stringredisserializer); // value序列化方式采用jackson序列化方式 template.setvalueserializer(jackson2jsonredisserializer); // hash的value序列化方式采用jackson序列化方式 template.sethashvalueserializer(jackson2jsonredisserializer); template.afterpropertiesset(); template.setenabletransactionsupport(true); return template; } /** * 重新实现stringredistmeplate:键值都是string的的数据 * @param redisconnectionfactory * @return */ @bean public stringredistemplate stringredistemplate(redisconnectionfactory redisconnectionfactory) { stringredistemplate template = new stringredistemplate(); jackson2jsonredisserializer jackson2jsonredisserializer = new jackson2jsonredisserializer(object.class); template.setconnectionfactory(redisconnectionfactory); stringredisserializer stringredisserializer = new stringredisserializer(); // key采用string的序列化方式 template.setkeyserializer(stringredisserializer); // hash的key采用string的序列化方式 template.sethashkeyserializer(stringredisserializer); // value序列化方式采用jackson序列化方式 template.setvalueserializer(jackson2jsonredisserializer); // hash的value序列化方式采用jackson序列化方式 template.sethashvalueserializer(jackson2jsonredisserializer); return template; }
4、创建redis连接工厂,同时注册bean
注意spring boot 1.x与spring boot 2.x的区别,已在代码中注释表明,spring boot 1.x使用的是jedisconnectionfactory 。而spring boot 2.x使用的是redisstandaloneconfiguration ,之后传入jedisconnectionfactory返回bean
/** * 注入redisconnectionfactory * @return */ @bean public redisconnectionfactory redisconnectionfactory(jedispoolconfig jedispoolconfig) { log.info("初始化jedisconnectionfactory"); /* 在spring boot 1.x中已经过时,采用redisstandaloneconfiguration配置 jedisconnectionfactory jedisconnectionfactory = new jedisconnectionfactory(jedispoolconfig); jedisconnectionfactory.sethostname(host); jedisconnectionfactory.setdatabase(database);*/ // jedisconnectionfactory配置hsot、database、password等参数 redisstandaloneconfiguration redisstandaloneconfiguration = new redisstandaloneconfiguration(); redisstandaloneconfiguration.sethostname(host); redisstandaloneconfiguration.setport(port); redisstandaloneconfiguration.setdatabase(database); // jedisconnectionfactory配置jedispoolconfig jedisclientconfiguration.jedispoolingclientconfigurationbuilder jedispoolconfigbuilder = (jedisclientconfiguration.jedispoolingclientconfigurationbuilder)jedisclientconfiguration.builder(); jedispoolconfigbuilder.poolconfig(jedispoolconfig); return new jedisconnectionfactory(redisstandaloneconfiguration); }
5、完整的redisconfig配置类
/** * * @author jian * @date 2019/4/14 * @description * 1) redistemplate(或stringredistemplate)虽然已经自动配置,但是不灵活(第一没有序列化,第二泛型为<object, object>不是我们想要的类型) * 所以自己实现redistemplate或stringredistemplate) * 2) 采用rediscachemanager作为缓存管理器 * */ @configuration @enablecaching public class redisconfig extends cachingconfigurersupport { private static final logger log = loggerfactory.getlogger(redisconfig.class); /** * redis配置属性读取 */ @value("${spring.redis.host}") private string host; @value("${spring.redis.port}") private int port; @value("${spring.redis.database}") private int database; @value("${spring.redis.jedis.pool.max-idle}") private int maxidle; @value("${spring.redis.jedis.pool.max-wait}") private long maxwaitmillis; @value("${spring.redis.jedis.pool.max-active}") private int maxactive; /** * jedispoolconfig配置 * @return */ @bean public jedispoolconfig jedispoolconfig() { log.info("初始化jedispoolconfig"); jedispoolconfig jedispoolconfig = new jedispoolconfig(); jedispoolconfig.setmaxtotal(maxactive); jedispoolconfig.setmaxwaitmillis(maxwaitmillis); jedispoolconfig.setmaxidle(maxidle); return jedispoolconfig; } /** * 注入redisconnectionfactory * @return */ @bean public redisconnectionfactory redisconnectionfactory(jedispoolconfig jedispoolconfig) { log.info("初始化jedisconnectionfactory"); /* 在spring boot 1.x中已经过时,采用redisstandaloneconfiguration配置 jedisconnectionfactory jedisconnectionfactory = new jedisconnectionfactory(jedispoolconfig); jedisconnectionfactory.sethostname(host); jedisconnectionfactory.setdatabase(database);*/ // jedisconnectionfactory配置hsot、database、password等参数 redisstandaloneconfiguration redisstandaloneconfiguration = new redisstandaloneconfiguration(); redisstandaloneconfiguration.sethostname(host); redisstandaloneconfiguration.setport(port); redisstandaloneconfiguration.setdatabase(database); // jedisconnectionfactory配置jedispoolconfig jedisclientconfiguration.jedispoolingclientconfigurationbuilder jedispoolconfigbuilder = (jedisclientconfiguration.jedispoolingclientconfigurationbuilder)jedisclientconfiguration.builder(); jedispoolconfigbuilder.poolconfig(jedispoolconfig); return new jedisconnectionfactory(redisstandaloneconfiguration); } /** * 采用rediscachemanager作为缓存管理器 * @param connectionfactory */ @bean public cachemanager cachemanager(redisconnectionfactory connectionfactory) { rediscachemanager rediscachemanager = rediscachemanager.create(connectionfactory); return rediscachemanager; } /** * 生成key的策略:根据类名+方法名+所有参数的值生成唯一的一个key * @return */ @bean @override public keygenerator keygenerator() { return (object target, method method, object... params) -> { stringbuilder sb = new stringbuilder(); sb.append(target.getclass().getname()); sb.append(method.getname()); for (object obj : params) { sb.append(obj.tostring()); } return sb.tostring(); }; } /** * 重新实现redistemplate:解决序列化问题 * @param redisconnectionfactory * @return */ @bean @suppresswarnings({"rawtype", "unchecked"}) public redistemplate<string, object> redistemplate(redisconnectionfactory redisconnectionfactory){ redistemplate<string, object> template = new redistemplate(); template.setconnectionfactory(redisconnectionfactory); jackson2jsonredisserializer jackson2jsonredisserializer = new jackson2jsonredisserializer(object.class); objectmapper om = new objectmapper(); // 设置任何字段可见 om.setvisibility(propertyaccessor.all, jsonautodetect.visibility.any); // 设置不是final的属性可以转换 om.enabledefaulttyping(objectmapper.defaulttyping.non_final); log.info("objectmapper: {}", om); jackson2jsonredisserializer.setobjectmapper(om); stringredisserializer stringredisserializer = new stringredisserializer(); // key采用string的序列化方式 template.setkeyserializer(stringredisserializer); // hash的key采用string的序列化方式 template.sethashkeyserializer(stringredisserializer); // value序列化方式采用jackson序列化方式 template.setvalueserializer(jackson2jsonredisserializer); // hash的value序列化方式采用jackson序列化方式 template.sethashvalueserializer(jackson2jsonredisserializer); template.afterpropertiesset(); template.setenabletransactionsupport(true); return template; } /** * 重新实现stringredistmeplate:键值都是string的的数据 * @param redisconnectionfactory * @return */ @bean public stringredistemplate stringredistemplate(redisconnectionfactory redisconnectionfactory) { stringredistemplate template = new stringredistemplate(); jackson2jsonredisserializer jackson2jsonredisserializer = new jackson2jsonredisserializer(object.class); template.setconnectionfactory(redisconnectionfactory); stringredisserializer stringredisserializer = new stringredisserializer(); // key采用string的序列化方式 template.setkeyserializer(stringredisserializer); // hash的key采用string的序列化方式 template.sethashkeyserializer(stringredisserializer); // value序列化方式采用jackson序列化方式 template.setvalueserializer(jackson2jsonredisserializer); // hash的value序列化方式采用jackson序列化方式 template.sethashvalueserializer(jackson2jsonredisserializer); return template; } }
三、测试
1、编写redis工具类
虽然redistemplate与stringredistemplate模板有提供的主要数据访问方法:
- opsforvalue():操作只有简单属性的数据
- opsforlist():操作含有list的数据
- opsforset():操作含有set的数据
- opsforhash():操作含有hash的数据
- opsforzset():操作含有有序set类型zset的数据
但是相关比较抽象,实现起来比较复杂,有必要进一步封装,比如使用redistmeplate中的简单value的get操作:
object result = null; valueoperations<serializable, object> operations = redistemplate.opsforvalue(); result = operations.get(key);
但是封装之后,相对客户端用户来说比较明了
/** * 读取缓存 * * @param key * @return */ public object get(final string key) { object result = null; valueoperations<serializable, object> operations = redistemplate.opsforvalue(); result = operations.get(key); return result; }
完整的简单工具类如下:
@component public class redisutils { @autowired private redistemplate redistemplate; /** * 批量删除对应的value * * @param keys */ public void remove(final string... keys) { for (string key : keys) { remove(key); } } /** * 批量删除key * * @param pattern */ public void removepattern(final string pattern) { set<serializable> keys = redistemplate.keys(pattern); if (keys.size() > 0) { redistemplate.delete(keys); } } /** * 删除对应的value * * @param key */ public void remove(final string key) { if (exists(key)) { redistemplate.delete(key); } } /** * 判断缓存中是否有对应的value * * @param key * @return */ public boolean exists(final string key) { return redistemplate.haskey(key); } /** * 读取缓存 * * @param key * @return */ public object get(final string key) { object result = null; valueoperations<serializable, object> operations = redistemplate.opsforvalue(); result = operations.get(key); return result; } /** * 写入缓存 * * @param key * @param value * @return */ public boolean set(final string key, object value) { boolean result = false; try { valueoperations<serializable, object> operations = redistemplate.opsforvalue(); operations.set(key, value); result = true; } catch (exception e) { e.printstacktrace(); } return result; } /** * 写入缓存 * * @param key * @param value * @return */ public boolean set(final string key, object value, long expiretime) { boolean result = false; try { valueoperations<serializable, object> operations = redistemplate.opsforvalue(); operations.set(key, value); redistemplate.expire(key, expiretime, timeunit.seconds); result = true; } catch (exception e) { e.printstacktrace(); } return result; } }
2、person实体类
需要注意的是一定要实现序列化,并且有序列化版本id
public class person implements serializable { private final long serialversionuid = 1l; private string id; private string name; private int age; private string gender; public string getid() { return id; } public void setid(string id) { this.id = id; } public string getname() { return name; } public void setname(string name) { this.name = name; } public int getage() { return age; } public void setage(int age) { this.age = age; } public string getgender() { return gender; } public void setgender(string gender) { this.gender = gender; } @override public string tostring() { return "person{" + "id='" + id + '\'' + ", name='" + name + '\'' + ", age=" + age + ", gender='" + gender + '\'' + '}'; } }
3、编写测试类
redis工具类spring已经做了管理(增加@compent注解),使用很简单,只需要注入redisutils即可
@runwith(springrunner.class) @springboottest public class redistest { @autowired private redisutils redisutils; @test public void test(){ person person = new person(); person.setage(23); person.setid("001"); person.setname("zhangsan"); redisutils.set("person-001", person); system.out.println(redisutils.get("person-001")); } }
4、测试结果
在ide控制台中:
在登录客户端后查看value值
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对的支持。