浏览器有一个cookie:user-key 标记用户身份,一个月后过期
如果第一次使用jd的购物车功能,都会给一个临时的用户身份
浏览器以后保存,每次都会带上这个cookie
登录:session有
没登录:按照cookie里面带来user-key来做
第一次,如果没有临时用户,帮忙创建一个临时用户
利用拦截器【CartInterceptor(实现HandlerInterceptor接口)重写preHandle方法】以及
ThreadLocal机制(public static ThreadLocal threadLocal=new ThreadLocal<>();),
在所有请求到达之前,封装了当前的用户信息,
无论是临时用户还是登陆的用户,达到在请求到了以后快速获得用户信息。
要实现上述的功能,需要再购物车服务,创建拦截器,拦截所有访问购物车服务的请求(判断用户的登录状态,并封装传递给Controller目标请求)
创建自定义拦截器CartInterceptor实现HandlerInterceptor接口,并注入到bean容器中,并添加拦截器的实现方法(目标方法执行之前)preHandle(HttpServletRequest request, HttpServletResponse response, Object handler),要看当前用户有没有登录,直接从request(被springsession包装过得)里获取到HttpSession,再通过getAttribute方法获取当前用户信息,如果当前用户已经登录,则保存用户id。
每次发送请求都会带cookic的,获取cookic里面的数据,如果有临时用户的user-key字段则带上
将userId和userKey字段都写入UserInfoTo中。
最后所有请求购物车的用户可以快速得到用户信息,登录的有userId,没有登录的有userKey,可以通过JDK提供的ThreadLocal(在同一个线程共享数据)来实现。
一个请求(tomcat开一个线程进行处理)进来,从拦截器->controller->service->dao->响应结束,都是同一个线程。
具体实现:在同一个线程期间,上一个(controller)放的数据,下一个(service)想要共享,怎么办??就可以使用ThreadLocal。
*ThreadLocal的核心原理就是一个Map
,这个Map的key就是Thread(当前线程),value就是要共享的数据,线程不一样,里面的value就不一样。
在拦截器里面写静态的公共变量:public static ThreadLocal threadLocal=new ThreadLocal<>();
在目标方法(购物车请求)执行之前,也就是在拦截器里面,将我们之前获取到的userId和userKey放到ThreadLocal里面去。
在每个目标方法(购物车请求)执行之时,UserInfoTo userInfoTo = CartInterceptor.threadLocal.get();就可以获取到用户信息
如果浏览器没有user-key,我们通过uuid生成user-key。
后端遇到的问题
1、购物车成功页面刷新、重新加入购物车导致商品数量累增的问题。(类似于重复提交)
解决方案:
每次添加购物车成功,不要直接展示添加购物车成功页面,重定向到购物车成功页面,添加成功后跳到新页面,只刷新页面而不累增数量
用的什么技术:
threadlocal、redis的Hash操作保存购物车信息、线程池异步任务调度、重定向-解决刷新不自定更新购物车商品数量
线程池的异步任务调度:设置异步任务-CompletableFuture.runAsync(()),CompletableFuture.allOf(future1, future2).get();
在异步的任务完成后,需要用其结果继续操作时,无需等待。可以直接通过thenAccept、thenApply、thenCompose等方式将前面异步处理的结果交给另外一个异步事件处理线程来处理。
