스프링/1. 스프링 핵심 원리

27강. 스프링 컨테이너와 싱글톤

sdafdq 2023. 7. 18. 23:03

 

public class SingletonTest {
    ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);

    @Test
    @DisplayName("싱글톤")
    public void springContainer(){
        MemberService memberService1 = ac.getBean("memberService", MemberService.class);
        MemberService memberService2 = ac.getBean("memberService", MemberService.class);

        assertThat(memberService1).isSameAs(memberService2);
    }
}

스프링 빈은 기본 설정이 싱글톤으로 관리해 줌

스프링 덕에 싱글톤 단점, 유연성이라던지 구현체에 의존한다더니 이런 게 극복됨.

 

assertThat(~~~).isSameAs(~~~)

는 이건 정말로 객체가 같은 레퍼런스를 가리키는지, 같은 객체인지,

isNotSameAs도 있음

 

 

 

 

싱글톤 주의점

변수가 공유되게 하면 안됨. 오로지 읽기만 가능하게끔.

특정 클라이언트에 의존하는 멤버변수가 있으면 안됨.

필드 대신 자바에 공유되지 않는 지역변수, 파라미터, 쓰레드 로컬 등을 사용 해야 함.

특정 클라이언트가 값을 변경할 수 있는 필드가 있으면 안됨.

 

그러니까, 싱글톤이 특정한 클라이언트에 관련된 필드를 가지지 말고 마치 프린터?

자판기? 같이 입력이 들어오면 그것에 대해 처리만 해주고 깨끗이 잊어버리는 그런 이야기 같은데,

특정한 상태가 없는. 

 

그래서 빈도 마찬가지임. 뭔가 값을 필드에 저장한다던지 그러면 안됨. 그냥 딱 자기 역할만 하고 끝내야 함.

 

이런 문제들 때문에 동시성 이런 것도 이야기 하는 듯?

 

 

 

public class StatefullService {
    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;
    }
}

예를 들어, 이런게 있다고 해봄.

 

class StatefullServiceTest {

    @Test
    void statefulServiceSingleton(){
        ApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);

        StatefullService statefullService1 = ac.getBean("statefullService", StatefullService.class);
        StatefullService statefullService2 = ac.getBean("statefullService", StatefullService.class);

        statefullService1.order("userA",10000);
        statefullService2.order("userB",20000);

        int price = statefullService1.getPrice();
    }

    static class  TestConfig{
        @Bean
        public StatefullService statefullService(){
            return new StatefullService();
        }

    }


}

저렇게 Bean으로 등록하고, 저렇게 하면 어떻게 되겠음?

statefullService1, userA껄 조회해도 20000이 나옴.

실제 돈이 오가는 곳에서 이러면 어떻겠음?

 

멀티쓰레드에서 저런 문제 서로 필드에 접촉하면서 생김.

 

 

무상태로 바꿈

public class StatefullService {
    public int order(String name, int price){
        System.out.println("name = " + name + " price = " +price);
        return price;
    }
}

getPrice는 굳이 쓸모 없어서 지운거고, 가격 알려면 return 받으면 되고, 무엇보다도 클라이언트에 따라 좌지우지 되는 멤버를 지움