Post

Filter Annotation

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
//@ComponentScan에 중첩 클래스로 있다
@Retention(RetentionPolicy.RUNTIME)
@Target({})
@interface Filter {

  /**
   * The type of filter to use.
   * <p>Default is {@link FilterType#ANNOTATION}.
   * @see #classes
   * @see #pattern
   */
  FilterType type() default FilterType.ANNOTATION;

  /**
   * Alias for {@link #classes}.
   * @see #classes
   */
  @AliasFor("classes")
  Class<?>[] value() default {};

  /**
   * The class or classes to use as the filter.
   * <p>The following table explains how the classes will be interpreted
   * based on the configured value of the {@link #type} attribute.
   * <table border="1">
   * <tr><th>{@code FilterType}</th><th>Class Interpreted As</th></tr>
   * <tr><td>{@link FilterType#ANNOTATION ANNOTATION}</td>
   * <td>the annotation itself</td></tr>
   * <tr><td>{@link FilterType#ASSIGNABLE_TYPE ASSIGNABLE_TYPE}</td>
   * <td>the type that detected components should be assignable to</td></tr>
   * <tr><td>{@link FilterType#CUSTOM CUSTOM}</td>
   * <td>an implementation of {@link TypeFilter}</td></tr>
   * </table>
   * <p>When multiple classes are specified, <em>OR</em> logic is applied
   * &mdash; for example, "include types annotated with {@code @Foo} OR {@code @Bar}".
   * <p>Custom {@link TypeFilter TypeFilters} may optionally implement any of the
   * following {@link org.springframework.beans.factory.Aware Aware} interfaces, and
   * their respective methods will be called prior to {@link TypeFilter#match match}:
   * <ul>
   * <li>{@link org.springframework.context.EnvironmentAware EnvironmentAware}</li>
   * <li>{@link org.springframework.beans.factory.BeanFactoryAware BeanFactoryAware}
   * <li>{@link org.springframework.beans.factory.BeanClassLoaderAware BeanClassLoaderAware}
   * <li>{@link org.springframework.context.ResourceLoaderAware ResourceLoaderAware}
   * </ul>
   * <p>Specifying zero classes is permitted but will have no effect on component
   * scanning.
   * @since 4.2
   * @see #value
   * @see #type
   */
  @AliasFor("value")
  Class<?>[] classes() default {};

  /**
   * The pattern (or patterns) to use for the filter, as an alternative
   * to specifying a Class {@link #value}.
   * <p>If {@link #type} is set to {@link FilterType#ASPECTJ ASPECTJ},
   * this is an AspectJ type pattern expression. If {@link #type} is
   * set to {@link FilterType#REGEX REGEX}, this is a regex pattern
   * for the fully-qualified class names to match.
   * @see #type
   * @see #classes
   */
  String[] pattern() default {};

}

필터라는 것은 아래와 같이 쓰인다

1
2
3
4
5
6
7
8
9
10
11
12
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
  @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class)})
public @interface SpringBootApplication {
  //자세한 내용은 생략
}

includeFilter 와 excludeFilter

위에 보면 excludeFilter가 보이는데 이는 ComponentScan에 포함시키지 않는다는 의미이다. 반대로 includeFilter라는 기능도 있는데 이는 ComponentScan에 포함시킨다는 기능이다.

1
2
3
@ComponentScan(
  includeFilters = {
    @Filter(type = FilterType.상수, classes = 클래스명.class)})
1
2
3
@ComponentScan(
  excludeFilters = {
    @Filter(type = FilterType.상수, classes = 클래스명.class)})
  • 각각 ComponentScan의 대상이지만 제외시키거나 포함시켜야할 때 사용한다.
  • Type은 ComponentScan에 포함/제외 기준 설정이며 이 값은 Spring에서 상수로 제공한다.
  • classes는 ComponentScan에 포함/제외할 특정 클래스를 명시하는 설정이다


FilterType의 옵션

총 다섯가지의 옵션이 있다

옵션설명예시
FilterType.ANNOTATIONannotation을 기준으로 인식한다
(default 설정이므로 생략 가능하다)
org.example.SomAnnotation
FilterType.ASSIGNABLE_TYPE지정한 타입과 자식 타입까지 인식한다org.example.SomeClass
FilterType.ASPECTJAspectJ 패턴을 사용한다org.example..*Service+
FilterType.REGEX정규표현식을 사용한다org.example.Default.*
FilterType.CUSTOMTypeFilter라는 인터페이스를 구현하여 사용한다org.example.ExampleFilter


그렇다면 이 기능은 언제 사용될까?

패키기를 따라서 ComponentScan이 동작하다가 이 Annotation이 붙은 클래스를 ComponentScan할 때 사용된다. 이때 @Filter의 옵션을 보고 어떻게 빈으로 등록할지 결정한다.


예제 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
        includeFilters = {
                @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = CustomIncludeComponent.class)
        },
        excludeFilters = {
                @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = CustomExcludeComponent.class)
        }
)
public @interface MySpringBootApplication {
}

ComponentScan을 따로 해줄 @SpringBootApplication과 비슷한 애노테이션을 만든다

1
2
3
4
5
6
7
8
9
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomExcludeComponent {
}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomIncludeComponent {
}

그리고 컴포넌트 스캔을 할지 안할지 결정해주는 애노테이션도 만들어준다

1
2
3
4
5
6
7
8
9
10
11
12
13
@CustomExcludeComponent
public class ExcludeClass {
    public ExcludeClass() {
        System.out.println("ExcludeClass Generated");
    }
}

@CustomIncludeComponent
public class IncludeClass {
    public IncludeClass() {
        System.out.println("IncludeClass Generated");
    }
}

위의 애노테이션들을 각각의 클래스에 적용해준 후에

1
2
3
4
5
6
7
@MySpringBootApplication
public class DemoSpringApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoSpringApplication.class, args);
    }

}

이 코드에 적용하면

1
2
3
4
5
6
7
8
9
10
11
12
13
  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v3.2.0)

2023-12-02T10:19:55.186+09:00  INFO 37628 --- [           main] c.e.demospring.DemoSpringApplication     : Starting DemoSpringApplication using Java 18.0.1.1 with PID 37628 (C:\Users\usr\IdeaProjects\demoSpring\build\classes\java\main started by usr in C:\Users\usr\IdeaProjects\demoSpring)
2023-12-02T10:19:55.188+09:00  INFO 37628 --- [           main] c.e.demospring.DemoSpringApplication     : No active profile set, falling back to 1 default profile: "default"
2023-12-02T10:19:55.188+09:00 DEBUG 37628 --- [           main] o.s.boot.SpringApplication               : Loading source class com.example.demospring.DemoSpringApplication
IncludeClass Generated
2023-12-02T10:19:55.497+09:00 DEBUG 37628 --- [           main] inMXBeanRegistrar$SpringApplicationAdmin : Application Admin MBean registered with name 'org.springframework.boot:type=Admin,name=SpringApplication'

위와 같은 로그가 찍히면서 IncludeClass가 생성됨을 알 수 있다.

This post is licensed under CC BY 4.0 by the author.