Develop/Spring+JPA

[Spring] @Aspect는 컴포넌트 스캔 대상일까?

연로그 2022. 5. 25. 22:17
반응형

 많은 기술 블로그들이나 책을 보면 컴포넌트 스캔에 대한 이야기가 나온다.

 

 컴포넌트 스캔은 @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를 컴포넌트 스캔 대상으로 설정할 수는 있으나 디폴트 스캔 대상은 아니라는 것은 확실하게 확인했다.

반응형