๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
โญ Personal_Study/Spring

ComponentScan

by ํฌ์ŠคํŠธ์‰์ดํฌ 2023. 1. 7.

์ปดํฌ๋„ŒํŠธ ์Šค์บ”

์ปดํฌ๋„ŒํŠธ ์Šค์บ” & ์˜์กด๊ด€๊ณ„ ์ž๋™์ฃผ์ž…

๋งค๋ฒˆ @Bean์ด๋‚˜ XML ํŒŒ์ผ๋กœ ์Šคํ”„๋ง ๋นˆ์„ ์ง์ ‘ ๋“ฑ๋กํ•ด์ฃผ๋ฉด ๋งค์šฐ ๋ฒˆ๊ฑฐ๋กœ์›€

โœ” ์ปดํฌ๋„ŒํŠธ ์Šค์บ”: ์„ค์ • ์ •๋ณด๊ฐ€ ์—†์–ด๋„ ์ž๋™์œผ๋กœ ์Šคํ”„๋ง๋นˆ์„ ๋“ฑ๋กํ•ด์ฃผ๋Š” ๊ธฐ๋Šฅ

โœ” ์˜์กด ๊ด€๊ณ„ ์ž๋™ ์ฃผ์ž…: @Autowired

package hello.core;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;

@Configuration
@ComponentScan(
        excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Configuration.class)
)
public class AutoAppConfig {

}

โœ” @ComponentScan์„ ์„ค์ • ์ •๋ณด์— ๋ถ™์—ฌ์ฃผ๋ฉด @Component annotation์ด ๋ถ™์€ ํด๋ž˜์Šค๋ฅผ ์Šค์บ”ํ•ด์„œ ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ๋™๋กํ•œ๋‹ค

@Bean์œผ๋กœ ๋”ฐ๋กœ ํด๋ž˜์Šค๋ฅผ ๋“ฑ๋กํ•˜์ง€ ์•Š์•„๋„ ๋œ๋‹ค

// MemoryMemberRepository.java

@Component
public class MemoryMemberRepository implements MemberRepository {}
// RateDiscountPolicy.java

@Component
public class RateDiscountPolicy implements DiscountPolicy {}
// MemberServiceImpl.java

@Component
public class MemberServiceImpl implements MemberService {

    private final MemberRepository memberRepository;

    @Autowired
    public MemberServiceImpl(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }
}
@Component
public class OrderServiceImpl implements OrderService {

    private final MemberRepository memberRepository;
    private final DiscountPolicy discountPolicy;

    @Autowired
    public OrderServiceImpl (MemberRepository memberRepository, DiscountPolicy discountPolicy) {
        this.memberRepository = memberRepository;
        this.discountPolicy = discountPolicy;
    }
}

์„ค์ •์ •๋ณด(์ด์ „์˜ Appconfig)์ด ๋”ฐ๋กœ ์—†๊ธฐ ๋•Œ๋ฌธ์— ์˜์กด๊ด€๊ณ„ ์ฃผ์ž…๋„ ํด๋ž˜์Šค ์•ˆ์—์„œ ํ•ด๊ฒฐํ•ด์•ผ ํ•œ๋‹ค

@Autowired์˜์กด๊ด€๊ณ„ ์ž๋™ ์ฃผ์ž…

ํ…Œ์ŠคํŠธ ์ฝ”๋“œ

public class AutoAppConfigTest {

    @Test
    void basicScan() {
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AutoAppConfig.class);

        MemberService memberService = ac.getBean(MemberService.class);
        assertThat(memberService).isInstanceOf(MemberService.class);
    }
}
ClassPathBeanDefinitionScanner - Identified candidate component class:
.. RateDiscountPolicy.class
.. MemberServiceImpl.class
.. MemoryMemberRepository.class
.. OrderServiceImpl.class

์ปดํฌ๋„ŒํŠธ ์Šค์บ”์ด ์ž˜ ๋™์ž‘ํ•œ๋‹ค

์ปดํฌ๋„ŒํŠธ ์Šค์บ”์˜ ์›๋ฆฌ

1. @ComponentScan

โœ” @ComponentScan ์€ @Component ๊ฐ€ ๋ถ™์€ ๋ชจ๋“  ํด๋ž˜์Šค๋ฅผ ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•œ๋‹ค.

์Šคํ”„๋ง ๋นˆ์˜ ๊ธฐ๋ณธ ์ด๋ฆ„์€ ํด๋ž˜์Šค๋ช…์„ ์‚ฌ์šฉํ•˜๋˜ ๋งจ ์•ž๊ธ€์ž๋งŒ ์†Œ๋ฌธ์ž๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.(MemberServiceImpl -> memberServiceImpl )

๋นˆ ์ด๋ฆ„ ์ง์ ‘ ์ง€์ •: ๋งŒ์•ฝ ์Šคํ”„๋ง ๋นˆ์˜ ์ด๋ฆ„์„ ์ง์ ‘ ์ง€์ •ํ•˜๊ณ  ์‹ถ์œผ๋ฉด @Component("memberService2") ์ด๋Ÿฐ์‹์œผ๋กœ ์ด๋ฆ„์„ ๋ถ€์—ฌํ•˜๋ฉด ๋œ๋‹ค.

2. @Autowired ์˜์กด๊ด€๊ณ„ ์ž๋™ ์ฃผ์ž…

โœ” ์ƒ์„ฑ์ž์— @Autowired ๋ฅผ ์ง€์ •ํ•˜๋ฉด ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ์ž๋™์œผ๋กœ ํ•ด๋‹น ์Šคํ”„๋ง ๋นˆ์„ ์ฐพ์•„์„œ ์ฃผ์ž…

๊ธฐ๋ณธ์ ์œผ๋กœ๋Š” ํƒ€์ž…์ด ๊ฐ™์€ ๋นˆ์„ ์ฐพ์•„์„œ ์ฃผ์ž…ํ•œ๋‹ค (getBean(MemberRepository.class)์™€ ๋™์ผ)

ํƒ์ƒ‰ ์œ„์น˜์™€ ๊ธฐ๋ณธ ์Šค์บ”๋Œ€์ƒ

ํƒ์ƒ‰ํ•  ํŒจํ‚ค์ง€์˜ ์‹œ์ž‘ ์œ„์น˜ ์ง€์ •

๋ชจ๋“  ์ž๋ฐ” ํด๋ž˜์Šค๋ฅผ ๋‹ค ์Šค์บ”ํ•˜๋ฉด ์‹œ๊ฐ„์ด ์˜ค๋ž˜ ๊ฑธ๋ฆผ -> ๊ผญ ํ•„์š”ํ•œ ์œ„์น˜๋ถ€ํ„ฐ ํƒ์ƒ‰ํ•˜๋„๋ก ์‹œ์ž‘ ์œ„์น˜ ์ง€์ • ๊ฐ€๋Šฅ

@ComponentScan(
    basePackages = "hello.core",
)

basePackages : ํƒ์ƒ‰ํ•  ํŒจํ‚ค์ง€์˜ ์‹œ์ž‘ ์œ„์น˜๋ฅผ ์ง€์ •ํ•œ๋‹ค. ์ด ํŒจํ‚ค์ง€๋ฅผ ํฌํ•จํ•ด์„œ ํ•˜์œ„ ํŒจํ‚ค์ง€๋ฅผ ๋ชจ๋‘ ํƒ์ƒ‰ํ•œ๋‹ค.

  • basePackages = {"hello.core", "hello.service"}์™€ ๊ฐ™์ด ์—ฌ๋Ÿฌ ์‹œ์ž‘ ์œ„์น˜๋ฅผ ์ง€์ •ํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

basePackageClasses : ์ง€์ •ํ•œ ํด๋ž˜์Šค์˜ ํŒจํ‚ค์ง€๋ฅผ ํƒ์ƒ‰ ์‹œ์ž‘ ์œ„์น˜๋กœ ์ง€์ •ํ•œ๋‹ค.

โœ” ์ง€์ •ํ•˜์ง€ ์•Š์œผ๋ฉด @ComponentScan ์ด ๋ถ™์€ ์„ค์ • ์ •๋ณด ํด๋ž˜์Šค์˜ ํŒจํ‚ค์ง€๊ฐ€ ์‹œ์ž‘ ์œ„์น˜๊ฐ€ ๋œ๋‹ค.

โœ” ๊ถŒ์žฅ ๋ฐฉ๋ฒ•: ํŒจํ‚ค์ง€ ์œ„์น˜๋ฅผ ์ง€์ •ํ•˜์ง€ ์•Š๊ณ  ์„ค์ • ์ •๋ณด ํด๋ž˜์Šค์˜ ์œ„์น˜๋ฅผ ํ”„๋กœ์ ํŠธ ์ตœ์ƒ๋‹จ์— ๋‘”๋‹ค(์Šคํ”„๋ง๋ถ€ํŠธ)

์ปดํฌ๋„ŒํŠธ ์Šค์บ” ๊ธฐ๋ณธ ๋Œ€์ƒ

์ปดํฌ๋„ŒํŠธ ์Šค์บ”์˜ ์Šค์บ” ๋Œ€์ƒ

  • @Component : ์ปดํฌ๋„ŒํŠธ ์Šค์บ”์—์„œ ์‚ฌ์šฉ
  • @Controlller : ์Šคํ”„๋ง MVC ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ์‚ฌ์šฉ
  • @Service : ์Šคํ”„๋ง ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์—์„œ ์‚ฌ์šฉ
  • @Repository : ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ ์ ‘๊ทผ ๊ณ„์ธต์—์„œ ์‚ฌ์šฉ
  • @Configuration : ์Šคํ”„๋ง ์„ค์ • ์ •๋ณด์—์„œ ์‚ฌ์šฉ

๊ธฐ๋ณธ์ ์œผ๋กœ ์• ๋…ธํ…Œ์ด์…˜์€ ์ƒ์†๊ด€๊ณ„๊ฐ€ ์—†๋‹ค -> ์œ„์™€ ๊ฐ™์€ ๊ธฐ๋Šฅ์€ ์ž๋ฐ”๊ฐ€ ์•„๋‹ˆ๋ผ ์Šคํ”„๋ง์—์„œ ์ž์ฒด์ ์œผ๋กœ ์ง€์›ํ•˜๋Š” ๊ธฐ๋Šฅ

โ€ป ์ฐธ๊ณ : ์• ๋…ธํ…Œ์ด์…˜์˜ ๊ธฐ๋Šฅ

  • @Controller : ์Šคํ”„๋ง MVC ์ปจํŠธ๋กค๋Ÿฌ๋กœ ์ธ์‹
  • @Repository : ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ ์ ‘๊ทผ ๊ณ„์ธต์œผ๋กœ ์ธ์‹ํ•˜๊ณ , ๋ฐ์ดํ„ฐ ๊ณ„์ธต์˜ ์˜ˆ์™ธ๋ฅผ ์Šคํ”„๋ง ์˜ˆ์™ธ๋กœ ๋ณ€ํ™˜
  • @Configuration : ์Šคํ”„๋ง ์„ค์ • ์ •๋ณด๋กœ ์ธ์‹ํ•˜๊ณ , ์Šคํ”„๋ง ๋นˆ์ด ์‹ฑ๊ธ€ํ†ค์„ ์œ ์ง€ํ•˜๋„๋ก ์ถ”๊ฐ€ ์ฒ˜๋ฆฌ
  • @Service : ํŠน๋ณ„ํ•œ ์ฒ˜๋ฆฌ๋ฅผ ํ•˜์ง€ ์•Š์ง€๋งŒ, ๋Œ€์‹  ๊ฐœ๋ฐœ์ž๋“ค์ด ํ•ต์‹ฌ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์ด ์—ฌ๊ธฐ์— ์žˆ๊ฒ ๊ตฌ๋‚˜ ๋ผ๊ณ  ๋น„์ฆˆ๋‹ˆ์Šค ๊ณ„์ธต์„ ์ธ์‹ํ•˜๋„๋ก ๋„์›€

ํ•„ํ„ฐ

  • includeFilters : ์ปดํฌ๋„ŒํŠธ ์Šค์บ” ๋Œ€์ƒ์„ ์ถ”๊ฐ€๋กœ ์ง€์ •
  • excludeFilters : ์ปดํฌ๋„ŒํŠธ ์Šค์บ”์—์„œ ์ œ์™ธํ•  ๋Œ€์ƒ์„ ์ง€์ •
// ์ปดํฌ๋„ŒํŠธ ์Šค์บ” ๋Œ€์ƒ์—์„œ ์ œ์™ธํ•  ์• ๋…ธํ…Œ์ด์…˜

package hello.core.scan.filter;

import java.lang.annotation.*;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyExcludeComponent {
}
// ์ปดํฌ๋„ŒํŠธ ์Šค์บ” ๋Œ€์ƒ์— ์ถ”๊ฐ€ํ•  ์• ๋…ธํ…Œ์ด์…˜


package hello.core.scan.filter;

import java.lang.annotation.*;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyIncludeComponent {
}
// ์ปดํฌ๋„ŒํŠธ ์Šค์บ” ๋Œ€์ƒ์— ์ถ”๊ฐ€ํ•  ํด๋ž˜์Šค

package hello.core.scan.filter;

@MyIncludeComponent
public class BeanA {
}
// ์ปดํฌ๋„ŒํŠธ ์Šค์บ” ๋Œ€์ƒ์—์„œ ์ œ์™ธํ•  ํด๋ž˜์Šค

package hello.core.scan.filter;

@MyExcludeComponent
public class BeanB {
}
// ํ…Œ์ŠคํŠธ์ฝ”๋“œ

public class ComponentFilterAppConfigTest {

    @Test
    void filterScan() {
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(ComponentFilterAppConfig.class);

        BeanA beanA = ac.getBean("beanA", BeanA.class);
        Assertions.assertThat(beanA).isNotNull();

        org.junit.jupiter.api.Assertions.assertThrows(
                NoSuchBeanDefinitionException.class,
                () -> ac.getBean("beanB", BeanB.class));
    }

    @Configuration
    @ComponentScan(
            includeFilters =  @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = MyIncludeComponent.class),
            excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = MyExcludeComponent.class)
    )
    static class ComponentFilterAppConfig{

    }
}

ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•ด๋ณด๋ฉด BeanA๋Š” ๋“ฑ๋ก๋˜๊ณ  BeanB๋Š” ๋“ฑ๋ก๋˜์ง€ ์•Š๋Š”๋‹ค.

FilterType ์˜ต์…˜

5๊ฐ€์ง€ ์˜ต์…˜ ์กด์žฌ

  1. ANNOTATION: ๊ธฐ๋ณธ๊ฐ’, ์• ๋…ธํ…Œ์ด์…˜์„ ์ธ์‹ํ•ด์„œ ๋™์ž‘ํ•œ๋‹ค. ex) org.example.SomeAnnotation
  2. ASSIGNABLE_TYPE: ์ง€์ •ํ•œ ํƒ€์ž…๊ณผ ์ž์‹ ํƒ€์ž…์„ ์ธ์‹ํ•ด์„œ ๋™์ž‘ํ•œ๋‹ค. ex) org.example.SomeClass
  3. ASPECTJ: AspectJ ํŒจํ„ด ์‚ฌ์šฉ ex) org.example..*Service+
  4. REGEX: ์ •๊ทœ ํ‘œํ˜„์‹ ex) org.example.Default.*
  5. CUSTOM: TypeFilter ์ด๋ผ๋Š” ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•ด์„œ ์ฒ˜๋ฆฌ ex) org.example.MyTypeFilter

์ผ๋‹จ์€ ๊ธฐ๋ณธ ์„ค์ •์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ ๊ถŒ์žฅ!

์ค‘๋ณต ๋“ฑ๋ก๊ณผ ์ถฉ๋Œ

์ž๋™ ๋นˆ ๋“ฑ๋ก vs ์ž๋™ ๋นˆ ๋“ฑ๋ก

ConflictingBeanDefinitionException ์˜ˆ์™ธ ๋ฐœ์ƒ

์ˆ˜๋™ ๋นˆ ๋“ฑ๋ก vs ์ž๋™ ๋นˆ ๋“ฑ๋ก

์ˆ˜๋™ ๋นˆ ๋“ฑ๋ก์ด ์šฐ์„ ๊ถŒ์„ ๊ฐ€์ง€๋‚˜ ๊ถŒ์žฅํ•˜์ง€๋Š” ์•Š๋Š”๋‹ค!!

์Šคํ”„๋ง๋ถ€ํŠธ์—์„œ๋Š” ์—๋Ÿฌ์ฒ˜๋ฆฌ!

๋Œ“๊ธ€