beanDefinition, 싱글톤 방식 (Spring)

손원진·2023년 4월 20일
0

[SPRING]

목록 보기
12/15
 AnnotationConfigApplicationContext ac =new AnnotationConfigApplicationContext(AppConfig.class);
    GenericXmlApplicationContext acl = new GenericXmlApplicationContext("appConfig.xml");

    @Test
    @DisplayName("빈 설정 메타정보 확인")
    void findApplicationBean(){

        String[] beanDefinitionNames = ac.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
            BeanDefinition beanDefinition = ac.getBeanDefinition(beanDefinitionName);

            if( beanDefinition.getRole() == BeanDefinition.ROLE_APPLICATION){
                System.out.println(beanDefinitionName  +"       "+ beanDefinition);
            }

        }
    }

빈과 관련된 메타정보를 확인하기위해 beanDefinition 사용,

싱글톤 방식 (트레픽 관리를 위해 사용)

유저 여러명이 사이트에 방문했을 때, 객체를 여러개 생성하면 ex) MemberService memberService = new MemberService();

여러개의 인스턴스가 만들어져서, 컴퓨터가 맛탱이 가버릴 수 있다.

그럴때 사용하는 것이 싱글톤 방식인데 자바코드를 보며 스프링을 통해 어떻게 개선되었는지 한번 보자

public class SingletonTest(){
	
    //스테틱 파이널로 내부에 객체를 생성(단일 객체를 생성하기 위함) 
	public static final SingletonTest instance = new SingletonTest();
    
    //클래스 스테틱 메소드를 이용해 인스턴스를 반환할 수 있도록 진행 
    public static SingletonTest getInstance(){
    	return instance;
    }
    //생성자를 private로 하여, new 를 사용한 새로운 객체가 만들어 지는 것을 막음 
    private SingletonTest(){
    	
    }
}


위에 보이는 코드는 싱글톤 방식으로 여러개의 객체가 만들어지는 것을 막을 수 있다.

그럼 위 싱글톤 방식이 아무 문제가 없을까? 전혀아니다

  • 의존성 주입의 원칙 DIP 규칙 위배 - 추상클래스가 구현체를 의존하고 있음
  • 안티 코드
  • 자식클래스 생성 어려움
  • OCP 위반 - 코드를 클라이언트에서 직접 변경해야함
  • 생성자를 사용하지 못함

그래서 스프링에선 스프링 컨테이너에서 단일 객체를 생성할 수 있도록 지원한다.
스프링Container에서 빈 이름과 빈객체를 구분해서 관리를 한다

 @Test
    @DisplayName("싱글톤 진짜 하나밖에 호출한돼 빈객체는? 와우")
    void singleTonRealOne(){

        ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
        MemberService memberService = ac.getBean("memberService",MemberService.class);
        MemberService memberService1 = ac.getBean("memberService",MemberService.class);



        System.out.println("memberService1 = " + memberService1);
        System.out.println("memberService = " + memberService);

        OrderService orderService = ac.getBean("orderService", OrderService.class);
        OrderService orderService1 = ac.getBean("orderService", OrderService.class);

        System.out.println("orderService1 = " + orderService1);
        System.out.println("orderService2 = " + orderService);

        assertThat(orderService).isSameAs(orderService1);
        assertThat(memberService).isSameAs(memberService1);

    }
}

여러개의 인스턴스 객체의 주소가 같다는 것을 확인할 수 있음

빈어노테이션 사용시 주의점. 실무에서 정말 많이 발생하는, stateful현상 유지로 인한 문제이다.

A라는 사용자가 접근하고 orderService를 이용한다고 했을 때 10000원 주문,
B라는 사용자는 20000원 주문 두 주문이 만약 겹친다면?

코드를 보며 생각해보자

public class StateFulServie{
	
    int price;
    
    public void orderService(String name, price)
        this.price = price;
    }
    
    public int getPrice(){
    	return price;
    }
}


import java.util.junit;
//테스트 코드 짜보기 
public class TestCode{
	
    @Test
    @Displayname("스테이트풀 테스트")
    void StatefulTest(){
    	ApplicationContext ac = new AnnotationConfigApplicationContext(StateFul.class);
        
        StateFulService sfc = ac.getBean("statefus",stateFulService);
    	StateFulService sfc2 = ac.getBean("statefus",stateFulService);
        sfc.order("손",10000); // 손은 만원을 주문
        sfc2.order("민",20000); // 민은 2만원을 주문 
        
        Assertions.assertThat(sfc.getPrice).isEqualTo(10000); // fail 
        
        
    }
    
    
    public class StateFul{
		
    	@bean
        public stateFulService statefuls(){
        	return statefuls;
    	}
    }	

}

위테스트 코드를 실행하면 손은 만원을 주문했는데 2만원이 나온다 이게 어떻게된 일일까?

객체 주소가 같아 공동의 변수를 사용하게 됨으로써 , 주문이 겹치게 되면 치명적인 문제가 발생한다.

그럼 어떻게 개선해야할까 ?

public class StateFulServie{

    public void orderService(String name,int price)
        this.price = price;
    }
    
    public int getPrice(){
    	return price;
    }
}

직접 메소드에 값을 넣고 꺼내면됨,

profile
매일 한 걸음

0개의 댓글