ApplicationContext
の追加機能
「入門」の章で説明したように、org.springframework.beans.factory
パッケージは、プログラムによる方法など、Bean を管理および操作するための基本機能を提供します。org.springframework.context
パッケージは、他のインターフェースの拡張に加えて、BeanFactory
インターフェースを継承する ApplicationContext
(Javadoc) インターフェースを追加して、よりアプリケーションフレームワーク指向のスタイルで追加機能を提供します。多くの人は、ApplicationContext
を完全に宣言的な方法で使用しており、プログラムで作成することさえせず、代わりに ContextLoader
などのサポートクラスを利用して、Jakarta EE Web アプリケーションの通常の起動プロセスの一部として ApplicationContext
を自動的にインスタンス化します。
よりフレームワーク指向のスタイルで BeanFactory
機能を強化するために、コンテキストパッケージは次の機能も提供します。
MessageSource
インターフェースを介した i18n スタイルのメッセージへのアクセス。ResourceLoader
インターフェースを介した URL やファイルなどのリソースへのアクセス。イベントの公開。つまり、
ApplicationEventPublisher
インターフェースを使用した、ApplicationListener
インターフェースを実装する Bean へ。HierarchicalBeanFactory
インターフェースを介して、アプリケーションの Web レイヤーなどの 1 つの特定のレイヤーにそれぞれを集中させる、複数の(階層)コンテキストのロード。
MessageSource
を使用した国際化
ApplicationContext
インターフェースは、MessageSource
と呼ばれるインターフェースを継承するため、国際化( "i18n" )機能を提供します。Spring は、メッセージを階層的に解決できる HierarchicalMessageSource
インターフェースも提供します。これらのインターフェースはともに、Spring がメッセージ解決に影響を与える基盤を提供します。これらのインターフェースで定義されているメソッドは次のとおりです。
String getMessage(String code, Object[] args, String default, Locale loc)
:MessageSource
からメッセージを取得するために使用される基本的な方法。指定されたロケールのメッセージが見つからない場合、デフォルトのメッセージが使用されます。渡された引数は、標準ライブラリが提供するMessageFormat
機能を使用して、置換値になります。String getMessage(String code, Object[] args, Locale loc)
: 基本的に前の方法と同じですが、1 つの違いがあります: デフォルトのメッセージは指定できません。メッセージが見つからない場合、NoSuchMessageException
がスローされます。String getMessage(MessageSourceResolvable resolvable, Locale locale)
: 上記のメソッドで使用されるすべてのプロパティは、このメソッドで使用できるMessageSourceResolvable
という名前のクラスでもラップされます。
ApplicationContext
がロードされると、コンテキストで定義された MessageSource
Bean を自動的に検索します。Bean の名前は messageSource
でなければなりません。そのような Bean が見つかった場合、前述のメソッドへのすべての呼び出しはメッセージソースに委譲されます。メッセージソースが見つからない場合、ApplicationContext
は同じ名前の Bean を含む親を見つけようとします。存在する場合、その Bean を MessageSource
として使用します。ApplicationContext
がメッセージのソースを見つけられない場合、空の DelegatingMessageSource
がインスタンス化されて、上記で定義されたメソッドへの呼び出しを受け入れることができます。
Spring は、3 つの MessageSource
実装、ResourceBundleMessageSource
、ReloadableResourceBundleMessageSource
、StaticMessageSource
を提供します。それらはすべて、ネストされたメッセージングを行うために HierarchicalMessageSource
を実装します。StaticMessageSource
はめったに使用されませんが、ソースにメッセージを追加するプログラム的な方法を提供します。次の例は、ResourceBundleMessageSource
を示しています。
<beans>
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames">
<list>
<value>format</value>
<value>exceptions</value>
<value>windows</value>
</list>
</property>
</bean>
</beans>
この例では、クラスパスに format
、exceptions
、windows
という 3 つのリソースバンドルが定義されていることを前提としています。メッセージを解決するリクエストはすべて、ResourceBundle
オブジェクトを介してメッセージを解決する JDK 標準の方法で処理されます。例の目的上、上記の 2 つのリソースバンドルファイルの内容が次のとおりであると想定します。
# in format.properties
message=Alligators rock!
# in exceptions.properties
argument.required=The {0} argument is required.
次の例は、MessageSource
機能を実行するプログラムを示しています。すべての ApplicationContext
実装は MessageSource
実装でもあるため、MessageSource
インターフェースにキャストできることに注意してください。
Java
Kotlin
public static void main(String[] args) {
MessageSource resources = new ClassPathXmlApplicationContext("beans.xml");
String message = resources.getMessage("message", null, "Default", Locale.ENGLISH);
System.out.println(message);
}
fun main() {
val resources = ClassPathXmlApplicationContext("beans.xml")
val message = resources.getMessage("message", null, "Default", Locale.ENGLISH)
println(message)
}
上記のプログラムからの出力は次のとおりです。
Alligators rock!
要約すると、MessageSource
は、クラスパスのルートに存在する beans.xml
というファイルで定義されています。messageSource
Bean 定義は、basenames
プロパティを介して多数のリソースバンドルを参照します。リストで basenames
プロパティに渡される 3 つのファイルは、クラスパスのルートにファイルとして存在し、それぞれ format.properties
、exceptions.properties
、windows.properties
と呼ばれます。
次の例は、メッセージ検索に渡される引数を示しています。これらの引数は String
オブジェクトに変換され、ルックアップメッセージのプレースホルダーに挿入されます。
<beans>
<!-- this MessageSource is being used in a web application -->
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="exceptions"/>
</bean>
<!-- lets inject the above MessageSource into this POJO -->
<bean id="example" class="com.something.Example">
<property name="messages" ref="messageSource"/>
</bean>
</beans>
Java
Kotlin
public class Example {
private MessageSource messages;
public void setMessages(MessageSource messages) {
this.messages = messages;
}
public void execute() {
String message = this.messages.getMessage("argument.required",
new Object [] {"userDao"}, "Required", Locale.ENGLISH);
System.out.println(message);
}
}
class Example {
lateinit var messages: MessageSource
fun execute() {
val message = messages.getMessage("argument.required",
arrayOf("userDao"), "Required", Locale.ENGLISH)
println(message)
}
}
execute()
メソッドの呼び出しの結果の出力は次のとおりです。
The userDao argument is required.
国際化( "i18n" )に関して、Spring のさまざまな MessageSource
実装は、標準の JDK ResourceBundle
と同じロケール解決およびフォールバックルールに従います。要するに、前に定義した messageSource
の例を続けると、英国(en-GB
)ロケールに対してメッセージを解決する場合は、それぞれ format_en_GB.properties
、exceptions_en_GB.properties
、windows_en_GB.properties
というファイルを作成します。
通常、ロケール解決は、アプリケーションの周囲の環境によって管理されます。次の例では、(イギリスの)メッセージが解決されるロケールは手動で指定されます。
# in exceptions_en_GB.properties argument.required=Ebagum lad, the ''{0}'' argument is required, I say, required.
Java
Kotlin
public static void main(final String[] args) {
MessageSource resources = new ClassPathXmlApplicationContext("beans.xml");
String message = resources.getMessage("argument.required",
new Object [] {"userDao"}, "Required", Locale.UK);
System.out.println(message);
}
fun main() {
val resources = ClassPathXmlApplicationContext("beans.xml")
val message = resources.getMessage("argument.required",
arrayOf("userDao"), "Required", Locale.UK)
println(message)
}
上記のプログラムを実行した結果の出力は次のとおりです。
Ebagum lad, the 'userDao' argument is required, I say, required.
MessageSourceAware
インターフェースを使用して、定義済みの MessageSource
への参照を取得することもできます。MessageSourceAware
インターフェースを実装する ApplicationContext
で定義されている Bean には、Bean が作成および構成されたときに、アプリケーションコンテキストの MessageSource
が注入されます。
Spring の MessageSource は Java の ResourceBundle に基づいているため、同じベース名のバンドルをマージせず、最初に見つかったバンドルのみを使用します。同じベース名を持つ後続のメッセージバンドルは無視されます。 |
ResourceBundleMessageSource の代替として、Spring は ReloadableResourceBundleMessageSource クラスを提供します。このバリアントは同じバンドルファイル形式をサポートしていますが、標準の JDK ベースの ResourceBundleMessageSource 実装よりも柔軟性があります。特に、(クラスパスからだけでなく)Spring リソースの場所からファイルを読み取ることができ、バンドルプロパティファイルのホットリロードをサポートします(その間に効率的にキャッシュします)。詳細については、ReloadableResourceBundleMessageSource javadoc を参照してください。 |
標準およびカスタムイベント
ApplicationContext
のイベント処理は、ApplicationEvent
クラスと ApplicationListener
インターフェースを介して提供されます。ApplicationListener
インターフェースを実装する Bean がコンテキストにデプロイされる場合、ApplicationEvent
が ApplicationContext
に公開されるたびに、その Bean が通知されます。基本的に、これは標準の Observer デザインパターンです。
Spring 4.2 以降、イベントインフラストラクチャが大幅に改善され、アノテーションベースのモデルと、任意のイベント(つまり、必ずしも ApplicationEvent から拡張されないオブジェクト)を公開する機能が提供されています。そのようなオブジェクトが公開されると、それをイベントにラップします。 |
次の表に、Spring が提供する標準イベントを示します。
イベント | 説明 |
---|---|
|
|
|
|
|
|
|
|
| HTTP リクエストが処理されたことをすべての Bean に通知する Web 固有のイベント。このイベントは、リクエストが完了すると公開されます。このイベントは、Spring の |
| サーブレット固有のコンテキスト情報を追加する |
独自のカスタムイベントを作成して公開することもできます。次の例は、Spring の ApplicationEvent
基本クラスを継承する単純なクラスを示しています。
Java
Kotlin
public class BlockedListEvent extends ApplicationEvent {
private final String address;
private final String content;
public BlockedListEvent(Object source, String address, String content) {
super(source);
this.address = address;
this.content = content;
}
// accessor and other methods...
}
class BlockedListEvent(source: Any,
val address: String,
val content: String) : ApplicationEvent(source)
カスタム ApplicationEvent
を公開するには、ApplicationEventPublisher
で publishEvent()
メソッドを呼び出します。通常、これは、ApplicationEventPublisherAware
を実装するクラスを作成し、Spring Bean として登録することにより行われます。次の例は、このようなクラスを示しています。
Java
Kotlin
public class EmailService implements ApplicationEventPublisherAware {
private List<String> blockedList;
private ApplicationEventPublisher publisher;
public void setBlockedList(List<String> blockedList) {
this.blockedList = blockedList;
}
public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
public void sendEmail(String address, String content) {
if (blockedList.contains(address)) {
publisher.publishEvent(new BlockedListEvent(this, address, content));
return;
}
// send email...
}
}
class EmailService : ApplicationEventPublisherAware {
private lateinit var blockedList: List<String>
private lateinit var publisher: ApplicationEventPublisher
fun setBlockedList(blockedList: List<String>) {
this.blockedList = blockedList
}
override fun setApplicationEventPublisher(publisher: ApplicationEventPublisher) {
this.publisher = publisher
}
fun sendEmail(address: String, content: String) {
if (blockedList!!.contains(address)) {
publisher!!.publishEvent(BlockedListEvent(this, address, content))
return
}
// send email...
}
}
構成時に、Spring コンテナーは、EmailService
が ApplicationEventPublisherAware
を実装していることを検出し、自動的に setApplicationEventPublisher()
を呼び出します。実際には、渡されるパラメーターは Spring コンテナー自体です。ApplicationEventPublisher
インターフェースを介してアプリケーションコンテキストと対話しています。
カスタム ApplicationEvent
を受信するには、ApplicationListener
を実装するクラスを作成し、それを Spring Bean として登録します。次の例は、このようなクラスを示しています。
Java
Kotlin
public class BlockedListNotifier implements ApplicationListener<BlockedListEvent> {
private String notificationAddress;
public void setNotificationAddress(String notificationAddress) {
this.notificationAddress = notificationAddress;
}
public void onApplicationEvent(BlockedListEvent event) {
// notify appropriate parties via notificationAddress...
}
}
class BlockedListNotifier : ApplicationListener<BlockedListEvent> {
lateinit var notificationAddress: String
override fun onApplicationEvent(event: BlockedListEvent) {
// notify appropriate parties via notificationAddress...
}
}
ApplicationListener
は一般的にカスタムイベントの型でパラメーター化されることに注意してください (前の例では BlockedListEvent
)。これは、onApplicationEvent()
メソッドが型安全のままであり、ダウンキャストの必要性を回避できることを意味します。イベントリスナーは必要な数だけ登録できますが、デフォルトでは、イベントリスナーはイベントを同期的に受信することに注意してください。これは、すべてのリスナーがイベントの処理を完了するまで、publishEvent()
メソッドがブロックされることを意味します。この同期シングルスレッドのアプローチの利点の 1 つは、リスナーがイベントを受信すると、トランザクションコンテキストが利用可能な場合、パブリッシャーのトランザクションコンテキスト内で動作することです。イベント発行のための別の戦略 (デフォルトでの非同期イベント処理など) が必要になった場合は、Spring の ApplicationEventMulticaster
(Javadoc) インターフェースと SimpleApplicationEventMulticaster
(Javadoc) 実装の javadoc を参照して、カスタム "applicationEventMulticaster" Bean 定義に適用できる構成オプションを確認してください。このような場合、ThreadLocals とロギングコンテキストはイベント処理に伝播されません。可観測性に関する関心事の詳細については、「 @EventListener
可観測性」セクションを参照してください。
次の例は、上記の各クラスを登録および構成するために使用される Bean 定義を示しています。
<bean id="emailService" class="example.EmailService">
<property name="blockedList">
<list>
<value>[email protected] (英語) </value>
<value>[email protected] (英語) </value>
<value>[email protected] (英語) </value>
</list>
</property>
</bean>
<bean id="blockedListNotifier" class="example.BlockedListNotifier">
<property name="notificationAddress" value="[email protected] (英語) "/>
</bean>
<!-- optional: a custom ApplicationEventMulticaster definition -->
<bean id="applicationEventMulticaster" class="org.springframework.context.event.SimpleApplicationEventMulticaster">
<property name="taskExecutor" ref="..."/>
<property name="errorHandler" ref="..."/>
</bean>
これらすべてをまとめると、emailService
Bean の sendEmail()
メソッドが呼び出されたときに、ブロックする必要があるメールメッセージがある場合、BlockedListEvent
型のカスタムイベントが発行されます。blockedListNotifier
Bean は ApplicationListener
として登録され、BlockedListEvent
を受信します。この時点で適切な関係者に通知できます。
Spring のイベントメカニズムは、同じアプリケーションコンテキスト内で Spring Bean 間の単純な通信用に設計されています。ただし、より高度なエンタープライズ統合のニーズに対応するため、別途保守される Spring Integration プロジェクトは、よく知られている Spring プログラミングモデルに基づいた、軽量でパターン指向 (英語) のイベント駆動型アーキテクチャの構築を完全にサポートします。 |
アノテーションベースのイベントリスナー
@EventListener
アノテーションを使用して、マネージド Bean の任意のメソッドにイベントリスナーを登録できます。BlockedListNotifier
は次のように書き直すことができます。
Java
Kotlin
public class BlockedListNotifier {
private String notificationAddress;
public void setNotificationAddress(String notificationAddress) {
this.notificationAddress = notificationAddress;
}
@EventListener
public void processBlockedListEvent(BlockedListEvent event) {
// notify appropriate parties via notificationAddress...
}
}
class BlockedListNotifier {
lateinit var notificationAddress: String
@EventListener
fun processBlockedListEvent(event: BlockedListEvent) {
// notify appropriate parties via notificationAddress...
}
}
メソッドシグネチャーは、リッスンするイベント型を再度宣言しますが、今回は、柔軟な名前を使用して、特定のリスナーインターフェースを実装しません。実際のイベント型が実装階層内のジェネリクスパラメーターを解決する限り、ジェネリクスを介してイベント型を絞り込むこともできます。
メソッドが複数のイベントをリッスンする必要がある場合、またはパラメーターをまったく指定せずに定義する場合、アノテーション自体でイベント型を指定することもできます。次の例は、その方法を示しています。
Java
Kotlin
@EventListener({ContextStartedEvent.class, ContextRefreshedEvent.class})
public void handleContextStart() {
// ...
}
@EventListener(ContextStartedEvent::class, ContextRefreshedEvent::class)
fun handleContextStart() {
// ...
}
特定のイベントのメソッドを実際に呼び出すために一致する SpEL
式を定義するアノテーションの condition
属性を使用して、追加のランタイムフィルタリングを追加することもできます。
次の例は、イベントの content
属性が my-event
と等しい場合にのみ呼び出されるようにノーティファイアを書き換える方法を示しています。
Java
Kotlin
@EventListener(condition = "#blEvent.content == 'my-event'")
public void processBlockedListEvent(BlockedListEvent blEvent) {
// notify appropriate parties via notificationAddress...
}
@EventListener(condition = "#blEvent.content == 'my-event'")
fun processBlockedListEvent(blEvent: BlockedListEvent) {
// notify appropriate parties via notificationAddress...
}
各 SpEL
式は、専用のコンテキストに対して評価されます。次の表に、条件付きイベント処理に使用できるように、コンテキストで使用できるようにするアイテムを示します。
名前 | ロケーション | 説明 | サンプル |
---|---|---|---|
イベント | ルートオブジェクト | 実際の |
|
引数配列 | ルートオブジェクト | メソッドの呼び出しに使用される引数(オブジェクト配列として)。 |
|
引数名 | 評価コンテキスト | メソッド引数の名前。何らかの理由で名前が利用できない場合(たとえば、コンパイルされたバイトコードにデバッグ情報がないため)、 |
|
メソッドシグネチャーが実際に公開された任意のオブジェクトを参照している場合でも、#root.event
を使用すると、基になるイベントにアクセスできることに注意してください。
別のイベントを処理した結果としてイベントを発行する必要がある場合、次の例に示すように、発行する必要があるイベントを返すようにメソッドシグネチャーを変更できます。
Java
Kotlin
@EventListener
public ListUpdateEvent handleBlockedListEvent(BlockedListEvent event) {
// notify appropriate parties via notificationAddress and
// then publish a ListUpdateEvent...
}
@EventListener
fun handleBlockedListEvent(event: BlockedListEvent): ListUpdateEvent {
// notify appropriate parties via notificationAddress and
// then publish a ListUpdateEvent...
}
この機能は、非同期リスナーではサポートされていません。 |
handleBlockedListEvent()
メソッドは、処理するすべての BlockedListEvent
に対して新しい ListUpdateEvent
を公開します。複数のイベントを公開する必要がある場合は、代わりに Collection
またはイベントの配列を返すことができます。
非同期リスナー
特定のリスナーにイベントを非同期で処理させたい場合は、通常の @Async
サポートを再利用できます。次の例は、その方法を示しています。
Java
Kotlin
@EventListener
@Async
public void processBlockedListEvent(BlockedListEvent event) {
// BlockedListEvent is processed in a separate thread
}
@EventListener
@Async
fun processBlockedListEvent(event: BlockedListEvent) {
// BlockedListEvent is processed in a separate thread
}
非同期イベントを使用するときは、次の制限に注意してください。
非同期イベントリスナーが
Exception
をスローした場合、呼び出し元に伝播されません。詳細については、AsyncUncaughtExceptionHandler
(Javadoc) を参照してください。非同期イベントリスナーメソッドは、値を返すことで後続のイベントを発行できません。処理の結果として別のイベントを公開する必要がある場合は、
ApplicationEventPublisher
(Javadoc) を挿入してイベントを手動で公開します。ThreadLocals およびロギングコンテキストは、デフォルトではイベント処理に伝播されません。可観測性に関する関心事の詳細については、「
@EventListener
可観測性」セクションを参照してください。
リスナーの順序付け
あるリスナーを別のリスナーの前に呼び出す必要がある場合は、次の例に示すように、@Order
アノテーションをメソッド宣言に追加できます。
Java
Kotlin
@EventListener
@Order(42)
public void processBlockedListEvent(BlockedListEvent event) {
// notify appropriate parties via notificationAddress...
}
@EventListener
@Order(42)
fun processBlockedListEvent(event: BlockedListEvent) {
// notify appropriate parties via notificationAddress...
}
一般的なイベント
ジェネリクスを使用して、イベントの構造をさらに定義することもできます。T
が作成された実際のエンティティの型である EntityCreatedEvent<T>
の使用を検討してください。例: 次のリスナー定義を作成して、Person
の EntityCreatedEvent
のみを受信できます。
Java
Kotlin
@EventListener
public void onPersonCreated(EntityCreatedEvent<Person> event) {
// ...
}
@EventListener
fun onPersonCreated(event: EntityCreatedEvent<Person>) {
// ...
}
型の消去により、これは、発生したイベントがイベントリスナーがフィルター処理するジェネリクスパラメーター(つまり、class PersonCreatedEvent extends EntityCreatedEvent<Person> { … }
のようなもの)を解決する場合にのみ機能します。
特定の状況では、すべてのイベントが同じ構造に従う場合、これは非常に面倒になります(前の例のイベントの場合のように)。そのような場合、ResolvableTypeProvider
を実装して、ランタイム環境が提供するものを超えてフレームワークをガイドできます。次のイベントは、その方法を示しています。
Java
Kotlin
public class EntityCreatedEvent<T> extends ApplicationEvent implements ResolvableTypeProvider {
public EntityCreatedEvent(T entity) {
super(entity);
}
@Override
public ResolvableType getResolvableType() {
return ResolvableType.forClassWithGenerics(getClass(), ResolvableType.forInstance(getSource()));
}
}
class EntityCreatedEvent<T>(entity: T) : ApplicationEvent(entity), ResolvableTypeProvider {
override fun getResolvableType(): ResolvableType? {
return ResolvableType.forClassWithGenerics(javaClass, ResolvableType.forInstance(getSource()))
}
}
これは、ApplicationEvent だけでなく、イベントとして送信する任意のオブジェクトに対しても機能します。 |
最後に、従来の ApplicationListener
実装と同様に、実際のマルチキャストは実行時にコンテキスト全体の ApplicationEventMulticaster
を介して行われます。デフォルトでは、これは呼び出し側スレッドで同期イベント発行を行う SimpleApplicationEventMulticaster
です。これは、"applicationEventMulticaster" Bean 定義を通じて置き換え / カスタマイズできます。すべてのイベントを非同期に処理する場合、および / またはリスナー例外を処理する場合:
@Bean
ApplicationEventMulticaster applicationEventMulticaster() {
SimpleApplicationEventMulticaster multicaster = new SimpleApplicationEventMulticaster();
multicaster.setTaskExecutor(...);
multicaster.setErrorHandler(...);
return multicaster;
}
低レベルのリソースへの便利なアクセス
アプリケーションコンテキストを最適に使用して理解するには、リソースに従って、Spring の Resource
の抽象化に慣れる必要があります。
アプリケーションコンテキストは ResourceLoader
であり、Resource
オブジェクトのロードに使用できます。Resource
は、本質的に JDK java.net.URL
クラスのより機能が豊富なバージョンです。実際、Resource
の実装は、必要に応じて java.net.URL
のインスタンスをラップします。Resource
は、クラスパス、ファイルシステムの場所、標準 URL で記述可能な場所、その他のバリエーションを含む、ほぼすべての場所から透過的に低レベルのリソースを取得できます。リソースの場所の文字列が特別なプレフィックスのない単純なパスである場合、それらのリソースがどこから来るかは、実際のアプリケーションコンテキスト型に固有で適切です。
アプリケーションコンテキストにデプロイされた Bean を構成して、特殊なコールバックインターフェース ResourceLoaderAware
を実装し、初期化時に自動的にコールバックされ、アプリケーションコンテキスト自体が ResourceLoader
として渡されます。Resource
型のプロパティを公開して、静的リソースへのアクセスに使用することもできます。それらは他のプロパティと同様にそこに注入されます。これらの Resource
プロパティを単純な String
パスとして指定し、Bean がデプロイされるときにそれらのテキスト文字列から実際の Resource
オブジェクトへの自動変換に依存することができます。
ApplicationContext
コンストラクターに提供されるロケーションパスは、実際にはリソース文字列であり、単純な形式では、特定のコンテキスト実装に従って適切に処理されます。たとえば、ClassPathXmlApplicationContext
は単純なロケーションパスをクラスパスロケーションとして扱います。また、実際のコンテキスト型に関係なく、クラスパスまたは URL から定義を強制的にロードするために、特別なプレフィックスを持つロケーションパス(リソース文字列)を使用できます。
アプリケーションの起動追跡
ApplicationContext
は、Spring アプリケーションのライフサイクルを管理し、コンポーネントに関する豊富なプログラミングモデルを提供します。その結果、複雑なアプリケーションは、同様に複雑なコンポーネントグラフと起動フェーズを持つことができます。
特定のメトリクスを使用してアプリケーションの起動手順を追跡すると、起動フェーズで時間が費やされている場所を理解できますが、コンテキストライフサイクル全体をよりよく理解する方法としても使用できます。
AbstractApplicationContext
(およびそのサブクラス)には、さまざまな起動フェーズに関する StartupStep
データを収集する ApplicationStartup
が装備されています。
アプリケーションコンテキストのライフサイクル (基本パッケージのスキャン、構成クラスの管理)
Bean のライフサイクル (インスタンス化、スマート初期化、後処理)
アプリケーションイベント処理
AnnotationConfigApplicationContext
のインストルメンテーションの例を次に示します。
Java
Kotlin
// create a startup step and start recording
StartupStep scanPackages = getApplicationStartup().start("spring.context.base-packages.scan");
// add tagging information to the current step
scanPackages.tag("packages", () -> Arrays.toString(basePackages));
// perform the actual phase we're instrumenting
this.scanner.scan(basePackages);
// end the current step
scanPackages.end();
// create a startup step and start recording
val scanPackages = getApplicationStartup().start("spring.context.base-packages.scan")
// add tagging information to the current step
scanPackages.tag("packages", () -> Arrays.toString(basePackages))
// perform the actual phase we're instrumenting
this.scanner.scan(basePackages)
// end the current step
scanPackages.end()
アプリケーションコンテキストには、すでに複数のステップが組み込まれています。これらの起動ステップを記録すると、特定のツールを使用して収集、表示、分析できます。既存の起動手順の完全なリストについては、専用の付録セクションを参照してください。
デフォルトの ApplicationStartup
実装は、オーバーヘッドを最小限に抑えるための no-op バリアントです。これは、デフォルトでは、アプリケーションの起動時にメトリクスが収集されないことを意味します。Spring Framework には、Java Flight Recorder を使用して起動ステップを追跡するための実装が付属しています: FlightRecorderApplicationStartup
。このバリアントを使用するには、作成されたらすぐに、そのインスタンスを ApplicationContext
に構成する必要があります。
開発者は、独自の AbstractApplicationContext
サブクラスを提供している場合、またはより正確なデータを収集したい場合にも、ApplicationStartup
インフラストラクチャを使用できます。
ApplicationStartup は、アプリケーションの起動時およびコアコンテナーにのみ使用することを目的としています。これは、Java プロファイラーや Micrometer (英語) のようなメトリクスライブラリに代わるものではありません。 |
カスタム StartupStep
の収集を開始するには、コンポーネントは、アプリケーションコンテキストから ApplicationStartup
インスタンスを直接取得するか、コンポーネントに ApplicationStartupAware
を実装させるか、任意のインジェクションポイントで ApplicationStartup
型を要求します。
開発者は、カスタム起動ステップを作成するときに "spring.*" 名前空間を使用しないでください。この名前空間は、内部 Spring の使用のために予約されており、変更される可能性があります。 |
Web アプリケーション用の便利な ApplicationContext インスタンス化
ApplicationContext
インスタンスを宣言的に作成するには、たとえば ContextLoader
を使用します。もちろん、ApplicationContext
実装の 1 つを使用して、ApplicationContext
インスタンスをプログラムで作成することもできます。
次の例に示すように、ContextLoaderListener
を使用して ApplicationContext
を登録できます。
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/daoContext.xml /WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
リスナーは contextConfigLocation
パラメーターをインスペクションします。パラメーターが存在しない場合、リスナーは /WEB-INF/applicationContext.xml
をデフォルトとして使用します。パラメーターが存在する場合、リスナーは事前定義の区切り文字(コンマ、セミコロン、空白)を使用して String
を分離し、値をアプリケーションコンテキストが検索される場所として使用します。Ant スタイルのパスパターンもサポートされています。例は、/WEB-INF/*Context.xml
(名前が Context.xml
で終わり、WEB-INF
ディレクトリにあるすべてのファイル用)および /WEB-INF/**/*Context.xml
(WEB-INF
のサブディレクトリ内のすべてのそのようなファイル用)です。
Spring ApplicationContext
を Jakarta EE RAR File としてデプロイする
Spring ApplicationContext
を RAR ファイルとしてデプロイし、コンテキストとそれに必要なすべての Bean クラスおよびライブラリ JAR を Jakarta EE RAR デプロイユニットにカプセル化することができます。これは、スタンドアロン ApplicationContext
(Jakarta EE 環境でのみホストされる)をブートストラップして、Jakarta EE サーバー機能にアクセスできるようにすることと同じです。RAR デプロイは、ヘッドレス WAR ファイルをデプロイするシナリオのより自然な代替手段です。実際には、Jakarta EE 環境で Spring ApplicationContext
をブートストラップするためにのみ使用される HTTP エントリポイントのない WAR ファイルです。
RAR デプロイは、HTTP エントリポイントを必要とせず、メッセージエンドポイントとスケジュールされたジョブのみで構成されるアプリケーションコンテキストに最適です。このようなコンテキストの Bean は、JTA トランザクションマネージャー、JNDI バインド JDBC DataSource
インスタンス、JMS ConnectionFactory
インスタンスなどのアプリケーションサーバーリソースを使用できます。また、プラットフォームの JMX サーバーに登録できます。すべて Spring の標準トランザクション管理と JNDI および JMX サポート機能を使用します。アプリケーションコンポーネントは、Spring の TaskExecutor
抽象化を通じてアプリケーションサーバーの JCA WorkManager
と対話することもできます。
RAR デプロイに関連する構成の詳細については、SpringContextResourceAdapter
(Javadoc) クラスの javadoc を参照してください。
Jakarta EE RAR ファイルとしての Spring ApplicationContext の単純なデプロイの場合:
すべてのアプリケーションクラスを RAR ファイル(ファイル拡張子が異なる標準の JAR ファイル)にパッケージ化します。
必要なすべてのライブラリ JAR を RAR アーカイブのルートに追加します。
META-INF/ra.xml
デプロイ記述子(SpringContextResourceAdapter
の javadoc に示されている)と対応する Spring XML Bean 定義ファイル(通常はMETA-INF/applicationContext.xml
)を追加します。結果の RAR ファイルをアプリケーションサーバーのデプロイディレクトリにドロップします。
このような RAR デプロイユニットは通常、自己完結型です。コンポーネントを外部に公開することはなく、同じアプリケーションの他のモジュールにも公開しません。RAR ベースの ApplicationContext との相互作用は、通常、他のモジュールと共有する JMS 宛先を介して行われます。また、RAR ベースの ApplicationContext は、たとえば、一部のジョブをスケジュールしたり、ファイルシステム内の新しいファイルに反応したりすることもあります(または同様のもの)。外部からの同期アクセスを許可する必要がある場合、(たとえば)RMI エンドポイントをエクスポートできます。これは、同じマシン上の他のアプリケーションモジュールによって使用される可能性があります。 |