JAVA/Spring

스프링 빈과 의존성 주입 (DI)

호두밥 2022. 1. 12. 23:38

제어 역전 Inversion Of Control 과 Ioc Container

IoC Inversion Of Control 제어 역전이란, 클라이언트 코드 관리하던 의존관계 제어를 프레임워크에서 가져가 관리하는 것을 말합니다. 

Ioc Container란 프레임워크에서 객체의 생성과 객체 간의 의존관계를 관리하는 컨테이너입니다. 스프링 프레임워크의 ApplicationContext가 대표적입니다. 그리고 스프링의 IoC 컨테이너에서 관리하는 객체를 빈(Bean)이라 합니다. 

스프링 빈 등록 @Component와  @Bean

빈을 등록하는 방법은 크게 2가지가 있습니다. 

첫 번째로 @Component를 이용해 클래스를 바로 빈으로 등록하는 방법입니다.

@Component
public class Company {
    // this body is the same as before
}

* @Service, @Controller, @Repository는 @Component와 동일한 기능(빈 등록)을 제공합니다. 비즈니스 로직이 반영된 빈이면 @Service, 데이터베이스 연결 관련 정보를 관리하는 bean이면 @Repository, 스프링 웹 MVC에서 Controller라면 @Controller로 설정합니다.  @Repository는  PersistenceException 예외 기능을 제공하고, @Controller에서만 @RequestMapping 어노테이션을 사용할 수 있습니다. @Service는 현재는 별도로 추가 기능을 제공하지 않습니다. 

두 번째로 @Configuration, @ComponentScan, @Bean 을 이용해 메소드 단위로 별도의 설정파일을 만들어 등록하는 방법이 있다.

@Configuration
@ComponentScan(basePackageClasses = Company.class)
public class Config {
    @Bean
    public Address getAddress() {
        return new Address("High Street", 1000);
    }
}

생성된 빈을 확인하려면, 혹은 IoC 컨테이너에서 꺼내서 사용하려면 ApplicationContext.getBean 을 이용하면 됩니다. 

// IoC 컨테이너 생성하기
ApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
// 빈 꺼내기
Company company = context.getBean("company", Company.class);

@Bean과 @Component

  • @Component는 클래스 레벨 어노테이션, @Bean은 메소드 레벨 어노테이션입니다.
    • @Component는 클래스 코드를 수정할 수 있는 경우에만 적용이 가능하고, @Bean은 제한없이 사용이 가능합니다.
  • @Component는 스프링에서 변화를 자동으로 감지하지만, @Bean은 수동으로 변경해야 합니다.
  • @Bean을 사용하면 클래스 정의와 인스턴스 생성을 분리하게 됩니다. 이를 통해 외부 클래스를 스프링 빈으로 등록할 수 있습니다.

스프링 의존성 주입 @Autowired

@Autowired는 스프링에서 의존관계를 자동으로 주입해주는 어노테이션입니다. @Autowired는 3가지 방식으로 사용할 수 있습니다.

1. Properties (내부 객체에 직접 선언)

@Component
public class Service {  
    @Autowired
    private BeanObject beanObject;
}

2. Setter 

@Component
public class Service {      
    private BeanObject beanObject;
    
    @Autowired
    public void setBeanObject(BeanObject beanObject){
    	this.beanObject = beanObject;
    }
}

3. Constructor 생성자

@Component
public class Service {      
    private BeanObject beanObject;
    
    @Autowired
    public Service(BeanObject beanObject){
    	this.beanObject = beanObject;
    }
}

 

동일한 타입의 스프링 빈이 2개 이상인 경우 @Qualifier, @Primary

같은 타입의 스프링 빈이 2개 이상 등록된 경우 ApplicationContext.getBean을 빈을 조회하려고 하면 NoUniqueBeanDefinitionException 이 발생합니다. 이럴 때, @Qualifier와 @Primary를 이용해 해결할 수 있습니다. 

@Qualifier

@Qualifier는 추가적인 구분자를 지정해주는 방식입니다. 빈이 여러 개인 경우 @Qualifier에 등록된 내용이 일치하는 빈을 가져옵니다.

먼저 Bean을 등록하는 위치에 @Qualifier를 이용해 이름을 지정해줍니다.  

@Component
@Qualifier("firstCompany")
public class Company {
    // this body is the same as before
}

그리고 빈을 주입하는 위치에 @Qualifier를 이용해 주입받을 bean을 지정해줍니다. 

public class Service {
    @Autowired
    @Qualifier("firstCompany")
    private Company company;
}
public class Service {     
    private Company company;
    
    @Autowired
    public Service (@Qualifier("firstCompany") Company company){
    	this.company = company;
    }
}

@Qualifier는 @Qualifier끼리 빈 이름을 통해 매칭해나갑니다. 빈을 생성하는 곳과 의존성을 주입하는 곳, 두 곳에 선언해줘야 한다는 단점이 있습니다.  

매칭되는 빈을 찾지 못한 경우 NoSuchBeanDefinitionException이 발생합니다.

@Primary

@Primary는 우선순위를 지정하는 방식입니다. 빈이 여러 개일 때 @Primary가 선언된 빈을 가져옵니다. 

@Component
@Primary
public class Company {
    // this body is the same as before
}

@Qualifier와 @Primary

@Qualifier는 세부적, 수동으로 등록하는 설정이고, @Primary는 기본값처럼 동작합니다. 만약 둘 다 사용하게 된다면, @Qualifier가 우선시 됩니다. 그래서 빈이 여러 개일때 기본적으로는 @Primary를 이용해 기본값 빈을 설정하고, 지역적으로, 혹은 특수한 경우에 @Qualifier로 빈을 관리하는 방법이 추천됩니다. 

참고자료

 

'JAVA > Spring' 카테고리의 다른 글

쓰레드로컬 Thread Local (데이터 경합 방지)  (0) 2023.07.08
@RequestMapping 과 @Controller  (0) 2022.01.26
Spring MVC 구조  (0) 2022.01.23