• 카테고리

    질문 & 답변
  • 세부 분야

    백엔드

  • 해결 여부

    미해결

@Component 사용시 implement 된 구체화 클래스가 두 개일 경우

20.10.24 19:14 작성 조회수 281

9

안녕하세요 강사님 오랜만에 질문드립니다.

DiscountPolicy 인터페이스를

FixDiscountPolicy

RateDiscountPolicy  두 곳 모두에서 implement하고

@Component를 적용하면

ComponentScan에서 Autowired를 사용하면

OrderserviceImpl 에서  당연히 타입으로 두개의 구체화 클래스가 있으니까 오류나잖아요.

그럼 저희가 처음에 기획 했던건 fix랑 rate랑 유연하게 코드를 변경 할 수 있느냐에서 출발했는데, 지금 Autowired 사용한 강의 구조에서는 fix와 rate의 변경시에 @Component 를 썼다 지웠다해서 적용 해줘야된다는건데,,, 다른 좋은 방법이 있나요? 뭔가 코드의 변경을 최소화 하기위해서 였는데 AppConfigAuto파일에서의 변경이아니고 구체화 클래스 내의 어노테이션을 삽입/삭제해서는 조금 이상한거같아서요...

내용이 넘 길고 주저리썼네요 ㅠ 의견 부탁드립니다.

답변 3

·

답변을 작성해보세요.

11

안녕하세요. ljh881202님 좋은 질문입니다.

애노테이션 기반으로 이렇게 설정을 하게 되면, 결국 코드에 붙어있는 애노테이션 자체를 변경해야 하는 단점이 있습니다.

이 부분을 설명하기 전에 먼저 과거 이야기를 해드리겠습니다.

과거에는 설정 정보를 자바 코드로 설정하는 대신에 xml을 사용했는데요. 이때는 xml 파일을 변경하면 되기 때문에, 정말 자바 코드도 컴파일 하지 않아도 설정 정보를 변경할 수 있었습니다.

그런데 설정 정보를 편리하게 자바 코드로 설정하면서 컴파일은 한번 해야하는 단점이 발생합니다. 트레이드 오프인 것이지요.

애노테이션의 경우도 비슷합니다. 애노테이션 기반의 컴포넌트 스캔은 매우 편리하제만, 변경이 필요할 때 컴포넌트 애노테이션을 붙이면서 변경해야 하기 때문에, 실용성은 늘어났지만, 대상 코드의 애노테이션을 손대야 하는 단점이 있습니다.

이런 구분에서 또 트레이드 오프가 발생합니다.

그런데 사실 중요한 포인트가 있습니다. 이렇게 컴포넌트 스캔을 사용하고, 대상을 변경해도 클라이언트 코드에는 아무런 변경 없이 DI가 가능합니다. 따라서 이 부분의 장점은 그대로 유지할 수 있습니다.

제가 권장하는 방법은 실용적인 관점에서 컴포넌트 스캔을 주로 사용하고, 정말 유연하게 변경해야 하는 경우에는 @Bean을 사용해서 수동으로 설정 정보를 통해서 관리하는 것을 권장합니다. 추가로 강의를 조금 더 진행하면 아시겠지만, 이렇게 같은 빈이 여러개 있을 때 문제를 해결하는 실용적인 절충안 들이 많이 있습니다^^

도움이 되셨길 바래요.

스프링님의 프로필

스프링

2022.03.17

안녕하세요 영한님. 위의 질문에서 제일 이상적인 방법이 매번 @Component를 붙였다 떼엇다 하는게 아닌. 빌드 프로퍼티를 통해서RateDiscountPolicy 와 FixDiscountPolicy 를 @Configuration 내부에서 정의 해주는거일 것 같은데 맞을까요?? 물론 이렇게하면 원래 클래스에서 @Component 를 다 떼줘야할거같긴합니다. 

요는, 자동등록 말고 수동등록에서 property 에 따라 동적으로 주입하면 되지않을까? 입니다.

 

@Configuration
public class AppConfig {
       @Bean("discountPolicy")
       public DiscountPolicy discountPolicy() {
               DiscountEnum getDisCountPolicyType = {{ get property }}
               switch(getDisCountPolicyType) {
                       case DiscountEnum.RATE: return new RateDiscountPolicy();
                       case DiscountEnum.FIX: return new FixDiscountPolicy();
                       default: throw Error(' non support dicount policy' );
               }
       }
}

질문의 핵심이 자동등록 상황에 대한 것이어서, 말씀하신 것 처럼 수동등록의 경우 다양한 처리 방안이 가능합니다.

감사합니다.

4

 상세한 답변 감사합니다^^

강의 내용이 많은 도움이 됩니다!!

0

강택연님의 프로필

강택연

2021.03.30

안녕하세요 선생님 질문이 있어 문의드립니다.

해당 영상의 7:16쯤 화면에

call AppConfig.memberRepository
call AppConfig.memberService
call AppConfig.orderService

이 세라인이 찍히는 것을 볼 수 있습니다.

이 로그(?)는 AppConfig에서 실행시킨 내용입니다.

여기서 궁금한 점은,


@Configuration
@ComponentScan(
// basePackages = "hello.core.member",
excludeFilters = @ComponentScan.Filter (type = FilterType.ANNOTATION, classes = Configuration.class)
)
public class AutoAppConfig {

// @Bean(name = "memoryMemberRepository")
// MemberRepository memberRepository(){
// return new MemoryMemberRepository();
// }

}

위와 같이 exclude옵션으로 AppConfig.class에 기재된 @Bean들을 Bean객체로 만들지 않는것으로 인지하고 있었습니다.

제 생각에는 실행되지 않아야 할거 같은데 아래 코드와 같이  @Bean생성 함수들이 실행되는거 같아 문의 드립니다.

@Configuration
public class AppConfig {

@Bean
public MemberService memberService() {
System.out.println("call AppConfig.memberService");
return new MemberServiceImpl(memberRepository());
}

@Bean
public MemoryMemberRepository memberRepository() {
System.out.println("call AppConfig.memberRepository");
return new MemoryMemberRepository();
}

@Bean
public OrderService orderService(){
System.out.println("call AppConfig.orderService");
return new OrderServiceImpl(memberRepository(), discountPolicy());
}

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

}

마지막으로, 항상 좋은 강의 진행해 주셔서 정말 감사합니다.

요즘 정말 선생님 덕분에 개발에 흥미를 느끼고 삶이 즐거워 지고 있습니다.

정말 감사합니다.