
이전에 스프링 빈을 등록할 때는 자바 코드의 @Bean이나 XML의 등을 통해서
설정 정보에 직접 등록할 스프링 빈을 나열했다.아래 예제에서는 몇개가 안되었지만,
이렇게 등록해야 할 스프링 빈이 수십, 수백개가 되면 일일이 등록하기도 귀찮고,
설정 정보도 커지고, 누락하는 문제도 발생한다.package hello.core; import hello.core.discount.DiscountPolicy; import hello.core.discount.FixDiscountPolicy; import hello.core.member.MemberRepository; import hello.core.member.MemberServiceImpl; import hello.core.member.MemberService; import hello.core.member.MemoryMemberRepository; import hello.core.order.OrderService; import hello.core.order.OrderServiceImpl; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class AppConfig { @Bean public MemberService memberService(){ return new MemberServiceImpl(memberRepository()); } @Bean private static MemberRepository memberRepository() { return new MemoryMemberRepository(); } @Bean public OrderService orderService(){ return new OrderServiceImpl(memberRepository(), discountPolicy()); } @Bean public DiscountPolicy discountPolicy(){ return new FixDiscountPolicy(); } }
그래서 스프링은 설정 정보가 없어도 자동으로 스프링 빈을 등록하는 컴포넌트 스캔이라는 기능을 제공한다.
또 의존관계도 자동으로 주입하는@Autowired라는 기능도 제공한다.컴포넌트 스캔을 사용하려면 먼저
@ComponentScan을 설정 정보에 붙여주면 된다.
기존의AppConfig와는 다르게@Bean으로 등록한 클래스가 하나도 없다
package hello.core; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.FilterType; import static org.springframework.context.annotation.ComponentScan.*; @Configuration @ComponentScan( // @ComponentScan 을 사용하면 @Component이 붙은 다른 AppConfig 설정 파일도 같이 스캔한다. 따라서 다른 파일들은 스캔 대상에서 지워줘야한다. // @Configuration 내부에 @Component 가 붙어 있다. // 기존에 존재하던 설정 파일인 AppConfig.java 파일을 스캔대상에서 제외하기 위한 설정 excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Configuration.class) ) @ComponentScan( excludeFilters = @Filter(type = FilterType.ANNOTATION, classes = Configuration.class)) // AppConfig 와 달리 @Bean 이 없다. public class AutoAppConfig {}
이제 각 클래스가 컴포넌트 스캔의 대상이 되도록
@Component애노테이션을 붙여주자.
package hello.core.member;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
@Component // 컴포넌트 스캔 대상 설정 추가
public class MemoryMemberRepository implements MemberRepository {
private static Map<Long, Member> store = new HashMap<>();
@Override
public void save(Member member) {
store.put(member.getId(), member);
}
@Override
public Member findById(Long memberId) {
return store.get(memberId);
}
}
package hello.core.discount;
import hello.core.member.Grade;
import hello.core.member.Member;
import org.springframework.stereotype.Component;
@Component // 컴포넌트 스캔 대상 설정 추가
public class RateDiscountPolicy implements DiscountPolicy{
private int discountPercent = 10;
@Override
public int discount(Member member, int price) {
if (member.getGrade() == Grade.VIP)
return price * discountPercent / 100;
else{
return 0;
}
}
}
package hello.core.member;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component // 컴포넌트 스캔 대상 설정 추가
public class MemberServiceImpl implements MemberService{
private final MemberRepository memberRepository;
@Autowired // 의존 관계 자동 주입 설정 추가
public MemberServiceImpl(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
@Override
public void join(Member member) {
memberRepository.save(member);
}
@Override
public Member findMember(Long memberId) {
return memberRepository.findById(memberId);
}
}
package hello.core.order;
import hello.core.discount.DiscountPolicy;
import hello.core.member.Member;
import hello.core.member.MemberRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.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;
}
@Override
public Order createOrder(Long memberId, String itemName, int itemPrice) {
Member member = memberRepository.findById(memberId);
int discountPrice = discountPolicy.discount(member, itemPrice);
return new Order(memberId, itemName, itemPrice, discountPrice);
}
}
@ComponentScan은@Component가 붙은 모든 클래스를 스프링 빈으로 등록한다.
이때 스프링 빈의 기본 이름은 클래스명을 사용하되 맨 앞글자만 소문자를 사용한다빈 이름 기본 전략: MemberServiceImpl 클래스 memberServiceImpl
빈 이름 직접 지정: 만약 스프링 빈의 이름을 직접 지정하고 싶으면
@Component("memberService2")이런식으로 이름을 부여하면 된다.
생성자에@Autowired를 지정하면,
스프링 컨테이너가 자동으로 해당 스프링 빈을 찾아서 주입한다.이때 기본 조회 전략은 타입이 같은 빈을 찾아서 주입한다.
getBean(MemberRepository.class)와 동일하다고 이해하면 된다.