스프링 빈을 등록할 때 자바코드의 @Bean 이나 XML의 등을 통해서 설정 정보에 직접 등록했다. -> 등록할 빈이 많아지면 관리가 힘들다
설정 정보 없이 자동으로 스프링 빈을 등록 => 컴포넌트 스캔 기능
컴포넌트 스캔 = @Component
어노테이션이 붙은 클래스를 스캔해서 스프링 빈으로 등록
@ComponentScan
을 설정 정보에 붙여준다.@Component
가 붙은 클래스를 빈으로 등록할 때, 스프링 빈의 기본 이름은 클래스명을 사용하되 맨 앞글자만 소문자를 사용한다.@Component("memberService2")
@ComponentScan(
basePackages = "com.example"
)
basePackages
: 탐색할 패키지의 시작위치를 지정한다.basePackages
= {"com.example.service", "com.example.controller"} => 여러개의 시작 위치 지정 가능@ComponentScan
이 지정된 클래스의 package가 시작위치가 된다.통상적으로
basePackages
를 지정하지 않도록,@ComponentScan
이 붙은 설정정보 클래스를 프로젝트의 최상단 위치에 둔다.
그래서, 스프링 부트@SpringBootApplication
클래스가 프로젝트의 최상단에 위치하고 있다.@SpringBootApplication
어노테이션의 상세 코드를 보면@ComponentScan
이 있다.
@Component
: 컴포넌트 스캔에서 사용@Controller
: 스프링 MVC 컨트롤러에서 사용@Service
: 스프링 비즈니스 로직에서 사용@Repository
: 스프링 데이터 접근 계층에서 사용@Configuration
: 스프링 설정 정보에서 사용실은
@Component
어노테이션을 제외한 어노테이션의 상세 코드를 보면 기본적으로@Component
어노테이션이 선언되어 있다.
@Controller
: 스프링 MVC 컨트롤러로 인식@Repository
: 스프링 데이터 접근 계층으로 인식하고, 데이터 계층의 예외를 스프링 예외로 변환해준다.@Configuration
: 스프링 설정 정보로 인식, 스프링 빈이 싱글톤을 유지하도록 추가 처리@Service
: 비즈니스 계층을 인식하는데 도움컴포넌트 스캔에서 같은 빈 이름을 등록하는 경우
1. 자동 빈 등록 vs 자동 빈 등록
2. 수동 빈 등록 vs 자동 빈 등록
1. 자동 빈 등록 vs 자동 빈 등록
ConglictingBeanDefinitionException
예외 발생Overriding bean definition for bean 'duplicateBean' with a different
definition: replacing
이런 경우는 개발자가 의도적으로 설정하기 보단, 설정 오류로 나온 결과로 잡기 어려운 버그가 만들어 질 수 있다.
그래서 최근 스프링 부트에서는 수동 빈 등록과 자동 빈 등록이 충돌나면 오류가 발생하도록 기본 값을 바꾸었다.Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true
의존관계 자동 주입을 해주는
@Autowired
@Autowired
는 타입으로 검색한다.
1. 생성자 주입
@Component
public class OrderServiceImpl implements OrderService {
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
@Autowired
public OrderServiceImple(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
}
+ 빈의 생성자가 딱 1개만 있으면 @Autowired 생략 해도 자동주입 가능
2. 수정자 주입(setter 주입)
@Component
public class OrderServiceImpl implements OrderService {
private MemberRepository memberRepository;
private DiscountPolicy discountPolicy;
@Autowired
public void setMemberRepository(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
@Autowired
public void setDiscountPolicy(DiscountPolicy discountPolicy) {
this.discountPolicy = discountPolicy;
}
}
3. 필드 주입
@Configuration
같은 곳에서만 특별한 용도로 사용@Component
public class OrderServiceImpl implements OrderService {
@Autowired
private MemberRepository memberRepository;
@Autowired
private DiscountPolicy discountPolicy;
}
순수한 자바 테스트 코드에는 당연히
@Autowired
가 동작하지 않는다.@SpringBootTest
처럼
스프링 컨테이너를 테스트에 통합한 경우에만 가능하다.
4. 일반 메서드 주입
@Component
public class OrderServiceImpl implements OrderService {
private MemberRepository memberRepository;
private DiscountPolicy discountPolicy;
@Autowired
public void init(MemberRepository memberRepository, DiscountPolicy
discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
}
1. 불변
2. 누락
final
키워드를 사용할 수 있다. 그래서 생성자에서 혹시라도 값이 설정되지 않는 오류를 컴파일 시점에 막아준다.수정자 주입을 포함한 나머지 주입 방식은 모두 생성자 이후에 호출되므로, 필드에 final 키워드를 사용할 수 없다. 오직 생성자 주입 방식만 final 키워드를 사용할 수 있다.
[정리]
- 생성자 주입 방식을 선택하는 이유는 여러가지가 있지만, 프레임워크에 의존하지 않고, 순수한 자바 언어의 특징을 잘 살리는 방법이기도 하다.
- 기본으로 생성자 주입을 사용하고, 필수 값이 아닌 경우에는 수정자 주입 방식을 옵션으로 부여하면 된다. 생성자 주입과 수정자 주입을 동시에 사용할 수 있다.
- 항상 생성자 주입을 선택해라! 그리고 가끔 옵션이 필요하면 수정자 주입을 선택해라. 필드 주입은 사용하지 않는게 좋다.