Basic#8 Bean Scope


8. Bean Scope


1. Bean Scope?


ベンのライフサイクルを意味する
  • Singleton
  • Prototype
  • Web範囲
  • request
  • session
  • application
  • beanのデフォルトのライフサイクルは単一の例です.他のscopeも指定したい場合は、@Scope() annotationを使用します.

    2. Prototype scope


    Singletonが常に同じインスタンスを返す場合、Prototypeは毎回新しいインスタンスを生成して返します.

    Singleton


    containerを作成すると、初期化メソッドが実行され、beanを複数回表示しても同じインスタンスが参照されます.また、コンテナが終了するとbeanの終了方法が実行されます.

    Prototype


    コンテンツの作成時に作成されるのではなく、beanの表示時に作成され初期化されます.クエリ時にbeanが生成されるため、異なるインスタンスが生成されます.また,+DIの作成のみに関与するため,以降は管理しないため,コンテナが脱退してもbeanの脱退方法は実行されない.特定のbeanの終了メソッドが必要な場合は、直接呼び出す必要があります.
    @Scope("prototype")
    static class PrototypeBean {
    	@PostConstruc
        public void init() {
        	//init
        }
        
        @PreDestroy
        public void close() {
        	//close
        }
    }
    AnnotationConfigApplicationContext ac = new AnnoatationConfigApplicationContext(PrototypeBean.class);
    
    PrototypeBean bean1 = ac.getBean(PrototypeBean.class);	// 이 시점에 생성+DI
    PrototypeBean bean2 = ac.getBean(PrototypeBean.class);
    
    ac.close();	// PrototypeBean의 종료 method는 실행되지 않는다

    3.Singletonとの併用による不具合


    Singleton beanが注入されプロトタイプbeanが使用されると問題が発生する.Singletonはコンテナの作成時にbeanを作成しDIを実行する.つまり、Fieldを使用してPrototypeを持っている場合、そのPrototype beanもこの時点で作成されてインポートされるので、実際にはSingletonと何の違いもありません.
    これは、beanを再生成して使用する必要があるたびに問題が発生します.ライフサイクルもsingleton beanと一緒なので、singletonとして指定していない場合は、prototypeを他の方法で使用する必要があります.

    4.じゃあ、どうしていつも新しいモデルbeanを要求できるの?


    無知な方法はApplicationContextをドメインに持ち込み,それによってプロトタイプbeanを生成し導入することである.もちろん、このように使うのは望ましくありません.
    SpringはDL機能を提供してこれらの問題を解決する.外部でDIを行うのではなく、必要な関係を直接検索することを依存ログ(Dependency Lookup)、DLと呼び、プロトタイプbeanを要求する際に必要な機能です.

    5. DL (Dependency Lookup)


    ObjectProvider, ObjectFactory


    以前はApplicationContextを使用してprototype beanを問合せたが、springはDLにObjectProviderおよびObjectFactoryを提供した.
  • ObjectProvider
    コンテナ内のプロトタイプbeanを見つけて返します.DLに関連する複数の機能も含む.
  • @Autowired
    private ObjectProvider<PrototypeBean> prototypeBeanProvider;
    
    public void logic() {
    	PrototypeBean prototypeBean = prototypeBeanProvider.getObject();
        ...
    }
  • ObjectFactory
    ObjectProviderの親は、基本機能のみを持ちます.上記のサンプルコードでは、ObjectProviderをObjectFactoryに変更できます.
  • SSR-330 Provider


    Javaが提供するDL機能.DLのみを提供し、Java規格としてspringフレームワークに依存せず、簡単です.
    ただし、ObjectProviderはフロー処理などの便利な機能を提供し、フレームワークの変更や処理をほとんど必要としないため、ObjectProviderを使用することが多い.

    6. Web Scope


    Web scopeはWeb環境でのみ動作します.Prototypeとは異なり、終了ポイントまで管理されるため、終了メソッドは実行されます.
  • request
    HTTPリクエストの入力と終了の範囲.(各リクエストはbeanインスタンスを作成して管理します.)
  • session
  • websocket
  • request scopeの例


    トレースログのbeanを作成し、リクエストが入力されたときにそのbeanの操作例を実装します.
    ログのフォーマットは[UUID][requestURL]{message}とした.MyLogger.java
    @Component
    @Scope("request")
    public class MyLogger {
    	private String uuid;
        private String requestURL;
        
        // requestURL은 사용자가 HTTP 요청을 보내야만 알 수 있다. 즉 생성 시점에 지정할 수 없다.
        public void setRequestURL(String requestURL) {
        	this.requestURL = requestURL;
        }
        public void log(String message) {
        	System.out.println("["+uuid+"]["+requestURL+"] " + message);
        }
        
        @PostConstruct
        public void init() {
        	uuid = UUID.randomUUID().toString();
            System.out.println("["+uuid+"] request scope bean create");
        }
        @PreDestroy
        public void close() {
        	System.out.println("["+uuid+"] request scope bean close");
         }
     }
    HTTP要求がWeb上で送信されるまで、ユーザはrequestURLを知らなかったため、setterとして設定される.逆に、uuidは一意の値であり、要求のidである.そこで、初期化方法で管理を行う.LogDemoController.java
    @Controller
    @RequiredArgConstructor
    public class LogDemoController {
    	private final LogDemoService logDemoService;
        private final MyLogger myLogger;	// error
        
        @RequestMapping("log-demo")
        @ResponseBody
        public String logDemo(HttpServletRequest request) {
        	String requestUrl = request.getRequestURL().toString();
            
            myLogger.setRequestURL(requestURL);
            myLogger.log("controller test");
            logDemoService.logic("testID");
            return "OK";
        }
    }
    上のコードが間違っています.これは、MyLoggerがrequest scopeを有するライフサイクルのためです.すなわち、要求が入力された時点から応答が返されるまでの時間はライフサイクルであるが、LogDemoControllerはそれをフィールドとして携帯し、LogDemoController beanの実行および生成時に呼び出される問題をもたらす.
    ここでは,先に学習したProviderを用いてDLによりこの問題を解決することができる.LogDemoController.java
    	...
        private final ObjectProvider<MyLogger> myLoggerProvider;
        ...
        
        public String logDemo(HttpServletRequest request) {
        	...
            MyLogger myLogger = myLoggerProvider.getObject();
            ...
        }

    Proxy


    Providerのほか、Proxyも使用できます.エラー発生時のコードに戻るには、MyLoggerのscopeにパラメータを追加入力するだけです.MyLogger.java
    @Component
    @Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
    public class MyLogger {
    CGIBという名前のバイトコードライブラリを操作することによって、MyLoggerを継承する偽エージェントオブジェクトが生成される.Springcontainerは、偽エージェントオブジェクトをbeanとして登録し、内部で実際にオブジェクトを参照した場合、偽エージェントオブジェクトが真のオブジェクトに戻るように操作します.
    ダミーエージェントオブジェクトには委任ロジックのみがあるため、エンティティオブジェクトが検索されて返されます.containerの立場では,偽エージェントオブジェクトの内部要因よりも偽エージェントオブジェクト自体に注目するため,(多形性)singletonのように操作できる.
    重要な点は、providerでもproxyでも、実際に発生するまで遅延オブジェクトクエリを使用してscopeを処理することです.
    この文章を最後に金英漢のスプリングコア原理-プリミティブをまとめた.
    🛠 更新を続行する必要があります!
    2022.04.16首