@Component와 컴포넌트 스캔
@ComponentScan
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
@AliasFor("basePackages")
String[] value() default {};
@AliasFor("value")
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
...
}@ComponentScan클래스에 들어가면@AliasFor("basePackages")가 보인다.basePackages라는 값이 스트링으로 설정되어 있기 때문에 type-safe 하지 않다.
그래서 그것의 대안으로
basePackageClasses()라는 메서드가 선언되어 있다.이 메서드에 전달된 클래스 기준으로 컴포넌트 스캔을 시작한다.
@ComponentScan은@SpringBootApplication에 있기 때문에@SpringBootApplication애너테이션이 붙은 클래스부터 컴포넌트 스캔을 시작한다.따라서 여기에 속하지 않는 다른 패키지에 아무리
@Service같은 애너테이션을 붙여도 스캔이 되지 않는다.
필터
어떤 애너테이션을 스캔할지 또는 하지 않을지 설정한다.
@ComponentScan을 쓴다고 해서 모든 것을 다 빈으로 등록하는 것은 아니다.위 코드는
excludeFilters를 사용해TypeExcludeFilter와AutoConfigurationExcludeFilter클래스를 제외하고 있다.
종류
@Component
@Repository
@Service
@Controller
@Configuration
단점
싱글턴 빈은 초기에 다 생성하기 때문에 등록하는 빈이 많은 경우 구동 시간이 오래걸릴 수 있다.
하지만 크게 문제되지 않는다고 생각한다.
대안으로 펑션을 사용한 빈 등록을 할 수 있다.
평션을 이용한 빈 등록
리플렉션, 프록시 등의 기법은 성능에 영향을 주는데 이 방법은 이 두 가지 사용하지 않기 때문에 성능 상의 이점이 있다.
여기서 성능은 애플리케이션 구동 시간을 의미한다.
SpringApplicationBuilder 사용
ApplicationContextInitializer 사용
이렇게 하면 조건문 등 원하는 코드를 사이에 집어넣을 수 있다는 장점이 있다. 하지만 일일이 등록해야 하므로 컴포넌트 스캔을 대체하는 것은 힘들다.
동작 원리
실제 스캐닝은
ConfigurationClassPostProcessor라는BeanFactoryPostProcessor에 의해 처리된다.
BeanFactoryPostProcessor
다른 모든 빈을 만들기 이전에 적용한다.
즉, 다른 빈을 등록하기 전에 컴포넌트 스캔을 해서 등록하는 것이다.
여기서 다른 빈이란 앞의 예시처럼 직접 등록했거나
@Bean을 붙여서 만든 빈을 말한다.
Last updated
Was this helpful?