많은 기술 블로그들이나 책을 보면 컴포넌트 스캔에 대한 이야기가 나온다.
컴포넌트 스캔은 @Component 어노테이션을 붙인 클래스 뿐 아니라 @Controller, @Service 등이 붙은 클래스도 컴포넌트 스캔 대상에 포함된다. 그런데 @Aspect 어노테이션도 컴포넌트 스캔의 대상이라고 설명한 곳이 많았다. 하지만 @Aspect가 붙은 클래스가 정상적으로 동작하려면 @Component 또는 @Bean 어노테이션을 이용해야 한다.
아래는 내가 작성한 테스트용 코드다. @Repository를 적용한 LineDao 클래스, @Aspect를 적용한 LoggerAop 클래스 두 개를 sout 해봤다. LineDao는 무사히 가져왔지만 LoggerAop에서는 해당 bean을 찾을 수 없다는 오류가 발생한다.
🤔 컴포넌트 스캔 대상이라는건 자동으로 빈 등록해주는거라 생각했는데 별개의 개념이었던걸까?
의문점 1. 스프링의 @Aspect + @Component 권장
Spring 공식 문서에서도 아래와 같이 자동 탐지에 적절하지 않아 @Component와 함께 사용하라고 권장한다.
Autodetecting aspects through component scanning
You can register aspect classes as regular beans in your Spring XML configuration, via @Bean methods in @Configuration classes, or have Spring autodetect them through classpath scanning — the same as any other Spring-managed bean. However, note that the @Aspect annotation is not sufficient for autodetection in the classpath. For that purpose, you need to add a separate @Component annotation (or, alternatively, a custom stereotype annotation that qualifies, as per the rules of Spring’s component scanner).
(문서 5.4.2. Declaring an Aspect 참고)
의문점 2. @Component 클래스의 주석
@Component 클래스에 들어가면 아래와 같은 주석이 첨부되어 있다.
특정한 경우에 사용하는 Component라면서 @Aspect를 예로 드는데 정작 @Aspect에서는 @Component가 없다.
Indicates that an annotated class is a "component". Such classes are considered as candidates for auto-detection when using annotation-based configuration and classpath scanning.
Other class-level annotations may be considered as identifying a component as well, typically a special kind of component: e.g. the @Repository annotation or AspectJ's @Aspect annotation.
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Aspect {
/**
* @return the per clause expression, defaults to singleton aspect.
* Valid values are "" (singleton), "perthis(...)", etc
*/
String value() default "";
}
😈 스캔 대상이 될 수 있다 != 스캔 대상에 포함된다
나로선 이 의문점을 해결하기 힘들어서 스프링5 프로그래밍 입문 책의 저자이신 최범균님께 메일을 보냈다. 정말 감사하게도 친절하게 답장 해주셨다. 아래는 그 내용을 정리했다.
컴포넌트 스캔 대상이 될 수 있다는 것과 실제 컴포넌트 스캔할 때 대상에 포함되는 것이 다르다. @ComponentScan 어노테이션을 사용하면 기본적으로 @Component 또는 @Component를 사용해서 정의한 어노테이션이 컴포넌트 스캔 대상에 포함된다. Spring의 빈 등록을 도와주는 ClassPathBeanDefinitionScanner 클래스의 주석을 살펴보자.
A bean definition scanner that detects bean candidates on the classpath, registering corresponding bean definitions with a given registry (BeanFactory or ApplicationContext). Candidate classes are detected through configurable type filters. The default filters include classes that are annotated with Spring's @Component, @Repository, @Service, or @Controller stereotype.
🙄 @Aspect도 기본 스캔 대상으로 만들기
@Aspect 어노테이션은 기본 스캔 대상에 포함되지 않는 것이 확인 되었다. @Component를 통해 빈으로 등록할 수도 있겠지만 @Aspect 어노테이션 자체를 컴포넌트 스캔 대상에 포함시킬수도 있다. 아래와 같이 includeFilters 속성을 이용하면 된다.
@Configuration
@EnableAspectJAutoProxy
@ComponentScan(value = "aspect", includeFilters = @ComponentScan.Filter(value = Aspect.class))
public class AppCtxWithComponentScan {
// ...
}
사실 의문점 2번에 대해서는 아직 잘 풀리지 않았다. (대체 왜 @Component에서 @Aspect를 언급한걸까...)
@Aspect를 컴포넌트 스캔 대상으로 설정할 수는 있으나 디폴트 스캔 대상은 아니라는 것은 확실하게 확인했다.
'Develop > Spring+JPA' 카테고리의 다른 글
[Spring 5 프로그래밍 입문] chapter 5, 6 - 컴포넌트 스캔과 빈 라이프 사이클 (0) | 2022.06.04 |
---|---|
[Spring Boot] 내가 설정하지 않아도 동작하는 어노테이션 (2) | 2022.05.28 |
[Spring 5 프로그래밍 입문] chapter 3, 4 - 의존 주입 (4) | 2022.05.21 |
[Spring 5 프로그래밍 입문] chapter 2 - 스프링 시작하기 (0) | 2022.05.21 |
[Spring] Spring의 트랜잭션 관리 (feat: @Transactional) (6) | 2022.05.08 |