코드스테이츠 - 스프링 - DI

kwak woojong·2022년 6월 16일
0

코드스테이츠

목록 보기
24/36
post-thumbnail

스프링 컨테이너는 객체 관리를 위해 만들어졌다.

이를테면 각각 서비스가 가지는 Repository 클래스가 각기 다른 객체일 경우

원하는 서비스가 만들어지지 않을 수 있다.

뭐 static을 이용해서 어느 정도 구현할 수 있을 순 있다. 그래도 한계가 있음.

때문에 new 생성자로 객체를 만드는 것이 아니라, config같은 클래스를 통해서 1번만 생성하고 필요한 클래스에 인자로 주입을 한다.

이것이 의존성 주입 즉, DI다. (Dependency Injection)

싱글톤 문제, 정책 변경으로 인한 클래스 교체 등등 해결이 가능하다.

그리고 이 DI를 적절히 잘 이용하기 위해 만든게 Sprign컨테이너다.

예전엔 xml로 일일히 다 설정해줬다는데, 요샌 어노테이션으로 다 가능.

@Configuration
public class AppConfig {

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

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

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

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

DiscountPolicy가 바뀌면 return 만 딱 바꿔주면 된다.

만약 Repository가 다른 DB로 바뀐다? 그럼 저 메서드에서 리턴만 빼주면 됨.

스프링 컨테이너를 활용하면

class hello.core.AppConfig$$EnhancerBySpringCGLIB$$77446392

이렇게 요상하게 생긴 녀석을 볼 수 도 있음.
주소값인데 뒤에 springCGLIB가 붙어 있다.


스프링 컨테이너의 종류

BeanFactory

  • 스프링 컨테이너의 최상위 인터페이스
  • BeanFactory는 빈을 등록하고 생성하고 조회하고 돌려주는 등 빈을 관리하는 역할을 한다.
  • getBean() 메소드를 통해 빈을 인스턴스화 할 수 있다.
  • @Bean이 붙은 메서드의 이름을 스프링 빈의 이름으로 사용해 빈 등록을 한다.
    만약 메서드 이름이 아니라 다른게 필요하면 따로 설정해줄 수 있다.
    @Bean(name = "이름")으로 가능함.

ApplicationContext

  • BeanFactory의 기능을 상속받아 제공한다.
  • 빈을 관리하고 검색하는 기능은 BeanFactory가 제공하는데, 얘는 그 외의 부가기능을 제공한다.

빈(Bean)

스프링 컨테이너에 의해 관리되는 컴포넌트들을 빈이라고 한다.

보통 빈은 객체를 의미함.

스프링 컨테이너에 등록된 빈을 스플이 빈이라고 한다.

@Bean이 적힌 메서드를 모두 호출해서 반환된 객체를 스프링 컨테이너에 등록한다.

getBean을 통해 빈 객체를 가져올 순 있는데, 사용하면 안된다. 테스트 할때나 쓸거임...

ApplicationContext를 이 빈 객체 테스트할 때 쓸 수 있다.

@Configuration
@RequiredArgsConstructor
public class SpringDataJpaConfig {

    private final SpringDataJpaItemRepository springDataJpaItemRepository;

    @Bean
    public ItemService itemService() {
        return new ItemServiceV1(itemRepository());
    }

    @Bean
    public ItemRepository itemRepository() {
        return new JpaItemRepositoryV2(springDataJpaItemRepository);
    }
}

SpringDataJPA를 사용하는 Config 클래스다.
@Configuration, @Bean이 붙은 것을 확인할 수 있다.

Bean Definition

  • Bean은 BeanDefinition(빈 설정 메타정보)로 정의되고 이에 따라서 활용하는 방법이 달라지게 된다.

속성에 따라 컨테이너가 Bean을 어떻게 생성하고 관리할지 결정한다.


빈 스코프

빈은 그 스코프 별로 총 6개를 가지고 있다.

일반적으로 같은 객체를 계속 보내주는 Singleton, 주입만 해주고 빠지는 prototype (그래서 계속 새로운 객체를 만들어 준다.) 등이 있다.

만약 빈 없이 싱글톤을 만들려면 좀 귀찮아진다. 클래스 자체에서 객체를 만들어 놓고 얘를 뽑아오게 해야함.

public class SingletonService {

    private static final SingletonService instance = new SingletonService();

    public static SingletonService getInstance(){
        return instance;
            }

    private SingletonService() {
    }
    public void logic() {
        System.out.println("싱글톤 객체 로직 호출");
    }
}
@Test
    @DisplayName("싱글톤 패턴을 적용한 객체 사용")
    void singletonServiceTest(){
        SingletonService singletonService1 = SingletonService.getInstance();
        SingletonService singletonService2 = SingletonService.getInstance();

        System.out.println("singletonService1 = " + singletonService1);
        System.out.println("singletonService2 = " + singletonService2);
        assertThat(singletonService1).isSameAs(singletonService2);
    }

두 singletonService는 같은 객체를 가리키고 있다.

문제는 상기와 같은 방법을 쓰면, 구체화된 클래스를 의존해야 한다는 문제점이 생긴다. 이는 SOLID 관점에서 문제가 된다.

이에 우리는 스프링을 쓰는거고 싱글톤 컨테이너가 이걸 해결해준다.

ㄹㅇ 앵간하면 스프링이 다 해결해줌


Java 기반 컨테이너 설정

DI쓸 클래스에

  • @Configuration
    을 해주면 된다. 그럼 이제 스프링은 그 클래스를 config 관련 클래스로 인식한다.

그럼 클래스 자체가 bean으로 등록 된다. 그리고 클래스 자체를 파싱해서 @Bean이 붙어 있는 메소드를 찾아서 빈을 생성해준다.

  • @Bean은 수동으로 빈을 등록 하기 위해 쓰는 어노테이션이다.
    상기 Configuration과 같이 쓰게 되면, 싱글톤도 보장된다. 컨테이너에 빈을 넣기 위해 쓰는 거라면 앵간하면 Configuration과 같이 쓰게 될 것이다.

Component 어노테이션과 ComponentScan

스프링은 컴포넌트 스캔을 사용해 @Component 어노테이션이 있는 클래스를 찾아서 자동으로 빈 등록을 해준다.

@Controller - 스프링 MVC 컨트롤러에서 사용
@Service - 스프링 비즈니스 로직에 사용
@Repository - 스프링 데이터 접근 계층에 사용 (DB)
@Component - 컴포넌트 스캔에서 사용
@Configuration - 스프링 설정 정보에서 사용

여기서 Service는 딱히 별 다른 능력은 없다. 그냥 구분하기 편하라고 이름 넣어준 어노테이션임

이 어노테이션을 이용하려면 main또는 app클래스에서 @ComponentScan으로 컴포넌트를 찾는 탐색 범위를 지정해주어야 한다. 스프링 부트는 SpringBootConfiguration 하위에 기본적으로 포함되어 있어 별도의 설정이 필요하진 않다.

ComponentScan은 간단한 의존관계 주입은 죄다 스프링에 맡길 수 있다.

빈 등록을 다 주석처리하고 @ComponentScan에 맡겨 버렸다.

이러면 빈 이름만 조금 신경쓰면 다 해결이 된다.


아오오옹


profile
https://crazyleader.notion.site/Crazykwak-36c7ffc9d32e4e83b325da26ed8d1728?pvs=4<-- 포트폴리오

0개의 댓글