ThreadLocalの役割は何ですか?
ThreadLocalは、スレッドセーフの問題を解決するための良いアイデアです。スレッドごとに独立した変数コピーを提供することにより、変数への同時アクセスの競合を解決します。多くの場合、ThreadLocalは、同期同期メカニズムを直接使用してスレッドセーフの問題を解決するよりも単純で便利であり、結果として得られるプログラムの同時実行性は高くなります。
ThreadLocalのアプリケーションシナリオは何ですか?
Javaのマルチスレッドプログラミングでは、複数のスレッドによる共有変数への安全なアクセスを確保するために、通常、同期を使用して、一度に1つのスレッドのみが共有変数を操作するようにします。この場合、クラス変数をThreadLocal型オブジェクトに配置して、変数が各スレッドに独立したコピーを持つようにすることができます。あるスレッドが変数を読み取り、別のスレッドによって変更されるという現象は発生しません。最も一般的なThreadLocalの使用シナリオは、データベース接続、セッション管理などを解決することです。いくつかのシナリオを以下に示します
このようにして、ThreadLocalを介して任意の場所でユーザーデータを取得できます。
需要分析:
*これはショッピングカートのシナリオです。ユーザーがログインしていないときにショッピングカートを追加できます。また、ログイン後にショッピングカートを追加することもできます。
*ブラウザにはcookie:user-keyがあり、これはユーザーのIDを識別し、1か月で期限切れになります。
* jdのショッピングカート機能を初めて使用する場合は、一時的なユーザーIDが与えられます。
*ブラウザは保存します。それは後に、あなたとあなたがこのクッキーを訪問するたびにそれを持って来る
*
*ログインセッションはい
*ノーログイン、クッキー内のユーザーキーに従って行う
*初回:何も一時的なユーザーが存在しない場合は、ヘルプが一時的なユーザーを作成します- >インターセプター
* @ returnを使用して
依存関係を導入します。
<!-- 引入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>
plication.ymlの構成情報を変更します
#配置Redis缓存
spring:
redis:
host: 81.68.112.20
port: 6379
#整合Spring Session 指定session是存到redis里
session:
store-type: redis
最初にインターセプターを作成します
**
* @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);
}
}
}
コントローラのThreadLocalを介してユーザーデータを取得します。
/**
* 浏览器有一个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";
}