-
@ComponetScan
-
AutoAppConfig
-
탐색 위치와 기본 스캔 대상
-
중복 등록과 충돌
-
자동 빈 등록 vs 자동 빈 등록
-
수동 빈 등록 vs 자동 빈 등록
-
자동 주입
-
생성자 주입 (권장)
-
수정자 주입
-
필드 주입
-
일반 메서드 주입
-
수동 등록 빈에 자동 등록 빈의 의존관계를 주입
-
스프링 빈을 주입하지 않고 동작하도록 작성하기
-
의존관계 주입시 동일한 타입의 빈이 2개 이상이라면
-
해결 1. @Autowired 필드명
-
해결 2. 빈 등록시 @Qualifier 붙이기
-
해결 3. @Primary
-
조회한 빈이 모두 필요할 때, List, Map 사용
@ComponetScan
AppConfig에서 `@Bean`으로 모두 등록해주었다. 빈으로 등록해야한게 수백만개가 된다면 일일이 등록하기 너무 귀찮고, 설정 정보도 커지게 된다.
`@ComponetScan` 을 설정 정보에 붙여주면 된다.
AutoAppConfig
파일 구조는 다음과 같다

프로젝트 최상단에 AutoappConfig.java가 있으므로 프로젝트에서 컴포넌트로 등록해준 것을 다 빈으로 등록한다.
@Configuration
//@ComponentScan: 스프링 빈을 다 끌어다가 자동으로 스프링 빈으로 끌어올리는..
@ComponentScan(
//스프링 빈 등록에서 제외할 것
excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Configuration.class)
)
public class AutoAppConfig {
}
컴포넌트 스캔은 `@Componet` 애노테이션이 붙은 클래스를 스캔해서 스프링 빈으로 등록한다.
@Component
public class MemberServiceImpl implements MemberService {
private final MemberRepository memberRepository;
@Autowired
public MemberServiceImpl(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
}
`@Componet`: MemberServiceIml 이름으로 빈으로 등록
`@Autowired`: MemberRepository 타입으로 빈을 찾아서 의존관계 주입
탐색 위치와 기본 스캔 대상
탐색할 패키지의 시작 위치 지정
모든 자바 클래스를 다 컴포넌트 스캔하면 시간이 오래걸린다. 그래서 꼭 필요한 위치부터 탐핵하도록 시작 위치를 지정할 수 있다.
@ComponentScan(
basePackages = "hello.core",
}
basePackages: 탐색할 패키지의 시작 위치를 지정, 이 패키지를 포함해서 하위 패키지를 모두 탐색한다.
지정하지 않으면 @ComponetScan이 붙은 설정 정보 클래스의 패키지가 시작 위치가 된다.
스프링 부트는를 사용하면 스프링 부트의 대표 시작 정보인 @SpringApplication을 이 프로젝트 시작 위치에 두는 것이 관례이다.
중복 등록과 충돌
컴포넌트 스캔에서 같은 빈 이름을 등록하면?
- 자동 빈 등록 vs 자동 빈 등록
- 수동 빈 등록 vs 자동 빈 등록
자동 빈 등록 vs 자동 빈 등록
`ConflictingBeanDefinitionException` 예외 발생
수동 빈 등록 vs 자동 빈 등록
수동 빈이 자동 빈을 오버라이딩 하면서 수동 빈 등록이 우선권을 가진다.
스프링 부트에서는 수동 빈 등록과 자동 빈 등록이 충돌나면 오류가 발생한다.
자동 주입
생성자 주입 (권장)
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
@Autowired
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy
discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
애플리케이션 동작 중에는 한개의 인스턴스만 사용하기 위해 `fnal`을 붙여서 불변 값으로 설정
생성자가 딱 1개만 있으면 @Autowired 생략 가능하다. -> 롬복(`@RequiredArgsConstructor`) 어노테이션 사용
생성자 주입을 사용해야하는 이유는 아래와 같다.
불변
- 대부분의 의존관계 주입은 한번 일어나면 애플리케이션 종료시점까지 의존관계가 변하면 안된다.
- OCP 원칙을 잘 준수해서 인터페이스를 사용해서 수정에 닫혀있도록 잘 설계했다면 구현체가 변경되어도 의존관계 주입에 사용된 인터페이스 타입이 변하지 않는다.
- 예를 들어서 회원 저장소가 memory에서 DB로 변경되더라도 `MemberRepository` 라는 인터페이스를 구현한 MemoryMemberRepotiroty와 DbMemberRepository 두 개가 존재하기 때문에 MemberRepository 타입의 빈을 의존관계로 설정했다면 저장소가 변경되는 것과 상관없이 의존관계를 불변이다.
- 수정자 주입을 사용하면, setXxx 메서드를 public으로 열어두어야한다.
- 누군가가 실수로 변경할 수도 있고, 변경하면 안되는 메서드를 열어두는 것은 좋은 설계 방법이 아니다.
- 생성자 주입은 객체를 생성할 때 딱 1번만 호출되고 이후에 호출되는 일이 없다 -> @Configuration 을 사용하면 싱글톤을 보장하는 빈을 생성하므로 생성자가 한번만 호출되도록 보장되어 있다.
누락
- 프레임워크 없이 순수한 자바 코드를 단위 테스트 하는 경우에 다음과 같이 수정자 의존관계인 경우, @Autowired 가 프레임워크 안에서 동작할 때는 의존과계가 없으면 오류를 발생하지만, 지금은 프레임워크 없는 순수한 자바 코드로만 단위 테스트를 수행하므로 @Autowired 가 동작하지 않는다. 따라서 MemberReporitory 인스턴스가 없다면 null로 들어가게 되고, NPE를 발생시킨다.
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;
}
//...
}
@Test
void createOrder() {
OrderServiceImpl orderService = new OrderServiceImpl();
orderService.createOrder(1L, "itemA", 10000);
}
final
생성자 주입을 사용하면 필드에 final 키워드를 사용할 수 있다.
생성자에 값이 설정되어 있지않으면 컴파일 오류로 알려준다.
수정자 주입
setter 수정자 메서드를 이용해서 의존관계 주입
선택, 변경 가능성이 있는 의존 관계에 사용된다.
private MemberRepository memberRepository;
private DiscountPolicy discountPolicy;
@Autowired
public void setMemberRepository(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
@Autowired
public void setDiscountPolicy(DiscountPolicy discountPolicy) {
this.discountPolicy = discountPolicy;
}
@Autowired의 기본 동작은 주입할 대상이 없으면 오류 발생
-> 만일 주입할 대상이 없어도 동작하게 하려면 @Autowired(required = false) 로 지정하면 된다.
필드 주입
@Autowired
private MemberRepository memberRepository;
@Autowired
private DiscountPolicy discountPolicy;
단점
- 외부에서 변경이 불가능해서 테스트 하기 힘들다
- DI 프레임워크가 없으면 아무것도 할 수 없다. -> 자바 테스트 코드에는 @Autowired 가 동작하지 않음. @SpringBootTest 처럼 스프링 컨테이너를 테스트에 통합한 경우에만 가능
- 스프링 설정을 목적으로 하는 @Configuration 같은 곳에서만 특별한 용도로 사용
- 애플리케이션의 코드와 관계 없는 테스트 코드에 사용
일반 메서드 주입
일반적으로 잘 사용하지 않는다.
private MemberRepository memberRepository;
private DiscountPolicy discountPolicy;
@Autowired
public void init(MemberRepository memberRepository, DiscountPolicy
discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
수동 등록 빈에 자동 등록 빈의 의존관계를 주입
@Bean 에서 파라미터에 의존관계는 자동 주입된다. 수동 등록시 자동 등록된 빈의 의존관계가 필요할 때 문제를 해결 할 수 있다.
@Bean
OrderService orderService(MemberRepository memberRepoisitory, DiscountPolicy
discountPolicy) {
return new OrderServiceImpl(memberRepository, discountPolicy);
}
스프링 빈을 주입하지 않고 동작하도록 작성하기
자동 주입 대상을 옵션으로 처리하는 방법은 다음과 같다.
- @Autowired(required=false): 자동 주입할 대상이 없으면 수정자 메서드 자체가 호출 안된다.
- @Nullable: 자동 주입할 대상이 없으면 Null 이 입력된다.
- Optional<>: 자동 주입할 대상이 없으면 Optional.empty 가 입력된다.
@Autowired(required = false)
public void setNoBean1(Member member) {
System.out.println("setNoBean1 = " + member);
}
//null 호출
@Autowired
public void setNoBean2(@Nullable Member member) {
System.out.println("setNoBean2 = " + member);
}
//Optional.empty 호출
@Autowired(required = false)
public void setNoBean3(Optional<Member> member) {
System.out.println("setNoBean3 = " + member);
}
의존관계 주입시 동일한 타입의 빈이 2개 이상이라면
@Component
public class FixDiscountPolicy implements DiscountPolicy {}
@Component
public class RateDiscountPolicy implements DiscountPolicy {}
@Autowired
private DiscountPolicy discountPolicy
@Autowired는 타입으로 빈을 조회 후 주입한다.
DiscountPolicy 의 하위 타입인 FixDiscountPolicy, RateDiscountPolicy 둘 다 스프링 빈으로 등록되어 있다면 `NoUniqueBeanDefinitionException` 오류가 발생한다.
이 문제를 해결하기 위해서 하위타입으로 지정할 수 있지만, 하위 타입으로 지정하면 DIP를 위배하고 유연성이 떨어진다.
해결 1. @Autowired 필드명
@Autowired 는 타입 매칭을 시도하고, 이때 여러 빈이 있으면 필드 이름, 파라미터 이름으로 빈 이름을 추가 매칭한다.
@Autowired
private DiscountPolicy rateDiscountPolicy;
DiscountPolicy 타입의 빈을 찾고, 2개 이상명 rateDiscountPolicy 이름으로 빈 추가 매칭
해결 2. 빈 등록시 @Qualifier 붙이기
@Component
@Qualifier("fixDiscountPolicy")
public class FixDiscountPolicy implements DiscountPolicy {}
@Component
@Qualifier("mainDiscountPolicy")
public class RateDiscountPolicy implements DiscountPolicy {}
주입시에 @Qualifier를 붙여주고 등록한 이름을 적어준다
@Autowired
public OrderServiceImpl(MemberRepository memberRepository,
@Qualifier("mainDiscountPolicy") DiscountPolicy
discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
만일 @Qualifier 로 주입할 때 @Qualifier("mainDiscountPolicy")를 찾지 못하면 mainDiscountPolicy 라는 이름의 스프링 빈을 추가로 찾는다.
하지만 @Qualifier("mainDiscountPolicy") 이렇게 문자열로 적어주면 컴파일시 타입 체크가 되지 않는다.
이럴 경우 애노테이션을 직접 만들어서 해결하면 된다
애노테이션 직접 만들기
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER,
ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Qualifier("mainDiscountPolicy")
public @interface MainDiscountPolicy {
}
@Component
@MainDiscountPolicy
public class RateDiscountPolicy implements DiscountPolicy {}
자동 주입에서 타입이 다르면 컴파일 오류가 뜨도록 할 수 있다.
//생성자 자동 주입
@Autowired
public OrderServiceImpl(MemberRepository memberRepository,
@MainDiscountPolicy DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
해결 3. @Primary
@Primary는 우선순위를 정하는 방법이다.
@Component
@Primary
public class RateDiscountPolicy implements DiscountPolicy {}
@Component
public class FixDiscountPolicy implements DiscountPolicy {}
@Autowired 시에 여러 빈이 매칭되면 @Primary가 우선권을 가진다.
같은 타입의 빈에 @Primary와 @Qualifier 를 붙여주었다면 @Qualifier가 우선순위가 높게 동작한다.
@Qualifier가 더 상세히 동작하기 때문이다.
조회한 빈이 모두 필요할 때, List, Map 사용
List, Map 자료구조로 등록된 빈 조회한 실습 코드
- `Map<String, DiscountPolicy> : map의 키에 스프링 빈의 이름을 넣어주고, 그 값으로 DiscountPolicy 타입으로 조회한 모든 스프링 빈을 담아준다.
- FixDiscountPolicy와 RateDiscountPolicy가 DiscountPolicy의 하위 타입이라면 이 두개의 빈이 Map에 각각 fixDiscountPolicy, rateDiscountPolicy 이름을 키로 가지고 빈이 map에 담기게 된다.
- `List<DiscountPolicy>` : DiscountPolicy 타입으로 조회한 모든 스프링 빈을 담아준다.
만약 해당하는 타입의 스프링 빈이 없다면, 빈 컬렉션이나 Map 을 주입한다.
'Spring > Spring 핵심 원리' 카테고리의 다른 글
[Spring] 싱글톤 컨테이너, @Configuration과 바이트코드 조작 (3) | 2024.11.07 |
---|---|
[Spring] Ioc, DI, 컨테이너 (0) | 2024.11.07 |
[Spring] SOLD 원칙 고려하면서 도메인 설계(1) - OCP, DIP (2) | 2024.10.30 |
빈 생명주기 콜백 (0) | 2023.09.18 |
자동 빈 등록 vs. 수동 빈 등록 - 실무 운영 기준 (0) | 2023.09.17 |
@ComponetScan
AppConfig에서 @Bean
으로 모두 등록해주었다. 빈으로 등록해야한게 수백만개가 된다면 일일이 등록하기 너무 귀찮고, 설정 정보도 커지게 된다.
@ComponetScan
을 설정 정보에 붙여주면 된다.
AutoAppConfig
파일 구조는 다음과 같다

프로젝트 최상단에 AutoappConfig.java가 있으므로 프로젝트에서 컴포넌트로 등록해준 것을 다 빈으로 등록한다.
@Configuration //@ComponentScan: 스프링 빈을 다 끌어다가 자동으로 스프링 빈으로 끌어올리는.. @ComponentScan( //스프링 빈 등록에서 제외할 것 excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Configuration.class) ) public class AutoAppConfig { }
컴포넌트 스캔은 @Componet
애노테이션이 붙은 클래스를 스캔해서 스프링 빈으로 등록한다.
@Component public class MemberServiceImpl implements MemberService { private final MemberRepository memberRepository; @Autowired public MemberServiceImpl(MemberRepository memberRepository) { this.memberRepository = memberRepository; } }
@Componet
: MemberServiceIml 이름으로 빈으로 등록
@Autowired
: MemberRepository 타입으로 빈을 찾아서 의존관계 주입
탐색 위치와 기본 스캔 대상
탐색할 패키지의 시작 위치 지정
모든 자바 클래스를 다 컴포넌트 스캔하면 시간이 오래걸린다. 그래서 꼭 필요한 위치부터 탐핵하도록 시작 위치를 지정할 수 있다.
@ComponentScan( basePackages = "hello.core", }
basePackages: 탐색할 패키지의 시작 위치를 지정, 이 패키지를 포함해서 하위 패키지를 모두 탐색한다.
지정하지 않으면 @ComponetScan이 붙은 설정 정보 클래스의 패키지가 시작 위치가 된다.
스프링 부트는를 사용하면 스프링 부트의 대표 시작 정보인 @SpringApplication을 이 프로젝트 시작 위치에 두는 것이 관례이다.
중복 등록과 충돌
컴포넌트 스캔에서 같은 빈 이름을 등록하면?
- 자동 빈 등록 vs 자동 빈 등록
- 수동 빈 등록 vs 자동 빈 등록
자동 빈 등록 vs 자동 빈 등록
ConflictingBeanDefinitionException
예외 발생
수동 빈 등록 vs 자동 빈 등록
수동 빈이 자동 빈을 오버라이딩 하면서 수동 빈 등록이 우선권을 가진다.
스프링 부트에서는 수동 빈 등록과 자동 빈 등록이 충돌나면 오류가 발생한다.
자동 주입
생성자 주입 (권장)
private final MemberRepository memberRepository; private final DiscountPolicy discountPolicy; @Autowired public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) { this.memberRepository = memberRepository; this.discountPolicy = discountPolicy; }
애플리케이션 동작 중에는 한개의 인스턴스만 사용하기 위해 fnal
을 붙여서 불변 값으로 설정
생성자가 딱 1개만 있으면 @Autowired 생략 가능하다. -> 롬복(@RequiredArgsConstructor
) 어노테이션 사용
생성자 주입을 사용해야하는 이유는 아래와 같다.
불변
- 대부분의 의존관계 주입은 한번 일어나면 애플리케이션 종료시점까지 의존관계가 변하면 안된다.
- OCP 원칙을 잘 준수해서 인터페이스를 사용해서 수정에 닫혀있도록 잘 설계했다면 구현체가 변경되어도 의존관계 주입에 사용된 인터페이스 타입이 변하지 않는다.
- 예를 들어서 회원 저장소가 memory에서 DB로 변경되더라도
MemberRepository
라는 인터페이스를 구현한 MemoryMemberRepotiroty와 DbMemberRepository 두 개가 존재하기 때문에 MemberRepository 타입의 빈을 의존관계로 설정했다면 저장소가 변경되는 것과 상관없이 의존관계를 불변이다.
- 예를 들어서 회원 저장소가 memory에서 DB로 변경되더라도
- 수정자 주입을 사용하면, setXxx 메서드를 public으로 열어두어야한다.
- 누군가가 실수로 변경할 수도 있고, 변경하면 안되는 메서드를 열어두는 것은 좋은 설계 방법이 아니다.
- 생성자 주입은 객체를 생성할 때 딱 1번만 호출되고 이후에 호출되는 일이 없다 -> @Configuration 을 사용하면 싱글톤을 보장하는 빈을 생성하므로 생성자가 한번만 호출되도록 보장되어 있다.
누락
- 프레임워크 없이 순수한 자바 코드를 단위 테스트 하는 경우에 다음과 같이 수정자 의존관계인 경우, @Autowired 가 프레임워크 안에서 동작할 때는 의존과계가 없으면 오류를 발생하지만, 지금은 프레임워크 없는 순수한 자바 코드로만 단위 테스트를 수행하므로 @Autowired 가 동작하지 않는다. 따라서 MemberReporitory 인스턴스가 없다면 null로 들어가게 되고, NPE를 발생시킨다.
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; } //... }
@Test void createOrder() { OrderServiceImpl orderService = new OrderServiceImpl(); orderService.createOrder(1L, "itemA", 10000); }
final
생성자 주입을 사용하면 필드에 final 키워드를 사용할 수 있다.
생성자에 값이 설정되어 있지않으면 컴파일 오류로 알려준다.
수정자 주입
setter 수정자 메서드를 이용해서 의존관계 주입
선택, 변경 가능성이 있는 의존 관계에 사용된다.
private MemberRepository memberRepository; private DiscountPolicy discountPolicy; @Autowired public void setMemberRepository(MemberRepository memberRepository) { this.memberRepository = memberRepository; } @Autowired public void setDiscountPolicy(DiscountPolicy discountPolicy) { this.discountPolicy = discountPolicy; }
@Autowired의 기본 동작은 주입할 대상이 없으면 오류 발생
-> 만일 주입할 대상이 없어도 동작하게 하려면 @Autowired(required = false) 로 지정하면 된다.
필드 주입
@Autowired private MemberRepository memberRepository; @Autowired private DiscountPolicy discountPolicy;
단점
- 외부에서 변경이 불가능해서 테스트 하기 힘들다
- DI 프레임워크가 없으면 아무것도 할 수 없다. -> 자바 테스트 코드에는 @Autowired 가 동작하지 않음. @SpringBootTest 처럼 스프링 컨테이너를 테스트에 통합한 경우에만 가능
- 스프링 설정을 목적으로 하는 @Configuration 같은 곳에서만 특별한 용도로 사용
- 애플리케이션의 코드와 관계 없는 테스트 코드에 사용
일반 메서드 주입
일반적으로 잘 사용하지 않는다.
private MemberRepository memberRepository; private DiscountPolicy discountPolicy; @Autowired public void init(MemberRepository memberRepository, DiscountPolicy discountPolicy) { this.memberRepository = memberRepository; this.discountPolicy = discountPolicy; }
수동 등록 빈에 자동 등록 빈의 의존관계를 주입
@Bean 에서 파라미터에 의존관계는 자동 주입된다. 수동 등록시 자동 등록된 빈의 의존관계가 필요할 때 문제를 해결 할 수 있다.
@Bean OrderService orderService(MemberRepository memberRepoisitory, DiscountPolicy discountPolicy) { return new OrderServiceImpl(memberRepository, discountPolicy); }
스프링 빈을 주입하지 않고 동작하도록 작성하기
자동 주입 대상을 옵션으로 처리하는 방법은 다음과 같다.
- @Autowired(required=false): 자동 주입할 대상이 없으면 수정자 메서드 자체가 호출 안된다.
- @Nullable: 자동 주입할 대상이 없으면 Null 이 입력된다.
- Optional<>: 자동 주입할 대상이 없으면 Optional.empty 가 입력된다.
@Autowired(required = false) public void setNoBean1(Member member) { System.out.println("setNoBean1 = " + member); } //null 호출 @Autowired public void setNoBean2(@Nullable Member member) { System.out.println("setNoBean2 = " + member); } //Optional.empty 호출 @Autowired(required = false) public void setNoBean3(Optional<Member> member) { System.out.println("setNoBean3 = " + member); }
의존관계 주입시 동일한 타입의 빈이 2개 이상이라면
@Component public class FixDiscountPolicy implements DiscountPolicy {} @Component public class RateDiscountPolicy implements DiscountPolicy {}
@Autowired private DiscountPolicy discountPolicy
@Autowired는 타입으로 빈을 조회 후 주입한다.
DiscountPolicy 의 하위 타입인 FixDiscountPolicy, RateDiscountPolicy 둘 다 스프링 빈으로 등록되어 있다면 NoUniqueBeanDefinitionException
오류가 발생한다.
이 문제를 해결하기 위해서 하위타입으로 지정할 수 있지만, 하위 타입으로 지정하면 DIP를 위배하고 유연성이 떨어진다.
해결 1. @Autowired 필드명
@Autowired 는 타입 매칭을 시도하고, 이때 여러 빈이 있으면 필드 이름, 파라미터 이름으로 빈 이름을 추가 매칭한다.
@Autowired private DiscountPolicy rateDiscountPolicy;
DiscountPolicy 타입의 빈을 찾고, 2개 이상명 rateDiscountPolicy 이름으로 빈 추가 매칭
해결 2. 빈 등록시 @Qualifier 붙이기
@Component @Qualifier("fixDiscountPolicy") public class FixDiscountPolicy implements DiscountPolicy {}
@Component @Qualifier("mainDiscountPolicy") public class RateDiscountPolicy implements DiscountPolicy {}
주입시에 @Qualifier를 붙여주고 등록한 이름을 적어준다
@Autowired public OrderServiceImpl(MemberRepository memberRepository, @Qualifier("mainDiscountPolicy") DiscountPolicy discountPolicy) { this.memberRepository = memberRepository; this.discountPolicy = discountPolicy; }
만일 @Qualifier 로 주입할 때 @Qualifier("mainDiscountPolicy")를 찾지 못하면 mainDiscountPolicy 라는 이름의 스프링 빈을 추가로 찾는다.
하지만 @Qualifier("mainDiscountPolicy") 이렇게 문자열로 적어주면 컴파일시 타입 체크가 되지 않는다.
이럴 경우 애노테이션을 직접 만들어서 해결하면 된다
애노테이션 직접 만들기
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Qualifier("mainDiscountPolicy") public @interface MainDiscountPolicy { }
@Component @MainDiscountPolicy public class RateDiscountPolicy implements DiscountPolicy {}
자동 주입에서 타입이 다르면 컴파일 오류가 뜨도록 할 수 있다.
//생성자 자동 주입 @Autowired public OrderServiceImpl(MemberRepository memberRepository, @MainDiscountPolicy DiscountPolicy discountPolicy) { this.memberRepository = memberRepository; this.discountPolicy = discountPolicy; }
해결 3. @Primary
@Primary는 우선순위를 정하는 방법이다.
@Component @Primary public class RateDiscountPolicy implements DiscountPolicy {} @Component public class FixDiscountPolicy implements DiscountPolicy {}
@Autowired 시에 여러 빈이 매칭되면 @Primary가 우선권을 가진다.
같은 타입의 빈에 @Primary와 @Qualifier 를 붙여주었다면 @Qualifier가 우선순위가 높게 동작한다.
@Qualifier가 더 상세히 동작하기 때문이다.
조회한 빈이 모두 필요할 때, List, Map 사용
List, Map 자료구조로 등록된 빈 조회한 실습 코드
- `Map<String, DiscountPolicy> : map의 키에 스프링 빈의 이름을 넣어주고, 그 값으로 DiscountPolicy 타입으로 조회한 모든 스프링 빈을 담아준다.
- FixDiscountPolicy와 RateDiscountPolicy가 DiscountPolicy의 하위 타입이라면 이 두개의 빈이 Map에 각각 fixDiscountPolicy, rateDiscountPolicy 이름을 키로 가지고 빈이 map에 담기게 된다.
List<DiscountPolicy>
: DiscountPolicy 타입으로 조회한 모든 스프링 빈을 담아준다.
만약 해당하는 타입의 스프링 빈이 없다면, 빈 컬렉션이나 Map 을 주입한다.
'Spring > Spring 핵심 원리' 카테고리의 다른 글
[Spring] 싱글톤 컨테이너, @Configuration과 바이트코드 조작 (3) | 2024.11.07 |
---|---|
[Spring] Ioc, DI, 컨테이너 (0) | 2024.11.07 |
[Spring] SOLD 원칙 고려하면서 도메인 설계(1) - OCP, DIP (2) | 2024.10.30 |
빈 생명주기 콜백 (0) | 2023.09.18 |
자동 빈 등록 vs. 수동 빈 등록 - 실무 운영 기준 (0) | 2023.09.17 |