지금까지는 @Configuration이 붙은 자바 설정 파일에 @Bean으로 일일이 스프링 빈을 등록하고, 생성자를 통해서 일일이 의존성을 주입해주었다.
@Configuration
public class AppConfig {
@Bean
public MemberService() {
return new MemberServiceImpl(memberRepository());
}
@Bean
public OrderService orderService() {
return new OrderServiceImpl(memberRepository(), discountPolicy());
}
@Bean
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
@Bean
public DsicountPolicy discountPolicy() {
return new RateDiscountPolicy();
}
}
하지만 등록해야할 스프링 빈이 수백개가 된다면 일일이 등록하기도 힘들고, 설정 정보도 커지고, 누락하는 문제도 발생한다.
그래서 스프링은 설정 정보가 없어도 자동으로 스프링 빈을 등록하는 컴포넌트 스캔
이라는 기능을 제공한다.
또 의존관계를 자동으로 주입하는 @Autowired
라는 기능도 제공한다.
컴포넌트 스캔을 사용하려면 @Configuration이 붙은 설정정보에 @ComponentScan
을 붙여주면 된다.
(보통 @SpringBootApplication
설정을 통해 이미 @ComponentScan
설정되어있음)
@Configuration
@ComponentScan
public class AutoAppConfig {
// 기존의 AppConfig와 다르게 @Bean으로 등록한 클래스가 하나도 없다.
}
컴포넌트 스캔은 이름 그대로 @Component이 붙은 클래스를 스캔해서 스프링 빈으로 등록
한다.
이제 등록하고자 하는 클래스에 @Component
를 붙임으로서 스프링 컨테이너에 스프링 빈으로 등록할 수 있다.
@Component
public class MemoryMemberRepository implements MemberRepository {}
@Component
public class RateDiscountPolicy implements DiscountPolicy {}
@Component
public class MemberServiceImpl implements MemberService {
private final MemberRepository memberRepository;
@Autowired
public MemberServiceImpl(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
}
@Component
public class OrderServiceImpl implements OrderService {
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
@Autowired
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
}
이전 AppConfig의 경우 @Bean으로 직접 설정 정보를 작성하고, 생성자 주입을 통해 의존관계도 직접 명시했다.
하지만 이제는 더이상 이런 설정 정보 자체가 없기 때문에, 의존관계 주입도 각각의 @Component 클래스 안에서 해결해야한다.
=> @Autowired
는 의존관계를 자동으로 주입해준다.
(@Autowired 를 사용하면 생성자에서 여러 의존관계도 한번에 주입받을 수 있다)
@ComponentScan
@Autowired 의존관계 자동주입
컴포넌트 스캔은 @Component 뿐만 아니라 다음과 내용도 추가로 대상에 포함한다.
@Component
: 컴포넌트 스캔에서 사용
@Controlller
: 스프링 MVC 컨트롤러에서 사용
@Service
: 스프링 비즈니스 로직에서 사용
@Repository
: 스프링 데이터 접근 계층에서 사용
@Configuration
: 스프링 설정 정보에서 사용
(해당 클래스의 소스 코드를 보면 @Component 를 포함하고 있는 것을 알 수 있다.)
@Component
public class OrderServiceImpl implements OrderService {
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
@Autowired
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
}
@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;
}
}
@Component
public class OrderServiceImpl implements OrderService {
@Autowired
private MemberRepository memberRepository;
@Autowired
private DiscountPolicy discountPolicy;
}
순수한 자바 테스트 코드에는 당연히 @Autowired가 동작하지 않는다. @SpringBootTest 처럼 스프링 컨테이너를 테스트에 통합한 경우에만 가능하다.
어쩌면 당연한 이야기이지만 의존관계 자동 주입은 스프링 컨테이너가 관리하는 스프링 빈이어야 동작한다. 스프링 빈이 아닌 Member 같은 클래스에서 @Autowired 코드를 적용해도 아무 기능도 동작하지 않는다.
생성자 주입을 써라. 그 이유는 다음과 같다.
항상 생성자 주입을 선택해라!
그리고 가끔 옵션이 필요하면(필수 값이 아닌 경우) 수정자 주입을 선택해라.
필드 주입은 사용하지 않는게 좋다.
이 글은 인프런 김영한님의 스프링 핵심 원리 - 기본편의 [섹션6. 컴포넌트 스캔]과 [섹션7. 의존관계 자동 주입]을 기반으로 정리한 글입니다.