Spring ContainerにSpring Beanを自動で登録する方法(@ComponentScan, @Component, @Autowired)

- 以前のような＠Beanを使う手動登録ではなく、**自動Bean登録**を行う。
    <details>
    <summary>以前のコード</summary>
    <div>
    
    ```java
    package hello.hellospring;
    
    @Configuration
    public class AppConfig {
    
        @Bean
        public MemberService memberService() {
            return new MemberServiceImpl(memberRepository());
        }
    
        @Bean
        public OrderService orderService() {
            return new OrderServiceImpl(memberRepository(), discountPolicy());
        }
    
        @Bean
        public MemberRepository memberRepository() {
            return new MemoryMemberRepository();
        }
    
        @Bean
        public DiscountPolicy discountPolicy() {
            return new RateDiscountPolicy();
        }
    }
    ```
    </div>
    </details>
- Spring Beanの基本名はclass名を使うが、一番最初の文字だけ小文字を使う。

## 使用方法


1. Spring設定情報ファイルの@Configurationに追加で **@ComponentScan** を付ける。
    - @Configuration:該当クラスがSpring Beanの設定ファイルであることを示す。
    - @ComponentScan:SpringBeanとして登録するクラスを見つける方法を指定する。
    ```java
    package hello.hellospring;
    
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.FilterType;
    
    @Configuration
    @ComponentScan
    public class AutoAppConfig {
    	// 以前とは異なり、@Beanを別に作成しなくてもよい。
    }
    ```
2. classに **@Component**を付ける。
    - @Componentが付いたすべてのクラスはSpring Beanに自動的に登録される。
    - @Componentだけでなく、次の内容も追加でComponentScan対象に含める。
    以下のannotationはComponentScan + 付加機能を持っている。
    
    | Annotation | 説明 |
    | --- | --- |
    | @Controller | Spring MVC Controllerで使用 |
    | @Service | Springビジネスロジックで使用、特別な処理をせず、核心ビジネスロジックがここにあることを認識するのに役立つ。|
    | @Repository 　| Spring データ アクセス階層で使用、データ階層の例外をSpring例外に変換してくれる。|
    ```java
    @Component // 又は @Repository
    public class MemoryMemberRepository implements MemberRepository{
    	...
    }
    ```

3. @Autowiredを付けて依存関係の自動注入を行う。
    - Spring Containerが自動的に該当Spring Beanを探して注入する。
    - 基本照会戦略は同じタイプのBeanを探すことで、以下の場合、ac.getBean(MemberTepository.class)と同一であると理解すればよい。
    ```java
    @Component // 又は @Service
    public class MemberServiceImpl implements MemberService{
    
        private final MemberRepository memberRepository;
    
        @Autowired
        public MemberServiceImpl(MemberRepository memberRepository){
            this.memberRepository = memberRepository;
        }
    		...
    }
    ```
    - 依存関係が複数あっても一度に注入してもらえる。
    ```java
    @Component // 又は @Service
    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;
    }

    ```
    
4. Annotation Config Application Contextは従来と同様に使用する。
    ```java
    public class AutoAppConfigTest {
    
        @Test
        void basicScan() {
            AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AutoAppConfig.class);
    
            MemberService memberService = ac.getBean(MemberService.class);
            Assertions.assertThat(memberService).isInstanceOf(MemberService.class);
    
        }
    }
    ```
    
## 重複登録と衝突 
### Component Scanで同じBeanの名前を登録したら？
1. 自動Bean登録vs自動Bean登録
    - Springでエラー(Conflicting Bean DefinitionException)を発生させる。
2. 手動Bean登録vs自動Bean登録
    - 手動Bean登録が優先権を持ち、手動Beanが自動BeanをOverrideして成功に出る。
    - しかし、このような場合は、様々な設定がねじれて作られる場合がほとんどなので、**Spring Bootでは衝突したらエラーが発生するように基本値を変えた。**


## ComponentScanの探索位置
- 探索するパッケージの開始位置を指定することができる。
- しかし、**パッケージ位置を指定せず、設定情報クラスの位置をプロジェクト最上段に置くことを最もお勧めする。** 何も付けないと、@ComponentScanが付いた設定情報classのpackageが開始位置になるからだ。（例のAutoAppConfigの場合、package hello.hellospringである。）
- **Springbootを使えば、SpringBootApplicationにすでに@ComponentScanが基本的に付いているので、どうせBeanが自動的に登録されて@ComponentScan自体を書く必要がなくなる。**
1. basePackages
    - このpackageを含めて下位packageをすべて探索する。
    ```java
    @ComponentScan(
    	basePackages = "hello.hellospring.member",
    )
    
    // いくつも可能だ。
    @ComponentScan(
    	basePackages = {"hello.hellospring.member", "hello.hellospring.order"}
    )
    ```
2. basePackageClasses
    - 指定したclassのpackageを探索開始の上に指定する。
    ```java
    @ComponentScan(
    	// package hello. hellospring; を探索開始とする。
    	basePackageClasses = AutoAppConfig.class,
    )  
    ```