Don't give up!

[Spring Boot] Bean 등록 본문

Spring Boot

[Spring Boot] Bean 등록

Heang Lee 2021. 8. 4. 23:43

Spring의 IoC와 Bean 등록

Spring은 BeanFactory 인터페이스를 상속받는 ApplicationContext를 통해 IoC(Inversion of Control)를 수행할 객체 Bean을 등록합니다.

Spring에서 Bean을 등록하는 방법에는 크게 3가지로 나누어 볼 수 있습니다.

  1. XML을 통한 Bean 등록
  2. 자바 Configuration 클래스를 통한 Bean 등록
  3. Component Scan을 통한 Bean 등록

Bean을 등록하고 정상적인 Bean이 등록되었는지 확인하기 위해 코드를 정의하고, 테스트해보고자 합니다.

public class MyRepository {
    public String value = "MyRepository";
}

public interface MyService {
    String getName();
    MyRepository getRepository();
}

public class MyServiceImpl implements MyService{
    private final MyRepository myRepository;

    public MyServiceImpl(MyRepository repository) {
        this.myRepository = repository;
    }

    @Override
    public String getName() {
        return this.myRepository.value;
    }

    @Override
    public MyRepository getRepository() {
        return myRepository;
    }
}

1. XML에 Bean 정보를 등록

<?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="myService" class="com.community.service.MyServiceImpl">
        <constructor-arg name="repository" ref="myRepository"/>
    </bean>
    <bean id="myRepository" class="com.community.repository.MyRepository"/>
</beans>

XML 파일에 bean 태그로 클래스와 ID값을 등록하여 Bean을 등록할 수 있습니다.

의존성을 주입받는 Bean은 constructor-arg 태그를 통해 이름에 맞는 Bean을 생성자 인수로 주입받습니다.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:MyXML.xml")
public class MyBeanTest {
    @Autowired
    MyService myService;

    @Autowired
    MyRepository myRepository;

    @Test
    public void testRepositoryName(){
        String result = myService.getName();

        Assertions.assertEquals("MyRepository", result);
    }

    @Test
    public void testSame(){
        Assertions.assertEquals(myRepository, myService.getRepository());
    }
}

Bean 등록을 테스트하는 코드입니다.

MyService를 주입받아 MyService에서 Repository의 함수를 호출하는 테스트 testRepositoryName과 테스트 클래스의 변수 myRepository와 MyService의 생성자에 주입받은 myRepository와 동일한 인스턴스인지 확인하는 테스트 testSame을 작성하였습니다.

테스트를 정상적으로 통과하였습니다.

2. 자바 configuration을 통한 등록

@Configuration
public class MyConfiguration {
    @Bean
    public MyRepository myRepository() {
        return new MyRepository();
    }

    @Bean
    public MyService myService(MyRepository repository){
        return new MyServiceImpl(repository);
    }
}

Configuration어노테이션을 갖는 클래스를 선언하고, @Bean 어노테이션으로 Bean을 등록합니다.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {MyConfiguration.class})
public class MyBeanTest {
    @Autowired
    MyService myService;

    @Autowired
    MyRepository myRepository;

    @Test
    public void testRepositoryName(){
        String result = myService.getName();

        Assertions.assertEquals("MyRepository", result);
    }

    @Test
    public void testSame(){
        Assertions.assertEquals(myRepository, myService.getRepository());
    }
}

XML이 아닌 configuration 클래스로 Bean을 등록을 테스트하기 위해서는 @ContextConfiguration의 classes에 configuration 클래스들을 지정하여야 합니다.

테스트를 통과함을 확인하였습니다.

3. Component Scan을 통한 Bean 등록

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    <context:component-scan base-package="com.community"/>
</beans>
@Component
public class MyRepository {
    public String value = "MyRepository";
}

public interface MyService {
    String getName();
    MyRepository getRepository();
}

@Component
public class MyServiceImpl implements MyService{
    private final MyRepository myRepository;

    public MyServiceImpl(MyRepository repository) {
        this.myRepository = repository;
    }

    @Override
    public String getName() {
        return this.myRepository.value;
    }

    @Override
    public MyRepository getRepository() {
        return myRepository;
    }
}

Spring 2.5부터는 Component Scan을 지원합니다.

context:component-scan 태그에 컴포넌트 스캔을 시작할 베이스 패키지를 지정하면, 해당 패키지에서부터 하위 패키지로 @Component 어노테이션을 갖는 클래스들을 Bean으로 등록합니다.

정상적으로 테스트를 통과하였습니다. 테스트 코드는 XML을 사용하는 테스트 코드와 동일합니다.

 

@Configuration
//@ComponentScan(basePackageClasses = com.community.WebCommunityBatchApplication.class)
@ComponentScan(basePackages = {"com.community"})
public class MyConfiguration {
}

자바 Configuration에서도 Component Scan을 사용할 수 있습니다.

@ComponentScan어노테이션의 basePackages 또는 basePackageClasses를 지정하면 패키지 혹은 클래스를 기준으로 다른 클래스들의 @Component를 읽어 Bean으로 등록합니다.

테스트 코드는 자바 Configuration을 통한 빈 등록에서의 테스트 코드와 동일하며 테스트를 통과함을 확인하였습니다. 

 

XML vs Java Configuration

XML을 사용하여 Bean을 정의했을 때 얻는 이점은 다음과 같습니다.

  1. XML으로 정의된 Bean들은 XML 파일에 모두 모여있다. (필요한 경우 xml 파일을 분리하여 사용할 수 있다.)
  2. Bean들이 XML 파일의 정보를 토대로 설정되기 때문에 별도의 컴파일이 필요 없다.
  3. 테스트 환경과 실제 환경을 쉽게 변경할 수 있다. 테스트 환경용 xml파일과 실제 환경용 xml파일을 두어 오픈하는 파일을 다르게하는 것뿐만으로도 설정을 달리할 수 있다.

Java로 Bean을 정의했을 때 얻는 이점은 다음과 같습니다.

  1. 설정 파일을 따로 관리할 필요가 없다. 설정 파일에 대한 별도의 공부가 필요없다.
  2. 자바로 작성되므로 자바로 코드를 작성하는 개발자가 이해하기 더 쉽다. 또한 IDE의 자동완성 기능을 사용할 수 있다.
  3. 에러를 컴파일 단계에서 확인할 수 있다. 또한 자바 코드이므로 디버깅할 수 있다.

개발환경에 따라 다를 수 있겠지만 다음을 이유로 자바로 Bean을 등록하는 것이 선호되는 것 같습니다.

  1. 같은 환경에서의 xml 변경시에도 이를 재배포해야한다. (properties나 yml을 사용하여 설정할 수 있다)
  2. Component-Scan을 사용할 경우 등록되는 Bean들을 XML에서 확인하기 어렵다. Component-Scan과 Bean 등록을 동시에 선언할 경우 xml파일과 스캔되는 Bean들을 번갈아가며 확인해야한다.
  3. 자바는 컴파일 단계에서 오류를 발견하고 디버깅할 수 있다.