일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 커밋메세지수정
- MySQL
- 테스트
- 메소드명
- 추후정리
- docker명령어
- 네이티브쿼리
- 검색
- 테스트메소드
- foreignkey
- WeNews
- 외부키
- AuthenticationEntryPoint
- ㅔㄴ션
- subquery
- appspec
- appspec.yml
- 서브쿼리
- application.yml
- querydsl
- ubuntu
- 2 > /dev/null
- 적용우선순위
- 메세지수정
- Query
- 포트
- 참조키
- EC2
- 컨테이너실행
- 예약
- Today
- Total
제뉴어리의 모든것
[스프링 핵심 원리] 컴포넌트 스캔 본문
컴포넌트 스캔이란?
@Component라는 애노테이션을 찾아내서 해당 애노테이션이 붙은 클래스를 스프링빈으로 등록하는 것.
컴포넌트 스캔 사용법
1. @ComponentScan이라는 애노테이션을 설정정보에 붙여준다.
@ComponentScan
public class BasicTest { //그냥 설정파일 이름이 BasicTest라고 생각하면 된다.
}
2. @ComponentScan이 붙은 설정파일을 스프링 컨테이너에게 인자로 전달한다
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
new로 생성되는 객체는 ApplicationContext의 구현체이다.
3. 빈으로 등록하고 싶은 클래스에 @Component 애노테이션을 붙인다.
@Component
class BeanInstance{ }
@Component
class DIInstance{ }
4. 위에 코드들을 포함하고 있는 전체 코드를 실행
- 위에 내용을 전제로 한 전체 코드
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.stereotype.Component;
@ComponentScan
public class BasicTest {
@Test
@DisplayName("컴포넌트 스캔 테스트")
void TestComponentScan() {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(BasicTest.class);
String[] beanDefinitionNames = ac.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
BeanDefinition beanDefinition = ac.getBeanDefinition(beanDefinitionName);
if (beanDefinition.getRole() == BeanDefinition.ROLE_APPLICATION) { //애플리케이션단에서 추가한 빈만
System.out.println("등록된 빈 : "+ ac.getBean(beanDefinitionName));
}
}
BeanInstance beanInstance = ac.getBean(BeanInstance.class);
beanInstance.diInstanceLogic();
}
}
@Component
class BeanInstance{
private final DIInstance diInstance;
@Autowired //의존성 주입 애노테이션
public BeanInstance(DIInstance diInstance) {
this.diInstance = diInstance;
}
public void diInstanceLogic() {
diInstance.logic();
}
}
@Component
class DIInstance{
public void logic() {
System.out.println("DIInstance.logic 호출");
}
}
코드는 테스트코드로 작성되었으며, 빈으로 등록된 클래스들은 한눈에 보기 위해 한개의 java 파일에 작성 하였다.
- 결과 화면
- 등록 된 빈 목록은 아래와 같다 (애플리케이션 레벨에서 등록 된 빈이다.)
- BasicTest : 스프링 컨테이너인 AnnotationConfigApplicationContext 에게 전달되는 설정파일 인자이자,
현재 코드들을 포함하고 있는 클래스. (설정파일 클래스도 빈으로 등록된다)
- BeanInstance : Component 스캔으로 읽혀진 빈
- DIInstance : Component 스캔으로 읽혀진 빈
- "DIInstance.logic 호출" 부분설명 :
BeanInstance 의 필드인 DIInstance에 new로써 객체를 생성하는 부분이 존재 하지 않음에도
정상적으로 DIInstance 의 객체가 할당되어 있다.
이것은 @Autowired 애노테이션으로
생성자의 매개변수로 넘어오는 객체타입의 빈이 스프링컨테이너에서 발견되면 주입 해주라는 애노테이션이다.
컴포넌트스캔의 범위
@Component를 찾는 범위는 기본적으로
@ComponentScan이 붙어있는 클래스의 위치를 포함하여, 하위 디렉토리 모두를 탐색한다.
+ 찾는 범위를 지정하는 방법
- @ComponentScan( basePackages = "hello.core", } :
---> "hello.core" 디렉토리를 포함하여 하위 디렉토리 모두를 탐색하겠다는 의미이다.
- basePackages = {"hello.core", "hello.service"} 이렇게 여러 시작 위치를 지정할 수도 있다.
- basePackageClasses : 지정한 클래스의 패키지를 탐색 시작 위치로 지정한다.
컴포넌트스캔과 자동주입의 동작과정
- @ComponentScan 은 @Component 가 붙은 모든 클래스를 스프링 빈으로 등록한다.
이때 스프링 빈의 기본 이름은 클래스명을 사용하되 맨 앞글자만 소문자를 사용한다.
빈 이름 기본 전략: MemberServiceImpl 클래스 memberServiceImpl 빈 이름 직접 지정: 만약 스프링 빈의 이름을 직접 지정하고 싶으면 @Component("memberService2") 이런식으로 이름을 부여하면 된다
key : basicTest ---------------> value : BasicTest객체
key : beanInstance---------------> value : BeanInstance객체
key : dIInstance---------------> value : DIInstance객체 - 생성자에 @Autowired 를 지정하면, 스프링 컨테이너가 자동으로 해당 스프링 빈을 찾아서 주입한다.
이때 기본 조회 전략은 타입이 같은 빈을 찾아서 주입한다.
getBean(MemberRepository.class) 와 동일하다고 이해하면 된다.
컴포넌트 스캔 기본 대상
- 컴포넌트 스캔은 @Component 뿐만 아니라 다음과 내용도 추가로 대상에 포함한다.
- @Component : 컴포넌트 스캔에서 사용
- @Controlller : 스프링 MVC 컨트롤러에서 사용
- @Service : 스프링 비즈니스 로직에서 사용
- @Repository : 스프링 데이터 접근 계층에서 사용
- @Configuration : 스프링 설정 정보에서 사용
참고: 사실 애노테이션에는 상속관계라는 것이 없다. 그래서 이렇게 애노테이션이 특정 애노테이션을 들고 있는 것을 인식할 수 있는 것은 자바 언어가 지원하는 기능은 아니고, 스프링이 지원하는 기능이다
- 컴포넌트 스캔의 용도 뿐만 아니라 다음 애노테이션이 있으면 스프링은 부가 기능을 수행한다.
- @Controller : 스프링 MVC 컨트롤러로 인식
- @Repository : 스프링 데이터 접근 계층으로 인식하고, 데이터 계층의 예외를 스프링 예외로 변환해준다.
데이터를 저장하는 매체를 mysql을 쓸때와, nosql 쓸때, 그리고 기타 다른 매체를 썻을때 같은 Connection 을 얻어 올때 같은 부분에서의 에러여도 에러 메세지들이 다를것이다. 그런것들을 스프링 예외로 한번 감싸서 동일한 로직으로 처리 할 수 있게 해준다는것이다.
- @Configuration : 앞서 보았듯이 스프링 설정 정보로 인식하고, 스프링 빈이 싱글톤을 유지하도록 추가 처리를 한다.
- @Service : 사실 @Service 는 특별한 처리를 하지 않는다. 대신 개발자들이 핵심 비즈니스 로직이 여기에 있겠구나 라고 비즈니스 계층을 인식하는데 도움이 된다.
참고: useDefaultFilters 옵션은 기본으로 켜져있는데, 이 옵션을 끄면 기본 스캔 대상들이 제외된다. 그냥 이런 옵션이 있구나 정도 알고 넘어가자
필터
- includeFilters : 컴포넌트 스캔 대상을 추가로 지정한다.
- excludeFilters : 컴포넌트 스캔에서 제외할 대상을 지정한다
- 사용 법:
@ComponentScan( includeFilters = @Filter(type = FilterType.ANNOTATION, classes = MyIncludeComponent.class),
excludeFilters = @Filter(type = FilterType.ANNOTATION, classes = MyExcludeComponent.class))
FilterType 옵션
FilterType은 5가지 옵션이 있다.
- ANNOTATION: 기본값, 애노테이션을 인식해서 동작한다. ex) org.example.SomeAnnotation
- ASSIGNABLE_TYPE: 지정한 타입과 자식 타입을 인식해서 동작한다. ex) org.example.SomeClass
- ASPECTJ: AspectJ 패턴 사용 ex) org.example..*Service+
- REGEX: 정규 표현식 ex) org\.example\.Default.*
- CUSTOM: TypeFilter 이라는 인터페이스를 구현해서 처리 ex) org.example.MyTypeFilter
중복 등록과 충돌
컴포넌트 스캔에서 같은 빈 이름을 등록하면 어떻게 될까?
- 자동 빈 등록 vs 자동 빈 등록
- 수동 빈 등록 vs 자동 빈 등록
자동 빈 등록 vs 자동 빈 등록
컴포넌트 스캔에 의해 자동으로 스프링 빈이 등록되는데, 그 이름이 같은 경우 스프링은 오류를 발생시킨다.
-> ConflictingBeanDefinitionException 예외 발생
수동 빈 등록 vs 자동 빈 등록
@Component // 컴포넌트스캔으로 자동빈 등록
public class MemoryMemberRepository implements MemberRepository {}
@Configuration // 수동 빈 등록을 위한 애노테이션
@ComponentScan(
excludeFilters = @Filter(type = FilterType.ANNOTATION, classes =
Configuration.class)
)
public class AutoAppConfig {
@Bean(name = "memoryMemberRepository") // 수동 빈 등록
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
}
수동 빈 등록 vs 자동 빈 등록 결과
- 이전 스프링부트에서의 결과
이 경우 수동 빈 등록이 우선권을 가진다.
(수동 빈이 자동 빈을 오버라이딩 해버린다.)
오버라이딩 되는 로그 :
Overriding bean definition for bean 'memoryMemberRepository' with a different definition: replacing
- 최근 스프링부트에서의 결과
최근 스프링 부트에서는 수동 빈 등록과 자동 빈 등록이 충돌나면 오류가 발생하도록 기본 값을 바꾸었다
에러메세지 :
Overriding bean definition for bean 'memoryMemberRepository' with a different definition: replacing
@Component와 @Bean의 차이
- @Coponent
- @ComponentScan의 대상이다.
- @ComponentScan과 같이 쓰인다.
- 클래스 레밸에서 붙여진다. - @Bean
- @Configuration의 대상이다.
- @Configuration과 같이 쓰인다.
- 메소드 레밸에서 붙여진다.
'Spring Boot > 스프링 핵심 원리' 카테고리의 다른 글
[스프링 핵심 원리] 빈 생명주기 콜백 (0) | 2022.08.15 |
---|---|
[스프링 핵심 원리] 싱글톤 컨테이너 (0) | 2022.08.10 |
[스프링 핵심 원리] 스프링 컨테이너와 스프링 빈 (0) | 2022.08.08 |
[스프링 핵심 원리] 객체 지향 설계와 스프링 ~ 스프링 핵심 원리 이해2 - 객체 지향 원리 적용 (0) | 2022.08.07 |