
@Component를 사용하면 @ComponentScan에 의해 자동으로 스캔되어 해당 클래스를 Bean으로 등록해준다. 그래서 일반적으로는 @Component를 사용하여 Bean을 자동으로 등록하여 관리하는 것이 좋다.
하지만 기술적인 문제나 공통적인 관심사를 처리할 때 사용하는 객체들은 수동으로 등록하는 것이 좋다. 공통 로그처리같은 비즈니스 로직을 지원하기 위한 부가적이고 공통적인 기능들을 기술지원 Bean이라고 하는데 이 기술지원 Bean들을 수동 등록하면 문제가 발생했을 때 해당 위치를 파악하기 쉽다는 장점이 있다.
비밀번호를 암호화할 때 사용하는 PasswordEncoder의 구현체 BCryptPasswordEncoder를 Bean으로 수동 등록하는 예로 설명해보자면
@Configuration
public class PasswordConfig {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
public interface Food {
void eat();
}
public class Chicken implements Food {
@Override
public void eat() {
System.out.println("치킨을 먹습니다.");
}
}
public class Pizza implements Food {
@Override
public void eat() {
System.out.println("피자을 먹습니다.");
}
}
위와 같이 Food의 구현체 Chicken, Pizza가 있다고 하자.
@SpringBootTest
public class BeanTest {
@Autowired
Food food;
}
이 때 Food food; 필드에 @Autowired를 사용하여 Bean 객체를 주입려고 시도한다. 하지만 "Could not autowired. There is more than one bean of 'Food' type."이라는 오류가 발생한다.
즉, food 필드에 Bean을 주입해야 하는데 같은 타입의 Bean 객체가 하나 이상이기 때문에 어떤 Bean을 등록해야할 지 알 수 없어서 오류가 발생하는 것이다.
@SpringBootTest
public class BeanTest {
@Autowired
Food pizza;
@Autowired
Food chicken;
}
이렇게 등록된 Bean 이름을 정확하게 명시해주면 문제를 해결할 수 있다. 여기서 알 수 있는 것은 @Autowired는 기본적으로 Bean Type으로 DI를 지원하고, 연결이 되지 않을 경우 Bean Name으로 찾는다는 것을 알 수 있다.
@Component
@Primary
public class Chicken implements Food {
@Override
public void eat() {
System.out.println("치킨을 먹습니다.");
}
}
위의 Chicken 클래스에 @Primary를 추가한다.
@SpringBootTest
public class BeanTest {
@Autowired
Food food;
}
이 경우 Chicken 클래스에 @Primary가 있기 때문에 같은 타입의 Bean이 여러 개 있더라도 @Primary가 붙은 Bean 객체를 우선적으로 주입한다.
@Component
@Qualifier("pizza")
public class Pizza implements Food {
@Override
public void eat() {
System.out.println("피자를 먹습니다.");
}
}
Pizza 클래스에 @Qualifier("pizza")를 추가한다.
@SpringBootTest
public class BeanTest {
@Autowired
@Qualifier("pizza")
Food food;
}
Bean 객체를 주입하고자 하는 필드에도 @Qualifier를 추가해주면 해당 Bean 객체가 주입된다.
같은 타입의 Bean들에 @Primary와 @Qualifier가 동시에 적용되어 있다면 어떤 Bean 주입할까?
이 경우 @Qualifier의 우선순위가 더 높다.
같은 타입의 Bean이 여러 개 있을 때는 범용적으로 사용되는 Bean 객체에는 @Primary를 설정하고 지엽적으로 사용되는 Bean 객체에는 @Qualifier를 사용하는 것이 효율적이다.