인프런/스프링핵심원리(기본)

15) ★★★ 다양한 의존관계 주입방법,옵션처리,생성자 주입 선택,final 키워드,롬복과 최신 키워드, 조회할 빈이 2개이상(문제발생)(@Autowired,@Qualfier,@Primary), 어노테이션 직접 만들기

backend dev 2022. 12. 28.

의존관계 주입은 크게 4가지 방법이 있다.

1. 생성자 주입

2. 수정자 주입(== setter 주입)

3. 필드 주입

4. 일반 메서드 주입

 

생성자 주입

@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. 생성자 호출시점에 딱 1번만 호출되는것이 보장된다. 

 -> 값을 한번 세팅하고 ,그 후로 세팅 못하게 막을 수 있다. 

    그래서 불변,필수 의존관계에 사용된다.   

private final MemberRepository memberRepository;

(private로 필드가 구성되어있고 생성자를 통해서만 수정된다. 그런데 값을 한번 세팅하면 그 후로 세팅 못한다. ==> 불변)

(final로 필드가 구성되어있으므로 무조건 값이 있어야한다 == 생성할때 전달인자를 꼭 받아야한다 ==> 필수)

 

즉 final이므로 불변 및 필수 -> private이므로 외부에서는 접근 불가 

 

하지만 @Autowired 습관화를 들여야겠다.

스프링컨테이너가 해당 클래스를 빈으로 등록하면서 생성자가 하나면 알아서 의존성주입을 해주기때문에 없어도 되긴하다!

하지만 생성자가 2개이상이라면 스프링컨테이너는 어떤 생성자를 가지고 해당 빈을 만들어줄지 모르기 때문에 2개이상이면 생성자 하나를 선택해서 @Autowired를 붙여준다.

 


수정자 주입

'

@Component
public class OrderServiceImpl implements OrderService {

    private MemberRepository memberRepository; //수정자 주입이니까 final을 빼준다. 수정이 가능해야하므로
    private DiscountPolicy discountPolicy;

    @Autowired
    public void setMemberRepository(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }
    @Autowired
    public void setDiscountPolicy(DiscountPolicy discountPolicy) {
        this.discountPolicy = discountPolicy;
    }

이런식으로 사용한다.  당연하지만  setter에는 @Autowired를 붙여야 주입이 된다.

@Autowired 어노테이션을 보고 스프링컨테이너가 주입을 해주는것이기 때문이다.

 

특징 

 - 선택,변경 가능 

@Autowired
public void setMemberRepository(MemberRepository memberRepository) {
    this.memberRepository = memberRepository;
}

만약 다음과 같은 setter의 전달인자인 멤버리포지토리가 빈에 등록이 안되있다?

그러면 주입이 안될수도있다. 그럴때도 사용가능  (선택적 의존관계주입)

주입안되고 쓸수있지만 추가적인 코드가 필요하다.

변경은 외부에서 setter로 인해 강제로 변경 가능하다는점이다 ( 그런일은 거의 없다고한다)

 

스프링은 @Component로 빈을 등록하는 과정과 @Autowired로 의존관계를 주입해주는 과정이 나눠져있다.

 

수정자 주입은 빈을 등록하고 난후에 @Autowired를 보고 의존관계를 주입해주는 2가지 과정으로 나눠 진행되고

 

생성자 주입같은 경우는 빈을 생성하기 위해 동시에 의존관계주입까지 한방에 진행된다.

@Autowired
public void setMemberRepository(MemberRepository memberRepository) {
    this.memberRepository = memberRepository;
}
@Autowired
public void setDiscountPolicy(DiscountPolicy discountPolicy) {
    this.discountPolicy = discountPolicy;
}

@Autowired
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
    this.memberRepository = memberRepository;
    this.discountPolicy = discountPolicy;
}

이렇게 수정자도 있는데 생성자도 @Autowired가 되어있다면?

3개다 호출된다. 물론 생성자 먼저 호출될것이고 그다음은 순서대로 된다.

스프링컨테이너는 싱글톤패턴이기에 객체끼리는 같다.

하지만 저렇게 코드를 짜는것은 같은일을 중복하는것이므로 생성자를 지우던지 setter를 지운다.


필드주입

이름 그대로 필드에 바로 주입하는 방법이다.

@Autowired
private MemberRepository memberRepository;
@Autowired
private DiscountPolicy discountPolicy;

이런식으로


일반메서드 주입

@Autowired
public void init(MemberRepository memberRepository, DiscountPolicy
    discountPolicy) {
    this.memberRepository = memberRepository;
    this.discountPolicy = discountPolicy;
}

이렇게 일반 메소드에 하는것인데 일반적으로 잘 사용하지 않는다.

스프링빈으로 등록된 클래스 내부에서 의존관계 주입을 시도해야한다.

스프링빈의 의존관계를 설정하기 위해 주입을 하는것이니까.

 

스프링 컨테이너에 등록된 빈에서만 의존관계주입(@Autowired)를 하는게 당연하다.

 

 


Context테스트 오류

 

https://www.inflearn.com/questions/1089023/%EC%84%B9%EC%85%98-7-%EC%98%B5%EC%85%98-%EC%B2%98%EB%A6%AC-%EC%A0%84%EC%B2%B4-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%A4%91-coreapplicationtests-%ED%81%B4%EB%9E%98%EC%8A%A4%EC%9D%98-contextloads-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%8B%A4%ED%8C%A8-%EC%A7%88%EB%AC%B8%EC%9E%85%EB%8B%88%EB%8B%A4

 

 

빈 이름은 다르지만 빈 객체의 타입이 같은경우 오류 발생

build tool이 인텔리제이면 발생, gradle로 수정하면  의존관계 주입시 매개변수의 이름을 보고 빈 이름과 같은것을 주입해준다. 

https://www.inflearn.com/questions/1119403


옵션처리

 

옵션처리 테스트

public class AutowiredTest {

    @Test
    void AutowiredOption() {
        ApplicationContext ac = new AnnotationConfigApplicationContext(
            TestBean.class); //TestBean.class안에 @Bean이 없으니 아무것도 빈등록이 안될것이다.

    }

    static class TestBean {

        @Autowired(required = false) // required = false 를 해뒀을때, 자동 주입할 대상이 없으면 (== member가 빈에 등록안되어있으면) 수정자 메서드 자체가 호출 안된다.
        public void setNoBean1(Member member) { // Member는 스프링빈이 등록되어있지않다. member는 그냥 엔티티클래스
            System.out.println("member1 = " + member);
        }

        @Autowired
        public void setNoBean2(@Nullable Member member) { //@Nullable 어노테이션을 붙이면 자동 주입할 대상이 없을시 null값이 입력된다.
            System.out.println("member2 = " + member);
        }

        @Autowired
        public void setNoBean3(Optional<Member> member) {
            System.out.println("member3 = " + member);
        }
    }

}

1. required = false 해둔곳은  넣어줄 빈이 없다면 아예 메소드 실행조차 안된다.

2. @Nullable 붙이면 주입할 대상이 없을시 null을 전달인자로 넣는다.

3. Optional 로 받으면 주입할 대상이 없을시 Optional.empty값을 넣는다.

 


생성자 주입을 선택하라!

과거에는 수정자 주입과 필드 주입을 많이 사용했지만, 

 

최근에는 스프링을 포함한 DI 프레임워크 대부분이생성자 주입을 권장한다. 

 

그 이유는 다음과 같다.

 

불변

 

누락

프레임워크 없이 순수한 자바 코드를 단위 테스트하는 경우가 엄청 많다.

다음과 같이수정자 의존관계인 경우

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;
    }
    //...
}

이 경우에 OrderServiceImpl을 자바코드로 단위테스트하고 싶다면

@Test
void createOrder() {
    OrderServiceImpl orderService = new OrderServiceImpl();
    orderService.createOrder(1L, "itemA", 10000);
}

이런식으로 자바코드를 짜서 테스트해볼것이다 ( OrderserviceImpl 객체를 생성하고 객체메소드를 사용해보고)

 

하지만 createOrder안에서 

    private MemberRepository memberRepository;
    private DiscountPolicy discountPolicy;

이 두가지 필드가 필요한 상황이다.  그래서 "실행하고 나면"  오류가 발생한다. (널 포인트 예외)

 

 

그런데 생성자 주입을 사용한다면

"실행 시키기 전에 컴파일 오류가 발생한다."   ( 오류 발견하기 쉽다.)

 

생성자 주입으로 바뀌었으니까 임의로 구현체 클래스를 생성해서 전달인자로 넣어주고 테스트하면 될것이다.

 

 

 

final 키워드

생성자 주입을 사용하면 필드에 final 키워드를 사용할 수 있다.

final덕분에 생성자를 사용할때 필수값들이 전달안되는것을 컴파일오류로 확인해서 수정할 수 있게끔 된다.

 

final은 선언과 동시에 초기화를 하거나.

생성자로 입력받거나 할때만 사용할 수 있다.

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

이런식으로 생성자 주입할때 필드에 final 이 들어간것을 확인할 수 있다 ( private는 불변을 위해)

필수적인 요소는 생성자 주입을 사용하고, 필수값이 아닌경우는 수정자 주입을 이용해서 옵션으로 부여하면된다.

(생성자 주입, 수정자 주입을 동시에 사용할 수 있기 때문이다.)

 

 


롬복과 최신 키워드

막상 개발을 해보면, 대부분이 다 불변이고, 그래서 다음과 같이 생성자 주입할때 필드에 final 키워드를 사용하게 된다.

 

그런데 생성자도 만들어야하고, 주입 받은 값을 대입하는 코드(생성자 안에 this. ~ )도 만들어야하고

 

필드 주입처럼 좀 편리하게 사용하는 방법은 없을까?

 

plugins {
   id 'java'
   id 'org.springframework.boot' version '3.0.0'
   id 'io.spring.dependency-management' version '1.1.0'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'

//lombok 설정 추가 시작
configurations {
   compileOnly {
      extendsFrom annotationProcessor
   }
}
//lombok 설정 추가 끝

repositories {
   mavenCentral()
}

dependencies {
   implementation 'org.springframework.boot:spring-boot-starter'
// implementation 'org.projectlombok:lombok:1.18.22'  //@Getter와 같은 어노테이션 사용하기 위해 롬복 추가

   //lombok 라이브러리 추가 시작
   compileOnly 'org.projectlombok:lombok'
   annotationProcessor 'org.projectlombok:lombok'
   testCompileOnly 'org.projectlombok:lombok'
   testAnnotationProcessor 'org.projectlombok:lombok'
   //lombok 라이브러리 추가 끝

   testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

tasks.named('test') {
   useJUnitPlatform()
}

build.gradle에 다음과 같은 코드 추가해서 롬복 추가한다.

 

설치가 되어있는지 확인한다.

한가지 더 확인할것

annotation processors 검색해서 enable시켜줘야한다

 

이렇게 설정 다했으면 인텔리제이에서 롬복을 사용가능하다.

 

롬복 몇가지 기능

@Getter //게터
@Setter // 세터
@AllArgsConstructor // 전체 필드값(객체 변수 모두)을 전달받는 생성자 함수 생성
@ToString //클래스명만 입력해도 toString 붙인거처럼 출력한다.
@RequiredArgsConstructor // final이 붙거나, @Nonnull이 붙은 필수값이 붙은 필드를 가진 변수들을 받는 생성자를 만들어준다.

public class LombokTest {
    private String name;
    private int age;
    private final int height;
    private final int weight;

    public static void main(String[] args) {
        LombokTest lombokTest = new LombokTest("asd",12,180,100); //롬복테스트안에는 따로 생성자가 없는데, @AllArgsConstructor덕분에 필드값을 다 전달받는 생성자 생김
        LombokTest lombokTest2 = new LombokTest(180, 100); // @RequiredArgsConstructor 덕분에 필수값 2개를 받는 생성자가 만들어진다.
        lombokTest.setName("수정후이름");
        System.out.println("lombokTest name = " + lombokTest.name);
        System.out.println(lombokTest); // lombokTest.toString() 할 필요없이 클래스명만 입력해도 toString 붙인거처럼 출력한다.
    }
}

이 기능 말고 더있다. ( 추가적으로 검색해서 공부하기)

Ctrl + F12를 해보면 어떤 getter,setter,생성자가 있는지 체크가능.

 

OrderServiceImpl 간단히 만들기

1. @Autowired는 생성자가 하나일때 생략해도 된다.

1.1 생성자가 2개인데 @Autowired를 생략했을때 나오는 오류 모습

public class OrderServiceImpl implements OrderService {

    private final MemberRepository memberRepository;
    private final DiscountPolicy discountPolicy;
    private String test;

//        @Autowired
    public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
        this.memberRepository = memberRepository;
        this.discountPolicy = discountPolicy;
    }

    public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy,
        String test) {
        this.memberRepository = memberRepository;
        this.discountPolicy = discountPolicy;
        this.test = test;
    }

이렇게 필드에 String test를 추가했고, final들을 받는 생성자와, 모든 필드를 받는 생성자가 있다.

(모든 필드를 받는 생성자는 @AllArgsConstructor로 대체 가능하고, 필수 클래스변수를 받는 생성자는 @RequiredArgsConstructor로 대체가능하다.) 

이럴때 

컴파일 오류가 발생&nbsp;Class doesn't contain matching constructor for autowiring

어떤 생성자를 통해 의존관계 주입을 받을건지 @Autowired로 표시해달라는것이다.

 

위의 코드를 롬복어노테이션을 이용한다면

@RequiredArgsConstructor
@AllArgsConstructor
public class OrderServiceImpl implements OrderService {

    private final MemberRepository memberRepository;
    private final DiscountPolicy discountPolicy;
    
    private String test;

    

생성자를 어노테이션으로 대체한다. ( 물론 위의 컴파일 오류(Class doesn't contain matching constructor for autowiring) 는 계속 나타난다. 

 

 

1.2 생성자가 2개인데 둘다 @Autowired를 붙인다면?

    @Autowired
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
    this.memberRepository = memberRepository;
    this.discountPolicy = discountPolicy;
}

@Autowired
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy,
    String test) {
    this.memberRepository = memberRepository;
    this.discountPolicy = discountPolicy;
    this.test = test;
}

Only one constructor can have @Autowired annotation 컴파일 에러발생 (스프링컨테이너가 어떤 생성자를 가지고 빈을 생성및 의존관계 주입할지 모르기때문)

 

1.3 생성자가 1개이면 @Autowired를 안붙여도 된다는 사실을 이용해서 롬복으로 깔끔하게 만들기

@RequiredArgsConstructor
public class OrderServiceImpl implements OrderService {

    private final MemberRepository memberRepository;
    private final DiscountPolicy discountPolicy;
    

@RequiredArgsConstructor 어노테이션 하나만 남긴다.

필수값 변수만 받는 생성자 1개만 생성되므로 해당 생성자에 @Autowired가 알아서 적용될것이다.

 

새로 추가할게 있다면 필드에 추가해주기만 하면되서 아주 편하다.

 

 

해당 클래스에 스프링빈을 주입받아야하는 변수가 있을때 ( 의존관계를 주입받아야 하는 필드가 있을때)

생성자 주입을 사용할거라면 , 거기다가 생성자는 1개라면 @RequiredArgsConstructor를 이용해서 대체한다. 

@RequiredArgsConstructor
public class MemberServiceImpl implements MemberService {

    private final MemberRepository memberRepository;

멤버서비스 임플도 롬복을 이용해서 깔끔하게 수정하였다.

 


조회할 빈이 2개 이상 (문제발생)

 

기본적으로 @Autowired는 스프링빈을 타입(Type)으로 조회한다.

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

OrderServiceImpl이 이렇게 있다고 하였을때

 

@Autowired는 memberRepository, discountPolicy를 주입하기 위해 컨테이너에서 타입으로 조회한다.

예전에 했던  getBean을 이용하여

ac.getBean(MemberRepository.class);
ac.getBean(DiscountPolicy.class);

이런식으로 찾아온다. 

 

이전에 공부했듯이, 타입으로 조회할때 조회된 빈이 2개 이상이면 문제가 발생한다. (예전에는 "직접" 빈 이름까지 적어줘서 문제를 해결했었다. )

 

예를 들어

지금은스프링빈으로  DiscountPolicy를 상속한  RateDiscountPolicy.class만 등록되어있는데 

FixDiscountPolicy.class도 스프링빈을 추가해보겠다.

@Component
public class FixDiscountPolicy implements DiscountPolicy {

@Component를 클래스위에 붙여 컴포넌트스캔이 되게해서, 스프링빈으로 등록하였다.

그렇다면 스프링컨테이너에 DiscountPolicy 타입 빈이 2개가 있다, 빈이름은 서로 다른(클래스명이니까)

 

 

이렇게되면 DiscountPolicy를 주입받는 곳에서는 에러가 발생한다. 어떤 빈을 줘야할지 모르니까.

이러한 오류가 발생한다.

의존관계 자동주입시 , 같은 타입의 빈이 2개 이상인 문제 해결 방법 3가지

조회 대상 빈이 2개 이상일때 해결방법

1. @Autowired 필드 명 매칭

@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 final MemberRepository memberRepository;
    private final DiscountPolicy rateDiscountPolicy;

    @Autowired
    public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy rateDiscountPolicy) {
        this.memberRepository = memberRepository;
        this.rateDiscountPolicy = rateDiscountPolicy;
    }

빈 이름인 rateDiscountPolicy ( 컴포넌트 스캔시 빈 이름은 클래스이름에다가 맨앞글자 소문자)

로 필드 이름과 ,  파라미터 이름을 바꾸어주었더니 테스트를 통과하였다. ( 이름만 변경해야한다! 자료형을 바꾸면 안됨)

[@Autowired가 타입으로 조회했는데 빈이 2개이상이여서, 필드이름과 파라미터 이름을 가지고 추가로 조회해서 주입해준것이다]

2. @Qulifier -> @Qulifier끼리 매칭 -> 빈 이름 매칭

 

@Qualifier("mainDiscountPolicy")
public class RateDiscountPolicy implements DiscountPolicy {
@Component
@Qualifier("fixDiscountPolicy")
public class FixDiscountPolicy implements DiscountPolicy {

이렇게 각각 클래스에 @Qualifer를 붙이며 이름을 정해준다(빈이름이 바뀌는게 아님, @Qualifer를 사용하기 위한 구분이름을 붙여주는것이다.)

@Component
public class OrderServiceImpl implements OrderService {

    private final MemberRepository memberRepository;
    private final DiscountPolicy discountPolicy;

    @Autowired
    public OrderServiceImpl(MemberRepository memberRepository, @Qualifier("mainDiscountPolicy") DiscountPolicy discountPolicy) {
        this.memberRepository = memberRepository;
        this.discountPolicy = discountPolicy;
    }

생성자 파라미터 앞 부분에 @Qualifer("사용할 @Quilfier 구분자이름") 을 붙여주면 해당 @Quilfier 구분자가 붙은 스프링빈이 주입된다.

생성자 주입에도 사용할 수 있고, 수정자 주입, 필드 주입에도 사용할 수 있고,

직접 빈 등록(@Bean) 할때도 사용할 수 있다.

스프링 컨테이너는 @Qualfier 구분자로 검색했는데 없으면 해당 구분자를 빈이름으로 생각하고 다시 조회해본다.

 

3. @Primary 사용

@Component
@Primary
public class RateDiscountPolicy implements DiscountPolicy {
@Component
public class FixDiscountPolicy implements DiscountPolicy {

스프링컨테이너에는 위에 2개클래스가 다 빈으로 등록되어있지만 DiscountPolicy를 주입할때

RateDiscountPolicy를 사용하고 싶다면 그 클래스에만 @Primary를 붙여주면 된다.

활용

스프링은 상세하게 동작하거나 , 좁은 범위에서 동작하는게 우선순위가 높다.

주로 사용하는 데이터베이스 스프링빈은 @Primary를 사용해서 주입 또는 조회가 필요한곳에서 @Qulifer없이 편하게 주입 또는 조회를 할 수 있고

 

가끔 사용하는 서브 데이터베이스는 @Qualifer를 붙여 구분자를 등록해놓고,

서브 데이터베이스가 필요한곳에 구분자를 이용하여 조회 또는 주입을 해준다.   (메인 데이터베이스에 @Primary 붙인걸 지울 필요없이 @Qualfier가 더 우선순위가 높기때문에 잘 동작한다) 

 

이렇게 둘다 사용할 수 있는것은 @Qualifer가 우선순위가 더 높기 때문이다.

 


어노테이션 직접 만들기

"문자열"과 같은 문자는 컴파일타임에 체크가 안된다.

@Qualifer 구분자가 문자기 때문에  @Qualifer를 지정할때 오타를 내거나, @Qualifer를 사용할때 오타를 내도 컴파일타임에는 어떤 오류가 발생하지않아서 실행해봐야 문제가 발생한것을 알 수 있다. ( 컴파일 타임에 오류가 발생하는게 베스트)

 

그래서 어노테이션을 만들어서 깔끔하게 운영하는 방법이 있다.

 

1. 자바파일 생성에서 어노테이션을 체크해서 어노테이션생성 파일을 만든다.

public @interface MainDiscountPolicy {

}

2. ctrl + N 으로 @Qualifer를 검색해서 들어간다.

3. Qualifer 어노테이션 정의 파일 위쪽에 모든 어노테이션을 복사해온다.

4. @Qualifer 어노테이션을 붙여준다. (내가 만든 어노테이션이 @Qualifer 기능도 해야하니까)

@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE,
    ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
//윗부분은 @Qualifer 정의하는 문서에서 가져온것
@Qualifier("mainDiscountPolicy") //추가해야하는 부분.
public @interface MainDiscountPolicy {

}

이런식으로 만들어준다.

이제 @Qualifer 말고만든 어노테이션을 붙여준다.

@Component
@MainDiscountPolicy
public class RateDiscountPolicy implements DiscountPolicy {

이렇게 직접만든 어노테이션을 붙여서 설정해주고

 

사용할때도 @Quliafer("구분자명") 이런식으로 적을 필요없이 만든 어노테이션만 사용해주면 된다.

@Component
public class OrderServiceImpl implements OrderService {

    private final MemberRepository memberRepository;
    private final DiscountPolicy discountPolicy;

    @Autowired
    public OrderServiceImpl(MemberRepository memberRepository, @MainDiscountPolicy DiscountPolicy discountPolicy) {
        this.memberRepository = memberRepository;
        this.discountPolicy = discountPolicy;
    }

댓글