赞
踩
单机,不涉及哨兵、集群
set key value
get key
incr key
decr key
自增或者自减,比如自动刷新浏览量
作缓存,set后加自动销毁时间。ex 数字。 ex秒,px毫秒
ttl cache 查看剩余时间
flushdb删库。生产环境下,禁止!
导包+配置
guava本地缓存,后面令牌桶会用
看redis的自动配置类
注解:
less
复制代码
@Configuration (proxyBeanMethods = false) @ConditionalOnClass(RedisOperations.class) @EnableConfigurationProperties (RedisProperties.class) @Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class }) public class RedisAutoConfiguration {
@ConditionalOnMissingBean (name= "redisTemplate")没有这个bean,实例化下面的bean
redisTemplate,外面访问redis的接口/类,需要提前实例化。
实例化时(运行方法),传入redisConnectionFactory(方法的参数)。
@Import的内容就是构建Factory的。
因为redisConnectionFactory持有redis的链接,set进redisTemplate,redisTemplate才能调用链接访问redis
实例化的方式太简单,只new了一下,这样在服务器redis中存入的数据(字符串,json格式字符串等)默认序列化为了二进制数据,很不方便。
要重写
自己写一个配置类,实例化好,覆盖掉自带的。
自己重写的configuration/RedisConfiguration
注:redis的kv结构,如果v是存哈希,哈希也有kv,都要序列化
common/FastJsonSerializer——自定义的value和哈希value序列化工具
实现RedisSerializer接口
序列化和反序列化方法
序列化方法:
SerializerFeature.WriteClassName
是转换成json时,在json串开头添加最底层的classname类名信息。bash
复制代码
{classname:xxx, id:1,username:zhangsan}
反序列化: 传入之前序列化的字节,为空返回空 不为空,把字节转换成json字符串 再用fastJson工具解析字符串成对象,得到的是object对象,Feature.SupportAutoType底层是转换成之前存的时候标记的类
注:调用fastjson的API,非自己写
java
复制代码
public class FastJsonSerializer implements RedisSerializer<Object> { public static final Charset UTF_8 = Charset.forName("UTF-8"); @Override public byte[] serialize(Object obj) throws SerializationException { if (obj == null) { return null; } String json = JSON.toJSONString(obj, SerializerFeature.WriteClassName); return json.getBytes(UTF_8); } @Override public Object deserialize(byte[] bytes) throws SerializationException { if (bytes == null || bytes.length <= 0) { return null; } String json = new String(bytes, UTF_8); return JSON.parseObject(json, Object.class, Feature.SupportAutoType); } }
=====================================================以上Redis配置完成
代码解析
哪个bean要用redis,就注入redistemplate。
opsFor___()方法,处理对应格式数据
string字符串是opForvalue方法。
作缓存功能
redis里面一般存很多kv,所以key存的时候要分级、用冒号分割,要结合语义。
opsFor___().set/get/increment
redis里面看
格式固定
注:在事务未提交前,中间得不到kv的value值。execute()方法执行完吗,事务提交以后才能得到
csharp
复制代码
@Test public void testTransactional() { Object result = redisTemplate.execute(new SessionCallback() { @Override public Object execute(RedisOperations operations) throws DataAccessException { String redisKey = "test:tx"; operations.multi(); operations.opsForSet().add(redisKey, "zhangsan"); operations.opsForSet().add(redisKey, "lisi"); operations.opsForSet().add(redisKey, "wangwu"); System.out.println(operations.opsForSet().members(redisKey)); return operations.exec(); } }); System.out.println(result); }
获取类的类名?object的操作
反射的API
从库里读到字符串,包含类名,怎么返回处理成对象。如下字符串
所有框架底层都用到类反射。
框架的配置,类名等都是字符串,获取字符串,来实例化对应的对象。就是通过类反射。
用redis实现全局分布式的session。
session只在controller层使用,一般都在servicecontrollor。
session是基于http协议,跟request请求有关,所有在controller层(前后端沟通的接口)。
项目中的controllor有session的,都要替换成redis
Ctrl+shift+f:在所有路径下搜索
三个地方都有session,
java
复制代码
@Autowired private RedisTemplate redisTemplate;
getOTP(参数:手机号)
方法获取验证码,其内部调用generateOTP()方法通过随机数生成验证码,获取到后,redisTemplate.opsForValue().set(phone, otp, 5, TimeUnit.MINUTES)
,key是手机号,value是验证码,存到redis里。typescript
复制代码
private String generateOTP() { StringBuilder sb = new StringBuilder(); Random random = new Random(); for (int i = 0; i < 4; i++) { sb.append(random.nextInt(10)); } return sb.toString(); } @RequestMapping(path = "/otp/{phone}", method = RequestMethod.GET) @ResponseBody public ResponseModel getOTP(@PathVariable("phone") String phone/*, HttpSession session*/) { // 生成OTP String otp = this.generateOTP(); // 绑定OTP // session.setAttribute(phone, otp); redisTemplate.opsForValue().set(phone, otp, 5, TimeUnit.MINUTES); // 发送OTP logger.info("[牛客网] 尊敬的{}您好, 您的注册验证码是{}, 请注意查收!", phone, otp); return new ResponseModel(); }
register(参数:输入的验证码、用户对象)
方法,用redisTemplate.opsForValue().get(user.getPhone());
由用户对象get手机号,再从redis中get到手机号真实的验证码less
复制代码
@RequestMapping(path = "/register", method = RequestMethod.POST) @ResponseBody public ResponseModel register(String otp, User user/*, HttpSession session*/) { // 验证OTP // String realOTP = (String) session.getAttribute(user.getPhone()); String realOTP = (String) redisTemplate.opsForValue().get(user.getPhone()); if (StringUtils.isEmpty(otp) || StringUtils.isEmpty(realOTP) || !StringUtils.equals(otp, realOTP)) { throw new BusinessException(PARAMETER_ERROR, "验证码不正确!"); } // 加密处理 user.setPassword(Toolbox.md5(user.getPassword())); // 注册用户 userService.register(user); return new ResponseModel(); }
user =userService.login(phone, md5pwd);
把账号密码放到user对象中String token = UUID.randomUUID().toString().replace("-", "");
生成,同时去掉横线less
复制代码
@RequestMapping(path = "/login", method = RequestMethod.POST) @ResponseBody public ResponseModel login(String phone, String password/*, HttpSession session*/) { if (StringUtils.isEmpty(phone) || StringUtils.isEmpty(password)) { throw new BusinessException(PARAMETER_ERROR, "参数不合法!"); } String md5pwd = Toolbox.md5(password); User user = userService.login(phone, md5pwd); // session.setAttribute("loginUser", user); String token = UUID.randomUUID().toString().replace("-", ""); redisTemplate.opsForValue().set(token, user, 1, TimeUnit.DAYS); return new ResponseModel(token); }
less
复制代码
@RequestMapping(path = "/logout", method = RequestMethod.GET) @ResponseBody public ResponseModel logout(/*HttpSession session*/String token) { // session.invalidate(); if (StringUtils.isNotEmpty(token)) { redisTemplate.delete(token); } return new ResponseModel(); }
getUser(参数:tocken)
方法,判断令牌不为空,user = (User) redisTemplate.opsForValue().get(token);
,从redis中get到tocken对应的value(对象序列化后的json串),转换成user对象sql
复制代码
@RequestMapping(path = "/status", method = RequestMethod.GET) @ResponseBody public ResponseModel getUser(/*HttpSession session*/String token) { // User user = (User) session.getAttribute("loginUser"); User user = null; if (StringUtils.isNotEmpty(token)) { user = (User) redisTemplate.opsForValue().get(token); } return new ResponseModel(user); }
user = (User) session.getAttribute("loginUser");
。用redis后create(参数:itemId商品id、amount购买数量、promotionId活动、token用户令牌)
方法,根据tocken在redis中获取userid,user = (User)redisTemplate.opsForValue().get(token);
,get到token对应user,后面获取useridless
复制代码
@Controller @RequestMapping("/order") @CrossOrigin(origins = "${nowcoder.web.path}", allowedHeaders = "*", allowCredentials = "true") public class OrderController implements ErrorCode { @Autowired private OrderService orderService; @Autowired private RedisTemplate redisTemplate; @RequestMapping(path = "/create", method = RequestMethod.POST) @ResponseBody public ResponseModel create(/*HttpSession session, */ int itemId, int amount, Integer promotionId, String token) { // User user = (User) session.getAttribute("loginUser"); User user = (User) redisTemplate.opsForValue().get(token); orderService.createOrder(user.getId(), itemId, amount, promotionId); return new ResponseModel(); } }
preHandle(参数:request、response、handler)
方法,格式固定,返回布尔类型。request.getParameter("token")
。redisTemplate.hasKey(token)
【底层调用的Linux命令:exists key。1存在0不存在】。返回的提示信息,代码可以细看一下
typescript
复制代码
@Component public class LoginCheckInterceptor implements HandlerInterceptor, ErrorCode { @Autowired private RedisTemplate redisTemplate; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // HttpSession session = request.getSession(); // User user = (User) session.getAttribute("loginUser"); String token = request.getParameter("token"); if (token == null || !redisTemplate.hasKey(token)) { response.setContentType("application/json"); response.setCharacterEncoding("utf-8"); PrintWriter writer = response.getWriter(); Map<Object, Object> data = new HashMap<>(); data.put("code", USER_NOT_LOGIN); data.put("message", "请先登录!"); ResponseModel model = new ResponseModel(ResponseModel.STATUS_FAILURE, data); writer.write(JSONObject.toJSONString(model)); return false; } return true; } }
controller的方法想要什么参数,就声明什么。如果前端传入一样的参数,就会通过反射机制解析、赋值进来
1.登录login
window.sessionStorage.setItem("token", result.data);
存储token
2.user
换token后,客户端用ajax返回服务器信息,没法自动携带token,所以我们要拼到url上去
url: SERVER_PATH + "/user/status?token=" + window.sessionStorage.getItem("token")
所有需要客户端携带token的地方,都要这样处理
如订单controller,创建订单需要传入token。前端item.js同样拼上去
如果请求很多,jquery有监听器拦截,可以拦截强制拼一个上去。
token存在本地sessionstorage上,地址栏不显示。
也可以tocken放到请求头里
作者:用户7288159815883
链接:https://juejin.cn/post/7232520169481502781
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。