threadLocal을 사용해보자
-> threadLocal 에 set을 하고 동일 thread 내에서 전역변수처럼 get하여 사용할 수 있다.
환경은 다음과 같다
- springboot 2.7.18
- openjdk 11.0.21
먼저 RequestContext 클래스를 생성하고 Map<String, Object> 형식의 Map을 선언한다.
public class RequestContext {
private final HttpServletRequest request;
private final HttpServletResponse response;
private final Map<String, Object> map;
public RequestContext(HttpServletRequest request, HttpServletResponse response) {
this.request = request;
this.response = response;
this.map = new HashMap<>();
}
public HttpServletRequest getRequest() {
return request;
}
public HttpServletResponse getResponse() {
return response;
}
public Map<String, Object> getMap() {
return map;
}
}
다음으로 RequestContextThreadLocalHolder 클래스를 생성하고 RequestContext 의 Map을 ThreadLocal에 저장할 수 있도록 공통 메소드를 생성한다.
public class RequestContextThreadLocalHolder {
private static final ThreadLocal<RequestContext> REQUEST_CONTEXT_THREAD_LOCAL = new ThreadLocal<>();
public static void setRequestContext(RequestContext requestContext) {
REQUEST_CONTEXT_THREAD_LOCAL.set(requestContext);
}
public static RequestContext getRequestContext() {
return REQUEST_CONTEXT_THREAD_LOCAL.get();
}
public static void remove() {
REQUEST_CONTEXT_THREAD_LOCAL.remove();
}
public static void createAndSetRequestContext(HttpServletRequest request) {
createAndSetRequestContext(request, null);
}
public static void createAndSetRequestContext(HttpServletRequest request, HttpServletResponse response) {
RequestContext requestContext = new RequestContext(request, response);
setRequestContext(requestContext);
}
public static HttpServletRequest getRequest() {
RequestContext requestContext = getRequestContext();
if (requestContext != null && requestContext.getRequest() != null) {
return requestContext.getRequest();
}
return getRequestAttributes().getRequest();
}
public static HttpServletResponse getResponse() {
RequestContext requestContext = getRequestContext();
if (requestContext != null && requestContext.getResponse() != null) {
return requestContext.getResponse();
}
return getRequestAttributes().getResponse();
}
public static ServletRequestAttributes getRequestAttributes() {
ServletRequestAttributes servletReqAttr = (ServletRequestAttributes) RequestContextHolder
.getRequestAttributes();
return servletReqAttr;
}
@SuppressWarnings("unchecked")
public static <T> T getValue(String key) {
RequestContext rc = REQUEST_CONTEXT_THREAD_LOCAL.get();
if (rc == null) {
createAndSetRequestContext(null, null);
rc = REQUEST_CONTEXT_THREAD_LOCAL.get();
}
Map<String, Object> map = rc.getMap();
return (T) map.get(key);
}
public static <T> void setValue(String key, T val) {
RequestContext rc = REQUEST_CONTEXT_THREAD_LOCAL.get();
if (rc == null) {
createAndSetRequestContext(null, null);
rc = REQUEST_CONTEXT_THREAD_LOCAL.get();
}
Map<String, Object> map = rc.getMap();
map.put(key, val);
}
}
key, val을 입력받아 ThreadLocal에 set하고 key를 입력받아 다시 get할수 있는 ContextUtil UtilityClass를 생성한다.
@Slf4j
@UtilityClass
public class ContextUtil {
public static void setThreadLocal(String key, String val) {
RequestContextThreadLocalHolder.setValue(key, val);
}
public static String getThreadLocal(String key) {
return RequestContextThreadLocalHolder.getValue(key);
}
}
이제 테스트해보자.
swagger에 접속하여 /set-thread-local api를 호출하여 특정 thread의 threadLocal에 key, val을 세팅하고, 곧바로 threadLocal에서 해당key로 get하여 val을 확인해본다.
이후 /get-thread-local api를 반복 호출하여 각 threadLocal에서 리턴되는 값을 찍어본다.
//UtilTestController.java
@Slf4j
@RestController
@RequestMapping("/api/utils")
public class UtilTestController {
private final UtilTestService utilTestService;
public UtilTestController(UtilTestService utilTestService) {
this.utilTestService = utilTestService;
}
@PostMapping("/set-thread-local")
public ResponseEntity<String> setThreadLocal(@RequestBody SampleVO vo) {
utilTestService.setThreadLocal(vo.getName(), vo.getLeague());
String leagueName = ContextUtil.getThreadLocal(vo.getName());
log.debug("leagueName from threadLocal is {}", leagueName);
return ResponseEntity.ok(leagueName);
}
@PostMapping("/get-thread-local")
public ResponseEntity<Void> getThreadLocal(@RequestBody SampleVO vo) {
String leagueName = ContextUtil.getThreadLocal(vo.getName());
log.debug("leagueName from threadLocal is {}", leagueName);
return ResponseEntity.ok().build();
}
}
//UtilTestService.java
public interface UtilTestService {
void setThreadLocal(String key, String val);
String getThreadLocal(String key);
}
//UtilTestServiceImpl.java
@Service
public class UtilTestServiceImpl implements UtilTestService {
@Override
public void setThreadLocal(String key, String val) {
// TODO Auto-generated method stub
ContextUtil.setThreadLocal(key, val);
}
@Override
public String getThreadLocal(String key) {
// TODO Auto-generated method stub
return ContextUtil.getThreadLocal(key);
}
}
swagger를 통해 Lee Kang-in, League1 을 각각 key, val로 threadLocal에 set 한뒤, 테스트한 결과는 다음과 같다
http-nio-8080-exec-5 번 thread에 값이 set 되었고, 다른 thread에서는 null로 찍히다가 다시 5번 에서 Ligue1 이 찍히는 것을 확인할 수 있다.


그만 알아보자.
'개발 > java' 카테고리의 다른 글
| jasypt (1) | 2024.11.13 |
|---|---|
| @Aspect로 공통header 처리 (2) | 2024.02.19 |
| springboot resttemplate config 와 restClient 생성 (1) | 2024.01.19 |
| springboot rabbitmq config와 DLQ 예제 (0) | 2024.01.18 |
| springboot + redis @Cacheable 사용 (3) | 2024.01.11 |