😽
Re:ゼロから始めるSpring Boot実践#1 WebBlog開発#5 ThreadLocal
ThreadLocal とは
各スレッドに独立した変数のコピーを持たせることで、スレッドごとに安全にデータを保持する仕組み。
なぜ必要か
通常、複数のスレッドが同じ変数を共有すると、データ競合や予期しない動作を引き起こすことがある。ThreadLocalを使えば、スレッドごとに「専用の変数領域」が与えられるため、他のスレッドに影響されることなく安全にデータを扱える。
例
public class ThreadLocalTest {
private static ThreadLocal<String> threadLocal = new ThreadLocal<>();
public void doSomething(String name) {
threadLocal.set(name); // 各スレッド専用に値をセット
System.out.println("Hello " + threadLocal.get());
threadLocal.remove(); // メモリリーク防止のため削除
}
}
ThreadLocalの実装
前回の記事で紹介したコードを使ってThreadLocalを実装する。
前回の記事:https://zenn.dev/articles/cbfc44bc891b18/edit
JWTを解析してユーザ名を取得する処理を共通化し、JWTを解析したデータをThreadLocal保存して利用できるようにする。
ThreadLocalUtilメソッドを作成
ThreadLocalUtilを作成してThreadLocalへのデータ追加、取得、削除機能を提供する。
public class ThreadLocalUtil {
//ThreadLocalオブジェクトを作成
private static final ThreadLocal THREAD_LOCAL = new ThreadLocal();
//キーでバリューを取得
public static <T> T get(){
return (T) THREAD_LOCAL.get();
}
//キーでバリューを保存
public static void set(Object value){
THREAD_LOCAL.set(value);
}
//メモリリークを防止するため、クリーンする。
public static void remove(){
THREAD_LOCAL.remove();
System.out.println("THREAD_LOCAL" + " : " + THREAD_LOCAL.get()); //本当に削除されたかを確認するため
}
public void getClass(Map<String, Object> claims) {
}
}
ThreadLocalへのデータ保存と削除
前回作成したLoginInterceptorを下記のように改修する。
1.JWTを解析した後のデータをThreadLocalに保存する。
2.リクエストセッションが完了したら、ThreadLocalを削除する。
@Component //自動でインスタンス化し、他クラスに注入できるようにするため。
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception{
//tokenを検証
String token = request.getHeader("Authorization");
try {
Map<String, Object> claims = JwtUtil.parseToken(token);
//claimsをthreadlocalに保存する ※今回追加部分
ThreadLocalUtil.set(claims);
return true;
} catch (Exception e) {
//httpステータスコード401
response.setStatus(401);
return false;
}
}
// ↓今回追加部分
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// ThreadLocalをクリーンする
ThreadLocalUtil.remove();
}
}
UserControllerの改修
コメントアウトは削除した部分。
@GetMapping("/userInfo")
public Result<User> userInfo(/*@RequestHeader(name="Authorization") String token*/){
//Map<String, Object> map = JwtUtil.parseToken(token);
Map<String, Object> map = ThreadLocalUtil.get(); //書き換えた部分
String username = (String) map.get("username");
User user = userService.findByUserName(username);
return Result.success(user);
}
ThreadLocal削除の確認
ThreadLocalUtilメソッドで仕込んでコードの実行結果を確認すると、ちゃんと削除されたのを確認できた。
APIテスト
問題なし、記載を省略。
Discussion