Redis学习------实战篇----2024/02/28
1.集群的session共享问题
2.基于Redis实现共享session登录
//4.保存验证码到redis
stringRedisTemplate.opsForValue().set(LOGIN_CODE_KEY+phone,code,LOGIN_CODE_TTL, TimeUnit.MINUTES);
RedisTemplate
RedisTemplate使用的是JdkSerializationRedisSerializer存入数据,会将数据先序列化成字节数组,然后在存入Redis数据库。
如果数据是复杂的对象类型,而取出的时候又不想做任何的数据转换,直接从Redis里面取出一个对象,那么使用RedisTemplate是更好的选择。
你会看到你的数据不是以可读的形式展现的,而是以字节数组显示
当然从Redis获取数据的时候,也会默认将数据当做字节数组转化,这样就会导致一个问题,当需要获取的数据,不是以字节数组存在redis当中,而是正常的可读的字符串的时候,
RedisTemplate就无法获取导数据,这个时候获取到的值就是NULL。这个时候StringRedisTempate就派上了用场。
StringRedisTemplate使用的是StringRedisSerializer,当你的redis数据库里面本来存的是字符串数据,或者你要存取的数据就是字符串类型数据的时候,那么你就使用StringRedisTemplate即可。
当redis中存入的数据是可读形式而非字节数组时,使用redisTemplate取值的时候会无法获取导出数据,获得的值为null。可以使用 StringRedisTemplate 试试。
出现报错
java.lang.ClassCastException: class java.lang.Long cannot be cast to class java.lang.String (java.lang.Long and java.lang.String are in module java.base of loader ‘bootstrap’)
at org.springframework.data.redis.serializer.StringRedisSerializer.serialize(StringRedisSerializer.java:36) ~[spring-data-redis-2.3.9.RELEASE.jar:2.3.9.RELEASE]
原因是这个类的属性是Long,不是String类型
解决方法
Map<String, Object> userMap = BeanUtil.beanToMap(userDTO,
new HashMap<>(),
CopyOptions.create()
.setIgnoreNullValue(true)
.setFieldValueEditor((fieldName,fieldValue)->fieldValue.toString()));
这段代码是使用了 BeanUtil 类中的 beanToMap 方法将一个 userDTO 对象转换为一个 Map<String, Object> 类型的对象。在转换过程中,使用了 CopyOptions 类的 create 方法创建了一些配置选项,包括忽略空值和字段值编辑器。
在这里,setIgnoreNullValue(true) 表示忽略空值,即在转换过程中忽略掉值为 null 的属性;setFieldValueEditor((fieldName,fieldValue)->fieldValue.toString()) 则表示对字段值进行编辑,将字段值转换为字符串类型。
通过这段代码,可以将 userDTO 对象的属性值以键值对的形式存储到 userMap 中,且在此过程中满足了忽略空值和字段值编辑的需求。
登录后的数据存储
/**
* 短信验证码登录
* @param loginForm
* @param session
* @return
*/
@Override
public Result login(LoginFormDTO loginForm, HttpSession session) {
//1.校验手机号
String phone = loginForm.getPhone();
//2.如果不符合,返回错误信息
if(RegexUtils.isPhoneInvalid(phone)){
return Result.fail("手机号格式错误");
}
//3.校验验证码
String cacheCode = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY + phone);
String code = loginForm.getCode();
//4.不一致报错
if(code==null || !cacheCode.toString().equals(code)){
return Result.fail("验证码错误");
}
//5.根据手机号查询用户是否存在
User user = query().eq("phone", phone).one();
if (user == null) {
//6.不存在,创建新用户
user=createUserWithPhone(phone);
}
//7.保存用户到redis中
//7.1随机生成token,作为登录令牌
String token = UUID.randomUUID().toString(true);
//7.2将User对象转换为Hash存储
UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
Map<String, Object> userMap = BeanUtil.beanToMap(userDTO,
new HashMap<>(),
CopyOptions.create()
.setIgnoreNullValue(true)
.setFieldValueEditor((fieldName,fieldValue)->fieldValue.toString()));
//7.3存储
String tokenKey=LOGIN_USER_KEY+token;
stringRedisTemplate.opsForHash().putAll(tokenKey,userMap);
//7.4设置token有效期
stringRedisTemplate.expire(tokenKey,LOGIN_USER_TTL,TimeUnit.MINUTES);
//8.返回token
return Result.ok(token);
}
LoginInterceptor.java的代码
private StringRedisTemplate stringRedisTemplate;
public LoginInterceptor(StringRedisTemplate stringRedisTemplate) {
this.stringRedisTemplate = stringRedisTemplate;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//1. 获取请求头中的token
String token = request.getHeader("authorization");
if ( StrUtil.isBlank(token)) {
response.setStatus(401);
return false;
}
String key=RedisConstants.LOGIN_USER_KEY + token;
//2. 基于token获取redis中的用户
Map<Object, Object> userMap = stringRedisTemplate.opsForHash()
.entries(key);
//3.判断用户是否存在
if(userMap.isEmpty()){
//4.用户不存在进行拦截
response.setStatus(401);
return false;
}
//5.查询到的Hash数据转换为UserDto对象
UserDTO userDTO = BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);
// 6.用户存在将用户信息保存到ThreadLocal
UserHolder.saveUser((UserDTO) userDTO);
//7. 刷新token的有效期
stringRedisTemplate.expire(key,RedisConstants.LOGIN_USER_TTL, TimeUnit.MINUTES);
//8.放行
return HandlerInterceptor.super.preHandle(request, response, handler);
}
3.登录拦截器的优化
public class RefreshTokenInterceptor implements HandlerInterceptor {
private StringRedisTemplate stringRedisTemplate;
public RefreshTokenInterceptor(StringRedisTemplate stringRedisTemplate) {
this.stringRedisTemplate = stringRedisTemplate;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//1. 获取请求头中的token
String token = request.getHeader("authorization");
if ( StrUtil.isBlank(token)) {
return true;
}
String key=RedisConstants.LOGIN_USER_KEY + token;
//2. 基于token获取redis中的用户
Map<Object, Object> userMap = stringRedisTemplate.opsForHash()
.entries(key);
//3.判断用户是否存在
if(userMap.isEmpty()){
//4.用户不存在进行拦截
return true;
}
//5.查询到的Hash数据转换为UserDto对象
UserDTO userDTO = BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);
// 6.用户存在将用户信息保存到ThreadLocal
UserHolder.saveUser(userDTO);
//7. 刷新token的有效期
stringRedisTemplate.expire(key,RedisConstants.LOGIN_USER_TTL, TimeUnit.MINUTES);
//8.放行
return HandlerInterceptor.super.preHandle(request, response, handler);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
//移除用户
UserHolder.removeUser();
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//1.判断是否需要拦截---ThreadLocal中是否有用户
if(UserHolder.getUser()==null){
response.setStatus(401);
return false;
}
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
//移除用户
UserHolder.removeUser();
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
4.商户查询缓存
5.添加Redis缓存
@Service
public class ShopServiceImpl extends ServiceImpl<ShopMapper, Shop> implements IShopService {
@Resource
private StringRedisTemplate stringRedisTemplate;
@Override
public Result queryById(Long id) {
//1.从redis中查询缓存
String key = CACHE_SHOP_KEY + id;
String shopJson = stringRedisTemplate.opsForValue().get(key);
//2.判断是否存在
//3.存在则直接返回
if (StrUtil.isNotBlank(shopJson)){
Shop shop = JSONUtil.toBean(shopJson, Shop.class);
return Result.ok(shop);
}
//4.不存在,根据ID查询数据库
Shop shop = getById(id);
//5.不存在,返回错误
if (shop== null){
return Result.fail("店铺不存在");
}
//6.存在,写入redis
stringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(shop));
//7.返回
return Result.ok(shop);
}
}
缓存商品列表
@Service
public class ShopTypeServiceImpl extends ServiceImpl<ShopTypeMapper, ShopType> implements IShopTypeService {
@Resource
private StringRedisTemplate stringRedisTemplate;
@Override
public Result queryList() {
String key = CACHE_TYPE_LIST;
String shopJson = stringRedisTemplate.opsForValue().get(key);
//查询缓存中是否有数据
if(StrUtil.isNotBlank(shopJson)){
List<ShopType> shopTypeList = JSONUtil.toList(shopJson, ShopType.class);
return Result.ok(shopTypeList);
}
List<ShopType> shopTypeList = query().orderByAsc("sort").list();
//将数据库中的数据保存到缓存中
stringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(shopTypeList));
return Result.ok(shopTypeList);
}
}
5.缓存更新策略
6.数据库和缓存的双写一致的实现
@Override
@Transactional
public Result update(Shop shop) {
Long shopId = shop.getId();
if (shopId==null){
return Result.fail("查询的店铺不存在");
}
//1.更新数据库
updateById(shop);
//2.删除缓存
stringRedisTemplate.delete(CACHE_SHOP_KEY + shopId);
return null;
}
原文地址:https://blog.csdn.net/m0_68185369/article/details/136326215
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!