스프링 빈 등록하기

inhalin·2022년 7월 19일
0

김영한님의 스프링 핵심 원리 - 기본편 강의를 듣고 정리한 내용입니다.

스프링 기반 @Bean 으로 등록

  • 설정 파일로 지정하는 클래스에 @Configuration을 추가한다.
  • 각 메서드에 @Bean을 붙여서 스프링 컨테이너에 스프링 빈으로 등록해준다.
@Configuration
public class AppConfig {

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

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

스프링 컨테이너

ApplicationContext를 스프링 컨테이너라고 하고 컨테이너에 빈으로 등록하면 스프링에서 알아서 객체를 생성하고 의존성 주입을 관리해준다.

xml의 <bean> 으로 등록

위에서 만들어준 AppConfig.java 코드랑 똑같은 내용을 xml로도 설정해줄 수 있다. 요새는 많이 쓰진 않는다고 한다. 참고삼아 보면서 테스트 코드 돌려보는 것까지만 했다.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="memberService" class="hello.core.member.MemberServiceImpl">
        <constructor-arg name="memberRepository" ref="memberRepository"/>
    </bean>

    <bean id="memberRepository" class="hello.core.member.MemoryMemberRepository"/>

    <bean id="orderService" class="hello.core.order.OrderServiceImpl">
        <constructor-arg name="memberRepository" ref="memberRepository"/>
        <constructor-arg name="discountPolicy" ref="discountPolicy"/>
    </bean>

    <bean id="discountPolicy" class="hello.core.discount.RateDiscountPolicy"/>
</beans>

컴포넌트 스캔

위의 방법들은 스프링 빈을 직접 나열해서 일일이 등록해야 한다. 만약 등록해야 할 빈의 개수가 수십, 수백개가 되면 누락되는 경우도 있고, 무엇보다 귀찮다.

그래서 스프링에서는 자동으로 컴포넌트 스캔을 해서 빈을 등록하고 자동으로 의존관계도 주입한다.

  • @ComponentScan 설정파일에 붙이면 자동으로 @Component가 붙은 모든 클래스를 스캔해서 스프링 빈으로 등록한다.
  • @Component 컴포넌트 스캔 시 스프링 빈으로 등록할 클래스에 붙여준다.
  • @utowired 생성자에 붙이면 자동으로 의존 관계를 주입한다.

컴포넌트 스캔 기본 대상

  • @Component 기본 컴포넌트 스캔 대상
  • @Controller 스프링 MVC 컨트롤러
  • @Service 스프링 비즈니스 로직
  • @Repository 스프링 데이터 접근 계층
  • @Configuration 스프링 설정 정보

위에 있는 목록에서 @Component 외의 다른 애노테이션을 타고 들어가면 @Component가 다 붙어있는 걸 볼 수 있다. 하지만 애노테이션은 상속을 받고 하는 게 아니고 그런 개념도 없다. 애노테이션 안에 붙어있는 에노테이션을 인식하는건 자바가 아니라 스프링이 지원하는 기능이다.

애노테이션은 메타정보라서 스캔 용도만이 아니라 스프링의 부가 기능을 수행한다. 그래서 예를 들어 그냥 @Component로 붙이지 않고 @Controller라고 붙이면 스프링에서 해당 클래스를 스프링 MVC 컨테이너로 인식한다. 마찬가지로 @Configuration은 스프링 설정 정보로 인식해서 스프링 빈이 싱글톤을 유지하도록 추가 처리를 자동으로 해주게 된다.

필터로 컴포넌트 스캔 대상 추가/제외하기

  • includeFilters 추가로 스캔할 대상 지정
  • excludeFilters 컴포넌트 스캔에서 제외할 대상 지정
@Configuration
@ComponentScan(
            includeFilters = @Filter(type = FilterType.ANNOTATION, classes = MyIncludeComponent.class),
            excludeFilters = @Filter(type = FilterType.ANNOTATION, classes = MyExcludeComponent.class)
    )
static class ComponentFilterAppConfig {}    

참고로 type = FilterType.ANNOTATION은 기본 설정값이라 빼줘도 똑같이 동작한다.

중복 등록과 충돌

자동 빈 등록시 이름이 중복되는 경우

자동 빈 등록시 이름이 같으면 충돌이 날 수 있다. 테스트를 위해서 임의로 빈 이름을 똑같이 지정해주고 테스트 코드 돌려보면 실패하는걸 확인할 수 있다.

@Component("service")
public class MemberServieImpl {}

@Component("service")
public class OrderServieImpl {}

org.springframework.beans.factory.BeanDefinitionStoreException: Failed to parse configuration class [hello.core.AutoAppConfig]; nested exception is org.springframework.context.annotation.ConflictingBeanDefinitionException: Annotation-specified bean name 'service' for bean class [hello.core.order.OrderServiceImpl] conflicts with existing, non-compatible bean definition of same name and class [hello.core.member.MemberServiceImpl]

에러 메시지에 뭐가 잘못 됐는지 친절하고 자세하게 나온다. 스프링은 에러 메시지만 잘 읽어도 거의 다 문제 해결이 가능하다.

자동 빈 등록과 수동 빈 등록시 이름이 중복되는 경우

만약에 수동으로 등록한 빈이 자동 등록된 빈과 이름이 같으면 수동 등록한 빈으로 오버라이딩을 해버린다. 근데 이렇게 설정 파일에서 오버라이딩 되면서 나는 오류는 나중에 버기 찾기가 어려워진다. 그래서 스프링 부트에서는 기본 설정에서 아예 프로그램 실행이 안되고 오버라이딩이 안되도록 막아두었다.

application.propertiesspring.main.allow-bean-definition-overriding=true를 추가해주면 오버라이딩 돼서 부트가 실행되게 할 수는 있다.

개발에서 명확하지 않은건 하면 안된다. 내가 스프링을 너무 잘 알아서 자동 등록, 수동 등록 다 해도 괜찮아! 라고 해도 개발은 혼자 하는 게 아니다. 여러명의 개발자가 같이 하는거기 때문에 애매한 상황을 만들지 않는 것이 가장 좋다.

코드가 줄어들고 예쁘지만 불명확하고 애매한 부분이 생기는 경우와, 코드를 복붙해서 더 만들어야 하지만 더 명확하고 한눈에 잘 들어오는 경우가 있을 때 명확한 것을 선택하는 것이 대부분의 경우 더 낫다.

— 강의 내용 중

0개의 댓글