스프링 빈은 Thread safe 하나,

스프링 빈 내에서의 멤버변수는 Thread safe 하지 않다.

--> race condition 을 유의해야 한다.

--> stateless 하게 개발해야 한다.

 

※ race condition : 

두 개 이상의 프로세스가 공통 자원을 병행적으로(concurrently) 읽거나 쓸 때,

공용 데이터에 대한 접근이 어떤 순서에 따라 이루어졌는지에 따라 그 실행 결과가 달라지는 상황을 말한다.

※ stateful :

상태 변화에 의존적인 상태를 의미.

ex)서버 session 은 stateful, 클라이언트가 토큰을 들고있는 방식은 stateless (공부중)

 

[Example]

1
2
3
4
5
6
7
8
9
10
11
@Service
public class TestServiceImpl implements TestService {
  
    private int idx = 0;
 
    @Override
    public void test(){
        System.out.println("idx : " + idx);
        idx++;
    }
}
cs

ControllerA  에서 TestService를 주입받아 test() 메소드 실행시 idx 값이 호출될 때마다 증가하며,

ControllerB 에서도 TestService를 주입받아 test() 메소드 실행시 idx 값이 호출될 때마다 증가함을 확인 할 수 있다.

 

만약 멤버변수를 단순 읽기 전용으로 사용한다면 문제되지 않지만, 위와 같이 읽기 뿐만 아닌 쓰는 용도로 사용한다면 문제가 된다.

 

이처럼 스프링 Bean 은 Default로 싱글톤 디자인패턴을 따르므로, 

멤버변수에 값을 저장(쓰는 행위)하는 식의 stateful한 설계는 피해야 한다.

(가급적 스프링 빈으로 관리되는 객체만 멤버변수로 사용하는게 좋다)

 

 

[코드 개선]

위와 같은 코드는 멤버변수를 제거하고, 메소드 내에서 지역변수로 선언 및 파라미터로 값을 들고 다니는 방식으로 수정을 하거나(권장), 빈의 scope 를 prototype 으로 주어 호출시마다 새로운 인스턴스를 생성하도록 해야 한다.

@Scope("prototype") annotation 을 class 선언 부에 달아주면 해당 객체는 싱글톤이 아닌 프로토타입 객체가 된다.

※ 싱글톤 빈 내에서 프로토타입 빈 생성을 할 경우 정상적으로 프로토타입 빈 동작이 되지 않는다(호출마다 빈이 생성되야 하나 그렇지 못함)

 

[prototype Scope를 적용한 코드]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Service
@Scope("prototype")
public class TestServiceImpl implements TestService, InitializingBean, DisposableBean {
  
    private int idx = 0;
 
    @Override
    public void test(){
        System.out.println("idx : " + idx);
        idx++;
    }
 
    @Override 
    public void afterPropertiesSet() throws Exception { 
        logger.info("created service! "); 
    }
 
    @Override 
    public void destroy() throws Exception { 
        logger.info("destroyed service! "); 
    }
 
}
cs

※ InitializingBean, DisposableBean 인터페이스의 afterPropertiesSet(), destroy() 메소드는 빈이 생성, 제거 될 때 각각 호출된다.

 

코드를 위와 같이 수정한 후, 이전과 동일하게

1) ControllerA  에서 TestService를 주입받아 test() 메소드 실행,

2) ControllerB 에서도 TestService를 주입받아 test() 메소드 실행한 경우 결과는 아래와 같다.

[실행 결과]

created service!
idx : 0
created service!
idx : 1

 

참고:

토비의 스프링 3.1(도서)

https://gmlwjd9405.github.io/2018/11/10/spring-beans.html

https://beyondj2ee.wordpress.com/2013/02/28/%EB%A9%80%ED%8B%B0-%EC%93%B0%EB%A0%88%EB%93%9C-%ED%99%98%EA%B2%BD%EC%97%90%EC%84%9C-%EC%8A%A4%ED%94%84%EB%A7%81%EB%B9%88-%EC%A3%BC%EC%9D%98%EC%82%AC%ED%95%AD/

https://gmlwjd9405.github.io/2018/11/10/spring-beans.html

 

 

반응형

+ Recent posts