스프링 핵심 원리 - 기본편
https://www.inflearn.com/course/스프링-핵심-원리-기본편/dashboard
섹션 6. 컴포넌트 스캔
6.1 컴포넌트 스캔과 의존관계 자동 주입 시작하기
AutoAppConfig.java
package hello.core.order;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
@Configuration // 설정 정보
@ComponentScan ( // 자동으로 스프링 빈에 등록 // @Component가 붙은 애노테이션을 모두 찾아서 자동으로 스프링 빈에 등록해줌
excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Configuration.class)
// 예제에서 충돌을 방지하기 위해, AppConfig와 TestConfig 등 전에 만들었던 설정 정보는 스캔 대상에서 제외한다.
)
public class AutoAppConfig {
// 기존의 AppConfig와는 다르게 @Bean으로 등록한 클래스는 하나도 없다.
}
MemoryMemberRepository @Component 추가
RateDiscountPolicy @Component 추가
MemberServiceImpl @Component, @Autowired 추가
OrderServiceImpl @Component, @Autowired 추가
AutoAppConfigTest.java
package hello.core.scan;
import hello.core.member.MemberService;
import hello.core.AutoAppConfig;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
public class AutoAppConfigTest {
@Test
void BasicScan() {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AutoAppConfig.class);
MemberService memberService = ac.getBean(MemberService.class);
assertThat(memberService).isInstanceOf(MemberService.class);
}
}
13:14:16.217 [main] DEBUG org.springframework.context.annotation.ClassPathBeanDefinitionScanner - Identified candidate component class: file [/Users/yoosiwon/Desktop/spring/core/out/production/classes/hello/core/discount/RateDiscountPolicy.class]
13:14:16.222 [main] DEBUG org.springframework.context.annotation.ClassPathBeanDefinitionScanner - Identified candidate component class: file [/Users/yoosiwon/Desktop/spring/core/out/production/classes/hello/core/member/MemberServiceImpl.class]
13:14:16.223 [main] DEBUG org.springframework.context.annotation.ClassPathBeanDefinitionScanner - Identified candidate component class: file [/Users/yoosiwon/Desktop/spring/core/out/production/classes/hello/core/member/MemoryMemberRepository.class]
13:14:16.224 [main] DEBUG org.springframework.context.annotation.ClassPathBeanDefinitionScanner - Identified candidate component class: file [/Users/yoosiwon/Desktop/spring/core/out/production/classes/hello/core/order/OrderServiceImpl.class]
// Autowired에 대한 정보도 로그에서 확인 가능 - Autowiring by ...
13:14:16.381 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Autowiring by type from bean name 'memberServiceImpl' via constructor to bean named 'memoryMemberRepository'
13:14:16.383 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Autowiring by type from bean name 'orderServiceImpl' via constructor to bean named 'memoryMemberRepository'
13:14:16.383 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Autowiring by type from bean name 'orderServiceImpl' via constructor to bean named 'rateDiscountPolicy'
컴포넌트 스캔과 자동 의존관계 주입이 어떻게 동작하는지 그림으로 알아보자. (1.~2.)
1. @ComponentScan
2. @Autowired 의존관계 자동 주입
6.2 탐색 위치와 기본 스캔 대상
탐색할 패키지의 시작 위치 지정
@ComponentScan(
basePackages = "hello.core.member", // member 포함 하위 파일만 등록
...
)
권장하는 방법
컴포넌트 스캔 기본 대상
6.3 필터
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
컴포넌트 스캔 대상에 추가할 애노테이션
package hello.core.scan.filter;
import java.lang.annotation.*;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyIncludeComponent { // 애노테이션 // Include: 컴포넌트스캔에 포함
}
컴포넌트 스캔 대상에서 제외할 애노테이션
package hello.core.scan.filter;
import java.lang.annotation.*;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyExcludeComponent { // 애노테이션 // Exclude: 컴포넌트스캔에서 제외
}
컴포넌트 스캔 대상에 추가할 클래스
package hello.core.scan.filter;
@MyIncludeComponent
public class BeanA {
}
컴포넌트 스캔 대상에서 제외할 클래스
package hello.core.scan.filter;
@MyExcludeComponent
public class BeanB {
}
설정 정보와 전체 테스트 코드
package hello.core.scan.filter;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.springframework.context.annotation.ComponentScan.*;
public class ComponentFilterAppConfigTest {
@Test
void filterScan() {
ApplicationContext ac = new AnnotationConfigApplicationContext(ComponentFilterAppConfig.class);
BeanA beanA = ac.getBean("beanA", BeanA.class);
assertThat(beanA).isNotNull();
//BeanB beanB = ac.getBean("beanB", BeanB.class);
assertThrows(
NoSuchBeanDefinitionException.class,
() -> ac.getBean("beanB", BeanB.class)
);
}
// 나만의 Component 스캔 기능
@Configuration
@ComponentScan(
includeFilters = @Filter(type = FilterType.ANNOTATION, classes = MyIncludeComponent.class),
excludeFilters = @Filter(type = FilterType.ANNOTATION, classes = MyExcludeComponent.class)
)
static class ComponentFilterAppConfig {
}
}
FilterType 옵션
@ComponentScan(
includeFilters = @Filter(type = FilterType.ANNOTATION, classes = MyIncludeComponent.class),
excludeFilters = {
@Filter(type = FilterType.ANNOTATION, classes = MyExcludeComponent.class),
@Filter(type = FilterType.ASSIGNABLE_TYPE, classes = BeanA.class) // 추가
}
)
6.4 중복 등록과 충돌
1. 자동 빈 등록 vs. 자동 빈 등록
2. 수동 빈 등록 vs. 자동 빈 등록
@Component // 소문자로 등록 (memoryMemberRepository)
public class MemoryMemberRepository implements MemberRepository {
...
}
public class AutoAppConfig {
// 6.4 중복 등록 예제
@Bean(name = "memoryMemberRepository") // 소문자로 똑같이 등록
MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
}
수동 빈 등록시 남는 로그
Overriding bean definition for bean 'memoryMemberRepository' with a different definition: replacing
수동 빈 등록, 자동 빈 등록 오류시 스프링 부트 에러
// 로그 확인
***************************
APPLICATION FAILED TO START
***************************
Description:
The bean 'memoryMemberRepository', defined in class path resource [hello/core/AutoAppConfig.class], could not be registered. A bean with that name has already been defined in file [/Users/yoosiwon/Desktop/spring/core/out/production/classes/hello/core/member/MemoryMemberRepository.class] and overriding is disabled.
Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true
FIN.
[스프링 핵심 원리 - 기본편] week14 (0) | 2022.11.27 |
---|---|
[스프링 핵심 원리 - 기본편] week13 (0) | 2022.11.20 |
[스프링 핵심 원리 - 기본편] week11 (0) | 2022.10.12 |
[스프링 핵심 원리 - 기본편] week10 (0) | 2022.10.04 |
[스프링 핵심 원리 - 기본편] week09 (0) | 2022.09.28 |
댓글 영역