主页:写程序的小王叔叔的博客欢迎来访👀
支持:点赞收藏关注
1 配置
1 pom.xml
<!--引用Jedis--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-cache</artifactId></dependency>
2 spring.xml
##################Redis配置######### Redis服务器地址spring.redis.host=127.0.0.1 # Redis服务器连接端口spring.redis.port=6379 # Redis服务器连接密码(默认为空)spring.redis.password=123456789 # 连接池最大连接数(使用负值表示没有限制)spring.redis.pool.max-active=8 # 连接池最大阻塞等待时间(使用负值表示没有限制)spring.redis.pool.max-wait=-1 # 连接池中的最大空闲连接spring.redis.pool.max-idle=8 # 连接池中的最小空闲连接spring.redis.pool.min-idle=0 # 连接超时时间(毫秒)spring.redis.timeout=0 spring.redis.commandTimeout=5000 #连接的数据库spring.redis.database=0 # redis.clusterspring.redis.cluster.nodes=127.0.0.1:6379 ####################################################
3 CacheManager
直接配置会提示错误:
new RedisCacheManager(redisTemplate)没有此构造
现在这么写会有报错
The constructor RedisCacheManager(RedisTemplate) is undefined。
这是因为Spring Boot 2.x版本删除了RedisCacheManager这个构造器, 也不可以通过之前的setDefaultExpiration方法设置默认的缓存过期时间等。
通过设置RedisConnectionFactory factory进行解决,最后RedisConfig.java如下:
/**** 配置redis缓存* @author Administrator**///启用缓存publicclassRedisConfigextendsCachingConfigurerSupport{ //注入 RedisConnectionFactorypublicRedisConnectionFactoryredisConnectionFactory; //实例化 RedisTemplate 对象 设置数据存入 redis 的序列化方式publicRedisTemplate<String, Object>functionDomainRedisTemplate() { RedisTemplate<String, Object>redisTemplate=newRedisTemplate<>(); //定义key生成策略redisTemplate.setKeySerializer(newStringRedisSerializer()); redisTemplate.setHashKeySerializer(newStringRedisSerializer()); redisTemplate.setHashValueSerializer(newJdkSerializationRedisSerializer()); redisTemplate.setValueSerializer(newJdkSerializationRedisSerializer()); redisTemplate.setConnectionFactory(redisConnectionFactory); returnredisTemplate; } //缓存管理器 --》配置缓存时间publicCacheManagercacheManager(RedisConnectionFactoryfactory) { RedisCacheConfigurationconfig=RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofSeconds(-1)); RedisCacheManagercacheManager=RedisCacheManager.builder(factory).cacheDefaults(config).build(); returncacheManager; } }
创建实体类,一定要实现Serializable序列化,因为redis保存对象的时候要求对象是序列化
如:
实体类配置好后,将redis的操作的接口类进行封装后,进行使用:
publicinterfaceRedisService{ publicbooleanset(finalStringkey, Objectvalue); publicbooleanset(finalStringkey, Objectvalue, LongexpireTime); publicvoidremove(finalString... keys); publicvoidremovePattern(finalStringpattern); publicvoidremove(finalStringkey); publicbooleanexists(finalStringkey); publicObjectget(finalStringkey); publicvoidhmSet(Stringkey, ObjecthashKey, Objectvalue); publicObjecthmGet(Stringkey, ObjecthashKey); publicvoidlPush(Stringk,Objectv); publicList<Object>lRange(Stringk, longl, longl1); publicvoidadd(Stringkey,Objectvalue); publicSet<Object>setMembers(Stringkey); publicvoidzAdd(Stringkey,Objectvalue,doublescoure); publicSet<Object>rangeByScore(Stringkey,doublescoure,doublescoure1); }
package***; importjava.io.Serializable; importjava.util.List; importjava.util.Set; importjava.util.concurrent.TimeUnit; importorg.springframework.beans.factory.annotation.Autowired; importorg.springframework.data.redis.core.HashOperations; importorg.springframework.data.redis.core.ListOperations; importorg.springframework.data.redis.core.RedisTemplate; importorg.springframework.data.redis.core.SetOperations; importorg.springframework.data.redis.core.ValueOperations; importorg.springframework.data.redis.core.ZSetOperations; importorg.springframework.stereotype.Service; "all") (publicclassRedisServiceImplimplementsRedisService{ privateRedisTemplateredisTemplate; /*** 写入缓存* @param key* @param value* @return*/publicbooleanset(finalStringkey, Objectvalue) { booleanresult=false; try { ValueOperations<Serializable, Object>operations=redisTemplate.opsForValue(); operations.set(key, value); result=true; } catch (Exceptione) { e.printStackTrace(); } returnresult; } /*** 写入缓存设置时效时间* @param key* @param value* @return*/publicbooleanset(finalStringkey, Objectvalue, LongexpireTime) { booleanresult=false; try { ValueOperations<Serializable, Object>operations=redisTemplate.opsForValue(); operations.set(key, value); redisTemplate.expire(key, expireTime, TimeUnit.SECONDS); result=true; } catch (Exceptione) { e.printStackTrace(); } returnresult; } /*** 批量删除对应的value* @param keys*/publicvoidremove(finalString... keys) { for (Stringkey : keys) { remove(key); } } /*** 批量删除key* @param pattern*/publicvoidremovePattern(finalStringpattern) { Set<Serializable>keys=redisTemplate.keys(pattern); if (keys.size() >0) redisTemplate.delete(keys); } /*** 删除对应的value* @param key*/publicvoidremove(finalStringkey) { if (exists(key)) { redisTemplate.delete(key); } } /*** 判断缓存中是否有对应的value* @param key* @return*/publicbooleanexists(finalStringkey) { returnredisTemplate.hasKey(key); } /*** 读取缓存* @param key* @return*/publicObjectget(finalStringkey) { Objectresult=null; ValueOperations<Serializable, Object>operations=redisTemplate.opsForValue(); result=operations.get(key); returnresult; } /*** 哈希 添加* @param key* @param hashKey* @param value*/publicvoidhmSet(Stringkey, ObjecthashKey, Objectvalue){ HashOperations<String, Object, Object>hash=redisTemplate.opsForHash(); hash.put(key,hashKey,value); } /*** 哈希获取数据* @param key* @param hashKey* @return*/publicObjecthmGet(Stringkey, ObjecthashKey){ HashOperations<String, Object, Object>hash=redisTemplate.opsForHash(); returnhash.get(key,hashKey); } /*** 列表添加* @param k* @param v*/publicvoidlPush(Stringk,Objectv){ ListOperations<String, Object>list=redisTemplate.opsForList(); list.rightPush(k,v); } /*** 列表获取* @param k* @param l* @param l1* @return*/publicList<Object>lRange(Stringk, longl, longl1){ ListOperations<String, Object>list=redisTemplate.opsForList(); returnlist.range(k,l,l1); } /*** 集合添加* @param key* @param value*/publicvoidadd(Stringkey,Objectvalue){ SetOperations<String, Object>set=redisTemplate.opsForSet(); set.add(key,value); } /*** 集合获取* @param key* @return*/publicSet<Object>setMembers(Stringkey){ SetOperations<String, Object>set=redisTemplate.opsForSet(); returnset.members(key); } /*** 有序集合添加* @param key* @param value* @param scoure*/publicvoidzAdd(Stringkey,Objectvalue,doublescoure){ ZSetOperations<String, Object>zset=redisTemplate.opsForZSet(); zset.add(key,value,scoure); } /*** 有序集合获取* @param key* @param scoure* @param scoure1* @return*/publicSet<Object>rangeByScore(Stringkey,doublescoure,doublescoure1){ ZSetOperations<String, Object>zset=redisTemplate.opsForZSet(); returnzset.rangeByScore(key, scoure, scoure1); } }
redis-key的整理,创建一个key的管理文件,进行将所有要缓存的业务类进行统一管理,如
/***** 整合redis缓存key文件* @author Administrator**/publicclassRedisServiceKey { publicstaticfinalStringProduct_REDIS_KEY="Product_KEY"; }
基本配置就全部完成,可以启动。
启动后可能遇见的问题:
1.问题:HostAndPort need to be seperated by ':'.
解决:如上图,添加端口127.0.0.1:6379
扩展:host变成ip:port,集群多个ip和端口用“,”分割,
2.问题:再次启动出现问题:org.springframework.data.redis.core.HashOperations
解决:缺少redis中HashOperationsbean,其实在下面已经提示了,所以在redisconfig中注入bean,并同时在再调用下就可以了
//实例化 HashOperations 对象,可以使用 Hash 类型操作
publicHashOperations<String, String, Object>hashOperations(RedisTemplate<String, Object>redisTemplate) { returnredisTemplate.opsForHash(); } //实例化 ValueOperations 对象,可以使用 String 操作publicValueOperations<String, Object>valueOperations(RedisTemplate<String, Object>redisTemplate) { returnredisTemplate.opsForValue(); } //实例化 ListOperations 对象,可以使用 List 操作publicListOperations<String, Object>listOperations(RedisTemplate<String, Object>redisTemplate) { returnredisTemplate.opsForList(); } //实例化 SetOperations 对象,可以使用 Set 操作publicSetOperations<String, Object>setOperations(RedisTemplate<String, Object>redisTemplate) { returnredisTemplate.opsForSet(); } //实例化 ZSetOperations 对象,可以使用 ZSet 操作publicZSetOperations<String, Object>zSetOperations(RedisTemplate<String, Object>redisTemplate) { returnredisTemplate.opsForZSet(); }
如上图的Service.java文件中
3.问题:Cannot retrieve initial cluster partitions from initial URIs [RedisURI [host='127.0.0.1', port=6379]]
当前问题说:在配置redis集群时,地址用的是127的IP
解决 :在配置集群时,一定要用IP地址,不要用127/localhost等代替IP
4.问题:Command timed out 连接请求超时
将spring.xml中时间设长一点
# 连接超时时间(毫秒) spring.redis.timeout=200 spring.redis.commandTimeout=500000
通过上面的配置完全解决这个问题的所在.
2、通过redis实现CRUD
1 存list
Redis中的 ListOperations ---------> 存list
在controller.java中引入RedisTemplate
@Autowired protected RedisTemplate<String,Object> redisTemplate; List<WxUsers> wxUserList = wxUsersService.findAll(); redisTemplate.opsForList().rightPushAll(RedisServiceKey.WxMemberList_REDIS_KEY, wxUserList);
当前方法执行成功之后,在redis查看工具【Redis的初始,安装及运行和使用】中进行查看对应的redis-key的值:(可能会出现乱码情况,出现则进行修改RedisConfig.java中的setValueSerializer()的属性)
@Bean public RedisTemplate<String,Object> functionDomainRedisTemplate() { RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>(); Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); //定义key生成策略 redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setHashKeySerializer(new StringRedisSerializer()); redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer); redisTemplate.setValueSerializer(jackson2JsonRedisSerializer); redisTemplate.setConnectionFactory(redisConnectionFactory); return redisTemplate; }
附 : Springboot 、redis 、 jwt 实现token验证
- Token 为什么要存储到Redis?
- 1.Token 具有时效性,简单来说就是有效期,超过了这个期限Token 就会失效,需要用户重新登录。但是怎么在项目中实现这个时效性,这个时候就用到Redis,Redis天然支持支持设置过期时间以及通过一些第三方提供包的API到达自动续时效果。
- 2.Redis采用NoSQL技术,可以支持每秒十几万此的读/写操作,其性能远超数据库,在请求数量较多的时候,Redis也可以从容应对
- 3.用户登录信息一般不需要长效储存,放在内存中,可以减少数据库的压力;
2 取list
Redis中的 取出刚存的 list 判断是否存在 redisTemplate.hasKey(Constants.USER_TOKEN_KEY );
存在就取出 redisTemplate.opsForValue().get( Constants.USER_TOKEN_KEY );
List<WxUsers>wxUserList=redisTemplate.opsForList().range(RedisServiceKey.WxMemberList_REDIS_KEY, 0, -1); log.info(">>>>>>>>>>>>>>>list = {}", wxUserList); Iterator<WxUsers>it=wxUserList.iterator(); while(it.hasNext()){ WxUserswxUser=it.next(); if(!token.equals(wxUser.getWx_userOpenId())) { thrownewRuntimeException(ErrorMessage.error_login_no_user); } }
3 比对某个值
通过引入 在拦截器中配置redis缓存机制 实现比对redis中值是否相同
/**** 存入list- redis 并取出 list - redis - openid*/booleanhasKey=redisTemplate.hasKey(RedisServiceKey.WxMemberList_REDIS_KEY); if(hasKey) { //取listList<WxUsers>wxUserList=redisTemplate.opsForList().range(RedisServiceKey.WxMemberList_REDIS_KEY, 0, -1); log.info(">>>>>>>>>>>>>>>list = {}", wxUserList); Iterator<WxUsers>it=wxUserList.iterator(); while(it.hasNext()){ WxUserswxUser=it.next(); if(token.equals(wxUser.getWx_userOpenId())) { break; } // // 验证 token// JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(token)).build();// try {// jwtVerifier.verify(token);// } catch (JWTVerificationException e) {// throw new RuntimeException("401");// } } }else { //存listList<WxUsers>wxUserList=wxUsersService.findAll(); redisTemplate.opsForList().rightPushAll(RedisServiceKey.WxMemberList_REDIS_KEY, wxUserList); thrownewRuntimeException(ErrorMessage.error_login_no_user); }
以上代码是比对的所需内容,但是在启动项目时,会提示下面错误,
原因:
springboot拦截器是在Bean实例化之前执行的,所以Bean实例无法注入,也就是当我们想获取redis里面key是token的值时,发现redisTemplate没有是空的没有注入进来
解决:
知道拦截器执行在bean实例化前执行的,那么我们就让拦截器执行的时候实例化拦截器Bean,在拦截器配置类里面先实例化拦截器,然后再获取。
拦截器配置:WebAppConfig.java
publicclassWebAppConfigextendsWebMvcConfigurerAdapter { //访问图片方法publicvoidaddResourceHandlers(ResourceHandlerRegistryregistry) { Stringos=System.getProperty("os.name"); if (os.toLowerCase().startsWith("win")) { //如果是Windows系统registry.addResourceHandler("/images/**") // /apple/**表示在磁盘apple目录下的所有资源会被解析为以下的路径 .addResourceLocations("file:/D:/images/"); //媒体资源 } else { //linux 和macregistry.addResourceHandler("/images/**") .addResourceLocations("file:/usr/local/wwwroot/images/"); //媒体资源 } super.addResourceHandlers(registry); } //引用拦截器publicvoidaddInterceptors(InterceptorRegistryregistry) { registry.addInterceptor(handlerInterceptor()).addPathPatterns( "/**"); } //注册拦截器publicHandlerInterceptorhandlerInterceptor(){ returnnewHandlerInterceptor(); } }
过滤器配置:HandlerInterceptor.java
publicclassHandlerInterceptorextendsHandlerInterceptorAdapter { privatestaticfinalorg.slf4j.Loggerlog=org.slf4j.LoggerFactory.getLogger(HandlerInterceptor.class); privateRedisTemplate<String, Object>redisTemplate; privateValueOperations<String,Object>opsForValue; privateWxUsersServicewxUsersService; publicbooleanpreHandle(HttpServletRequestrequest, HttpServletResponseresponse, Objecthandler) { Stringtoken=request.getHeader(Constants.USER_OPENID_KEY);// 从 http 请求头中取出 token// 如果是handler方法直接继续执行if (!handler.getClass().isAssignableFrom(HandlerMethod.class)) { log.debug("This is handler."); System.out.println("-----This is handler.-----"); returntrue; } finalHandlerMethodhandlerMethod= (HandlerMethod) handler; finalMethodmethod=handlerMethod.getMethod(); finalClass<?>clazz=method.getDeclaringClass(); // 如果有auth注解,进行token登录验证if (clazz.isAnnotationPresent(Auth.class) ||method.isAnnotationPresent(Auth.class)) { log.debug("-----Auth----token:"+Constants.USER_OPENID_KEY+"-----"); System.out.println("-----Auth----token:"+Constants.USER_OPENID_KEY+"-----"); // 如果没有token进行拦截。if (token==null) { thrownewRuntimeException(ErrorMessage.error_fresh_user_login_all); } /**** 存入list- redis 并取出 list - redis - openid*/booleanhasKey=redisTemplate.hasKey(RedisServiceKey.WxMemberList_REDIS_KEY); if(hasKey) { //取listList<WxUsers>wxUserList=redisTemplate.opsForList().range(RedisServiceKey.WxMemberList_REDIS_KEY, 0, -1); log.info(">>>>>>>>>>>>>>>list = {}", wxUserList); Iterator<WxUsers>it=wxUserList.iterator(); while(it.hasNext()){ WxUserswxUser=it.next(); if(token.equals(wxUser.getWx_userOpenId())) { break; } // // 验证 token// JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(token)).build();// try {// jwtVerifier.verify(token);// } catch (JWTVerificationException e) {// throw new RuntimeException("401");// } } }else { //存listList<WxUsers>wxUserList=wxUsersService.findAll(); redisTemplate.opsForList().rightPushAll(RedisServiceKey.WxMemberList_REDIS_KEY, wxUserList); thrownewRuntimeException(ErrorMessage.error_login_no_user); } returntrue; } log.debug("-----Auth----No-----"); System.out.println("-----Auth----No-----"); returntrue; } /*** 在整个请求结束之后被调用,也就是在DispatcherServlet 渲染了对应的视图之后执行(主要是用于进行资源清理工作)*/publicvoidafterCompletion(HttpServletRequestrequest, HttpServletResponseresponse, Objecthandler, Exceptionex) { // System.out.println("执行了TestInterceptor的afterCompletion方法"); } }
4 传参调用
在小程序端head中输入要传的参数值:
//获取小程序平台基本信息wx.request({ url: base.path.www+'WxPlatform/SelectWxPlatformInfo', method: 'post', data: { }, header: { 'content-type': 'application/json;charset=UTF-8' , 'wx_userOpenId': wx.getStorageSync('wxMemberOpenId') }, success(res) { wx.setStorageSync('wxPlatformInfo', res.data.data.wxPlatformInfo); that.setData({ wxPlatformInfo: res.data.data.wxPlatformInfo }); } }),
然后在后台服务器中通过拦截器进行取head中的wx_userOpenId并进行验证是否验证通过
stringRedisTemplate.opsForValue().set("test", "100",60*10,TimeUnit.SECONDS);//向redis里存入数据和设置缓存时间stringRedisTemplate.opsForValue().get("test") //根据key获取缓存中的valstringRedisTemplate.boundValueOps("test").increment(-1); //val做-1操作stringRedisTemplate.boundValueOps("test").increment(1); //val +1stringRedisTemplate.getExpire("test") //根据key获取过期时间stringRedisTemplate.getExpire("test",TimeUnit.SECONDS) //根据key获取过期时间并换算成指定单位stringRedisTemplate.delete("test"); //根据key删除缓存stringRedisTemplate.hasKey("546545"); //检查key是否存在,返回boolean值stringRedisTemplate.expire("red_123",1000 , TimeUnit.MILLISECONDS); //设置过期时间stringRedisTemplate.opsForSet().add("red_123", "1","2","3"); //向指定key中存放set集合stringRedisTemplate.opsForSet().isMember("red_123", "1") //根据key查看集合中是否存在指定数据stringRedisTemplate.opsForSet().members("red_123"); //根据key获取set集合
遗留问题就是 redis的客户端工具显示的key是乱码,正在解决,有知道的大佬请帮下谢谢了!!!
扩展:
publicWxPlatformSelectWxDefaultPlatformInfo(StringwxAgentAddrId) { WxAgentAddresswxAgentAddress=wxAgentAddressService.getDefaultPlatform(wxAgentAddrId); WxPlatformplatform=wxPlatformRepository.getWxDafaultPlatformInfo(wxAgentAddress.getWx_areaId()); // 判断key是否存在if(!redisServiceImpl.exists(RedisServiceKey.WxPlatform_REDIS_KEY)) { // 获取key的valueWxPlatformwxPlat= (WxPlatform) redisServiceImpl.get(RedisServiceKey.WxPlatform_REDIS_KEY); if(wxPlat!=null&&!wxPlat.getWx_agentAddressId().equals(wxAgentAddrId)) { // 删除keyredisServiceImpl.remove(RedisServiceKey.WxPlatform_REDIS_KEY); } } // 保存keyredisServiceImpl.set(RedisServiceKey.WxPlatform_REDIS_KEY, platform); //WxPlatform wxPlat = (WxPlatform) redisServiceImpl.get(RedisServiceKey.WxPlatform_REDIS_KEY); returnplatform; }
在操作redis中的key时,每个key都要去判断下hashkey是否存在,然后进行业务的相关操作
⚠️注意 ~
💯本期内容就结束了,如果内容有误,麻烦大家评论区指出!
如有疑问❓可以在评论区💬或私信💬,尽我最大能力🏃♀️帮大家解决👨🏫!
如果我的文章有帮助到您,欢迎关注+点赞✔️鼓励博主🏃,您的鼓励是我分享的动力🏃🏃🏃~