본문 바로가기

백엔드/스프링

스프링 빈 등록 총정리

팩토리 메소드 방식

스프링 빈 등록시의 팩토리 메소드 방식은 아래와 같이 Configuration 클래스를 만들어서 안에 있는 메소드들에 Bean 어노테이션을 달아서 등록하는 방법이다. ( 수동적인 방법 )

@Configuration
public class AppConfig {
    @Bean
    public MemoryMemeberRepository memberRepository() {
        return new MemoryMemeberRepository();
    }
    
    @Bean
    public MemberService memberService() {
        return new MemberServiceImple(memberRepository());
    }

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

@Configuration을 달면 스프링은 GCLIB라는 라이브러리를 통해 바이트코드를 조작하여 AppConfig를 상속한 클래스를 만들어서 이 클래스를 스프링 빈에 등록한다. 한번 확인해보자

@Test
void testing() {
  ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);

  AppConfig bean = ac.getBean(AppConfig.class);
  System.out.println("bean.getClass() = " + bean.getClass());
}

hello.core.AppConfig가 아닌 다른 클래스가 나왔다. @Configuration을 달아줌으로써 만들어진 이 클래스가 AppConfig 내부에 Bean 어노테이션을 달아서 등록한 스프링 빈들을 모두 싱글톤으로 만들어준다. 이 말은 즉 @Configuration을 없애면 싱글톤 보장이 안된다는 것이다. (그냥 순수한 자바 코드가 돌아가는 것)

그냥 순수한 자바코드가 돌아가는 것이므로 스프링 빈에서 관리되고 있지 않은 객체들이 주입되는 것이다. 다음의 코드를 실행해서 테스트해보면

ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);

MemberServiceImple mS = ac.getBean(MemberServiceImple.class);
OrderServiceImpl oS = ac.getBean(OrderServiceImpl.class);
MemoryMemeberRepository mmR = ac.getBean(MemoryMemeberRepository.class);

System.out.println(mmR);
System.out.println(oS.getRep());
System.out.println(mS.getRep());

결과 1. 

@Configuration 있을때

결과 2.

@Configuration 없을때

 

※ 👾👾 

헷갈리지 말자!! 위의 테스트는 설정 정보 클래스에 @Configuration을 붙이지 않으면 @Bean이 달린 메소드들이 실행될 때마다 다른 객체를 생성해준다는, 싱글톤이 보장되지 않는 것에 대한 테스트이다. @Configuration이 없더라도 스프링 빈으로 등록은 되고, getBean(MemoryMemberRepository.class)으로 여러번 조회하면 스프링 빈에 등록된 것을 찾아오니 당연히 다 같은 객체가 나온다. 

 

기억하자 : 스프링 설정 정보에는 @Configuration을 달면 된다.

 

컴포넌트 스캔

팩토리 메서드 방식은 어찌되었든 자바 코드에다가 @Bean을 달아서 스프링 빈에 수동으로 등록을 해주는 것이다. 만약 등록해야하는 빈이 몇만개면......? 그래서 위의 방식보다 더 편한 컴포넌트 스캔 방식이 등장했다.

@Configuration
@ComponentScan
public class AutoAppConfig {}

이렇게 하면 끝이다. 이제 이 AutoAppConfig 설정 정보를 스프링에 등록해주면 된다.

@Test
void basicScan() {
  AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AutoAppConfig.class);

  MemberService memberService = ac.getBean(MemberService.class);
  assertThat(memberService).isInstanceOf(MemberService.class);
}

잘 동작하는 것을 확인할 수 있다. AutoAppConfig를 등록하면 @Configuration 어노테이션도 @Component의 기능이 있어서 수동등록의 예제 코드에 있던 AppConfig 설정 정보도 다 같이 등록된다.

ComponentScan을 해주면 Component 어노테이션이 붙은 클래스들을 모두 찾아서 스프링 빈으로 등록해준다. 찾는 범위는 따로 설정할 수 있는데 기본값은 AutoAppConfig 가 놓인 패키지~하위 패키지들이다. 참고로 스프링부트 프로젝트를 생성하면 처음부터 있는 CoreApplication 클래스엔 @SpringBootApplication가 붙어있는데 이 어노테이션이 ComponentScan을 상속받는다. (어노테이션엔 상속개념이 없지만 스프링이 지원해준다.)

 

그렇다면 의존관계 주입은 어떻게..?

팩토리 메서드 방식은 return new Obj(인자들 넘김); 하면서 의존관계 주입 했는데 Component들은 방법이 더 간단하다. 그냥 @Autowired 만 붙여주면 된다. @Autowired를 붙여주는 방법은 많이 있지만 생성자에 붙여주는 방법을 써야한다. 테스트 코드 같은 경우에는 필드주입으로 해도 상관없다.

1. Component들을 다 스프링 빈에 등록하고
2. 의존 관계들 주입 시작

하지만 생성자 주입 방식은 생성자가 호출되는 순간에 의존관계 주입이 이뤄져야 하므로 위의 순서가 지켜지지 않는다.

※ 필드주입을 사용하면 안되는 이유!
필드주입을 하게되면 순수한 자바코드에서 테스트할때 의존관계 주입을 해줄 수 있는 방법이 없기 때문..

기억하자 : 생성자 주입을 쓰자, 롬복의 RequiredArgsConstructor

 

왜 생성자 주입을 쓰는 이유에 대해 정리해보자면, 순수한 자바 코드로 테스트 짤때 어떤 의존관계가 필요한지는 생성자 메소드만 보면 다 알 수 있다. 수정자 주입방식은 코드 일일이 다 까봐야하는 불편함이 있다. 필드주입은 주입도 못하는 상황이 발생하고..

🐌 자동 주입시에 타입이 여러개면 충돌나지 않나???? 해결 방법은 @Qualifier와 @Primary로 가능하다. 주로 사용하는 빈을 Primary로 등록하면 해당 빈을 우선적으로 가져온다. 그리고 자주 안쓰는 빈은 @Qualifier를 통해 가져오면 된다.

 

자동 vs 수동

자동을 사용하는 추세.

기술 지원빈 설정은 수동으로 하자.

 

헷갈렸던 상황

자동 설정을 사용하면 수동 설정도 함께 등록되므로 위와 같은 에러가 발생했다. 의존관계 주입 과정에서 동일한 타입의 빈이 자동과 수동이 겹쳐서 충돌이 일어난 것이다. 이는 @Primary와 @Qualifier로 해결하면 되었다.

 

 

관련글

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B8%B0%EB%B3%B8%ED%8E%B8/dashboard

 

스프링 핵심 원리 - 기본편 - 인프런 | 강의

스프링 입문자가 예제를 만들어가면서 스프링의 핵심 원리를 이해하고, 스프링 기본기를 확실히 다질 수 있습니다., 스프링 핵심 원리를 이해하고, 성장하는 개발자가 되어보세요! 📣 확인해주

www.inflearn.com

 

 

 

'백엔드 > 스프링' 카테고리의 다른 글

프론트 컨트롤러 패턴  (0) 2021.08.17
Http Request 데이터 형식 정리  (0) 2021.08.17
스프링 빈 스코프  (0) 2021.08.13
스프링 설정 - Xml 형식  (0) 2021.08.12
스프링 리액트 연동 Rest API 써보기  (0) 2021.03.28