Basic#8 Bean Scope
8. Bean Scope
1. 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
を提供した.コンテナ内のプロトタイプbeanを見つけて返します.DLに関連する複数の機能も含む.
@Autowired
private ObjectProvider<PrototypeBean> prototypeBeanProvider;
public void logic() {
PrototypeBean prototypeBean = prototypeBeanProvider.getObject();
...
}
ObjectProviderの親は、基本機能のみを持ちます.上記のサンプルコードでは、ObjectProviderをObjectFactoryに変更できます.
SSR-330 Provider
Javaが提供するDL機能.DLのみを提供し、Java規格としてspringフレームワークに依存せず、簡単です.
ただし、ObjectProviderはフロー処理などの便利な機能を提供し、フレームワークの変更や処理をほとんど必要としないため、ObjectProviderを使用することが多い.
6. Web Scope
Web scopeはWeb環境でのみ動作します.Prototypeとは異なり、終了ポイントまで管理されるため、終了メソッドは実行されます.
HTTPリクエストの入力と終了の範囲.(各リクエストはbeanインスタンスを作成して管理します.)
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首
Reference
この問題について(Basic#8 Bean Scope), 我々は、より多くの情報をここで見つけました https://velog.io/@dev2danis/Spring-Basic8-Bean-Scopeテキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol