¿Cuál es el papel de ThreadLocal?
ThreadLocal es una buena idea para resolver el problema de seguridad de los subprocesos, ya que resuelve el conflicto del acceso concurrente a las variables proporcionando una copia de variable independiente para cada subproceso. En muchos casos, ThreadLocal es más simple y conveniente que usar directamente el mecanismo de sincronización sincronizada para resolver problemas de seguridad de subprocesos, y el programa resultante tiene una mayor simultaneidad.
¿Cuál es el escenario de aplicación de ThreadLocal?
En la programación multiproceso de Java, para garantizar el acceso seguro a las variables compartidas por varios subprocesos, la sincronización se utiliza generalmente para garantizar que solo un subproceso opere en variables compartidas a la vez. En este caso, puede poner la variable de clase en el objeto de tipo ThreadLocal, para que la variable tenga una copia independiente en cada hilo, y no habrá fenómeno de que un hilo lea la variable y sea modificado por otro hilo. El escenario de uso más común de ThreadLocal es resolver la conexión de la base de datos, la administración de sesiones, etc. A continuación se enumeran algunos escenarios
De esta forma, podemos obtener datos de los usuarios a través de ThreadLocal en cualquier lugar.
Análisis de demanda:
* Este es un escenario de carrito de compras. Puede agregar un carrito de compras cuando el usuario no ha iniciado sesión, y también puede agregar un carrito de compras después de iniciar sesión.
* El navegador tiene una cookie: user-key, que identifica la identidad del usuario, y vence en un mes.
* Si usa la función de carrito de compras de jd por primera vez, se le dará una identidad de usuario temporal.
* El navegador guardará más tarde y tráigala con usted cada vez que visite Esta cookie
*
* Sesión de inicio de sesión Sí
* Sin inicio de sesión, hágalo de acuerdo con la clave de usuario en la cookie
* Primera vez: Si no hay un usuario temporal, ayude a crear un usuario temporal - > Utilice el interceptor
* @return para
introducir dependencia:
<!-- 引入redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 整合Spring Session完成session共享问题 微服务自治,就不放在common里了-->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
Modificar la información de configuración de plication.yml
#配置Redis缓存
spring:
redis:
host: 81.68.112.20
port: 6379
#整合Spring Session 指定session是存到redis里
session:
store-type: redis
Primero crea un interceptor
**
* @author 孟享广
* @date 2021-02-03 3:01 下午
* @description 在执行目标方法之前,判断用户的登录状态,并封装传递给controller的目标请求
*/
public class CartInterceptor implements HandlerInterceptor {
//ThreadLocal 同一线程上信息共享
public static ThreadLocal<UserInfoTo> threadLocal = new ThreadLocal<>();
/**
* 在目标方法执行之前
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
UserInfoTo userInfoTo = new UserInfoTo();
HttpSession session = request.getSession();
MemberResVo memberResVo = (MemberResVo) session.getAttribute(AuthServiceConstant.LOGIN_USER);
if (memberResVo != null) {
//说明用户登录了
userInfoTo.setUserId(memberResVo.getId());
}
//只要有user-key 就赶紧取出value
Cookie[] cookies = request.getCookies();
if (cookies != null && cookies.length > 0) {
//有cookie 可能是临时用户,但是此方法针对登录用户
for (Cookie cookie : cookies) {
String name = cookie.getName();
if (name.equals(CartServiceConstant.TEMP_USER_COOKIE_NAME)) {
userInfoTo.setUserKey(cookie.getValue());
//执行到这,说明是临时用户
userInfoTo.setTempUser(true);
}
}
}
//如果没有登录 就准备临时set一个cookie,首先设置To的userKey
if (StringUtils.isEmpty(userInfoTo.getUserKey())) {
String uuid = UUID.randomUUID().toString();
userInfoTo.setUserKey(uuid);
}
//目标方法执行前,放入 threadLocal
threadLocal.set(userInfoTo);
//只要来到目标方法都放行 无条件放行
return true;
}
/**
* 业务执行之后,让浏览器保存cookie
* 分配临时用户,让浏览器保存
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
UserInfoTo userInfoTo = threadLocal.get();
//如果没有临时用户,一定要保存一个临时用户
if (!userInfoTo.isTempUser()) {
//不是临时用户
Cookie cookie = new Cookie(CartServiceConstant.TEMP_USER_COOKIE_NAME, userInfoTo.getUserKey());
//设置cookie作用域
cookie.setDomain("gulimall.com");
//cookie的过期时间
cookie.setMaxAge(CartServiceConstant.TEMP_USER_COOKIE_TIMEOUT);
response.addCookie(cookie);
}
}
}
Obtenga datos de usuario a través de ThreadLocal en Controller.
/**
* 浏览器有一个cookie:user-key:标识用户身份 一个月过期
* 假如是第一次登录,都会给一个临时身份
* 浏览器保存以后,每次访问都会带上这个cookie
*/
@GetMapping("/cart.html")
public String cartListPage(Map<String, Cart> map) throws ExecutionException, InterruptedException {
//1 快速得到用户信息,ThreadLocal获取用户信息
UserInfoTo userInfoTo = CartInterceptor.threadLocal.get();
System.out.println(userInfoTo);
return "cartList";
}