springboot threadLocal 테스트

2024. 1. 22. 22:24·개발/java

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 이 찍히는 것을 확인할 수 있다.

threadLocal에 set을 하고 조회하니 Ligue1이 잘 리턴된다.

 

 

3, 4번 thread에서는 null 로 찍히다가 5번에서 다시 Ligue1 으로 찍힌다.

 

그만 알아보자.

 

Git: tistory/util at dev · FullMooney/tistory (github.com)

728x90

'개발 > 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
'개발/java' 카테고리의 다른 글
  • jasypt
  • @Aspect로 공통header 처리
  • springboot resttemplate config 와 restClient 생성
  • springboot rabbitmq config와 DLQ 예제
yunapapa
yunapapa
working on the cloud
    250x250
  • yunapapa
    supermoon
    yunapapa
  • 전체
    오늘
    어제
    • 분류 전체보기 (94)
      • 개발 (20)
        • java (17)
        • web (2)
        • MSX (1)
        • Go (0)
      • CloudNative (50)
        • App Definition & Developeme.. (17)
        • Orchestration & Management (4)
        • Runtime (3)
        • Provisioning (7)
        • Observability & Analysis (14)
        • event review (5)
      • AWS (7)
      • 환경관련 (17)
      • 취미생활 (0)
        • 맛집 (0)
        • 게임 (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

    • CNCF Past Events
    • Kubernetes Korea Group
  • 공지사항

  • 인기 글

  • 태그

    k8s
    Pinpoint
    dop-c02
    오블완
    springboot
    OpenShift
    AWS
    티스토리챌린지
    Java
    devops
    kubernetes
    gitlab
    APM
    helm
    istio
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
yunapapa
springboot threadLocal 테스트
상단으로

티스토리툴바