- AppConfig처럼 @Bean을 사용하여 직접 코드를 작성하고 의존 관계도 작성해서 스프링 빈으로 등록할 수 있었음

- 이렇게 스프링 빈을 작성하면, 등록할 빈의 개수가 많아지면 하나하나 작성해줘야 하고, 관리도 어려울 수 있음

=> 스프링에서는 Java설정 파일이나 XML 없이도 스프링 빈을 등록할 것들을 찾아서 등록하는 컴포넌트 스캔이라는 기능을 제공하고, @AutoWired를 사용하여 의존관계 주입도 자동으로 해줌

 

AppConfig

@Configuration
public class AppConfig {

    @Bean
    public MemberService memberServiceImpl() {
        return new MemberServiceImpl(memberRepository());
    }

    @Bean
    public OrderService orderServiceImpl() {
        return new OrderServiceImpl(new MemoryMemberRepository(), new RateDiscountPolicy());
    }

    @Bean
    public MemberRepository memberRepository() {
        return new MemoryMemberRepository();
    }

    @Bean
    public DiscountPolicy discountPolicy(){
        return new RateDiscountPolicy();
        //return new FixDiscountPolicy()
    }

}

AutoAppConfig

- @ComponentScan사용

- @Bean사용 X

@Configuration
@ComponentScan(
        excludeFilters = @ComponentScan.Filter(type= FilterType.ANNOTATION, classes = Configuration.class)
)
public class AutoAppConfig {

}

 

@ComponentScan

- @ComponentScan을 붙여서 컴포넌트 스캔을 사용하면 @Componet가 붙은 클래스를 스캔하여 스프링 빈으로 등록

- 스프링 빈 등록시 빈 이름은 첫글자가 소문자인 클래스명을 사용 (@Bean은 메서드 명을 빈 이름으로)

@Component
public class MemberServiceImpl implements MemberService{
	......
}

@Component
public class OrderServiceImpl implements OrderService{
	......
}

@Component
public class RateDiscountPolicy implements DiscountPolicy{
	......
}

@Component
public class MemoryMemberRepository implements MemberRepository{
	......
}

 

- 생성자에 @AutoWired를 붙이면 자동으로 의존관계를 주입해줌

- 생성자 파라미터의 Type으로 스프링 컨테이너에서 스프링 빈을 찾아서 주입함

 

 

@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;
    }

   ......
}

 

1) 설정클래스에 @ComponentScan이 붙음

2) @Component가 붙은 클래스들을 클래스 명으로 스프링 반 등록함

3) @AutoWired + 생성자를 사용하여 의존관계를 자동으로 주입함 (생성자의 Type으로 빈 찾음)

=> @ComponentScan, @Component, @AutoWired는 항상 같이 사용됨

 

컴포넌트 스캔 범위

- 어플리케이션 구동 시 모든 클래스들을 스캔하려면 오래 걸리므로, 필요한 위치를 탐색하도록 지정

- @ComponetScan에 basepackage를 지정하지 않으면 @ComponetScan가 붙은 설정 클래스가 존재하는 패키지가 스캔 시작 위치 => 따로 basePackage를 지정하지 않고 설정 클래스를 프로젝트의 최상단에 두면 됨 => 하위 패키지까지 모두 스캔함

 

@Component

@Component

@Controller : 스프링 MVC 컨트롤러

@Service : 비즈니스 로직

@Repository : 데이터 접근 로직, 데이터 접근 예외를 스프링 예외로 변환해줌

@Configuration : 스프링 설정 정보

=> 컴포넌트 스캔 대상임 @Controller, @Service, @Repository, @Configuration은 내부에 @Component를 포함함

 

 

스프링 빈 중복 등록, 충돌

- @Bean : 수동등록, 메서드 명을 빈 이름으로 사용

- @Component : 자동 등록, 클래스 명을 빈 이름으로 사용

 

1) 자동등록으로 동일한 이름으로 빈을 등록하려 하는 경우 에러 발생 : ConflictingBeanDefinitionException

 

1-1) 동일한 이름은 아니지만 타입이 같은 빈을 두 개 등록하는 경우(FixDiscountPolicy, RateDiscountPolicy에 둘 다 @Component작성)

=> @AutoWired 의존관계 주입시 에러 발생 가능 (생성자 파라미터로 찾은 스프링 빈이 2개 이상...)

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'orderServiceImpl' defined in file [C:\Users\남궁준\Desktop\study\demo\out\production\classes\com\example\order\OrderServiceImpl.class]: Unsatisfied dependency expressed through constructor parameter 1; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.example.discount.DiscountPolicy' available: expected single matching bean but found 2: fixDiscountPolicy,rateDiscountPolicy
@Component
public class FixDiscountPolicy implements DiscountPolicy{
	......
}

@Component
public class RateDiscountPolicy implements DiscountPolicy{
	......
}
@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;
    }

   ......
}

 

2) 수동등록, 자동 등록으로 이름이 같은 빈을 등록하려 하는 경우 : 수동이 우선권(수동 빈이 자동 빈을 오버라이딩해버림) => 사용 X => 스프링 부트에서는 오류를 발생시킴(오버라이딩 막음)

@Component
public class MemoryMemberRepository implements MemberRepository{
	......
}
@Configuration
@ComponentScan(
        excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Configuration.class) // AppConfig.class 제외
)
public class AutoAppConfig {
    @Bean(name="memoryMemberRepository")
    public MemberRepository memberRepository() {
        return new MemoryMemberRepository();
    }
}