クラスパススキャンと管理対象コンポーネント
この章のほとんどの例では、XML を使用して、Spring コンテナー内の各 BeanDefinition
を生成する構成メタデータを指定します。前のセクション ( アノテーションベースのコンテナー構成 ) では、ソースレベルのアノテーションを通じて多くの構成メタデータを提供する方法を示しています。ただし、これらの例でも、「ベース」の Bean 定義は XML ファイルで明示的に定義されていますが、アノテーションは依存性注入のみを駆動します。このセクションでは、クラスパスをスキャンして候補コンポーネントを暗黙的に検出するオプションについて説明します。候補コンポーネントは、フィルター条件に一致するクラスであり、対応する Bean 定義がコンテナーに登録されています。これにより、XML を使用して Bean 登録を実行する必要がなくなります。代わりに、アノテーション ( @Component
など)、AspectJ 型の式、または独自のカスタムフィルター条件を使用して、コンテナーに登録されている Bean 定義を持つクラスを選択できます。
XML ファイルを使用するのではなく、Java を使用して Bean を定義できます。これらの機能の使用方法の例については、 |
@Component
およびその他のステレオタイプアノテーション
@Repository
アノテーションは、リポジトリ (データアクセスオブジェクトまたは DAO とも呼ばれます) のロールまたはステレオタイプを満たすクラスのマーカーです。このマーカーの用途には、例外変換で説明されている例外の自動変換があります。
Spring は、さらなるステレオタイプアノテーション @Component
、@Service
、@Controller
を提供します。@Component
は、Spring が管理するコンポーネントの一般的なステレオタイプです。@Repository
、@Service
、@Controller
は、より具体的なユースケース (それぞれ永続層、サービス層、プレゼンテーション層) に特化した @Component
です。コンポーネントクラスに @Component
のアノテーションを付けることもできますが、代わりに @Repository
、@Service
、または @Controller
のアノテーションを付けることによって、クラスはツールによる処理やアスペクトとの関連付けにより適切に適合します。例: これらのステレオタイプのアノテーションは、ポイントカットの理想的なターゲットになります。@Repository
、@Service
、@Controller
は、Spring Framework の将来のリリースで追加のセマンティクスも搭載される可能性があります。サービス層に @Component
と @Service
のどちらを使用するかを選択する場合は、明らかに @Service
の方が良い選択です。同様に、前述したように、@Repository
は永続層での自動例外変換のマーカーとしてすでにサポートされています。
メタアノテーションと合成アノテーションの使用
Spring が提供するアノテーションの多くは、独自のコードでメタアノテーションとして使用できます。メタアノテーションは、別のアノテーションに適用できるアノテーションです。例: 前出の @Service
アノテーションは、次の例に示すように、@Component
でメタアノテーションが付けられています。
Java
Kotlin
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component (1)
public @interface Service {
// ...
}
1 | @Component により、@Service は @Component と同じ方法で処理されます。 |
@Target(AnnotationTarget.TYPE)
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
@Component (1)
annotation class Service {
// ...
}
1 | @Component により、@Service は @Component と同じ方法で処理されます。 |
メタアノテーションを組み合わせて「合成アノテーション」を作成することもできます。例: Spring MVC からの @RestController
アノテーションは、@Controller
と @ResponseBody
で構成されます。
さらに、構成されたアノテーションは、必要に応じてメタアノテーションから属性を再宣言して、カスタマイズを可能にすることができます。これは、メタアノテーションの属性のサブセットのみを公開する場合に特に役立ちます。例: Spring の @SessionScope
アノテーションは、スコープ名を session
にハードコードしますが、それでも proxyMode
のカスタマイズを許可します。次のリストは、SessionScope
アノテーションの定義を示しています。
Java
Kotlin
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Scope(WebApplicationContext.SCOPE_SESSION)
public @interface SessionScope {
/**
* Alias for {@link Scope#proxyMode}.
* <p>Defaults to {@link ScopedProxyMode#TARGET_CLASS}.
*/
@AliasFor(annotation = Scope.class)
ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS;
}
@Target(AnnotationTarget.TYPE, AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
@Scope(WebApplicationContext.SCOPE_SESSION)
annotation class SessionScope(
@get:AliasFor(annotation = Scope::class)
val proxyMode: ScopedProxyMode = ScopedProxyMode.TARGET_CLASS
)
その後、proxyMode
を次のように宣言せずに @SessionScope
を使用できます。
Java
Kotlin
@Service
@SessionScope
public class SessionScopedService {
// ...
}
@Service
@SessionScope
class SessionScopedService {
// ...
}
次の例に示すように、proxyMode
の値をオーバーライドすることもできます。
Java
Kotlin
@Service
@SessionScope(proxyMode = ScopedProxyMode.INTERFACES)
public class SessionScopedUserService implements UserService {
// ...
}
@Service
@SessionScope(proxyMode = ScopedProxyMode.INTERFACES)
class SessionScopedUserService : UserService {
// ...
}
詳細については、Spring アノテーションプログラミングモデル [GitHub] (英語) wiki ページを参照してください。
クラスの自動検出と Bean 定義の登録
Spring は、ステレオタイプ化されたクラスを自動的に検出し、対応する BeanDefinition
インスタンスを ApplicationContext
に登録できます。例: 次の 2 つのクラスは、このような自動検出の対象です。
Java
Kotlin
@Service
public class SimpleMovieLister {
private MovieFinder movieFinder;
public SimpleMovieLister(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
}
@Service
class SimpleMovieLister(private val movieFinder: MovieFinder)
Java
Kotlin
@Repository
public class JpaMovieFinder implements MovieFinder {
// implementation elided for clarity
}
@Repository
class JpaMovieFinder : MovieFinder {
// implementation elided for clarity
}
これらのクラスを自動検出して対応する Bean を登録するには、@ComponentScan
を @Configuration
クラスに追加する必要があります。basePackages
属性は 2 つのクラスの共通の親パッケージです。(または、各クラスの親パッケージを含むコンマ区切り、セミコロン区切り、スペース区切りのリストを指定できます。)
Java
Kotlin
@Configuration
@ComponentScan(basePackages = "org.example")
public class AppConfig {
// ...
}
@Configuration
@ComponentScan(basePackages = ["org.example"])
class AppConfig {
// ...
}
簡潔にするために、前の例ではアノテーションの value 属性(つまり @ComponentScan("org.example") )を使用できます。 |
次の代替方法では XML を使用します。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="org.example"/>
</beans>
<context:component-scan> を使用すると、<context:annotation-config> の機能が暗黙的に有効になります。<context:component-scan> を使用する場合、通常 <context:annotation-config> 要素を含める必要はありません。 |
クラスパスパッケージをスキャンするには、対応するディレクトリエントリがクラスパスに存在する必要があります。Ant を使用して JAR をビルドする場合は、JAR タスクのファイルのみのスイッチをアクティブにしないでください。また、一部の環境では、セキュリティポリシーに基づいてクラスパスディレクトリが公開されない場合があります。たとえば、JDK 1.7.0_45 以降のスタンドアロンアプリ(マニフェストで 'Trusted-Library' の設定が必要です。stackoverflow.com/questions/19394570/java-jre-7u45-breaks-classloader-getresources (英語) を参照してください)。 モジュールパス (Java モジュールシステム) では、Spring のクラスパススキャンは、通常、期待どおりに動作します。ただし、コンポーネントクラスが |
さらに、コンポーネントスキャン要素を使用すると、AutowiredAnnotationBeanPostProcessor
と CommonAnnotationBeanPostProcessor
の両方が暗黙的に含まれます。つまり、2 つのコンポーネントは自動的に検出され、相互に接続されます。すべて XML で提供される Bean 構成メタデータはありません。
false の値を持つ annotation-config 属性を含めることにより、AutowiredAnnotationBeanPostProcessor および CommonAnnotationBeanPostProcessor の登録を無効にできます。 |
フィルターを使用してスキャンをカスタマイズする
デフォルトでは、@Component
、@Repository
、@Service
、@Controller
、@Configuration
でアノテーションが付けられたクラス、または @Component
でアノテーションが付けられたカスタムアノテーションのみが検出された候補コンポーネントです。ただし、カスタムフィルターを適用することにより、この動作を変更および拡張できます。@ComponentScan
アノテーションの includeFilters
または excludeFilters
属性として(または XML 構成の <context:component-scan>
要素の <context:include-filter />
または <context:exclude-filter />
子要素として)追加します。各フィルター要素には、type
および expression
属性が必要です。次の表で、フィルタリングオプションについて説明します。
フィルタータイプ | 式の例 | 説明 |
---|---|---|
アノテーション (default) |
| ターゲットコンポーネントの型レベルで存在またはメタ表示するアノテーション。 |
assignable |
| ターゲットコンポーネントが割り当てられる(拡張または実装する)クラス(またはインターフェース)。 |
aspectj |
| ターゲットコンポーネントによって照合される AspectJ 型式。 |
regex |
| ターゲットコンポーネントのクラス名と一致する正規表現。 |
custom |
|
|
次の例は、すべての @Repository
アノテーションを無視し、代わりに「スタブ」リポジトリを使用する構成を示しています。
Java
Kotlin
@Configuration
@ComponentScan(basePackages = "org.example",
includeFilters = @Filter(type = FilterType.REGEX, pattern = ".*Stub.*Repository"),
excludeFilters = @Filter(Repository.class))
public class AppConfig {
// ...
}
@Configuration
@ComponentScan(basePackages = ["org.example"],
includeFilters = [Filter(type = FilterType.REGEX, pattern = [".*Stub.*Repository"])],
excludeFilters = [Filter(Repository::class)])
class AppConfig {
// ...
}
次のリストは、同等の XML を示しています。
<beans>
<context:component-scan base-package="org.example">
<context:include-filter type="regex"
expression=".*Stub.*Repository"/>
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Repository"/>
</context:component-scan>
</beans>
アノテーションに useDefaultFilters=false を設定するか、<component-scan/> 要素の属性として use-default-filters="false" を指定することにより、デフォルトのフィルターを無効にすることもできます。これにより、@Component 、@Repository 、@Service 、@Controller 、@RestController または @Configuration でアノテーションが付けられたクラスまたはメタアノテーションが付けられたクラスの自動検出が事実上無効になります。 |
コンポーネント内での Bean メタデータの定義
Spring コンポーネントは、Bean 定義メタデータをコンテナーに提供することもできます。これは、@Configuration
アノテーション付きクラス内で Bean メタデータを定義するために使用されるのと同じ @Bean
アノテーションを使用して行うことができます。次の例は、その方法を示しています。
Java
Kotlin
@Component
public class FactoryMethodComponent {
@Bean
@Qualifier("public")
public TestBean publicInstance() {
return new TestBean("publicInstance");
}
public void doWork() {
// Component method implementation omitted
}
}
@Component
class FactoryMethodComponent {
@Bean
@Qualifier("public")
fun publicInstance() = TestBean("publicInstance")
fun doWork() {
// Component method implementation omitted
}
}
上記のクラスは、doWork()
メソッドにアプリケーション固有のコードを持つ Spring コンポーネントです。ただし、メソッド publicInstance()
を参照するファクトリメソッドを持つ Bean 定義にも貢献します。@Bean
アノテーションは、ファクトリメソッドおよびその他の Bean 定義プロパティ(@Qualifier
アノテーションによる修飾子値など)を識別します。指定できるその他のメソッドレベルのアノテーションは、@Scope
、@Lazy
、カスタム修飾子アノテーションです。
コンポーネントの初期化のロールに加えて、 |
前述のように、@Bean
メソッドのオートワイヤーの追加サポートとともに、オートワイヤーフィールドとメソッドがサポートされています。次の例は、その方法を示しています。
Java
Kotlin
@Component
public class FactoryMethodComponent {
private static int i;
@Bean
@Qualifier("public")
public TestBean publicInstance() {
return new TestBean("publicInstance");
}
// use of a custom qualifier and autowiring of method parameters
@Bean
protected TestBean protectedInstance(
@Qualifier("public") TestBean spouse,
@Value("#{privateInstance.age}") String country) {
TestBean tb = new TestBean("protectedInstance", 1);
tb.setSpouse(spouse);
tb.setCountry(country);
return tb;
}
@Bean
private TestBean privateInstance() {
return new TestBean("privateInstance", i++);
}
@Bean
@RequestScope
public TestBean requestScopedInstance() {
return new TestBean("requestScopedInstance", 3);
}
}
@Component
class FactoryMethodComponent {
companion object {
private var i: Int = 0
}
@Bean
@Qualifier("public")
fun publicInstance() = TestBean("publicInstance")
// use of a custom qualifier and autowiring of method parameters
@Bean
protected fun protectedInstance(
@Qualifier("public") spouse: TestBean,
@Value("#{privateInstance.age}") country: String) = TestBean("protectedInstance", 1).apply {
this.spouse = spouse
this.country = country
}
@Bean
private fun privateInstance() = TestBean("privateInstance", i++)
@Bean
@RequestScope
fun requestScopedInstance() = TestBean("requestScopedInstance", 3)
}
この例では、String
メソッドパラメーター country
を、privateInstance
という名前の別の Bean の age
プロパティの値に自動接続します。Spring Expression Language エレメントは、表記 #{ <expression> }
を介してプロパティの値を定義します。@Value
アノテーションの場合、式テキストを解決するときに Bean 名を検索するように式リゾルバーが事前構成されています。
Spring Framework 4.3 以降では、型 InjectionPoint
(またはそのより具象サブクラス: DependencyDescriptor
)のファクトリメソッドパラメーターを宣言して、現在の Bean の作成をトリガーするリクエストしているインジェクションポイントにアクセスすることもできます。これは、Bean インスタンスの実際の作成にのみ適用され、既存のインスタンスの挿入には適用されないことに注意してください。結果として、この機能はプロトタイプスコープの Bean に最も意味があります。他のスコープの場合、ファクトリメソッドは、指定されたスコープで新しい Bean インスタンスの作成をトリガーしたインジェクションポイントのみを確認します(たとえば、遅延シングルトン Bean の作成をトリガーした依存関係)。このようなシナリオでは、提供されたインジェクションポイントメタデータをセマンティックケアで使用できます。次の例は、InjectionPoint
の使用方法を示しています。
Java
Kotlin
@Component
public class FactoryMethodComponent {
@Bean @Scope("prototype")
public TestBean prototypeInstance(InjectionPoint injectionPoint) {
return new TestBean("prototypeInstance for " + injectionPoint.getMember());
}
}
@Component
class FactoryMethodComponent {
@Bean
@Scope("prototype")
fun prototypeInstance(injectionPoint: InjectionPoint) =
TestBean("prototypeInstance for ${injectionPoint.member}")
}
通常の Spring コンポーネントの @Bean
メソッドは、Spring @Configuration
クラス内の対応する @Bean
メソッドとは異なる方法で処理されます。違いは、@Component
クラスがメソッドとフィールドの呼び出しをインターセプトするために CGLIB で拡張されていないことです。CGLIB プロキシは、@Configuration
クラスの @Bean
メソッド内のメソッドまたはフィールドを呼び出すことにより、コラボレーションオブジェクトへの Bean メタデータ参照を作成する手段です。このようなメソッドは、通常の Java セマンティクスで呼び出されるのではなく、@Bean
メソッドのプログラム呼び出しで他の Bean を参照する場合でも、Spring Bean の通常のライフサイクル管理とプロキシを提供するためにコンテナーを通過します。対照的に、プレーン @Component
クラス内の @Bean
メソッドでメソッドまたはフィールドを呼び出すには、標準の Java セマンティクスがあり、特別な CGLIB 処理やその他の制約は適用されません。
静的な
最後に、実行時に利用可能な依存関係に応じて使用する複数のファクトリメソッドの配置として、単一のクラスが同じ Bean に対して複数の |
自動検出されたコンポーネントの命名
コンポーネントがスキャンプロセスの一部として自動検出されると、そのスキャナーに認識されている BeanNameGenerator
戦略によってその Bean 名が生成されます。
デフォルトでは、AnnotationBeanNameGenerator
が使用されます。Spring ステレオタイプアノテーションの場合、アノテーションの value
属性を介して名前を指定すると、その名前は対応する Bean 定義の名前として使用されます。この規則は、Spring ステレオタイプアノテーションの代わりに JSR-250 および JSR-330 アノテーションが使用される場合にも適用されます: @jakarta.annotation.ManagedBean
、@javax.annotation.ManagedBean
、@jakarta.inject.Named
、@javax.inject.Named
。
Spring Framework 6.1 以降、Bean 名の指定に使用されるアノテーション属性の名前は value
である必要はなくなりました。カスタムステレオタイプアノテーションでは、別の名前 ( name
など) で属性を宣言し、その属性に @AliasFor(annotation = Component.class, attribute = "value")
のアノテーションを付けることができます。具体的な例については、ControllerAdvice#name()
のソースコード宣言を参照してください。
Spring Framework 6.1 の時点で、規則に基づくステレオタイプ名のサポートは非推奨となり、フレームワークの将来のバージョンでは削除される予定です。カスタムステレオタイプアノテーションは、 |
このようなアノテーションやその他の検出されたコンポーネント (カスタムフィルターによって検出されたものなど) から明示的な Bean 名を導出できない場合、デフォルトの Bean 名ジェネレーターは大文字を含まない非修飾クラス名を返します。例: 次のコンポーネントクラスが検出された場合、名前は myMovieLister
および movieFinderImpl
になります。
Java
Kotlin
@Service("myMovieLister")
public class SimpleMovieLister {
// ...
}
@Service("myMovieLister")
class SimpleMovieLister {
// ...
}
Java
Kotlin
@Repository
public class MovieFinderImpl implements MovieFinder {
// ...
}
@Repository
class MovieFinderImpl : MovieFinder {
// ...
}
デフォルトの Bean 命名戦略に依存したくない場合は、カスタムの Bean 命名戦略を提供できます。まず、BeanNameGenerator
(Javadoc) インターフェースを実装し、デフォルトの引数なしのコンストラクターを必ず含めてください。次に、以下のアノテーションの例と Bean 定義が示すように、スキャナーの構成時に完全修飾クラス名を指定します。
複数の自動検出されたコンポーネントが同じ非修飾クラス名を持つために名前の競合が発生した場合 (つまり、同じ名前を持つが異なるパッケージに存在するクラス)、生成された Bean 名に対して完全修飾クラス名をデフォルトとする BeanNameGenerator を構成する必要がある場合があります。パッケージ org.springframework.context.annotation にある FullyQualifiedAnnotationBeanNameGenerator は、このような目的に使用できます。 |
Java
Kotlin
@Configuration
@ComponentScan(basePackages = "org.example", nameGenerator = MyNameGenerator.class)
public class AppConfig {
// ...
}
@Configuration
@ComponentScan(basePackages = ["org.example"], nameGenerator = MyNameGenerator::class)
class AppConfig {
// ...
}
<beans>
<context:component-scan base-package="org.example"
name-generator="org.example.MyNameGenerator" />
</beans>
一般的な規則として、他のコンポーネントが明示的に参照している場合は、アノテーションで名前を指定することを検討してください。一方、コンテナーが接続を担当する場合は、自動生成された名前で十分です。
自動検出されたコンポーネントのスコープを提供する
一般に Spring 管理コンポーネントと同様に、自動検出されたコンポーネントのデフォルトで最も一般的なスコープは singleton
です。ただし、@Scope
アノテーションで指定できる別のスコープが必要になる場合があります。次の例に示すように、アノテーション内でスコープの名前を指定できます。
Java
Kotlin
@Scope("prototype")
@Repository
public class MovieFinderImpl implements MovieFinder {
// ...
}
@Scope("prototype")
@Repository
class MovieFinderImpl : MovieFinder {
// ...
}
@Scope アノテーションは、具体的な Bean クラス(アノテーション付きコンポーネントの場合)またはファクトリメソッド(@Bean メソッドの場合)でのみイントロスペクトされます。XML Bean 定義とは対照的に、Bean 定義の継承という概念はなく、クラスレベルの継承階層はメタデータの目的には関係ありません。 |
Spring コンテキストでの「リクエスト」や「セッション」などの Web 固有のスコープの詳細については、リクエスト、セッション、アプリケーション、WebSocket スコープを参照してください。これらのスコープ用に事前に作成されたアノテーションと同様に、Spring のメタアノテーションアプローチを使用して独自のスコープアノテーションを作成することもできます。たとえば、@Scope("prototype")
でアノテーションが付けられたカスタムアノテーションメタは、カスタムスコーププロキシモードを宣言することもできます。
アノテーションベースのアプローチに依存するのではなく、スコープ解決のカスタム戦略を提供するために、ScopeMetadataResolver (Javadoc) インターフェースを実装できます。デフォルトの引数なしのコンストラクターを必ず含めてください。次に、以下のアノテーションと Bean 定義の例に示すように、スキャナーの構成時に完全修飾クラス名を指定できます。 |
Java
Kotlin
@Configuration
@ComponentScan(basePackages = "org.example", scopeResolver = MyScopeResolver.class)
public class AppConfig {
// ...
}
@Configuration
@ComponentScan(basePackages = ["org.example"], scopeResolver = MyScopeResolver::class)
class AppConfig {
// ...
}
<beans>
<context:component-scan base-package="org.example" scope-resolver="org.example.MyScopeResolver"/>
</beans>
特定の非シングルトンスコープを使用する場合、スコープオブジェクトのプロキシを生成する必要がある場合があります。推論は依存関係としてのスコープ Bean で説明されています。この目的のために、scoped-proxy 属性を component-scan 要素で使用できます。可能な 3 つの値は次のとおりです。no
、interfaces
、targetClass
。例: 次の構成では、標準の JDK 動的プロキシが生成されます。
Java
Kotlin
@Configuration
@ComponentScan(basePackages = "org.example", scopedProxy = ScopedProxyMode.INTERFACES)
public class AppConfig {
// ...
}
@Configuration
@ComponentScan(basePackages = ["org.example"], scopedProxy = ScopedProxyMode.INTERFACES)
class AppConfig {
// ...
}
<beans>
<context:component-scan base-package="org.example" scoped-proxy="interfaces"/>
</beans>
アノテーション付きの修飾子メタデータの提供
@Qualifier
アノテーションについては、修飾子を使用したアノテーションベースのオートワイヤーの微調整で説明しています。そのセクションの例では、@Qualifier
アノテーションとカスタム修飾子アノテーションを使用して、オートワイヤーの候補を解決するときにきめ細かな制御を提供します。これらの例は XML Bean 定義に基づいているため、XML の bean
要素の qualifier
または meta
子要素を使用して、候補の Bean 定義に修飾子メタデータが提供されました。コンポーネントの自動検出をクラスパススキャンに依存している場合、候補クラスの型レベルのアノテーションを修飾子メタデータに提供できます。次の 3 つの例は、この手法を示しています。
Java
Kotlin
@Component
@Qualifier("Action")
public class ActionMovieCatalog implements MovieCatalog {
// ...
}
@Component
@Qualifier("Action")
class ActionMovieCatalog : MovieCatalog
Java
Kotlin
@Component
@Genre("Action")
public class ActionMovieCatalog implements MovieCatalog {
// ...
}
@Component
@Genre("Action")
class ActionMovieCatalog : MovieCatalog {
// ...
}
Java
Kotlin
@Component
@Offline
public class CachingMovieCatalog implements MovieCatalog {
// ...
}
@Component
@Offline
class CachingMovieCatalog : MovieCatalog {
// ...
}
ほとんどのアノテーションベースの代替方法と同様に、XML を使用すると、同じ型の複数の Bean が修飾子メタデータのバリエーションを提供できる一方で、アノテーションメタデータはクラス定義自体にバインドされることに留意してください。クラスごとではなくインスタンス。 |