Goal
- @Component Scan에 대해 알아본다
- @Component Scan의 주요 기능을 알아본다
- @Component Scan의 동작 원리를 알아본다
@Component Scan란?
@Component어노테이션(@Service, @Repository, @Controller)이 부여된 Class들을 자동으로 Scan하여 Bean으로 등록해준다.
이전 xml파일에 <context:component-scan base-package="패키지 경로"/>를 이용해 지정해주었던 것을 Java파일을 이용하여 bean을 scan하기 위해서 생겨났다.
- 가장 중요한 설정은 basePackags로 값은 문자열이며, type-safe하지 않음
- basePackageClasses는 보다 type-safe함 - 해당 클래스 기준으로 컴포넌트 스캔 시작
- 보통 @Configuration부터 컴포넌트 스캔 시작
주요 기능
1. 스캔 위치 설정
package com.example.out;
@Service
public class MyService {
}
@SpringBootApplication
public class Demosping51Application {
@Autowired
MyService myService;
public static void main(String[] args) {
SpringApplication.run(Demosping51Application.class, args);
}
}
결과
***************************
APPLICATION FAILED TO START
***************************
Description:
Field myService in com.example.demosping51.Demosping51Application required a bean of type 'com.example.out.MyService' that could not be found.
The injection point has the following annotations:
- @org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'com.example.out.MyService' in your configuration.
=> com.example.out 패키지의 MyService에 해당하는 bean을 찾을 수 없으므로, 빈으로 등록하라
정리
스프링부트에서는 @SpringBootApplication에 @ComponentScan이 포함되어 있으므로, 해당 클래스가 컴포넌트 스캔 시작 지점이다
Demosping51Application 클래스와 해당 클래스를 담고있는 패키지 이하의 모든 것을 스캔한다
그러나, Demosping51Application가 속한 패키지 밖에 있는 것들은 컴포넌트 스캔이 불가능하다.
2. 필터
스캔하는 중에 어떤 어노테이션을 스캔할지 또는 하지 않을지 결정한다
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
컴포넌트 스캔 대상
- @Component
- @Reposiotry
- @Service
- @Controller
- @Configuration
참고
단점으로는 application context에서 bean들을 등록할 때 싱글톤 스콥인 빈들은 초기에 생성하기 때문에 어플리케이션 구동시 시간이 오래 걸릴 수 있다 (등록해야 하는 bean이 많은 경우에) - 펑셔널 기능을 사용해서 시간을 줄일 수 있지만 추천 x
펑션을 사용한 빈 등록
@SpringBootApplication
public class Demosping51Application {
public static void main(String[] args) {
System.out.println("------main-------");
var app = new SpringApplication(Demosping51Application.class);
app.addInitializers((ApplicationContextInitializer<GenericApplicationContext>) ctx -> {
ctx.registerBean(MyService.class);
ctx.registerBean(ApplicationRunner.class, () -> args1 -> System.out.println("functional bean definition"));
});
app.run(args);
}
}
public class MyService {
public void printBean(){
System.out.println(this.getClass());
}
}
// 클래스 확인용 Runner
@Component
public class MyServiceRunner implements ApplicationRunner {
@Autowired
MyService myService;
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("------MyServiceRunner-----");
myService.printBean();
}
}
결과
------main-------
------bookservice runner-------
class com.example.demosping51.RoiseRepository
------MyServiceRunner-----
class com.example.out.MyService
=> 이렇게 구현하면 어플리케이션 구동시 reflection, cg library, proxy를 사용하지 않기 때문에 성능상의 이점이 있다.
하지만, 이렇게 등록을 하게 되면 설정상의 문제를 야기할 수 있어 추천하지 않는다.
참고
클래스를 확인하려고 Runner를 생성하지 않고, Demosping51Application의 main 함수에 @Autowired MyService myService하고
밑에서 myService.printBean()으로 바로 접근하자 아래와 같은 오류가 남
Non-static field 'myService' cannot be referenced from a static context
이유는 아직 myService가 생성되지 않았음. 그리고 그걸 꺼내 쓰려고 했기 때문에 생긴 에러였다
그래서 Runner를 만들고 클래스를 확인하였다.
동작 원리
- @ComponentScan은 스캔할 패키지와 어노테이션에 대한 정보
- 실제 스캐닝은 ConfigurationClassPostProcessor라는 BeanFactoryPostProcessor에 의해 처리됨
- 실행되는 시점 : 다른 bean들을 만들기 이전에 BeanFactoryPostProcessor들의 구현체들을 실행한다.
- 그 말인 즉, 다른 bean(펑션을 사용한 빈 등록 or @Bean 직접 등록)들을 등록하기 전에 component scan을 해서 bean으로 등록을 해준다.
BeanFactoryPostProcessor (Spring Framework 6.0.11 API)
postProcessBeanFactory Modify the application context's internal bean factory after its standard initialization. All bean definitions will have been loaded, but no beans will have been instantiated yet. This allows for overriding or adding properties even
docs.spring.io
참고
스프링입문 강의 by 백기선
https://www.inflearn.com/course/lecture?courseSlug=spring&unitId=15538 https://www.inflearn.com/course/spring_revised_edition/dashboard
https://www.inflearn.com/course/spring_revised_edition/dashboard
'Spring > about spring' 카테고리의 다른 글
[IoC 컨테이너와 빈] Environment 프로파일, 프로퍼티 (0) | 2023.07.23 |
---|---|
[IoC 컨테이너와 빈] 빈의 스코프 (0) | 2023.07.22 |
[IoC 컨테이너와 빈] @Autowired (0) | 2023.07.22 |
[IoC 컨테이너와 빈] ApplicationContext (0) | 2023.07.19 |
[스프링 삼각형] PSA 잘 만든 인터페이스 (0) | 2023.07.17 |