🌈 [Section2] 19. DI (Dependency Injection) 2

ν˜„μ£ΌΒ·2022λ…„ 10μ›” 14일
0

bootcamp

λͺ©λ‘ 보기
38/71

πŸ“• 였늘 배운 λ‚΄μš©!

  • Java 기반 μ»¨ν…Œμ΄λ„ˆ(Container) μ„€μ •
  • μ»΄ν¬λ„ŒνŠΈ 슀캠 (Component Scan)
  • μ˜μ‘΄κ΄€κ³„ μ£Όμž… 방법

✏️ Java 기반 μ»¨ν…Œμ΄λ„ˆ(Container) μ„€μ •

βœ” μ€‘μš” μ• λ„ˆν…Œμ΄μ…˜

βœ”οΈ @Configuration

  • μ„€μ •νŒŒμΌμ˜ 클래슀 λ ˆλ²¨μ—μ„œλ§Œ μ‚¬μš©
    ( μš°λ¦¬κ°€ 생성할 객체 클래슀 μœ„μ— μž‘μ„± )

➜ "μ—¬κΈ° λ‚΄κ°€ 가지고 μžˆλŠ” Bean듀이 μžˆμ–΄ !!" ν•˜κ³  μ•Œλ €μ£ΌλŠ” 것

πŸ’‘ μ„€μ •νŒŒμΌ
μš°λ¦¬κ°€ μ‚¬μš©ν•˜λŠ” λΉˆλ“€μ˜ 정보와 μ˜μ‘΄μ„± 정보가 μ–΄λ–»κ²Œ 이루어져 μžˆλŠ”μ§€ μ“°μ—¬μžˆλŠ” 파일

  • 클래슀 λ ˆλ²¨μ—μ„œλ§Œ μ‚¬μš©

βœ”οΈ @Bean

  • μ„€μ •νŒŒμΌ λ‚΄μ˜ λ©”μ„œλ“œ λ ˆλ²¨μ—μ„œλ§Œ μ‚¬μš©
    ( 객체 클래슀 λ‚΄λΆ€μ˜ μƒμ„±μž(λ©”μ„œλ“œ) μœ„μ— μž‘μ„± )
    ➜ λ©”μ„œλ“œκ°€ Spring μ»¨ν…Œμ΄λ„ˆμ—μ„œ 관리할 μƒˆ 객체λ₯Ό μΈμŠ€ν„΄μŠ€ν™”, ꡬ성 및 μ΄ˆκΈ°ν™”ν•œλ‹€λŠ” 것을 λ‚˜νƒ€λ‚΄λŠ” 데 μ‚¬μš©

➜ μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆν•œν…Œ "μ–˜λ„€κ°€ μ‚¬μš©ν•  Bean듀이야 !!" ν•˜κ³  μ•Œλ €μ£ΌλŠ” 것

//μΈμŠ€ν„΄μŠ€ν™” ν•  λ•Œ
@Configuration //클래슀λͺ…에 이걸 μ“°κ³ 
public class DependencyConfig {
    @Bean //ν•΄λ‹Ή 클래슀 λ‚΄μ˜ λ©”μ„œλ“œλͺ…μ—λŠ” 이거 씀
    public MyService myService() {
        return new MyServiceImpl();
    }
}

πŸ’‘ XML μ„€μ • 방식

<beans>
    <bean id="myService" class="com.acme.services.MyServiceImpl"/>
</beans>

βœ” AnnotationConfigApplicationContext μ‚¬μš©ν•˜μ—¬ μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆλ₯Ό μΈμŠ€ν„΄μŠ€ν™”

(μ• λ„ˆν…Œμ΄μ…˜μ„ μ΄μš©ν•΄ Config 클래슀 μ„€μ •ν•˜λŠ” 방법)

  • ApplicationContext κ΅¬ν˜„ 방법
    ➜ μ•„λž˜μ™€ 같은 μ• λ„ˆν…Œμ΄μ…˜μ΄ 달린 클래슀λ₯Ό νŒŒλΌλ―Έν„°λ‘œ 전달 λ°›μŒ
  1. βœ”οΈ @Configuration 클래슀
    ➜ 클래슀 μœ„μ— @Configurationκ°€ μž…λ ₯되면, 이 μ• λ„ˆν…Œμ΄μ…˜μ΄ 뢙은 κ·Έ 클래슀 μžμ²΄κ°€ BeanDefinition으둜 λ“±λ‘λ˜κ³ , 클래슀 λ‚΄μ—μ„œ μ„ μ–Έλœ λͺ¨λ“  @Bean이 뢙은 λ©”μ„œλ“œλ“€λ„ Bean μ •μ˜λ‘œ 등둝됨
    ( @Configuration μ• λ„ˆν…Œμ΄μ…˜λ„ ctrl+B ν•΄μ„œ 보면, @Configuate μ• ν„°ν…Œμ΄μ…˜μ΄ λ‹¬λ €μžˆμ–΄ κ·Έ 객체 λ˜ν•œ Bean으둜 μžλ™ μ •μ˜λ¨ )

    Ex.

    public static void main(String[] args) {
        ApplicationContext a = new AnnotationConfigApplicationContext(DependencyConfig.class);
        ToDo todo = a.getBean(Todo.class);
        todo.doStuff();
    }

    βžœμœ„ μ˜ˆμ‹œμ—μ„œλŠ” @Configuration 클래슀(DependencyConfig.class)λ₯Ό νŒŒλΌλ―Έν„°μ— μž…λ ₯ν•¨μœΌλ‘œμ¨ μ‚¬μš©ν•˜κ³  있음

  2. βœ”οΈ @Component 클래슀

  3. βœ”οΈ JSR-330 메타데이터
    -> @Componentκ°€ 뢙은 ν΄λž˜μŠ€μ™€ JSR-330 ν΄λž˜μŠ€κ°€ 제곡되면 BeanDefinition으둜 등둝됨
    ( ν•„μš”ν•œ 경우 ν•΄λ‹Ή 클래슀 λ‚΄μ—μ„œ @Autowired/@Inject와 같은 DI 메타데이터가 μ‚¬μš©λ˜λŠ” κ²ƒμœΌλ‘œ κ°€μ • )

    Ex.

    public static void main(String[] args) {
        ApplicationContext a = new AnnotationConfigApplicationContext(ToDoImpl.class, Dependency1.class, Dependency2.class);
        ToDo todo = a.getBean(ToDo.class);
        todo.doStuff();
    }

    ➜ @Component/JSR-330 주석이 달린 ν΄λž˜μŠ€λŠ” μƒμ„±μžμ— μž…λ ₯으둜 μ‚¬μš©
    ( ToDoImpl/Dependency1/Dependency2에 μŠ€ν”„λ§ μ˜μ‘΄μ„± μ£Όμž… μ• λ„ˆν…Œμ΄μ…˜ @Autowiredλ₯Ό μ‚¬μš©ν•¨ )

βœ” @Bean μ• λ„ˆν…Œμ΄μ…˜ μ‚¬μš©ν•˜κΈ°

[μ°Έκ³ ] https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-java-bean-annotation

βœ”οΈ @Bean

πŸ’‘ XML μ‚¬μš© 방식

<beans>
    <bean id="transferService" class="com.acme.TransferServiceImpl"/>
</beans>

➜ μ• λ„ˆν…Œμ΄μ…˜ μ‚¬μš© 방식과 XML 방식 λͺ¨λ‘ ApplicationContext λ‚΄μ—μ„œ transferServiceλ₯Ό μ‚¬μš© κ°€λŠ₯ν•˜κ²Œ 함

βœ”οΈ κΈ°λ³Έ λ©”μ„œλ“œ μ‚¬μš©ν•˜μ—¬ 빈 μ •μ˜ν•˜λŠ” 방식
➜ κΈ°λ³Έ λ©”μ„œλ“œμ—μ„œ 빈(Bean) μ •μ˜κ°€ μžˆλŠ” μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•˜μ—¬ Bean configuration μ„€μ • κ°€λŠ₯

public interface BaseConfig {
    @Bean
    default TransferServiceImpl transferService() {
        return new TransferServiceImpl();
    }
}
@Configuration
public class DependencyConfig implements BaseConfig { //μ—¬κΈ°μ„œ μΈν„°νŽ˜μ΄μŠ€ κ΅¬ν˜„
}

βœ” Bean μ˜μ‘΄μ„±

@Bean-annotated λ©”μ„œλ“œλŠ” ν•΄λ‹Ή bean을 κ΅¬μΆ•ν•˜λŠ”λ° ν•„μš”ν•œ μ˜μ‘΄μ„±μ„ μ„€λͺ…ν•˜λŠ” 맀개 λ³€μˆ˜λ₯Ό κ°€μ§ˆ 수 있음
-> 이 νŒŒλΌλ―Έν„°λ‘œ μ˜μ‘΄μ„±μ„ κ΅¬μ²΄ν™”μ‹œν‚¬ 수 있음

@Configuration
public class DependencyConfig {

    @Bean
    public TransferService transferService(AccountRepository accountRepository) {
    // 이 λΆ€λΆ„μ—μ„œ λ§€κ°œλ³€μˆ˜ μ‚¬μš© 쀑
        return new TransferServiceImpl(accountRepository);
    }
}

βœ” @Configuration μ• λ„ˆν…Œμ΄μ…˜ μ‚¬μš©ν•˜κΈ°

@Configuration

  • ν•΄λ‹Ή 객체가 bean definitions의 μ†ŒμŠ€μž„μ„ λ‚˜νƒ€λ‚΄λŠ” μ• λ„ˆν…Œμ΄μ…˜

  • @Bean-annoted λ©”μ„œλ“œλ₯Ό 톡해 bean을 μ„ μ–Έ

  • 이 @Configuration-annoted 클래슀의 @Bean λ©”μ„œλ“œμ— λŒ€ν•œ ν˜ΈμΆœμ„ μ‚¬μš©ν•˜μ—¬ bean μ‚¬μ΄μ˜ μ˜μ‘΄μ„±μ„ μ •μ˜ν•  μˆ˜λ„ 있음

βœ”οΈ Bean κ°„ μ˜μ‘΄μ„± μ£Όμž…

  • μƒμ„±μž μ£Όμž…μ„ 톡해 μ°Έμ‘°
    ( But, 이 방식은 @Bean-annoted λ©”μ„œλ“œκ°€ @Configuration 클래슀 내에 μ„ μ–Έλœ κ²½μš°μ—λ§Œ μž‘λ™ )
    ( @Component ν΄λž˜μŠ€μ—λŠ” μ‚¬μš© λΆˆκ°€ )

    Ex.

    @Configuration
    public class AppConfig {
        @Bean
        public BeanOne beanOne() {
            return new BeanOne(beanTwo()); //μ•„λž˜μ„œ μ •μ˜ν•œ bean을 μ£Όμž…
        }
        @Bean
        public BeanTwo beanTwo() {
            return new BeanTwo();
        }
    }

    ➜ beanOne은 μƒμ„±μž μ£Όμž…μ„ 톡해 beenTwo에 λŒ€ν•œ μ°Έμ‘°λ₯Ό λ°›μŒ


✏️ Java 기반 μ„€μ • ν™˜κ²½μ—μ„œ λ‚΄λΆ€μ μœΌλ‘œ μž‘λ™ν•˜λŠ” 방식

@Configuration
public class DependencyConfig {

   @Bean
   public MemberService memberService1() {
       MemberServiceImpl memberService = new MemberServiceImpl();
       memberService.setMemberHyunju(memberHyunju()); //μ—¬κΈ° 1
       return memberService;
   }

   @Bean
   public MemberService memberService2() {
       MemberServiceImpl memberService = new MemberServiceImpl();
       memberService.setMemberHyunju(memberHyunju()); //μ—¬κΈ° 2
       return memberService;
   }

   @Bean
   public MemberHyunju memberHyunju() {
       return new MemberHyunjuImpl();
   }
}

μœ„ μ˜ˆμ‹œμ—μ„œ, memberHyunju()κ°€ memberService1()와 memberService2() μ—μ„œ 각각 ν•œλ²ˆμ”© ν˜ΈμΆœλ˜μ–΄ 2번 뢈리고 있음

이 λ©”μ„œλ“œλŠ” MemberHyunjuImpl의 μƒˆ μΈμŠ€ν„΄μŠ€λ₯Ό λ§Œλ“€κ³  이λ₯Ό λ°˜ν™˜ν•˜λ―€λ‘œ, 일반적으둜 두 개의 μΈμŠ€ν„΄μŠ€(각 μ„œλΉ„μŠ€λ§ˆλ‹€ ν•˜λ‚˜μ”©)κ°€ μžˆμ–΄μ•Ό 함

But, μ—¬κΈ°μ„œ λ¬Έμ œκ°€ 있음

Spring에 μΈμŠ€ν„΄μŠ€ν™”λœ λΉˆμ€ 기본적으둜 싱글톀 λ²”μœ„λ₯Ό κ°€μ Έ ν•œλ²ˆλ§Œ μ‚¬μš©λ˜μ–΄μ•Ό ν•˜λŠ”λ°,
μœ„ μ˜ˆμ‹œλŠ” ν•œ 객체가 두 κ³³μ—μ„œ 뢈리고 있음

λͺ¨λ“  @Configuration듀은 μ‹œμž‘ν•˜μžλ§ˆμž CGLIBλ₯Ό μ‚¬μš©ν•˜μ—¬ ν•˜μœ„λΆ„λ₯˜ 됨

κ·Έ ν•˜μœ„ ν΄λž˜μŠ€μ—μ„œ μžμ‹ λ©”μ„œλ“œλŠ” λΆ€λͺ¨ λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•˜κ³  μƒˆ μΈμŠ€ν„΄μŠ€λ₯Ό μƒμ„±ν•˜κΈ° 전에, λ¨Όμ € μ»¨ν…Œμ΄λ„ˆμ— μΊμ‹œλœ(λ²”μœ„ μ§€μ •λœ) bean듀이 μžˆλŠ”μ§€ 확인함

πŸ’‘ CGLIB (Code Generator Libray)
클래슀의 λ°”μ΄νŠΈ μ½”λ“œλ₯Ό μ‘°μž‘ν•˜μ—¬ ν”„λ‘μ‹œ 객체λ₯Ό 생성해 μ£ΌλŠ” 라이브러리

πŸ’‘ ν”„λ‘μ‹œ 객체 (Proxy)
νŠΉμ • 객체λ₯Ό 감싸 ν”„λ‘œνΌν‹° 읽기, 쓰기와 같은 객체에 κ°€ν•΄μ§€λŠ” μž‘μ—…μ„ μ€‘κ°„μ—μ„œ κ°€λ‘œμ±„λŠ” 객체

βœ” μ• λ„ˆν…Œμ΄μ…˜μ„ μ‚¬μš©ν•΄ μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆλ₯Ό κ΅¬μ„±ν•˜λŠ” 방법

@Import

  • ꡬ성을 λͺ¨λ“ˆν™”ν•˜λŠ”λ° μ‚¬μš©
    ( like. XML 파일 λ‚΄μ—μ„œ μš”μ†Œκ°€ μ‚¬μš©λ˜λŠ” 것 )

  • λ‹€λ₯Έ ꡬ성 클래슀(configuration 클래슀)μ—μ„œ @Bean definitionsλ₯Ό κ°€μ Έμ˜¬ 수 있게 함

@Configuration
public class ConfigA {

    @Bean
    public A a() {
        return new A();
    }
}

@Configuration
@Import(ConfigA.class) //μ—¬κΈ°
public class ConfigB {

    @Bean
    public B b() {
        return new B();
    }
}

➜ μœ„μ™€ 같이 μ»¨ν…μŠ€νŠΈλ₯Ό μΈμŠ€ν„΄νŠΈν™”ν•  λ•Œ ConfigA.class와 ConfigB.classλ₯Ό λ‘˜ λ‹€ μ§€μ •ν•˜μ—¬ ꡬ체화할 ν•„μš”μ—†μ΄, μ•„λž˜μ™€ 같이 ConfigB만 μ œκ³΅ν•˜μ—¬ ꡬ체화 ν•˜λ©΄ 됨

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(ConfigB.class);

    // now both beans A and B will be available...
    A a = ctx.getBean(A.class);
    B b = ctx.getBean(B.class);
}

➜ ctx에 Import(ConfigA.class) 받은 ConfigB.class μ‚¬μš©μœΌλ‘œ 인해 ctx.getBean(A.class)κ°€ κ°€λŠ₯해짐

➜ μ»¨ν…Œμ΄λ„ˆ μΈμŠ€ν„΄νŠΈν™”λ₯Ό λ‹¨μˆœν™”ν•  수 있음
( @Configuration이 μ ‘κ·Ό 방식 μƒμ„±ν•˜λŠ” λ™μ•ˆ ν•˜λ‚˜μ˜ 클래슀만 μ²˜λ¦¬ν•˜λ©΄ 되기 λ•Œλ¬Έμ— )

βœ”οΈ Imported @Bean Definitions에 μ˜μ‘΄μ„± μ£Όμž…μ˜ 문제점

  • μ‹€μ œλ‘œ μ‚¬μš©λ  λ•Œ, λΉˆμ€ 1개의 κ΅¬μ„±νŒŒμΌλ§Œ @Import 받지 μ•Šκ³ , μ—¬λŸ¬ ꡬ성 ν΄λž˜μŠ€κ°„μ— 걸쳐 μ„œλ‘œ μ˜μ‘΄μ„± 가짐

    XML을 μ‚¬μš©ν•  λ•ŒλŠ” μ»΄νŒŒμΌλŸ¬κ°€ κ΄€μ—¬ν•˜μ§€ μ•Šμ•„ μ»¨ν…Œμ΄λ„ˆ μ΄ˆκΈ°ν™” 쀑에 ref=”some Bean”을 μ„ μ–Έν•˜μ—¬ μŠ€ν”„λ§μœΌλ‘œ ν•΄κ²°ν•  수 μžˆμ–΄ λ¬Έμ œκ°€ λ˜μ§€ μ•ŠμŒ

  • @Configuration 클래슀λ₯Ό μ‚¬μš©ν•  λ•Œ, μžλ°” μ»΄νŒŒμΌλŸ¬λŠ” ꡬ성 λͺ¨λΈμ— μ œμ•½μ„ 두고, λ‹€λ₯Έ λΉˆμ— λŒ€ν•œ μ°Έμ‘°λŠ” μœ νš¨ν•œ μžλ°” ꡬ문이어야 함

πŸ’‘ 이의 해결방법
1. @Bean λ©”μ„œλ“œμ— 빈 μ˜μ‘΄μ„±μ„ μ„€λͺ…ν•˜λŠ” μž„μ˜μ˜ 개수의 νŒŒλΌλ―Έν„°λ„£κΈ°
2. @Autowired / @Value μ£Όμž… λ˜λŠ” λ‹€λ₯Έ beanκ³Ό λ™μΌν•œ κΈ°λŠ₯을 μ‚¬μš©ν•˜κΈ°
( 단, @Configuration의 μƒμ„±μž μ£Όμž…μ€ μŠ€ν”„λ§ ν”„λ ˆμž„μ›Œν¬ 4.3μ—μ„œλ§Œ 지원 )
( λŒ€μƒ 빈이 ν•˜λ‚˜μ˜ μƒμ„±μžλ§Œ μ •μ˜ν•˜λŠ” 경우 @Autowired 지정할 ν•„μš”κ°€ μ—†μŒ (ν•˜λ‚˜λ§Œ μžˆμœΌλ‹ˆκΉŒ μžλ™μœΌλ‘œ 지정됨))


✏️ μ˜μ‘΄κ΄€κ³„ 호좜 μˆœμ„œ μ˜ˆμ‹œ

➜ μ›Œλ“œλ‘œ μž‘μ„±ν•΄ 놓은 것 μ°Έκ³  !!


✏️ Component Scan

  • Bean으둜 등둝될 μ€€λΉ„κ°€ 된 ν΄λž˜μŠ€λ“€μ„ μŠ€μΊ”ν•˜μ—¬, μŠ€ν”„λ§ 빈으둜 μžλ™ λ“±λ‘ν•΄μ£ΌλŠ” 것
    ( μ§€κΈˆκΉŒμ§€λŠ” μžλ°” μ½”λ“œμ˜ @Bean or XML의 λ“±μ˜ μ„€μ • 정보에 등둝할 μŠ€ν”„λ§ λΉˆλ“€μ„ 직접 μž‘μ„±ν•˜κ³  의쑴 관계도 λͺ…μ‹œν–ˆμ—ˆμŒ )
    ➜ @Controller, @Service, @Component, @Repository μ• λ…Έν…Œμ΄μ…˜μ„ 뢙인 ν΄λž˜μŠ€λ“€μ΄ 빈으둜 등둝 될 μ€€λΉ„λ₯Ό ν•œ 것

    Component Scan은 기본적으둜 @Component μ• λ…Έν…Œμ΄μ…˜μ„ 빈 등둝 λŒ€μƒμœΌλ‘œ 포함함
    β €
    βœ”οΈ κ·Έλ ‡λ‹€λ©΄ @Controller / @Service λŠ” μ–΄λ–»κ²Œ μΈμ‹ν• κΉŒ?
    ➜ 이 두 μ• λ„ˆν…Œμ΄μ…˜λ„ ctrl+B ν•˜λ©΄ @Component μ• λ„ˆν…Œμ΄μ…˜μ΄ λΆ™μ–΄μžˆκΈ° λ•Œλ¬Έμ— μžλ™μœΌλ‘œ ν¬ν•¨λœλ‹€ !
    β €
    πŸ’‘ DependencyConfig와 λΉ„κ΅ν•œλ‹€λ©΄, μ–˜λŠ” @Bean으둜 λ“±λ‘ν•œ ν΄λž˜μŠ€κ°€ μ—†μŒ

  • @ComponentScan μ• λ„ˆν…Œμ΄μ…˜μ€ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ΄ 맨 처음 생성될 λ•Œ μžλ™μœΌλ‘œ λΆ™μ–΄μžˆκΈ° λ•Œλ¬Έμ— λ”°λ‘œ 쓰지 μ•Šμ•„λ„ 됨

    βœ”οΈ @Configuration이 뢙은 μ„€μ • νŒŒμΌλ„ μŠ€ν”„λ§ 빈으둜 μžλ™ 등둝됨
    ➜ @Configuration을 ctrl + B 해보면, 이 μ• λ„ˆν…Œμ΄μ…˜ μžμ²΄μ— @Component μ• λ„ˆν…Œμ΄μ…˜μ΄ λΆ™μ–΄μžˆκΈ° λ•Œλ¬Έ !

  • basePackageλ₯Ό 섀정해야함

  • @Autowired μ• λ„ˆν…Œμ΄μ…˜μœΌλ‘œ 어디에 μ˜μ‘΄κ΄€κ³„λ₯Ό μ£Όμž…ν• μ§€ μ •ν•˜λ©΄ μ˜μ‘΄κ΄€κ³„ λ˜ν•œ μžλ™μœΌλ‘œ μž…λ ₯ν•΄μ€Œ
    ( @ComponentScan & @Component만 μ‚¬μš©ν–ˆμ„ λ•Œμ—, μ˜μ‘΄μ„± κ΄€λ¦¬ν•˜λŠ” DependencyConfig ν΄λž˜μŠ€μ— μ–΄λ–€ 의쑴 객체λ₯Ό μ£Όμž…ν• μ§€ λͺ…μ‹œν•΄μ£Όμ§€ μ•ŠκΈ° λ•Œλ¬Έμ— @Autowired κΌ­ λΆ™μ—¬μ£ΌκΈ° )

βœ”οΈ DependencyConfig λ“±μ˜ @Configuration 섀정이 된 파일이 μžˆμ„ μ‹œ μ•„λž˜ μ½”λ“œ μΆ”κ°€ν•˜μ—¬ ComponentScan의 Fliter μ‚¬μš© κ°€λŠ₯
@ComponentScan(excludeFilters = @Filter(type = FilterType.ANNOTATION, classes = Configuration.class))

βœ” Component Scan μ‚¬μš© 방법

1. Java 파일 μ•ˆμ—μ„œ μ„€μ •ν•˜λŠ” 방법

@Configuration
@ComponentScan(basePackages = "com.rcod.lifelog")
public class ApplicationConfig {
}

➜ @Configuration - 이 ν΄λž˜μŠ€κ°€ XML을 λŒ€μ²΄ν•œλŠ” μ„€μ • νŒŒμΌμž„μ„ μ•Œλ €μ£Όμ–΄ ν•΄λ‹Ή 클래슀λ₯Ό μ„€μ •νŒŒμΌλ‘œ μ„€μ •

➜ @ComponentScan - basePackages μ„€μ •ν•΄μ€Œ
( 이λ₯Ό μ‚¬μš©ν•˜μ§€ μ•ŠμœΌλ©΄, 빈으둜 μ„€μ •ν•  ν΄λž˜μŠ€λ“€μ„ 직접 XML νŒŒμΌμ— 일일이 등둝해주어야 함 )

βœ”οΈ basePackages

  • 탐색할 νŒ¨ν‚€μ§€μ˜ μ‹œμž‘ μœ„μΉ˜λ₯Ό μ§€μ •ν•˜κ³ , ν•΄λ‹Ή νŒ¨ν‚€μ§€λΆ€ν„° ν•˜μœ„ νŒ¨ν‚€μ§€ λͺ¨λ‘ 탐색함
  • @ComponentScan()의 λ§€κ°œλ³€μˆ˜λ‘œ basePackages = β€œβ€λ₯Ό 쀄 수 있음
  • μ§€μ •ν•˜μ§€ μ•ŠμœΌλ©΄, @ComponentScan이 뢙은 μ„€μ • 정보 클래슀의 νŒ¨ν‚€μ§€κ°€ μ‹œμž‘ μœ„μΉ˜κ°€ 됨
  • μ„€μ • 정보 클래슀의 μœ„μΉ˜λ₯Ό ν”„λ‘œμ νŠΈ μ΅œμƒλ‹¨μ— 두고 νŒ¨ν‚€μ§€ μœ„μΉ˜λŠ” μ§€μ •ν•˜μ§€ μ•ŠλŠ” 방법이 κ°€μž₯ νŽΈν•  수 있음
    ( μŠ€ν”„λ§ λΆ€νŠΈλ₯Ό μ‚¬μš©ν•˜λ©΄ @SpringBootApplication λ₯Ό 이 ν”„λ‘œμ νŠΈ μ‹œμž‘ 루트 μœ„μΉ˜μ— λ‘λŠ” 것을 μΆ”μ²œ )
  • γ… @SpringBootApplication에 @ComponentScan이 λ“€μ–΄μžˆμŒ

2. XML λ°©μ‹μ˜ Component Scan

<beans>
    <context:component-scan base-package="com.acme"/>
</beans>

βœ” Component Scan κΈ°λ³Έ λŒ€μƒ

  • @Component
    ➜ μ»΄ν¬λ„ŒνŠΈ μŠ€μΊ”μ—μ„œ μ‚¬μš©

  • @Controller / @RestController
    ➜ μŠ€ν”„λ§ MVC 및 REST μ „μš© μ»¨νŠΈλ‘€λŸ¬μ—μ„œ μ‚¬μš©

  • @Service
    ➜ μŠ€ν”„λ§ λΉ„μ¦ˆλ‹ˆμŠ€ λ‘œμ§μ—μ„œ μ‚¬μš©
    ➜ νŠΉλ³„ν•œ 처리λ₯Ό ν•˜μ§€ μ•ŠμŒ
    ➜ κ°œλ°œμžλ“€μ΄ 핡심 λΉ„μ¦ˆλ‹ˆμŠ€ 둜직이 여기에 μžˆλ‹€λŠ” λΉ„μ¦ˆλ‹ˆμŠ€ 계측을 μΈμ‹ν•˜λŠ”λ° 도움이 됨

  • @Repository
    ➜ μŠ€ν”„λ§ 데이터 μ ‘κ·Ό κ³„μΈ΅μ—μ„œ μ‚¬μš©
    ➜ μŠ€ν”„λ§ 데이터 μ ‘κ·Ό κ³„μΈ΅μœΌλ‘œ μΈμ‹ν•˜κ³ , 데이터 κ³„μΈ΅μ˜ μ˜ˆμ™Έλ₯Ό μŠ€ν”„λ§ μ˜ˆμ™Έλ‘œ λ³€ν™˜

  • @Configuration
    ➜ μŠ€ν”„λ§ μ„€μ • μ •λ³΄μ—μ„œ μ‚¬μš©
    ➜ μŠ€ν”„λ§ μ„€μ • μ •λ³΄λ‘œ μΈμ‹ν•˜κ³ , μŠ€ν”„λ§ 빈이 싱글톀을 μœ μ§€ν•˜λ„λ‘ μΆ”κ°€ 처리 함
    ( ctrl + B 해보면, ν•΄λ‹Ή 클래슀의 μ†ŒμŠ€ μ½”λ“œμ—λŠ” @Componentλ₯Ό ν¬ν•¨ν•˜κ³  있음 )

βœ”οΈ ν•„ν„°

  • includeFilters
    ➜ μ»΄ν¬λ„ŒνŠΈ μŠ€μΊ” λŒ€μƒμ„ μΆ”κ°€λ‘œ 지정

  • excludeFilters
    ➜ μ»΄ν¬λ„ŒνŠΈ μŠ€μΊ”μ—μ„œ μ œμ™Έν•  λŒ€μƒμ„ 지정

βœ”οΈ FilterType μ˜΅μ…˜

  • ANNOTATION
    ➜ κΈ°λ³Έκ°’, μ• λ„ˆν…Œμ΄μ…˜μœΌλ‘œ μΈμ‹ν•΄μ„œ λ™μž‘
  • ASSIGNABLE_TYPE
    ➜ μ§€μ •ν•œ νƒ€μž…κ³Ό μžμ‹ νƒ€μž…μ„ μΈμ‹ν•΄μ„œ λ™μž‘
  • ASPECTJ
    ➜ AspectJ νŒ¨ν„΄ μ‚¬μš©
  • REGEX
    ➜ μ •κ·œ ν‘œν˜„μ‹μ„ λ‚˜νƒ€λƒ„
  • CUSTOM
    ➜ TypeFilterλΌλŠ” μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•΄μ„œ 처리

βœ” Component Scan λ™μž‘ κ³Όμ •

1. ConfigurationClassParser κ°€ Configuration 클래슀λ₯Ό νŒŒμ‹±
( @Configuration μ–΄λ…Έν…Œμ΄μ…˜ 클래슀λ₯Ό νŒŒμ‹±ν•˜λŠ” 것 )

2. ComponentScan 섀정을 νŒŒμ‹±
( base-package 에 μ„€μ •ν•œ νŒ¨ν‚€μ§€λ₯Ό κΈ°μ€€μœΌλ‘œ
ComponentScanAnnotationParserκ°€ μŠ€μΊ”ν•˜κΈ° μœ„ν•œ 섀정을 νŒŒμ‹± )

3. base-package 섀정을 λ°”νƒ•μœΌλ‘œ λͺ¨λ“  클래슀λ₯Ό λ‘œλ”©

4. ClassLoaderκ°€ λ‘œλ”©ν•œ ν΄λž˜μŠ€λ“€μ„ BeanDefinition으둜 μ •μ˜
( 생성할 빈의 λŒ€ν•œ μ •μ˜λ₯Ό ν•˜λŠ” 것 ).

5. 생성할 λΉˆμ— λŒ€ν•œ μ •μ˜λ₯Ό ν† λŒ€λ‘œ λΉˆμ„ 생성

[μ°Έκ³ ] https://jess-m.tistory.com/m/14


✏️ μ˜μ‘΄κ΄€κ³„ μ£Όμž… 방법

1. μƒμ„±μž μ£Όμž…

  • μƒμ„±μžμ— @Autowiredλ₯Ό ν•˜λ©΄ μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆμ— @Component둜 λ“±λ‘λœ λΉˆμ—μ„œ μƒμ„±μžμ— ν•„μš”ν•œ λΉˆλ“€μ„ μ£Όμž…

    νŠΉμ§•

    • μƒμ„±μž 호좜 μ‹œμ μ— λ”± 1번만 ν˜ΈμΆœλ˜λŠ” 것이 보μž₯
      ( μƒμ„±μžκ°€ 1개만 μ‘΄μž¬ν•˜λŠ” κ²½μš°μ—λŠ” @Autowiredλ₯Ό μƒλž΅ν•΄λ„ μžλ™ μ£Όμž… )
    • λΆˆλ³€κ³Ό ν•„μˆ˜ 의쑴 관계에 μ‚¬μš©
    • 생성과 λ™μ‹œμ— λ°”λ‘œ μ˜μ‘΄κ΄€κ³„λ₯Ό μ£Όμž…ν•˜κΈ° λ•Œλ¬Έμ— NullPointerException 을 방지
    • μ£Όμž…λ°›μ„ ν•„λ“œλ₯Ό final둜 μ„ μ–Έ κ°€λŠ₯

μƒμ„±μžκ°€ 1개일 λ•Œ @Autowired 없어도 μž‘λ™λ˜λŠ” 이유
μŠ€ν”„λ§μ΄ ν•΄λ‹Ή 클래슀 객체λ₯Ό μƒμ„±ν•˜μ—¬ λΉˆμ— λ„£μ–΄μ•Όν•˜λŠ”λ°, 생성할 λ•Œ μƒμ„±μžλ₯Ό λΆ€λ₯Ό 수 밖에 μ—†μŒ -> λΉˆμ„ λ“±λ‘ν•¨λ…€μ„œ 의쑴 관계도 μžλ™μœΌλ‘œ μ£Όμž…λ˜λŠ” 것

2. μˆ˜μ •μž μ£Όμž… (setter μ£Όμž…)

  • ν•„λ“œ 값을 λ³€κ²½ν•˜λŠ” μˆ˜μ •μž λ©”μ„œλ“œ setterλ₯Ό 톡해 μ˜μ‘΄κ΄€κ³„ μ£Όμž…

    νŠΉμ§•

    • 선택과 λ³€κ²½ κ°€λŠ₯성이 μžˆλŠ” 의쑴 관계에 μ‚¬μš©
    • μžλ°”λΉˆ ν”„λ‘œνΌν‹° κ·œμ•½μ˜ μˆ˜μ •μž λ©”μ„œλ“œ 방식을 μ‚¬μš©ν•˜λŠ” 방법

βœ”οΈ μƒμ„±μž μ£Όμž…κ³Όμ˜ 차이점

  • μƒμ„±μž λŒ€μ‹  setν•„λ“œλͺ… λ©”μ„œλ“œλ₯Ό μƒμ„±ν•˜μ—¬ 의쑴 관계 μ£Όμž…

  • λŸ°νƒ€μž„ μ‹œμ μ— 객체λ₯Ό κ»΄μ•Όν•˜λŠ” κ²½μš°μ— μ‚¬μš©
    (But, μ•„μ£Ό λ“œλ¬Όμ–΄μ„œ λ³„λ‘œ μ‚¬μš© X)

  • @Autowiredλ₯Ό μž…λ ₯ν•˜μ§€ μ•ŠμœΌλ©΄ 싀행이 μ•ˆλ¨

  • λ©”μ„œλ“œλ₯Ό public으둜 열어두어 변경이 κ°€λŠ₯ ( λΆˆλ³€ X )

Ex.

@Component
public class CoffeeService {
  private final MemberRepository memberRepository;
  private final CoffeeRepository coffeeRepository;

  @Autowired
  public void setMemberRepository(MemberRepository memberRepository) { //setν•„λ“œλͺ…
  //μ–˜λŠ” 빈으둜 λ“±λ‘λ˜λŠ” 것이 μ•„λ‹˜ (@componentλ₯Ό 달아야 빈이 됨)
  //μ–˜λŠ” κ·Έλƒ₯ λ‹¨μˆœν•˜κ²Œ μ»¨ν…Œμ΄λ„ˆκ°€ μ˜μ‘΄κ΄€κ³„λ₯Ό μ΄μ–΄μ£ΌλŠ” μ—­ν• λ§Œ 함
    this.memberRepository = memberRepository;
  }

  @Autowired
  public void setCoffeeRepository(CoffeeRepository coffeeRepository) { //setν•„λ“œλͺ…
    this.coffeeRepository = coffeeRepository;
  }
}

3. ν•„λ“œ μ£Όμž…

  • ν•„λ“œμ— @Autowired λΆ™μ—¬μ„œ λ°”λ‘œ μ£Όμž…ν•˜λŠ” 방법
    ( μš”μ¦˜ μ•„μ˜ˆ μ‚¬μš© X )

    νŠΉμ§•

    • μ½”λ“œκ°€ κ°„κ²°ν•΄μ„œ μ˜ˆμ „μ— 많이 μ‚¬μš©λœ λ°©μ‹μ΄μ§€λ§Œ, μ™ΈλΆ€μ—μ„œ 변경이 λΆˆκ°€λŠ₯ν•˜μ—¬ ν…ŒμŠ€νŠΈν•˜κΈ° νž˜λ“€λ‹€λŠ” 단점 쑴재
    • DI ν”„λ ˆμž„μ›Œν¬κ°€ μ—†μœΌλ©΄ 아무것도 ν•  수 μ—†μŒ
    • μ‹€μ œ μ½”λ“œμ™€ 상관 μ—†λŠ” νŠΉμ • ν…ŒμŠ€νŠΈλ₯Ό ν•˜κ³  싢을 λ•Œ μ‚¬μš©ν•  수 있음
      ( But, μ •μƒμ μœΌλ‘œ μž‘λ™λ˜κ²Œ ν•˜λ €λ©΄ κ²°κ΅­ setterκ°€ ν•„μš” -> μˆ˜μ •μž μ£Όμž…μ„ μ‚¬μš©ν•˜λŠ”κ²Œ 더 편리 )

πŸ’‘ μƒμ„±μžκ°€ 1개일 λ•Œ @Autowired 없어도 μž‘λ™λ˜λŠ” 이유
μŠ€ν”„λ§μ΄ ν•΄λ‹Ή 클래슀 객체λ₯Ό μƒμ„±ν•˜μ—¬ λΉˆμ— λ„£μ–΄μ•Όν•˜λŠ”λ°, 생성할 λ•Œ μƒμ„±μžλ₯Ό λΆ€λ₯Ό 수 밖에 μ—†μŒ -> λΉˆμ„ λ“±λ‘ν•˜λ©΄μ„œ 의쑴 관계도 μžλ™μœΌλ‘œ μ£Όμž…λ˜λŠ” 것

4. 일반 λ©”μ„œλ“œ μ£Όμž…

  • 일반 λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•΄ μ£Όμž…ν•˜λŠ” 방법
    ( μš”μ¦˜ μ•„μ˜ˆ μ‚¬μš© X )

    νŠΉμ§•

    • ν•œλ²ˆμ— μ—¬λŸ¬ ν•„λ“œλ₯Ό μ£Όμž… 받을 수 있음
    • 일반적으둜 μ‚¬μš©λ˜μ§€ μ•ŠμŒ

✏️ μ˜΅μ…˜ 처리

  • μ£Όμž…ν•  μŠ€ν”„λ§ 빈이 없을 λ•Œ λ™μž‘ν•΄μ•Ό ν•˜λŠ” κ²½μš°μ—, 였λ₯˜κ°€ λ°œμƒν•˜μ§€ μ•Šλ„λ‘ λŒ€μƒμ΄ μ—†λ‹€λŠ” 것을 ν‘œν˜„ν•˜κΈ° μœ„ν•΄ 처리 ν•˜λŠ” 것
  1. @Autowired만 μ‚¬μš©ν•  λ•Œ, required μ˜΅μ…˜ 기본값인 trueκ°€ μ‚¬μš©λ˜μ–΄ μžλ™ μ£Όμž… λŒ€μƒμ΄ μ—†μœΌλ©΄ 였λ₯˜κ°€ λ°œμƒν•˜λŠ” 경우

  2. μŠ€ν”„λ§ λΉˆμ„ μ˜΅μ…”λ„ν•˜κ²Œ ν•΄λ‘” μƒνƒœμ—μ„œ 등둝이 λ˜μ§€ μ•Šκ³ , κΈ°λ³Έ 둜직으둜 λ™μž‘ν•˜κ²Œ ν•˜λŠ” 경우

βœ”οΈ μžλ™ μ£Όμž… λŒ€μƒ μ˜΅μ…˜ 처리 방법

  1. @Autowired(required=false)
    μžλ™ μ£Όμž…ν•  λŒ€μƒμ΄ μ—†μœΌλ©΄ μˆ˜μ •μž λ©”μ„œλ“œ μžμ²΄κ°€ ν˜ΈμΆœλ˜μ§€ μ•Šκ²Œ 됨

  2. org.springframework.lang.@Nullable
    μžλ™ μ£Όμž…ν•  λŒ€μƒμ΄ μ—†μœΌλ©΄ null이 μž…λ ₯됨

  3. Optional<>
    μžλ™ μ£Όμž…ν•  λŒ€μƒμ΄ μ—†μœΌλ©΄ Optional.emptyκ°€ μž…λ ₯됨


✏️ μƒμ„±μž μ£Όμž… μ‚¬μš© 이유

1. λΆˆλ³€

  • μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ΄ 싀행될 λ•Œ μ •ν•΄μ Έμ„œ μ’…λ£Œ μ „κΉŒμ§€ λ³€κ²½λ˜μ§€ μ•ŠμŒ
    ( λ³€κ²½λ˜μ–΄μ„œλ„ μ•ˆλ¨ )
    -> 객체λ₯Ό 생성할 λ•Œ 졜초 1번만 호좜되고 κ·Έ μ΄ν›„μ—λŠ” λ‹€μ‹œ ν˜ΈμΆœλ˜λŠ” 일이 μ—†κΈ° λ•Œλ¬Έμ— λΆˆλ³€ν•˜κ²Œ 섀계 κ°€λŠ₯ν•œ 것

2. λˆ„λ½

  • ν˜ΈμΆœν•  λ•Œ, 의쑴 κ΄€κ²Œ μ£Όμž…μ΄ λˆ„λ½λ˜λ©΄ NPE(Null Point Exception) λ°œμƒ
  • μƒμ„±μž μ£Όμž… μ‚¬μš©ν•˜λ©΄, μ£Όμž… 데이터 λˆ„λ½ μ‹œ 컴파일 였λ₯˜ λ°œμƒ

3. final ν‚€μ›Œλ“œ μ‚¬μš© κ°€λŠ₯

  • ν•„λ“œμ— fainal ν‚€μ›Œλ“œ μ‚¬μš© κ°€λŠ₯
  • μƒμ„±μžμ—μ„œ 값이 μ„€μ •λ˜μ§€ μ•ŠμœΌλ©΄ 컴파일 μ‹œμ μ—μ„œ java: variable (데이터 이름) might not have been initializedλΌλŠ” 였λ₯˜ 확인 κ°€λŠ₯
    ( μƒμ„±μž μ£Όμž… μ™Έ λ‚˜λ¨Έμ§€ 방식은 μƒμ„±μž 이후에 ν˜ΈμΆœλ˜λŠ” ν˜•νƒœμ΄κΈ°μ— final ν‚€μ›Œλ“œ μ‚¬μš© λΆˆκ°€ )

4. μˆœν™˜ μ°Έμ‘°

  • μˆœν™˜ μ°Έμ‘° 방지
  • ν•„λ“œ μ£Όμž…κ³Ό μˆ˜μ •μž μ£Όμž…μ€ 빈이 μƒμ„±λœ 후에 μ°Έμ‘°ν•˜κΈ° λ•œλˆ„μ— μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ΄ μ–΄λ– ν•œ 였λ₯˜/κ²½κ³  없이 ꡬ동됨
    ( μ‹€μ œ μ½”λ“œ 호좜될 λ•ŒκΉŒμ§€ 문제 μ•Œ 수 μ—†μŒ )
  • μƒμ„±μžλ₯Ό 톡해 μ£Όμž…ν•˜κ²Œ 되면 BeanCurrentlyInCreationException λ°œμƒ

βœ”οΈ μƒμ„±μž μ£Όμž… λ°©μ‹μ˜ μž₯점 μš”μ•½

  • μ˜μ‘΄κ΄€κ³„ 섀정이 λ˜μ§€ μ•ŠμœΌλ©΄ 객체생성이 λΆˆκ°€λŠ₯
  • 컴파일 νƒ€μž„μ— 인지가 κ°€λŠ₯
  • NPE μ—λŸ¬ 방지가 κ°€λŠ₯
  • μ˜μ‘΄μ„± μ£Όμž…μ΄ ν•„μš”ν•œ ν•„λ“œλ₯Ό final 둜 μ„ μ–Έ κ°€λŠ₯
  • (μŠ€ν”„λ§μ—μ„œ) μˆœν™˜μ°Έμ‘° 감지가 κ°€λŠ₯
    ( μˆœν™˜ μ°Έμ‘° μ‹œ 앱ꡬ동이 μ‹€νŒ¨ν•˜κ²Œ 됨 )
  • ν…ŒμŠ€νŠΈ μ½”λ“œ μž‘μ„± 용이
  • μˆ˜μ •μž μ£Όμž…μ΄ ν•„μš”ν•œ κ²½μš°κ°€ μžˆμ„ 수 μžˆμ§€λ§Œ μ˜΅μ…˜μ΄ ν•„μš”ν•  λ•Œλ§Œ μ„ νƒν•˜λ©΄ 됨

✏️ μ˜μ‘΄λ°©μ‹ λ³€κ²½ μ˜ˆμ‹œ

➜ μ›Œλ“œλ‘œ μž‘μ„±ν•΄ 놓은 것 μ°Έκ³  !

✏️ μ»΄ν¬λ„ŒνŠΈ μŠ€μΊ” μ˜ˆμ‹œ

➜ μ›Œλ“œλ‘œ μž‘μ„±ν•΄ 놓은 것 μ°Έκ³  !

✏️ BeanDefinition μ˜ˆμ‹œ

➜ μ›Œλ“œλ‘œ μž‘μ„±ν•΄ 놓은 것 μ°Έκ³  !


🌈 λŠλ‚€μ 

μœΌμ•… μ•„μ§κΉŒμ§€λŠ” λ„ˆλ¬΄ 좔상적이라 잘 이해가 μ•ˆκ°€λŠ” 뢀뢄이 μ—¬λŸ¬κ΅°λ° μžˆλ‹€
λ­”κ°€ 직접 해봐야 이해가 잘 될 것 같은데 λͺ‡λͺ‡ λŒ€ν‘œμ μΈ λΆ€λΆ„λ§Œ 이해가 κ°€κ³  아직 μƒμ„Έν•œ 뢀뢄듀을 잘 λͺ¨λ₯΄κ² μ–΄μ„œ μ•žμœΌλ‘œ μ‹€μŠ΅ 많이 ν•˜λ©΄μ„œ 자주 λ‹€μ‹œ λ“€μ—¬λ‹€ 봐야겠닀ㅠㅠ

0개의 λŒ“κΈ€