컴포넌트 스캔과 의존관계 자동 주입 

 

지금까지 스프링 빈을 등록할때는 자바코드의 @Bean 이나 XML의 <bean> 등을 통해서 설정 정보에 직접

등록할 스프링 빈을 나열했다.

 

예제에서는 몇개가 안되었지만, 이렇게 등록할 스프링 빈이 많아지면 귀찮고, 설정 정보도 커지고, 누락할 

가능성도 발생하게 된다. 

 

그래서 스프링은 설정 정보가 없어도 자동으로 스프링 빈을 등록하는 컴포넌트 스캔이라는 기능을 제공한다.

 

의존과계 주입도 자동으로 해주는  @Autowired라는 기능을 제공한다.

package hello.core;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;

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

}

 기존의 AppConfig 를 살려두고 AutoAppConfig 클래스를 생성하였다.

 

컴포넌트 스캔을 사용하면 @Configuration이 붙은 설정 정보도 자동으로 등록되기 때문에 AppConfig도 함께

등록되게 되므로 excludeFilters를 이용하여 컴포넌트 스캔 대상에서 제외하였다.

 

컴포넌트 스캔을 사용하려면 먼저@ComponentScan을 설정 정보에 붙여주면 된다.

 

기존에 AppConfig와는 다르게 @Bean으로 등록한 클래스가 하나도없다.

 

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

MemoryMemberRepository에 @Component를 붙여준다.

 

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

    //Test
    public MemberRepository getMemberRepository() {
        return memberRepository;
    }
}

모든 구현클래스에 @Component 를 붙여줬다.

 

기존 AppConfig에서는 직접 @Bean으로 설정 정보를 작성, 의존관계 명시를 했다.

 

하지만 MemberServiceImpl 같은 경우에는 의존관계를 설정해주어야 하는데

이때 @Autowired로 생성자에 붙여주면 자동으로 의존관계를 주입해준다.

package hello.core.order;

import hello.core.discount.DiscountPolicy;
import hello.core.member.Member;
import hello.core.member.MemberRepository;
import hello.core.member.MemoryMemberRepository;
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);
    }

    //Test
    public MemberRepository getMemberRepository() {
        return memberRepository;
    }
}

OrderServiceImpl도 마찬가지로 @Component , @Autowired로 의존관계를 주입해준다.

 

package hello.core.scan;

import hello.core.AutoAppConfig;
import hello.core.member.MemberService;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import static org.assertj.core.api.Assertions.*;

public class AutoAppConfigTest {

    @Test
    void basicScan(){
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AutoAppConfig.class);

        MemberService memberService= ac.getBean(MemberService.class);
        assertThat(memberService).isInstanceOf(MemberService.class);
    }
}

그렇다면 테스트 코드를 작성 후 확인해보았다.

 

AnnotationConfigApplicationContext를 사용하는 것은 기존과 동일하다.

설정 정보를 AutoAppConfig 클래스를 넘겨준다.

실행해보자!

콘솔로그를 보면 ClassPathBeanDefinitionScanner라고 뜨는데 후보를 식별했다고 뜨는데 내가  @Component

애너테이션을 준 클래스들이 찍혀있다. @Autowired의 정보들도 로그에 뜨게된다.

 


 

1. @ComponentScan

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B8%B0%EB%B3%B8%ED%8E%B8/lecture/55368?speed=0.75&tab=note

@ComponentScan 은 @Component가 붙은 모든 클래스를 스프링 빈으로 등록한다.

 

그런데 빈 이름을 주의하자.

이때 스프링 빈의 기본 이름은 클래스명을 사용하되 맨 앞글자만 소문자로 사용한다.

빈 이름 기본 전략 : MemberServiceImpl -> memberServiceImpl

빈 이름 직접 지정 : @Component("memberService")

 

 

2. @Autowired 의존관계 자동 주입

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B8%B0%EB%B3%B8%ED%8E%B8/lecture/55368?speed=0.75&tab=note&mm=null

생성자에 @Autowired를 지정하면 스프링 컨테이너가 자동으로 해당 스프링 빈을 찾아서 주입한다.

찾을때는 타입으로 조회를 한다. 같은 타입이 있다면 주입을 해준다. 같은 타입이 여러개 있다면 충돌이 일어난다.

 

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B8%B0%EB%B3%B8%ED%8E%B8/lecture/55368?speed=0.75&tab=note&mm=close

OrderServiceImpl같은 경우에는 생성자에 파라미터가 많다.

이런 경우에도 다 찾아서 자동으로 주입해준다.(타입으로 탐색)

+ Recent posts