Spring의 Singleton 패턴

Hansu Kim·2022년 2월 17일
0

Spring boot

목록 보기
5/10

Singleton 패턴이란

클래스별로 인스턴스가 1개만 생성되도록 보장하는 디자인 패턴이다.

Stateful한 Singleton의 문제점

싱글톤 방식은 여러 클라이언트가 하나의 객체 인스턴스를 공유하기 때문에 객체를 Stateful하게 설계하면 안된다.

Stateful하게 설계한다는 것은 여러 쓰레드에서 접근 가능한 클레스 공유 변수를 통해 데이터가 처리되는 것이다.

Stateful하게 설계할 경우, 아래와 같은 문제점이 발생할 수 있다.

public class StatefulService {
    private int price; //상태를 유지하는 필드
    public void order(String name, int price){
        System.out.println("name = " + name + " price = " + price);
        this.price = price;
    }
    public int getPrice(){
        return price;
    }
}

클래스에서 공유되는 변수를 사용해 값을 처리할 경우, 여러 쓰레드에서 StatefulService Bean에 접근하여 order() 메소드를 호출하는 과정에서 데이터가 오염될 여지가 있다.

그에 따라 위의 코드는 아래와 같이 변경되어야 한다.

public class StatefulService {
    //private int price; //상태를 유지하는 필드
    public void order(String name, int price){
        System.out.println("name = " + name + " price = " + price);
        //this.price = price;
        return price;
    }
    public int getPrice(){
        return price;
    }
}

Stateless하게 설계한다는 것은?

Stateless한 설계는 구체적으로 아래와 같다.

  • 특정 클라이언트에 의존적인 필드가 있으면 안된다.
  • 특정 클라이언트가 값을 변경할 수 있는 필드가 있으면 안된다!
  • 가급적 읽기만 가능해야 한다.
  • 필드 대신에 자바에서 공유되지 않는, 지역변수, 파라미터, ThreadLocal 등을 사용해야 한다.

@Configuration과 Singleton

Spring에서는 각 Bean들이 Singleton으로 생성될 수 있도록 보장해준다.

@Test
    void configurationDeep(){
        ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
        AppConfig bean = ac.getBean(AppConfig.class);

        System.out.println("bean = " + bean.getClass());
    }

위의 코드를 실행할 경우, 아래와 같은 출력문을 확인할 수 있다.

bean = class group.core.AppConfig$$EnhancerBySpringCGLIB$$59b9e707

만약 순수한 클래스라면 "class hello.core.AppConfig"라고 출력되어야하나, 해당 클래스의 정보가 변경된 것을 확인할 수 있다.

AppConfig 클래스의 정보에 EnhancerBySpringCGLIB$$라고 표시된 이유는, @Configuration 어노테이션이 붙은 클래스의 경우, 스프링이 CGLIB라는 바이트코드 조작 라이브러리를 통해 AppConfig 클래스를 상속받아 임의의 다른 클래스를 스프링 빈으로 등록한 것이다.

그 임의의 다른 클래스가 바로 싱글톤이 보장되도록 해준다.

AppConfig를 상속받은 임의의 클래스는 아래와 같이 동작한다고 예상할 수 있다.

@Bean
public MemberRepository memberRepository() {
	if (memoryMemberRepository가 이미 스프링 컨테이너에 등록되어 있으면?) {
	    return 스프링 컨테이너에서 찾아서 반환;
	} else { 
    스프링 컨테이너에 없으면 기존 로직을 호출해서 MemoryMemberRepository를 생성하고 스프링 컨테이너에 등록 return 반환
	}
}

만약 @Configuration 없이 @Bean을 사용할 경우, 해당 클래스는 순수한 AppConfig로 스프링 빈에 등록된다. 그리고 AppConfig 클래스 내부 메소드들에서 new를 통해 주입된 Dependency class들은 스프링에서 따로 관리해주지 않기 때문에 싱글톤을 보장해주지 않는다.

결론

항상 @Configuration을 통해 스프링을 설정하자

0개의 댓글