멀티 쓰레드 환경과 스프링 빈
스프링 빈은 대부분 Singleton 패턴으로 생성되어 Application Context에 의해서 관리된다.
즉, 모든 Controller, Service, Repository는 하나의 인스턴스만 갖고 있는 것이다.
그런데 스프링 환경자체는 멀티 쓰레드인데 어떻게 Thread-Safe를 유지할 수 있을까 궁금해졌다.
정답부터 말하면 Thread-Safe하지 않다. 단지, Thread-Safe하도록 코드를 짤 뿐이다.
일단 구조를 Spring에서 요청이 들어오면 그 요청은 각각 하나의 쓰레드가 맡게 된다.
즉 모든 쓰레드는 스프링 빈을 공유하고 내부의 멤버변수도 공유하게 되는 것이다.
하나의 예를 보자
@RestController
public class UserController {
private final UserRepository userRepository;
private Long n = 0L;
public UserController(UserRepository userRepository) {
this.userRepository = userRepository;
}
@GetMapping(value = "/hello")
@Transactional
public Long getUser() throws NotFoundException, InterruptedException {
return userRepository.save(new User(++n,"user"+n)).orElseThrow(() -> new NotFoundException("존재하지 않습니다."));
}
}
단순히 repository에 저장하는 코드이다.
(물론 실제로는 id를 저렇게 생성하는 일은 없겠지만 잘못된 예시를 보여주기 위해서 id를 하나씩 증가시켜준다고 가정한다.)
로직상으로 봤을 때는 "/hello" 요청이 들어올때마다 user가 저장되고 id값이 하나씩 증가한다.
그러나 문제는 요청이 동시에 들어왔을 때 생긴다.
100명의 유저가 동시에 요청한다고 부하테스트를 넣었을 때 동일한 id값이 들어가는 것을 알 수 있다.
즉, Thread-Safe하지 않다는 뜻이다.
그렇다면 그동안 왜 문제없이 사용했을까?
그 이유는 객체가 상태를 가지지 않는 불변객체였기 때문이다.
우리가 보통 사용하는 @Controller, @Service같은 곳에서 사용하는 객체들은 주입을 받아서 사용하고 내부적으로 멤버 변수를 가지고 변화시키지 않는다.
다시 말해, 위의 n처럼 요청이 들어올 때마다 n값이 변하는게 아니라 한번 생성자를 통해 주입받은 후 그 객체 자체는 가지고 있는 멤버변수의 상태값을 변화시키지 않는 코드이다.
그러므로 스프링빈을 Thread-Safe하게 사용하기 위해서는 스프링 빈으로 주입되는 클래스는 모두 불변 객체여야한다.
아니면 스프링빈의 스코프를 Prototype으로 만들면 해당 빈이 불릴때마다 새로운 객체가 생성되게 된다.
'스프링(부트) > 스프링 내용 정리' 카테고리의 다른 글
Servlet과 Spring MVC (0) | 2021.01.07 |
---|---|
JPA (Java Persistence API) (0) | 2021.01.05 |
[스프링] Spring Data (0) | 2020.05.24 |
[스프링] Handler Interceptor (0) | 2020.05.03 |
[스프링] Spring MVC 예제 및 단위테스트 (0) | 2020.04.26 |
댓글