728x90

클라이언트가 요청할 때 마다 새로운 인스턴스가 생긴다. 이것은 서버에 많은 비용을 요구한다. 정말로 클라이언트의 요청마다 새로운 인스턴스가 생성이 되는지 확인을 해보겠다.

 

참조값을 확인하면 서로 다른 인스턴스인 것을 알 수 있다.

  • 우리가 만들었던 스프링 없는 순수한 DI 컨테이너인 AppConfig는 요청을 할 때 마다 객체를 새로 생성한다.
  • 고객 트래픽이 초당 100이 나오면 초당 100개 객체가 생성되고 소멸된다. -➡️ 메모리 낭비가 심하다.
  • 💡해결방안은 해당 객체가 딱 1개만 생성되고, 공유하도록 설계하면 된다. ➡️ 싱글톤 패턴

 

싱글톤 패턴

  • 클래스의 인스턴스가 딱 1개만 생성되는 것을 보장하는 디자인 패턴이다.
  • 그래서 객체 인스턴스를 2개 이상 생성하지 못하도록 막아야 한다.
    • private 생성자를 사용해서 외부에서 임의로 new 키워드를 사용하지 못하도록 막아야 한다.

 

  1. static 영역에 객체 instance를 미리 하나 생성해서 올려둔다.
  2. 이 객체 인스턴스가 필요하면 오직 getInstance() 메서드를 통해서만 조회할 수 있다. 이 메서드를 호출하면 항상 같은 인스턴스를 반환한다.
  3. 딱 1개의 객체 인스턴스만 존재해야 하므로, 생성자를 private으로 막아서 혹시라도 외부에서 new 키워드로 객체 인스턴스가 생성되는 것을 막는다.

  1. private으로 new 키워드를 막아두었다.
  2. 호출할 때 마다 같은 객체 인스턴스를 반환하는 것을 확인할 수 있다.

Singleton 을 적용한 후 이제는 새로운 인스턴스를 만들지 않는다는 것을 확인할 수 있다.

 

💡참고: 싱글톤 패턴을 구현하는 방법은 여러가지가 있다. 여기서는 객체를 미리 생성해두는 가장 단순하고 안전한 방법을 선택했다.

싱글톤 패턴을 적용하면 고객의 요청이 올 때 마다 객체를 생성하는 것이 아니라, 이미 만들어진 객체를 공유해서 효율적으로 사용할 수 있다. 하지만 싱글톤 패턴은 다음과 같은 수 많은 문제점들을 가지고 있다.

 

싱글톤 패턴 문제점

싱글톤은 생성된 인스턴스를 공유를 할 수 있고 확실한 객체 하나가 있다는 것이 보장이 되지만 수많은 단점이 있다.

  • 싱글톤 패턴을 구현하는 코드 자체가 많이 들어간다.
  • 의존관계상 클라이언트가 구체 클래스에 의존한다. ➡️ DIP를 위반한다.
  • 클라이언트가 구체 클래스에 의존해서 OCP 원칙을 위반할 가능성이 높다.
  • 테스트하기 어렵다. (싱글톤은 지정해서 가져오고 미리 인스턴스를 받아놓아서 설정이 끝나버리기 때문에 유연한 테스트가 어렵다.)
  • 내부 속성을 변경하거나 초기화 하기 어렵다.
  • private 생성자로 자식 클래스를 만들기 어렵다.
  • 결론적으로 유연성이 떨어진다. (DI 를 적용하기 어렵다.)
  • 안티패턴으로 불리기도 한다.

이러한 단점을 스프링 프레임워크가 전부 다 해결해준다. ➡️ 객체를 싱글톤으로 관리해준다. 

 

다음은 스프링 컨테이너(싱글톤 컨테이너) 의 역할에 대해서 알아보자.

⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️
 

[Spring] 싱글톤 컨테이너

싱글톤 컨테이너 = 스프링 컨테이너는 싱글톤 패턴의 문제점을 해결하면서, 객체 인스턴스를 싱글톤(1개만 생성) 으로 관리한다. = 스프링 빈(Bean)이 바로 싱글톤으로 관리되는 빈이다. 1. 스프링

ydontustudy.tistory.com

 

 

728x90

1. 스프링 컨테이너 생성

  • new AnnotationConfigApplicationContext(AppConfig.class)
  • 스프링 컨테이너를 생성할 때는 구성 정보를 지정해주어야 한다.
  • 여기서는 AppConfig.class 를 구성 정보로 지정했다.

2. 스프링 빈 등록

  • 스프링 컨테이너는 파라미터로 넘어온 설정 클래스 정보를 사용해서 스프링 빈을 등록한다.

빈(Bean) 이름

  • 빈 이름은 메서드 이름을 사용한다.
  • 빈 이름을 직접 부여할 수 도 있다.
    • Bean(name="memberService2")
    • 주의: 빈 이름은 항상 다른 이름을 부여 해야 한다. 같은 이름을 부여하면, 다른 빈이 무시되거나, 기존 빈을 덮어버리거나 설정에 따라 오류가 발생한다.

3. 스프링 빈 의존관계 설정 - 준비

4. 스프링 빈 의존관계 설정 - 완료

  • 스프링 컨테이너는 설정 정보를 참고해서 의존관계를 주입(DI)한다.
  • 단순히 자바 코드를 호출하는 것 같지만, 차이가 있다. 이 차이는 뒤에 싱글톤 컨테이너에서 설명한다.
참고: 스프링은 빈을 생성하고, 의존관계를 주입하는 단계가 나누어져 있다. 그런데 이렇게 자바 코드로 스프링 빈을 등록하면 생성자를 호출하면서 의존관계 주입도 한번에 처리된다. 여기서는 이해를 돕기 위해 개념적으로 나누어 설명했다. 자세한 내용은 의존관계 자동 주입에서 다시 설명하겠다.

 

728x90

AppConfig

@Configuration
public class AppConfig {// 모든 객체의 생성과 연결을 담당한다. (이렇게 하므로써 DIP 가 완성된다.)

@Bean
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
    }

    @Bean
public DiscountPolicy discountPolicy() {
//        return new FixDiscountPolicy();
return new RateDiscountPolicy();
    }

    @Bean
public MemberService memberService() {
return new MemberServiceImpl(memberRepository());
    }

    @Bean
public OrderService orderService() {
return new OrderServiceImpl(memberRepository(), discountPolicy());
    }
}

에서 

@Configuration
public classAppConfig {// 모든 객체의 생성과 연결을 담당한다. (이렇게 하므로써 DIP 가 완성된다.)

@Bean
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
    }

    @Bean
public DiscountPolicy discountPolicy() {
//        return new FixDiscountPolicy();
return new RateDiscountPolicy();
    }

    @Bean
public MemberService memberService() {
return new MemberServiceImpl(memberRepository());
    }

    @Bean
public OrderService orderService() {
return new OrderServiceImpl(memberRepository(), discountPolicy());
    }
}

로 바뀌었다.

 

MemberApp

public class MemberApp {

public static void main(String[] args) {
        AppConfig appConfig = new AppConfig();
        MemberService memberService = appConfig.memberService();
        Member member =newMember(1L,"memberA", 25L, Grade.VIP);
        memberService.join(member);

        Member findMember = memberService.findMember(1L);
        System.out.println("new member = "+ member.getName());
        System.out.println("find member = "+ findMember.getName());
        System.out.println("member = "+ member);
    }
}

에서

public class MemberApp {

public static void main(String[] args) {
        ApplicationContext applicationContext =newAnnotationConfigApplicationContext(AppConfig.class);// @Bean 들을 관리해준다.
        MemberService memberService = applicationContext.getBean("memberService", MemberService.class);// ( AppConfig 안에 있는 메서드 이름, 타입 )

        Member member =new Member(1L,"memberA", 25L, Grade.VIP);
        memberService.join(member);

        Member findMember = memberService.findMember(1L);
        System.out.println("new member = "+ member.getName());
        System.out.println("find member = "+ findMember.getName());
        System.out.println("member = "+ member);
    }
}

로 바뀌었다.

 

실행

이렇게 저장 되기 때문에 꺼낼 때 밑에처럼 타입에 이름을 주고 꺼내면 된다.

 

스프링 컨테이너

  • ApplicationContext 를 스프링 컨테이너라 한다.
  • 기존에는 개발자가 AppConfig 를 사용해서 직접 객체를 생성하고 DI를 했지만, 이제부터는 스프링 컨테이너를 통해서 사용한다.
  • 스프링 컨테이너는 @Configuration 이 붙은 AppConfig 를 설정(구성) 정보로 사용한다. 여기서 @Bean 이라 적힌 메서드를 모두 호출해서 반환된 객체를 스프링 컨테이너에 등록한다. 이렇게 스프링 컨테이너에 등록된 객체를 스프링 빈이라 한다.
  • 스프링 빈은 @Bean 이 붙은 메서드의 명을 스프링 빈의 이름으로 사용한다. ( memberService , orderService )
  • 이전에는 개발자가 필요한 객체를 AppConfig 를 사용해서 직접 조회했지만, 이제부터는 스프링 컨테이너를 통해서 필요한 스프링 빈(객체)를 찾아야 한다. 스프링 빈은 applicationContext.getBean() 메서드를 사용해서 찾을 수 있다.
  • 기존에는 개발자가 직접 자바코드로 모든 것을 했다면 이제부터는 스프링 컨테이너에 객체를 스프링 빈으로 등록하고, 스프링 컨테이너에서 스프링 빈을 찾아서 사용하도록 변경되었다.
  • 코드가 약간 더 복잡해진 것 같은데, 스프링 컨테이너를 사용하면 어떤 장점이 있을까? ⇒ 어마어마한 장점이 있다. 차근차근 알아보자

 

스프링 컨테이너가 생성되는 자세한 과정은 다음 글을 참고하면 된다.

⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️

 

[Spring] 스프링 컨테이너 생성 과정

1. 스프링 컨테이너 생성 new AnnotationConfigApplicationContext(AppConfig.class) 스프링 컨테이너를 생성할 때는 구성 정보를 지정해주어야 한다. 여기서는 AppConfig.class 를 구성 정보로 지정했다. 2. 스..

ydontustudy.tistory.com

 

+ Recent posts