Spring 핵심 원리 TIL (7)

YulHee Kim·2021년 8월 26일
0

Spring 핵심 원리

목록 보기
8/13
post-thumbnail

[참고 강의] 김영한님의 스프링 핵심 원리 - 기본편

💡 싱글톤 컨테이너

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

✏️ 싱글톤 컨테이너

  • 스프링 컨테이너는 싱글톤 패턴을 적용하지 않아도, 객체 인스턴스를 싱글톤으로 관리한다.
  • 스프링 컨테이너는 싱글톤 컨테이너 역할을 한다. 이렇게 싱글톤 객체를 생성하고 관리하는 기능을 싱글톤 레지스트리라 한다.
  • 스프링 컨테이너는 싱글톤 패턴의 모든 단점을 해결하면서 객체를 싱글톤으로 유지한다.
  • DIP, OCP, private 생성자, 테스트로 부터 자유롭게 싱글톤을 사용할 수 있다.

스프링 컨테이너를 사용하는 테스트 코드

@Test
@DisplayName("스프링 컨테이너와 싱글톤")
  void springContainer() {
      ApplicationContext ac = new
  AnnotationConfigApplicationContext(AppConfig.class);
     //1. 조회: 호출할 때 마다 같은 객체를 반환
      MemberService memberService1 = ac.getBean("memberService",
  MemberService.class);
     //2. 조회: 호출할 때 마다 같은 객체를 반환
      MemberService memberService2 = ac.getBean("memberService",
  MemberService.class);
     //참조값이 같은 것을 확인
      System.out.println("memberService1 = " + memberService1); 
      System.out.println("memberService2 = " + memberService2);
      
      //memberService1 == memberService2
      assertThat(memberService1).isSameAs(memberService2);
  }

스프링 컨테이너 덕분에 고객의 요청이 올 때마다 객체를 생성하는 것이 아니라, 이미 만들어진 객체를 공유해서 효율적으로 재사용할 수 있다.

✏️ 싱글톤 방식의 주의점

  • 싱글톤 패턴이든, 스프링 같은 싱글톤 컨테이너를 사용하든, 객체 인스턴스를 하나만 생성해서 공유하는 싱글톤 방식은 여러 클라이언트가 하나의 같은 객체 인스턴스를 공유하기 때문에 싱글톤 객체는 상태를 유지(stateful)하게 설계하면 안된다.
  • 무상태로 설계해야 한다!
    • 특정 클라이언트에 의존적인 필드가 있으면 안된다.
    • 특정 클라이언트 값을 변경할 수 있는 필드가 있으면 안된다!
    • 가급적 읽기만 가능해야 한다.
    • 필드 대신에 자바에서 공유되지 않는, 지역변수, 파라미터, ThreadLocal 등을 사용해야 한다.
  • 스프링 빈의 필드에 공유 값을 설정하면 정말 큰 장애가 발생할 수 있다

무상태로 설계하는 코드

public class StatefulService {

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

}

✏️ @Configuration과 싱글톤

스프링은 CGLIB이라는 바이트코드 조작 라이브러리를 사용해서 AppConfig 클래스를 상속받은 임의의 다른 클래스를 만들고, 다른 클래스를 스프링 빈으로 등록한 것이다.
이 임의의 다른 클래스가 바로 싱글톤이 보장되도록 해준다.

AppConfig@CGLIB 예상 코드

@Bean
public MemberRepository memberRepository() {

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

싱글톤이 보장되는 이유다😎
@Bean이 붙은 메서드마다 이미 스프링 빈이 존재하면 존재하는 빈을 반환하고,
스프링 빈이 없으면 생성해서 스프링 빈으로 등록하고 반환하는 코드가 동적으로 만들어진다.

💡 컴포넌트 스캔

✏️ 컴포넌트 스캔과 의존관계 자동 주입 시작하기

스프링은 설정 정보가 없어도 자동으로 스프링 빈을 등록하는 컴포넌트 스캔이라는 기능을 제공한다. 또 의존관계도 자동으로 주입하는 @Autowired라는 기능도 제공한다.

AutoAppConfig.java

@Configuration
@ComponentScan(
       excludeFilters = @Filter(type = FilterType.ANNOTATION, classes = Configuration.class))
public class AutoAppConfig {

}
  • 컴포넌트 스캔을 사용하려면 먼저 @ComponentScan을 설정정보에 붙여주면 된다.
  • 기존의 AppConfig와 다르게 @Bean으로 등록한 클래스가 하나도 없다.
  • 컴포넌트 스캔은 이름 그대로 @Component 애노테이션이 붙은 클래스를 스캔해서 스프링 빈으로 등록한다.

MemberServiceImpl @Component, @Autowired 추가

@Component
public class MemberServiceImpl implements MemberService {

      private final MemberRepository memberRepository;
      
      @Autowired
      public MemberServiceImpl(MemberRepository memberRepository) {
          this.memberRepository = memberRepository;
      }
}

MemoryMemberRepository @Component 추가, RateDiscountPolicy @Component CNrk, MemberServiceImpl @Component, @Autowired추가도 진행했다.

  • 이제는 이런 설정 정보 자체가 없기 때문에, 의존관계 주입도 이 클래스 안에서 해결해야 한다.
  • @Autowired는 의존관계를 자동으로 주입해준다.

컴포넌트 스캔에 대한 내용은 다음 TIL에서 더 자세히 다뤄보겠습니당 ~ 😎

profile
백엔드 개발자

0개의 댓글