Backend/Spring

Spring Bean 설정 방법

keepbang 2021. 8. 29. 23:11

1. XML 설정 사용

  • XML을 사용하면 컴파일 없이 빈 설정 정보를 변경할 수 있다.
  • 빈 설정에 에러가 났을 경우 컴파일러가 이를 잡아주지 못하고 문제가 생겼을 경우에 원인을 찾는게 어렵다.
  • 최근에는 스프링 부트를 많이 사용하면서 XML기반의 설정은 잘 사용하지 않는다.

XML 설정 방법

  • resources폴더 아래에 xml파일을 생성하여 설정한다.

[appConfig.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="spring.example.member.MemberServiceImpl" scope="singleton">
        <constructor-arg name="memberRepository" ref="memberRepository"/>
    </bean>
    <bean id="memberRepository" class="spring.example.member.MemoryMemberRepository"/>

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

    <bean id="discountPolicy" class="spring.example.discount.RateDiscountPolicy"/>

</beans>

id : 등록할 빈의 이름
class : 패키지명을 포함한 빈으로 정의할 클래스
scope : 빈의 스코프(singleton, prototype, reqeust...), 설정하지 않으면 기본적으로singleton이 적용된다.
autowired : 기본적으로 타입과 이름, 생성자등이 있다.(autowired방법)

constructor-arg : 생성자 주입을 할 경우 사용한다. 생성자의 매개변수를 등록해야한다.

constructor-arg를 사용하는 빈들은 아래와 같이 생성자 주입이 들어있다.

//[MemberServiceImpl]
public class MemberServiceImpl implements MemberService{

    private final MemberRepository memberRepository;

    public MemberServiceImpl(MemberRepository memberRepository){
        this.memberRepository = memberRepository;
    }

    ...
}

//[OrderServiceImpl]
public class OrderServiceImpl implements OrderService{

    private final MemberRepository memberRepository;
    private final DiscountPolicy discountPolicy;

    public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy){
        this. memberRepository = memberRepository;
        this.discountPolicy = discountPolicy;
    }

   ...
}

 

XML 설정 테스트

public class XmlAppContext {

    @Test
    void xmlAppContext(){
        ApplicationContext ac = new GenericXmlApplicationContext("appConfig.xml");
        MemberService memberService = ac.getBean("memberService", MemberService.class);
        assertThat(memberService).isInstanceOf(MemberService.class);
    }
}

 

[결과]

Singleton Bean으로 생성된걸 볼 수 있다.

21:27:56.135 [Test worker] DEBUG org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loaded 4 bean definitions from class path resource [appConfig.xml]
21:27:56.139 [Test worker] DEBUG org.springframework.context.support.GenericXmlApplicationContext - Refreshing org.springframework.context.support.GenericXmlApplicationContext@3cf16a77
21:27:56.187 [Test worker] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'memberService'
21:27:56.206 [Test worker] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'memberRepository'
21:27:56.215 [Test worker] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'orderService'
21:27:56.216 [Test worker] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'discountPolicy'

 

2. XML 컴포넌트 스캔

  • 위 방법과 같이 으로 빈을 하나하나 등록하면 너무 번거롭기 때문에... 컴포넌트 스캔을 사용해서 지정한 패키지 내에 있는 컴포넌트를 간단하게 모두 가져오는 방법이 있다.

[appConfig.xml]

<context:component-scan base-package="spring.example"/>

base-package 기준으로 @Component애노테이션을 붙인 클래스들을 스캔하여 빈으로 등록해준다.

 

Component 어노테이션 외에도 @Service, @Controller, @Repository, @Configuration애노테이션이 붙은 클래스들도 스캔을하는데 그 이유는 해당 어노테이션에 Component애노테이션이 포함되어 있기 때문이다.

Configuration 애노테이션

Component 관련 애노테이션은 클래스 위에 해당 애노테이션을 붙이면 된다.

 

위에 했던 테스트 실행 결과

base-package에 ','를 사용하여 여러개의 패키지를 쓸 수 있다.

<context:component-scan base-package="spring.example.member , spring.example.order"/>

 

3.  JAVA 설정 파일 사용

  • String 프레임워크는 3.1부터 Java 기반의 설정을 지원하기 시작했다.
  • XML문서는 길어지면 가독성이 떨어지고 String형 문서이기때문에 오타를 확인하거나 디버깅하기가 어렵다.
  • Java 설정 방식은 컴파일러의 도움을 받기 때문에 설정 정보 오류를 미리 알 수 있다.
  • 1번에서 작성한 xml config파일을 java설정으로 바꿀 수 있다.
@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 RateDiscountPolicy();
    }
}

컴포넌트 안에 작성된 @Bean을 등록시켜 준다.

 

4.  @SpringBootApplication

Spring boot를 사용하면 최상단에 @SpringBootApplication이 붙은 클래스가 하나 생성된다.

 

 

@SpringBootApplication은 @Configuration과 @ComponentScan을 가지고 있기 때문에 개발자가 특별한 선언을 하지 않아도 해당 클래스의 하위 패키지에 Component관련 애노테이션만 추가하면 auto-scan되어 빈이 등록된다.

 

 



@Component, @Bean의 차이점

 

Component와 Bean을 사용하여 등록하는 방법의 차이점을 알아보았다.

 

1.  @Component

  • 개발자가 직접 컨트롤이 가능한 class들을 등록할 때 사용된다.
  • Class Level에서 적용된다.

 

2.  @Bean

  • 개발자가 컨트롤이 불가능한 외부 라이브러리들을 Bean으로 등록하고 싶은 경우에 사용된다.
  • Method Level에서 적용된다.

 

[Reference]

  • 인프런 - 김영한님의 스프링 핵심 원리 기본편

 

 

@Bean vs @Component

Spring으로 개발을 하다보면 @Bean과 @Component를 언제 써야할지 헷갈릴때가 있다. 둘다 목적이 명확하지 않은 Bean을 생성할때 사용하는 어노테이션인데 왜 2개로 나누어져있나 궁금했었는데, 박재성

jojoldu.tistory.com