[Spring Boot]モノクロ調容器


1.Webアプリケーションとモノトーン
2.モノトーンパターン
3.単トンコンテナ
4.モノトーン方式の注意事項
5.構成とモノトーン
6.@構成とバイトコード操作の魔法

1.Webアプリケーションとモノトーン



Webアプリケーションは、通常、複数のお客様が同時に要求します.
    public MemberService memberService() { return new MemberServiceImpl(memberRepository()); }
クライアントAがmemberviceを要求した場合、memberviceを呼び出してmemberviceImpleオブジェクトを返します.
クライアントBがmemberviceを要求すると、memberviceを呼び出してmemberviceImpleオブジェクトを返します.
クライアントCがmemberviceを要求すると、memberviceを呼び出してmemberviceImpleオブジェクトを返します.
お客様が3回リクエストした場合、オブジェクトは3回生成されますか?
スプリング無しの純DI容器試験を行った.
	@Test
    @DisplayName("스프링 없는 순수한 DI 컨테이너")
    void pureContainer() {
        AppConfig appConfig = new AppConfig();
        //1. 조회: 호출할 때 마다 객체를 생성
        MemberService memberService1 = appConfig.memberService();

        //2. 조회: 호출할 때 마다 객체를 생성
        MemberService memberService2 = appConfig.memberService();

        //참조값이 다른 것을 확인
        System.out.println("memberService1 = " + memberService1);
        System.out.println("memberService2 = " + memberService2);
    }

出力の結果から,異なる参照値を持つ.
要するに,我々が作成したスプリングのない純DIコンテナAppConfigは,要求されるたびにオブジェクトを再作成する.
この場合、顧客トラフィックが毎秒100の場合、毎秒100個のオブジェクトが生成され、破棄され、メモリの浪費が深刻になります.
解決策は、1つのオブジェクトのみを作成および共有することです.
これはモノトーンの模様です.

2.モノトーンパターン


これは、クラスのインスタンスが1つしか生成されないことを保証する設計モードです.
モノトーンモードでは、複数のオブジェクトインスタンスが作成されないようにする必要があります.
privateジェネレータを使用して、外部で新しいキーワードを任意に使用することを阻止します.
モノトーンモードを実現する方法はいろいろあります.オブジェクトを事前に作成する方法を見てみましょう.
  • 静的領域にオブジェクトインスタンスを事前に作成してアップロードします.
  • オブジェクトインスタンスが必要な場合は、getInstance()メソッドでのみクエリーできます.
    このメソッドを呼び出すと、常に同じインスタンスが返されます.
  • にはオブジェクトインスタンスが1つしかありません.したがって、外部にないようにプライベート化できます.
    newキーを使用してオブジェクトインスタンスの作成をブロックします.
  • public class SingletonService {
    
        //1. static 영역에 객체를 딱 1개만 생성해둔다.
        private static final SingletonService instance = new SingletonService();
        
        //2. public으로 열어서 객체 인스턴스가 필요하면 이 static 메서드를 통해서만 조회하도록 허용한다.
        public static SingletonService getInstance() {
            return instance;
        }
    
        //3. 생성자를 private으로 선언해서 외부에서 new 키워드를 사용한 객체 생성을 못하게 막는다.
        private SingletonService() {
        }
        
    }

    テストの結果private accessでコンパイルエラーが発生しました.
    今回はgetInstance()でインスタンスをクエリーします.

    上記では、ばねのない純DI容器は、異なる参照値を有する.
    ただし、モノトーンモードを適用すると、参照値出力は同じになります.
    モノトーンモードは、生成されたインスタンスをインポートして使用します.したがって、同じオブジェクトのインスタンスが返されます.
    モノトーンモードを使用すると、お客様が要求するたびにオブジェクトを作成することなく、作成したオブジェクトを共有できます.
    もちろん、モノトーンモードにも多くの問題があります.

    ▶モノトーンパターンの問題

  • モノトーンモードを実現するコード自体は多くの必要がある.
  • 依存関係のため、クライアントは特定のクラスに依存します.→DIP違反.
  • クライアントは、OCPの原則に違反する特定のクラスに依存する可能性が高い.
  • をテストするのは難しいです.
  • の内部プロパティを変更または初期化するのは難しいです.
  • プライベートジェネレータでサブクラスを作成するのは難しいです.
  • 結論は柔軟性が悪い.
  • anti-patternとも呼ばれる.
  • 3.単トンコンテナ


    スプリングコンテナは単トンコンテナの役割を果たす.オブジェクトを作成して管理します.
    スプリングコンテナは、モノトーンモードを適用せず、オブジェクトインスタンスをモノトーンで管理します.
    このようなモノトーンオブジェクトを生成および管理する機能をモノトーンレジストリと呼ぶ.
    スプリングコンテナのこの機能は、オブジェクトを単一の色調に維持しながら、単一の色調モードの欠点を解決します.
  • モノトーンパターンは、乱雑なコードを追加する必要はありません.
  • は、DIP、OCP、テスト、プライベートジェネレータからモノトーンを自由に使用することができる.
  • スプリングコンテナを使用してモノトーンモードを適用します.
        @Test
        @DisplayName("스프링 컨테이너와 싱글톤")
        void springContainer() {
    
            ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
    
            MemberService memberService1 = ac.getBean("memberService", MemberService.class);
            MemberService memberService2 = ac.getBean("memberService", MemberService.class);
    
            //참조값이 다른 것을 확인
            System.out.println("memberService1 = " + memberService1);
            System.out.println("memberService2 = " + memberService2);

    出力の結果から、参照値は同じです.
    上にスプリングがない純DI容器では、異なる参照値が出力されている.

    これまで私たちが学んだspringbinは,単色調管理binである.
    スプリングコンテナを使用すると、作成したオブジェクトを共有し、お客様が要求するたびにオブジェクトを作成するのではなく、効率的に再利用できます.

    4.モノトーン方式の注意事項


    単一トーンモードを使用するか、スプリングなどの単一トーンコンテナを使用するかにかかわらず、1つのオブジェクトインスタンスを作成して1つのオブジェクトインスタンスを共有する単一トーン方式は、複数のクライアントが同じオブジェクトインスタンスを共有するため、単一トーンオブジェクトは「ステータスあり」に設計されるべきではありません.
    無状態に設計すべきです.
  • 特定のクライアントに依存フィールドはありません.
  • 特定のクライアントは、値を変更できるフィールドを持ってはいけません.
  • は、できるだけ読み取り専用にしてください.
  • フィールドではなくjavaで共有されていない、領域変数、パラメータ、ThreadLocalなどを使用する必要があります.
    スプリングシートのフィールドに共有値を設定すると、大きな障害が発生する可能性があります.
  • 維持状態とはどういう意味ですか?
    簡単な例を見てみましょう.
        private int price; // 상태를 유지하는 필드
    
        public void order(String name, int price) {
            System.out.println("name = " + name + "price = " + price);
            this.price = price; //여기가 문제!
        }
        void statefulServiceSingleton() {
            ApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);
            StatefulService statefulService1 = ac.getBean(StatefulService.class);
            StatefulService statefulService2 = ac.getBean(StatefulService.class);
    
            //ThreadA: A사용자 10000원 주문
            statefulService1.order("UserA", 10000);
    
            //ThreadB: B사용자 20000원 주문
            statefulService2.order("UserB", 20000);
    
            //ThreadA: 사용자A 주문 금액 조회
            int price = statefulService1.getPrice();
            System.out.println("price = " + price);
    ユーザAは10000元を注文し、ユーザBは20000元を注文する.
    ユーザーAが注文金額を照会すると、10000元が期待されますが、結果として20000元が出力されます.

    単一色調モードは同一のオブジェクトインスタンスを共有するため、ユーザAが注文金額を照会する前に、ユーザBは価格を20000ウォンに変更し、20000ウォンを出力した.
    これらの問題を解決するためには、無状態に設計する必要があります.
        public int order(String name, int price) {
            System.out.println("name = " + name + "price = " + price);
    //        this.price = price; //여기가 문제!
            return price;
        }
        void statefulServiceSingleton() {
            ApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);
            StatefulService statefulService1 = ac.getBean(StatefulService.class);
            StatefulService statefulService2 = ac.getBean(StatefulService.class);
    
            //ThreadA: A사용자 10000원 주문
            int userAprice = statefulService1.order("UserA", 10000);
    
            //ThreadB: B사용자 20000원 주문
            int userBprice = statefulService2.order("UserB", 20000);
    
            //ThreadA: 사용자A 주문 금액 조회
    //        int price = statefulService1.getPrice();
            System.out.println("price = " + userAprice);

    UserAの注文金額は正確に10000ウォンと印刷されています.

    5.構成とモノトーン


    次のAppConfigコードを確認します.
    @Configuration
    public class AppConfig {
    
        @Bean
        public MemberService memberService() {
            return new MemberServiceImpl(memberRepository());
        }
    
        @Bean
        public MemberRepository memberRepository() {
            return new MemoryMemberRepository();
        }
    
        @Bean
        public OrderService orderService() {
            return new OrderServiceImpl(memberRepository(), discountPolicy());
        }

  • @Bean memberService -> new MemoryMemberRepository()
    memberService bineを作成するコードが表示されると、memberRepository()が呼び出されます.
    このメソッドを呼び出すとnew MemoryMemberRepository()が呼び出されます.

  • @Bean orderService -> new MemoryMemberRepository()
    orderService空のコードを作成してもmemberRepository()が呼び出されます.
    このメソッドを呼び出すとnew MemoryMemberRepository()が呼び出されます.
  • その結果,2つの異なるMemoryMember Repositoryが生成され,単色調が破られるようになった.
    MemoryMemberRepositoryインスタンスは共有されていることに注意してください.

    AppConfigのjavaコードを表示する場合、new MemoryMemberRepositoryを2回呼び出して異なるインスタンスを生成する必要があるのは明らかですが、これはどういうことですか?
    AppConfigにコールログを残して確認します.

    出力結果はメソッドを1回だけ呼び出します.
    スプリングは単色調を保証した.
    すべての秘密は@Configurationが適用されるAppConfigにある.

    6.@構成とバイトコード操作の魔法


    スプリングコンテナは単トンレジストリです.スプリングドアが単色調であることを保証します.
    このためspringはクラスのバイトコードを処理するライブラリを使用する.
    上のjavaコードから見ると、必ず3回呼び出されます.
    しかし、確認の結果、一度だけ呼び出された.@Configurationを採用したからこそ.
    簡単なテストをしましょう.
    	@Test
      	void configurationDeep() {
           ApplicationContext ac = new AnnotationConfigApplicationContext();
           AppConfig bean = ac.getBean(AppConfig.class);
    
           System.out.println("bean = " + bean.getClass());
    
       }
    パラメータがAnnotationConfigApplicationContextに渡された値をspringbinとして登録します.
    したがって、AppConfig度はスプリングドアとなる.AppConfigスプリングシートを照会し、クラス情報を出力した.

    結果はおかしい.純粋なクラスの場合は、次のように出力します.
    $$の後ろの「~~CGIB」って何?
    ▶これは私が作成したクラスではなく、springがCGLIBのバイトコード操作ライブラリを使用して、AppConfigクラスを継承する任意の他のクラスを作成し、他のクラスをspringbineとして登録します.

    いずれの異なるクラスも単色調であることを保証します.
    △実際、CGIBの内部技術は非常に複雑である.@Configurationがなければ、@Beanを使ってもモノトーンは保証されません.
    したがって、ばね設定情報は常に@Configurationを使用しなければならない.
    Ref.
    スプリングコア原理(金英漢)