스프링에서 제공하는 스프링 컨테이너는 빈을 생성하고 관리한다. 다시말해, 빈이 생성되고 소멸되는 빈의 생명주기를 스프링 컨테이너에서 관리하는 것이다.
뿐만 아니라 스프링은 @Autowired
같은 애너테이션을 사용해 빈을 주입할 수 있게 DI를 지원하기도 한다.
스프링 컨테이너가 생성하고 관리하는 객체로, 어렵게 생갈할 필요없이 스프링만의 객체라고 생각하면 된다
스프링은 빈을 스프링 컨테이너에 등록하기 위해 XML 파일 설정, 애너테이션 추가 등의 여러가지 방법을 제공한다.
@Component
//클래스 MyBean을 빈으로 등록하는 애너테이션
public class MyBean {
}
클래스 용도에 따라 다른 애너테이션을 사용하기는 하지만, 사실 내부 코드를 보면 @Component
애네터이션이 모두 달려 있다.
클래스를 빈으로 등록하기만 해주면 될 때 사용하는 애너테이션
클래스를 설정 파일로 등록하면서 빈으로 등록해줄 때 사용하는 애너테이션
클래스를 ORM 매핑을 하면서 빈으로 등록해줄 때 사용하는 애너테이션
클래스를 라우터로 만들어주면서 빈으로 등록해줄 때 사용하는 애너테이션이다.
예를 들어 아래와 같은 코드가 있다고 가졍해보자.
@GetMapping("/test")
public String test() {
return "Hello, world!";
}
/test라는 GET 요청이 왔을 때
클래스가 비즈니스 로직을 담당하게 하면서 빈으로 등록시킬 때 사용하는 애너테이션
자주 발생하는 케이스는 아니며 등록 자체도 문제없다! 하지만 부를 때 같은 타입인 2개의 Bean 중에서 무엇을 선택할지 지정해주지 않으면 에러가 발생한다. 그럴땐 아래와 같이 원하는 Bean의 이름을 지정해주면 된다.
첫번째 방법. Bean의 이름으로 지정
public interface Food {
//인터페이스의 구현클래스를 빈에 등록하면 인터페이스도 빈에 등록된다
void eat();
}
import org.springframework.stereotype.Component;
@Component
class Pizza implements Food {
@Override
public void eat() {
System.out.println("피자를 먹습니다.");
}
}
@Component //빈 등록
class Chicken implements Food {
@Override
public void eat() {
System.out.println("치킨을 먹습니다.");
}
}
import com.sparta.springauth.food.Food;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
//DI 주입 사용할 수 있게 해주는 annotation
public class BeanTest {
//Food 인터페이스 주입받기
//인터페이스에 @Autowired 할 수 있을까? 있다! 인터페이스의 구현체가 주입된다.
@Autowired
//Food food; //오류 발생!! Could not autowire. There is more than one bean of 'Food' type.
// Food 타입의 빈이 2개이므로 어떤 것을 주입해줄지 모르겠다는 의미
Food pizza; //빈의 이름으로 직접 지정해주는 방법
@Autowired
Food chicken;
}
두번째 방법. @Primary
import com.sparta.springauth.food.Food;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class BeanTest {
@Autowired
Food food; //주입할 Bean을 @Primary로 지정해줬기 때문에 오류가 발생하지 않음
}
@Component
@Primary
class Pizza implements Food {
@Override
public void eat() {
System.out.println("피자를 먹습니다.");
}
}
세번째 방법. @Qualifier
package com.sparta.springauth;
import com.sparta.springauth.food.Food;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class BeanTest {
@Autowired
@Qualifier("chicken")
Food food;
@Test
@DisplayName("@Qualifier 와 @Primary 우선순위 비교")
//@Qualifier 가 @Primary 보다 우선순위가 높다
void test2() {
food.eat();
}
}
같은 Bean 타입의 객체에 @Qualifier와 @Primary가 같이 걸려있다면, @Qualifier의 우선순위가 높다.
참고로 범용적으로 사용되는 Bean 객체에는 @Primary, 일부에게만 달아줘야하는 Bean 객체에는 @Qualifier를 하나하나 붙여주는 것이 효율적이다.
[교재]
스프링부트3 백엔드 개발자되기 자바편