SpringのSingleton


Singleton


  • モノトーンとは?
  • Webアプリケーションは、通常、複数のユーザが同時に1つのサーバに複数の要求を発行することを要求する.Singletonとして設計されていないWebアプリケーションのjavaは、リクエストごとに1つのオブジェクトを作成し、10000のリクエストは10000の継承オブジェクトに1つ以上のオブジェクトを作成します.
  • のように設計されたアプリケーションは、大量のメモリを浪費します.
  • モノトーン設計を使用する場合、javaサーバが複数のリクエストを受信しても、クラスインスタンスが1つしか生成されないことを確認できます.

  • Javaを使用して単一トーンモードを実装し、ビューstatic finalを使用してjavaサーバ上で1つのインスタンスしか存在しないように設計し、getInstance()メソッドを使用すると、インスタンスが他のサービスロジックで1つのインスタンスを共有できます.
    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() { } public void logic() { 
      	System.out.println("싱글톤 객체 로직 호출"); 
      } 
    }

  • 実施したモノトーンモードテスト
    @Test
    @DisplayName("싱글톤 패턴을 적용한 객체 사용")
    public void singletonServiceTest() {
        //private으로 생성자를 막아두었다. 컴파일 오류가 발생한다.
        //new SingletonService();
        //1. 조회: 호출할 때 마다 같은 객체를 반환
        SingletonService singletonService1 = SingletonService.getInstance();
        //2. 조회: 호출할 때 마다 같은 객체를 반환
        SingletonService singletonService2 = SingletonService.getInstance();
        //참조값이 같은 것을 확인
        System.out.println("singletonService1 = " + singletonService1);
        System.out.println("singletonService2 = " + singletonService2);
    }
  • テストコードで出力されたオブジェクトのアドレス値を確認すると、同じオブジェクトが参照されていることがわかります.

    モノトーンパターンの設計をうまく実現したようだが,この設計には問題がある.
  • モノトーンモードの問題
  • モノトーンモード実装コード自体が長い.(インスタンスオブジェクト宣言コード、作成者制限)
  • DIP違反.
  • OCP違反の可能性が高い.
  • のテストは難しいです.
  • 生成者でサブクラスを作成するのは難しい.
  • 内部ロジックの変更と初期化は困難です.
  • 位にランクインしたため、柔軟性が低下した.
  • anti-patternとも呼ばれる.
  • これらの問題は多く、Webアプリケーションでデザインモードとしてモノトーンモードをどのように使用しますか?これはSpringのモノクロ調容器を用いることで,Springは上記のすべての問題を解決できるからである.

    Singleton Container


    モノクロコンテナは、モノクロモードの問題を解決し、モノクロモードのターゲットに従って実行できます.今まで私たちが学んだSpring Binはモノトーン管理のBindsです

  • Singleton Containerとは?
  • スプリングコンテナは、コードとして直接モノトーンモードを適用することなく、モノトーン管理オブジェクトインスタンスとして使用される.
  • スプリング容器は単トン容器として機能する.モノトーンオブジェクトを作成および管理する機能をモノトーンレジストリと呼びます.
  • スプリングコンテナは、モノトーンモードのすべての欠点を解決し、オブジェクトをモノトーンに保つことができる.
  • @Test
    @DisplayName("스프링 컨테이너와 싱글톤")
    void springContainer(){
        //AppConfig appConfig = new AppConfig();
        AnnotationConfigApplicationContext 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);
    
        Assertions.assertThat(memberService1).isSameAs(memberService2);
    }
    上記のテストコードを実行して、アドレス値が結果値と同じであることを確認します.

    ただし、アイテムをモノトーンで作成する場合は、注意が必要です.

    モノトーンの注意点


  • モノトーンの注意点
  • オブジェクトインスタンスを作成して共有する方法は、複数のクライアントが同じオブジェクトインスタンスを共有するため、オブジェクトはステータスを維持するように設計できません.
  • 無状態に設計する必要があります.
  • 特定のクライアントに依存フィールドはありません.
  • 特定のクライアントは、値を変更できるフィールドを持ってはいけません.
  • を読むように設計したほうがいいです.
  • フィールドではなく、領域変数、パラメータ、ThreadLocalなどを使用することが望ましい.
  • スプリングシートのフィールドに共有値を設定すると大きな障害になります!

  • アテンションポイントが認識されないときに発生する障害例コード
    public class StatefulService {
        private int price; //상태를 유지하는 필드
    
        public void order(String name, int price) {
            System.out.println("name = " + name + " price = " + price);
            this.price = price; //여기가 문제!
        }
        public int getPrice() {
            return price;
        }
    }
  • 上記のコードのようにステータスを変更できるステータスオブジェクトインスタンスを作成しました.
    @Test
    void statefulServiceSingleton(){
    
        AnnotationConfigApplicationContext 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);
    
        Assertions.assertThat(statefulService1.getPrice()).isEqualTo(20000);
    }
    上記のテストを行い、Aのユーザーは10000ウォンの商品を注文し、Bのユーザーは20000ウォンの商品を注文した.ただし、javaサーバは単一トーンモードであるため、インスタンスが共有され、インスタンスのpriceフィールドの値が変更されます.だから.

    前述したように、Aという10000ウォンの価格は20000ウォンに変更できない.

    @構成とモノトーン


    ここには好奇心のある場所があるかもしれませんそれではAppConfigファイルで確認してみましょう
    @Configuration
    public class AppConfig {
    
        //@Bean memberService -> new MemoryMemberRepository()
        //@Bean orderService -> new MemoryMemberRepository()
    
        @Bean
        public MemberService memberService(){
            System.out.println("call AppConfig.memberService");
            //생성자 주입
            return new MemberServiceImpl(memberRepository());
        }
    
        @Bean
        public MemberRepository memberRepository() {
            System.out.println("call AppConfig.memberRepository");
            return new MemoryMemberRepository();
        }
    
        @Bean
        public OrderService orderService(){
            System.out.println("call AppConfig.orderService");
            return new OrderServiceImpl(memberRepository(), discountPolicy());
        }
    
        @Bean
        public DiscountPolicy discountPolicy(){
            return new RateDiscountPolicy();
        }
    }
    上記のコードでは、memberServiceとorderServiceが2回MemoryMemerRepository()を呼び出し、memberRepository自体が1回呼び出され、合計3回MemoryMemerRepositoryが呼び出されます.では、このインスタンスをスプリングコンテナでどのように管理しますか?3回の呼び出しのすべてのインスタンスを作成する必要がありますか?インスタンスを共有しますか?モノトーンモードでは、1つに共有する必要があります.Springがこれを実現したのは,@Configurationでバイトコードを処理するライブラリCGIB技術を用いたためである.

    @構成のバイトコード操作

    @Bean
    public MemberRepository memberRepository() {
    
        if (memoryMemberRepository가 이미 스프링 컨테이너에 등록되어 있으면?) {
            return 스프링 컨테이너에서 찾아서 반환;
        } else { //스프링 컨테이너에 없으면
            기존 로직을 호출해서 MemoryMemberRepository를 생성하고 스프링 컨테이너에 등록
            return 반환
        }
    }
    上記のコードのような操作により、コンテナインスタンスを登録するかどうかを決定し、インスタンスを管理し、単一の色調であることを保証する.출처 https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B8%B0%EB%B3%B8%ED%8E%B8