[Spring boot] DI 설정에 관하여

이번 글에서 3가지의 DI 설정방식에 대하여 설명하고 각각의 장단점에 정리하할 예정이다.

앞글에서 DI 방식에는 3가지(Construction, setting, field) 방식이 있다는 것을 소개 했다. 그렇다면 그들을 실제 spring project에서 사용되는 방식에 대해 알아보자

XML-based Configuration

이 설정 방식을 사용하려면 applicationContext.xml 파일을 생성 후 원소 내부에 설성 해야 한다. 자세한 내용은 공식문서를 참조하면 좋을 것 같다.

//daos.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
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="..." class="...">  
        <!-- collaborators and configuration for this bean go here -->
    </bean>

    <bean id="..." class="...">
        <!-- collaborators and configuration for this bean go here -->
    </bean>

    <!-- more bean definitions go here -->

</beans>

이렇게 설정한 bean은 코드에서 아래와 같이 사용할 수 있다.

import org.springframework.beans.factory.getBean

// create and configure beans
val context = ClassPathXmlApplicationContext("services.xml", "daos.xml")

// retrieve configured instance
val service = context.getBean<PetStoreService>("petStore")

// use configured instance
var userList = service.getUsernameList()

ClassPathXmlApplicationContext을 이용하여 XML로 설정된 bean를 로드할 수 있고 getBean을 사용하여 bean을 얻을 수 있다.

Java-based Configuration

JAVA class 파일에 @Configuration을 사용하여 Bean에 대한 설정을 할 수 있다. 이 방식은 보통 DB 연결 Bean을 설정 및 injection 할때 사용된다.

@Configuration
class AppConfig {

    @Bean
    fun myService(): MyService {
        return MyServiceImpl()
    }
}

class형태로 정의된 Bean은 아래 방식으로 불러 올 수 있다.

import org.springframework.beans.factory.getBean

fun main() {
    val ctx = AnnotationConfigApplicationContext(AppConfig::class.java)
    val myService = ctx.getBean<MyService>()
    myService.doStuff()
}

Annotation-based Configuration

이 기능을 사용하려면 설정파일에 context:annotation-config/를 넣어줘야 한다.

<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

    <context:annotation-config/>
</beans>

@Autowired

앞글에서 소개한 DI 방식을 모두 적용 가능하다. 아래 예제는 setter-based DI 방식의 예제를 보여주고 있다.

@RestController
class Corporate(
){
    var corporateRepository: CorporateRepository? =null
    @Autowired
    fun setCoroprateRepository(corporateRepository: CorporateRepository){
        this.corporateRepository=corporateRepository
    }
    @GetMapping("corporates")
    fun get_corporate_information():List<Corporate>?{
        return corporateRepository?.findAll() ?: null
    }

    @PostMapping("corporates")
    fun creat_corporate(@RequestBody corporate: Corporate):Corporate?{
        return corporateRepository?.save(corporate) ?: null
    }
}

@Primary

여러개의 Bean이 있을경우 그중 하나를 기본 사용 bean으로 지성할 수 있다

public interface CustomerService {  
  public void customerService(); 
}

@Component
public class AccountService implements CustomerService {
      ....
}
@Component
@Primary
public class BankingService implements CustomerService { 
     ....
}

위 코드에서는 기보적으로 BankingService가 customerService의 구현체로 사용 된다.

@Qualifier

위의 Primary는 하나만 지정하여 기본적인 요소를 지정할 수 있다면 @Qualifier는 여러 autowire 후보들를 선택적으로 사용할 수 있게 한다.

@Component
public class AccountService implements CustomerService {

}
@Component
@Qualifier("BankingService")
public class BankingService implements CustomerService { 

}

@Component
public class SomeService {

  private CustomerService customerService;

  @Autowired
  @Qualifier("bankingservice")
  public BankingService(CustomerService customerService) {
    this.customerService = customerService;
  }
}

별칭을 달아주어서 spring에서 자동으로 dependency을 만들도록 한다고 생각하면 된다.

이외 Annotation들

Spring framework에서는 @Autowired 이외에도 상황에 맞게 사용할 수 있는 annotation을 제공한다.

@Component, @Service, @Repository, @Controller 이런 annotation들은 특별한 기능이 있는 것이 아니고 기본적으로 @Autowired와 같은 역할을 하지만 사용 목적성에 따라 코드의 가독성을 높힌다.

@ComponentScan

Spring framwork는 기본적으로 프로그램 시작 class와 하위 class에 대하여 scan을 하고 injection을 한다. 만약 다른곳에 정의한 bean들을 injection을 하고 싶다면 @ComponentScan 을 사용하면 된다.

@Configuration
@ComponentScan(basePackages="com.packt.springhighperformance.ch2.bankingapp.model")
public class AppConfig {

}

@Lazy

Autowired로 선언한 bean들은 프로그램 시작하면 바로 spring IoC container에 의해 바로 생성 되지만, 만약 빈번하게 사용되지 않는 요소에 대해서는 호출전에 생성할 수 있게 해주는 방식이다.

이 방식은 프로그램 개발 언어 레벨에서도 많이 하고 DB사용에 있어도 많이 사용되는 기법인데 spring에서 bean을 생성시 lazy 방식에 대하여 좀 더 경험해보고 경험담을 공유 할 예정이다.

마치며...

이상으로 DI 설정 방식에 대하여 간단히 알아 보았다. 위에 언급한 것들을 잘 조합하게 최적의 설정을 하는 방법을 찾으면 블로그에 공유할 예정이다. 이론적 정리와 프로젝트에서의 사용법 결합하여 글을 작성하면 추후 공부에도 더 도움이 될것 같아 이 글은 여기에서 마치고 다음번 프로젝트를 진행하면 업데이트 할 예정이다.