目次
1. BeanFactoryPostProcessorの基礎知識のまとめ
2. フレームワークでの BeanFactoryPostProcessor の使用
拡張 1: BeanFactoryPostProcessor インターフェースを実装して Bean 定義を変更する
拡張機能 2: BeanFactoryPostProcessor インターフェースを実装して新しい Bean 定義を登録する
拡張 3: BeanFactoryPostProcessor インターフェースを実装して Bean 定義を変更する
考察 1: カスタム BeanFactoryProcessor に @Component やその他のアノテーションを追加する必要がある理由
考察2: BeanFactoryProcessorの実行順序を制御するにはどうすればよいか
1. BeanFactoryPostProcessorの基礎知識のまとめ
BeanFactoryPostProcessor は Spring フレームワークの重要なインターフェースであり、BeanFactory が Bean 定義をロードした後、 Bean をインスタンス化する前に BeanFactory をカスタマイズおよび拡張するために使用されます。これにより、開発者は、Spring コンテナが構成ファイルをロードして Bean インスタンスを作成する前に、プロパティ値の変更、追加のメタデータの追加など、Bean 定義を操作できるようになります。
BeanFactoryPostProcessor インターフェースはメソッドを定義します。
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
throws BeansException;
アプリケーションが開始されると、Spring コンテナは BeanFactoryPostProcessor インターフェースを実装するすべてのクラスの postProcessBeanFactory メソッドを自動的に検出して呼び出します。開発者はこのメソッドを使用してカスタム ロジックを実装し、BeanFactory を変更および拡張できます。
BeanFactoryPostProcessor インターフェースを実装すると、次の機能を実現できます。
- Bean定義の変更: BeanFactoryにロードしたBean定義を変更できます。たとえば、特定の条件に基づいて Bean のプロパティ値を動的に変更したり、Bean のスコープを変更したりできます。
- Bean定義の新規登録: BeanFactoryにBean定義をロードした後、BeanFactoryに新規Bean定義を登録できます。このようにして、新しい Bean 定義を Spring コンテナに動的に追加して、動的な拡張を実現できます。
- カスタム メタデータの追加: カスタム メタデータを Bean 定義に追加して、後続のプロセッサで使用できます。このようにして、他のプロセッサまたはコンポーネントは、これらのメタデータに基づいて対応する処理を実行できます。
BeanFactoryPostProcessor の実装クラスは、コンテナが Bean 定義をロードするときに正しく呼び出せるように、Spring コンテナが開始される前にコンテナに登録する必要があることに注意してください。
要約すると、BeanFactoryPostProcessor は、BeanFactory のカスタム変更と拡張のための Spring フレームワークのインターフェースです。このインターフェースを実装すると、BeanFactory が Bean 定義をロードした後、Bean をインスタンス化する前に BeanFactory を変更できるため、高度なカスタム ロジックと関数の拡張を実現できます。
2. フレームワークでの BeanFactoryPostProcessor の使用
BeanFactoryPostProcessor には、Spring フレームワークでも多数の使用例があります。その中で最も古典的なケースは、Spring フレームワークの BeanFactoryPostProcessor 実装クラスである PropertyPlaceholderConfigurer で、コンテナーのロード時に構成ファイル内のプレースホルダーを動的に置き換え、プレースホルダーを実際のプロパティ値に置き換えることができます。
前述した、BeanFactoryPostProcessor インターフェースで定義されている主なメソッドに戻ります。
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
throws BeansException;
ご覧のとおり、このメソッドによって受信されるパラメータは ConfigurableListableBeanFactory 型であり、Spring コンテナのコア インターフェイスであり、BeanFactory インターフェイスのサブインターフェイスです。ConfigurableListableBeanFactory は、BeanFactory から継承したメソッドを通じてコンテナ内のすべての Bean を取得でき、BeanPostProcessor などのインターフェイスをサポートすることで、Bean のスケーラビリティを強化します。
ソース コードを次のように表示します。
public class PropertyPlaceholderConfigurer extends PlaceholderConfigurerSupport
implements BeanFactoryPostProcessor, EnvironmentAware, EmbeddedValueResolverAware {
/** Default placeholder prefix: "${" */
public static final String DEFAULT_PLACEHOLDER_PREFIX = "${";
/** Default placeholder suffix: "}" */
public static final String DEFAULT_PLACEHOLDER_SUFFIX = "}";
// 用来替换占位符的属性解析器
private volatile PropertySourcesPlaceholderConfigurer pp;
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
processProperties(beanFactory, pp.getAppliedPropertySources());
this.pp = null;
}
...
}
ご覧のとおり、PropertyPlaceholderConfigurer は BeanFactoryPostProcessor インターフェイスを実装しているため、アプリケーション コンテキストがロードされるときにその postProcessBeanFactory メソッドが呼び出されます。
このメソッドでは、PropertyPlaceholderConfigurer は親クラス PlaceholderConfigurerSupport の processProperties メソッドを呼び出して、プロパティ プレースホルダーを置き換えます。
processProperties メソッドは、コンテナ内の BeanDefinition を順次走査し、キー「props」を持つ PropertyPlaceholderConfigurer クラスのすべての Bean を検索して処理し、配列内の各インスタンスを PropertySourcesPlaceholderConfigurer のプロパティ ソース リストに追加します。
次に、PropertyPlaceholderConfigurer は processProperties メソッドを呼び出して値 pp を返し、メソッドの最後でそれを null に設定します。このようにして、次回呼び出されたときに、新しい PropertySourcesPlaceholderConfigurer インスタンスが再作成されます。
一般に、BeanFactoryPostProcessor インターフェースを実装すると、Spring コンテナが Bean を作成する前に、Bean 定義の変更、ファイルのプレースホルダー変数の置換、その他の操作を行うことができます。古典的な BeanFactoryPostProcessor 実装クラスとして、PropertyPlaceholderConfigurer は、いつでも構成ファイル内のプレースホルダーを動的に置き換えて、それを Spring コンテナ内のすべての Bean に適用できます。
PropertyPlaceholderConfigurer に加えて、Spring フレームワークには CommonAnnotationBeanPostProcessor、AutowiredAnnotationBeanPostProcessor などの多数の BeanFactoryPostProcessor 実装クラスがあり、Spring のさまざまなシナリオで広く使用されています。
3. 拡張アプリケーションコードの解析例
拡張 1: BeanFactoryPostProcessor インターフェースを実装してBean 定義を変更する
この拡張機能を使用する本来の目的は、Spring コンテナが Bean 定義をロードした後に Bean 定義を変更する必要がある場合に、BeanFactoryPostProcessor インターフェースを使用してこれを実現できることです。以下は簡単な事例分析です。
MyTestMsgBean という Bean クラスがあるとします。
package org.zyf.javabasic.springextend.bfppext;
/**
* @author yanfengzhang
* @description 演示如何使用BeanFactoryPostProcessor来修改Bean的定义
* @date 2023/5/17 22:57
*/
public class MyTestMsgBean {
private String message;
public void setMessage(String message) {
this.message = message;
}
public void displayMessage() {
System.out.println("MyTestMsgBean Message: " + message);
}
}
ここで、 Spring コンテナが Bean 定義をロードした後、 MyTestMsgBean のメッセージ属性値を特定のデフォルト値に自動的に変更したいと考えています。
まず、カスタム クラス MyBeanFactoryPostProcessorForUpdateBean を作成して、次のように BeanFactoryPostProcessor インターフェイスを実装します。
package org.zyf.javabasic.springextend.bfppext;
import org.springframework.beans.BeansException;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.stereotype.Component;
/**
* @author yanfengzhang
* @description
* @date 2023/5/17 23:00
*/
@Component
public class MyBeanFactoryPostProcessorForUpdateBean implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
BeanDefinition beanDefinition = configurableListableBeanFactory.getBeanDefinition("myTestMsgBean");
MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();
propertyValues.add("message", "Hello, World!");
}
}
上記のコードでは、MyBeanFactoryPostProcessorForUpdateBean クラスがBeanFactoryPostProcessorインターフェースを実装し、postProcessBeanFactoryメソッドをオーバーライドします。このメソッドでは、configurableListableBeanFactory.getBeanDefinition(" myTestMsgBean ")を通じて MyTestMsgBean のBeanDefinition を取得し、MutablePropertyValuesを使用してメッセージ属性の値を変更します。
次に、MyTestMsgBean と MyBeanFactoryPostProcessorForUpdateBean の両方を Spring 構成ファイル (zyf_application_context.xml) に構成します。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="myTestMsgBean" class="org.zyf.javabasic.springextend.bfppext.MyTestMsgBean">
<property name="message" value="Original Message"/>
</bean>
<bean class="org.zyf.javabasic.springextend.bfppext.MyBeanFactoryPostProcessorForUpdateBean"/>
</beans>
上記の構成では、まず myTestMsgBean という MyTestMsgBean インスタンスを定義し、そのmessage属性に初期値を設定して、Spring コンテナに MyBeanFactoryPostProcessorForUpdateBean を登録します。
Spring コンテナが起動すると、まず設定ファイルがロードされて Bean 定義が解析され、次に MyBeanFactoryPostProcessorForUpdateBean クラスが BeanFactoryPostProcessorインターフェースを実装していることを自動的に検出し、そのpostProcessBeanFactoryメソッドを呼び出します。このメソッドでは、 MyTestMsgBean のメッセージ属性値を「Hello, World!」に変更しました。
最後に、MyTestMsgBean のインスタンスを取得し、そのメソッドを呼び出すことで、変更が有効になったことを確認できます。
package org.zyf.javabasic.springextend.bfppext;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author yanfengzhang
* @description
* @date 2023/5/17 23:07
*/
public class MyBeanFactoryPostProcessorForUpdateBeanTest {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("zyf_application_context.xml");
MyTestMsgBean myBean = context.getBean(MyTestMsgBean.class);
myBean.displayMessage();
}
}
上記のコードを実行すると、出力は初期値「MyTestMsgBean Message: Original Message」ではなく「MyTestMsgBean Message: Hello, World!」になります。これは、「MyBeanFactoryPostProcessorForUpdateBean」を使用して「MyTestMsgBean」の定義を変更し、そのプロパティ値を予期したデフォルト値に変更することに成功したことを示しています。
このケースでは、「BeanFactoryPostProcessor」インターフェースを使用して Bean 定義を変更する方法を示します。このインターフェースを実装し、「postProcessBeanFactory」メソッドで Bean 定義を変更することで、特定のニーズを満たすカスタム ロジックと拡張機能を簡単に実装できます。
拡張機能 2: BeanFactoryPostProcessor インターフェースを実装して新しい Bean 定義を登録する
この拡張機能を使用する本来の目的は、BeanFactory に Bean 定義をロードした後、BeanFactory に新しい Bean 定義を登録する必要があるときに、BeanFactoryPostProcessor インターフェースを使用してこれを実現できることです。以下は、BeanFactoryPostProcessor を使用して新しい Bean 定義を登録する方法を示す完全なケースです。
AdditionalBeanというBean クラスがあるとします。
package org.zyf.javabasic.springextend.bfppext;
/**
* @author yanfengzhang
* @description
* @date 2023/5/17 23:34
*/
public class AdditionalBean {
public void displayMessage() {
System.out.println("Additional Bean");
}
}
ここで、 Spring コンテナが Bean 定義をロードした後、AdditionalBean という名前の AdditionalBean インスタンスをコンテナに自動的に登録します。まず、BeanFactoryPostProcessor インターフェースを実装するカスタム クラス MyBeanFactoryPostProcessorForNewDefinRegi を作成します。
package org.zyf.javabasic.springextend.bfppext;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.stereotype.Component;
/**
* @author yanfengzhang
* @description
* @date 2023/5/17 23:36
*/
@Component
public class MyBeanFactoryPostProcessorForNewDefinRegi implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
BeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(AdditionalBean.class).getBeanDefinition();
beanDefinitionRegistry.registerBeanDefinition("additionalBean", beanDefinition);
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
// 这里留空
}
}
MyBeanFactoryPostProcessorForNewDefinRegi は、BeanDefinitionRegistryPostProcessorインターフェースを実装し、postProcessBeanDefinitionRegistryメソッドをオーバーライドします。このメソッドでは、BeanDefinitionBuilderを使用してAdditionalBeanのBeanDefinitionインスタンスを構築し、registry.registerBeanDefinitionメソッドを使用してそれを BeanDefinitionRegistry に登録します。ここで、最初のパラメーターは Bean の名前、2 番目のパラメーターは BeanDefinition インスタンスです。
次に、MyBeanFactoryPostProcessorForNewDefinRegi を設定します。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="myTestMsgBean" class="org.zyf.javabasic.springextend.bfppext.MyTestMsgBean">
<property name="message" value="Original Message"/>
</bean>
<bean class="org.zyf.javabasic.springextend.bfppext.MyBeanFactoryPostProcessorForUpdateBean"/>
<bean class="org.zyf.javabasic.springextend.bfppext.MyBeanFactoryPostProcessorForNewDefinRegi"/>
</beans>
上記の構成では、MyBeanFactoryPostProcessorForNewDefinRegi を Spring コンテナに登録するだけです。最後に、新しい Bean 定義が有効であることを確認するテスト クラスを作成します。
package org.zyf.javabasic.springextend.bfppext;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author yanfengzhang
* @description
* @date 2023/5/17 23:53
*/
public class MyBeanFactoryPostProcessorForNewDefinRegiTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("zyf_application_context.xml");
AdditionalBean additionalBean = context.getBean(AdditionalBean.class);
additionalBean.displayMessage();
}
}
上記のコードを実行すると、出力は「Additional Bean」となり、 AdditionalBeanという名前のAdditionalBeanインスタンスが正常に登録および取得されたことを示します。
この場合、postProcessBeanDefinitionRegistryメソッドを実装することで、BeanFactoryがBean定義をロードした後、BeanFactoryに新規Bean定義が登録されます。登録済み Bean 定義はBeanDefinitionBuilderを使用して作成され、BeanDefinitionRegistryのregisterBeanDefinitionメソッドを通じて登録されます。
次に、MyBeanFactoryPostProcessorForNewDefinRegi を Spring の設定ファイルに設定し、ApplicationContextを使用して登録された Bean インスタンスを取得することで、新しい Bean 定義が Spring コンテナに正常に登録されたかどうかを確認できます。
この例では、 BeanFactoryPostProcessorインターフェースを使用して新しい Bean 定義を登録する方法を示します。この方法により、BeanFactory が Bean 定義をロードした後、新しい Bean 定義を Spring コンテナに動的に追加できるため、柔軟な拡張と構成が実現します。
拡張 3: BeanFactoryPostProcessor インターフェースを実装してBean 定義を変更する
この拡張機能を使用する本来の目的は、後続のプロセッサで使用するために Bean 定義にカスタム メタデータを追加する必要がある場合に、BeanFactoryPostProcessor インターフェイスを使用してこれを実現できることです。以下は、カスタム メタデータの追加を示す完全なケースです。
前のコードと同様に、MyTestMsgBean という Bean クラスがあり、他のプロセッサまたはコンポーネントで使用できるように、customData などのカスタム メタデータを Bean 定義に追加するとします。
まず、カスタム クラス MyBeanFactoryPostProcessorForDefineMata を作成して、対応するインターフェイスを実装します。
package org.zyf.javabasic.springextend.bfppext;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.stereotype.Component;
/**
* @author yanfengzhang
* @description
* @date 2023/5/17 23:12
*/
@Component
public class MyBeanFactoryPostProcessorForDefineMata implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
BeanDefinition beanDefinition = beanDefinitionRegistry.getBeanDefinition("myTestMsgBean");
beanDefinition.setAttribute("customData", "This is custom data");
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
// 这里留空
}
}
上記のコードでは、MyBeanFactoryPostProcessorForDefineMata クラスがBeanDefinitionRegistryPostProcessorインターフェースを実装し、postProcessBeanDefinitionRegistryメソッドをオーバーライドします。このメソッドでは、myTestMsgBean という名前の Bean 定義を取得し、setAttributeメソッドを使用してカスタム メタデータCustomDataを Bean 定義に追加します。
次に MyBeanFactoryPostProcessorForDefineMata を設定します。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="myTestMsgBean" class="org.zyf.javabasic.springextend.bfppext.MyTestMsgBean">
<property name="message" value="Original Message"/>
</bean>
<bean class="org.zyf.javabasic.springextend.bfppext.MyBeanFactoryPostProcessorForUpdateBean"/>
<bean class="org.zyf.javabasic.springextend.bfppext.MyBeanFactoryPostProcessorForNewDefinRegi"/>
<bean class="org.zyf.javabasic.springextend.bfppext.MyBeanFactoryPostProcessorForDefineMata"/>
</beans>
上記の設定では、MyBeanFactoryPostProcessorForDefineMata を Spring コンテナに登録するだけです。最後に、Bean 定義のメタデータを取得するテスト クラスを作成し、それが正常に追加されたことを確認します。
package org.zyf.javabasic.springextend.bfppext;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author yanfengzhang
* @description
* @date 2023/5/17 23:15
*/
public class MyBeanFactoryPostProcessorForDefineMataTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("zyf_application_context.xml");
ConfigurableApplicationContext configurableContext = (ConfigurableApplicationContext) context;
BeanDefinition beanDefinition = configurableContext.getBeanFactory().getBeanDefinition("myTestMsgBean");
Object customData = beanDefinition.getAttribute("customData");
System.out.println("Custom Data: " + customData);
}
}
上記のコードを実行すると、出力は「カスタム データ: これはカスタム データです」となり、カスタム メタデータが Bean 定義に正常に追加されたことを示します。このケースでは、 BeanFactoryPostProcessorインターフェースを使用してカスタム メタデータを Bean 定義に追加する方法を示します。BeanDefinitionRegistryPostProcessorインターフェースのpostProcessBeanDefinitionRegistryメソッドを実装することにより、BeanFactory が Bean 定義をロードした後に、カスタム メタデータを Bean 定義に追加できます。追加されたメタデータは、後続のプロセッサまたはコンポーネントで使用され、メタデータに基づいて対応する処理ロジックを実行できることに注意してください。
考察 1: カスタム BeanFactoryProcessor が@Component
他のアノテーションを追加する必要がある理由
ソースコードを見ると、次のことがわかります。
String[] postProcessorNames = beanFactory
.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);
BeanFactoryPostProcessor を実装する BeanName をすべて取得します。すべての Bean が登録されていることが前提です。などのアノテーションをBeanDefinitionRegistry
追加することで@Component
、対応する Bean 定義情報を BeanFactory に登録し、その後の Bean のインスタンス化を容易にします。@Service
では、どこに登録されているのでしょうか?
ConfigurationClassPostProcessor
次のクラスが実装されており、BeanDefinitionRegistryPostProcessor
すべてのアノテーションをスキャンし@Component
、@Service
対応する Bean 定義を BeanFactory に登録します。
考察2:BeanFactoryProcessor
実行順序をどう制御するか
PriorityOrdered
実行順序は、インターフェイス Ordered
を実装することで制御できますBeanFactoryProcessor
。インターフェイスを実装したプロセッサが優先されPriorityOrdered
、次にOrdered
、最後にソート インターフェイスを実装していないプロセッサが優先されます。
4. 業務利用に関するご提案と注意事項
BeanFactoryPostProcessor インターフェースを使用してカスタム関数を実装する場合、使用上の注意と推奨事項をいくつか示します。
- BeanFactoryPostProcessor は、BeanFactory が Bean 定義をロードした後、Bean がインスタンス化される前に実行されます。これは、BeanFactoryPostProcessor は Bean によって定義されたプロパティを変更できますが、Bean インスタンスの状態は変更できないことを意味します。Bean のインスタンス化後に Bean のプロパティを変更したり、他の操作を実行したりする必要がある場合は、BeanPostProcessor インターフェースの使用を検討してください。
- BeanFactoryPostProcessor で循環依存関係を作成しないようにします。BeanFactoryPostProcessor の実行順序は Bean のインスタンス化の前であるため、BeanFactoryPostProcessor で新しい Bean インスタンスが作成され、そのインスタンスが他の Bean に依存している場合、循環依存の問題が発生する可能性があります。
- BeanFactoryPostProcessor に複雑なビジネス ロジックを導入しないようにしてください。BeanFactoryPostProcessor の主な目的は、複雑なビジネス ロジックを実行するのではなく、BeanFactory を変更および拡張することです。複雑なビジネス ロジックを実行する必要がある場合は、BeanPostProcessor やカスタム Service クラスなどの他の適切なコンポーネントの使用を検討できます。
- BeanFactoryPostProcessorの実行順序に注意してください。BeanFactoryPostProcessor 実装クラスが複数ある場合、それらの実行順序が結果に影響を与える可能性があります。@Order アノテーションを使用するか、Ordered インターフェースを実装して実行順序を指定するか、Ordered インターフェースの優先順位定数 (Ordered.HIGHEST_PRECEDENCE など) を使用できます。
- 新しい Bean 定義を登録する場合は、注意して BeanFactoryPostProcessor を使用してください。新しい Bean 定義を登録すると、アプリケーションが複雑になる可能性があるため、慎重に使用する必要があります。新しく登録された Bean 定義がアプリケーションのニーズを満たしていることを確認し、動的に登録された Bean への過度の依存を回避します。
- BeanFactoryPostProcessor を使用する場合は、単一責任の原則に従うことをお勧めします。各 BeanFactoryPostProcessor クラスは、コードを明確で保守しやすい状態に保つために、特定のタスクまたは機能に焦点を当てる必要があります。
- BeanFactoryPostProcessor のライフサイクルに注意してください。BeanFactoryPostProcessor 実装クラスは、アプリケーション コンテキストの起動フェーズ中に実行されるため、アプリケーションの起動パフォーマンスへの影響を避けるために、実行時間が短いことを確認する必要があります。これらの注意事項と提案に従うことで、BeanFactoryPostProcessor インターフェースをより適切に使用して、必要な機能を実現し、アプリケーションの通常の操作と保守性を確保できます。
5. ソースコード分析
(1) インターフェース定義の分析
/**
* Factory hook that allows for custom modification of an application context's
* bean definitions, adapting the bean property values of the context's underlying
* bean factory.
*
* <p>Useful for custom config files targeted at system administrators that
* override bean properties configured in the application context. See
* {@link PropertyResourceConfigurer} and its concrete implementations for
* out-of-the-box solutions that address such configuration needs.
*
* <p>A {@code BeanFactoryPostProcessor} may interact with and modify bean
* definitions, but never bean instances. Doing so may cause premature bean
* instantiation, violating the container and causing unintended side-effects.
* If bean instance interaction is required, consider implementing
* {@link BeanPostProcessor} instead.
*
* <h3>Registration</h3>
* <p>An {@code ApplicationContext} auto-detects {@code BeanFactoryPostProcessor}
* beans in its bean definitions and applies them before any other beans get created.
* A {@code BeanFactoryPostProcessor} may also be registered programmatically
* with a {@code ConfigurableApplicationContext}.
*
* <h3>Ordering</h3>
* <p>{@code BeanFactoryPostProcessor} beans that are autodetected in an
* {@code ApplicationContext} will be ordered according to
* {@link org.springframework.core.PriorityOrdered} and
* {@link org.springframework.core.Ordered} semantics. In contrast,
* {@code BeanFactoryPostProcessor} beans that are registered programmatically
* with a {@code ConfigurableApplicationContext} will be applied in the order of
* registration; any ordering semantics expressed through implementing the
* {@code PriorityOrdered} or {@code Ordered} interface will be ignored for
* programmatically registered post-processors. Furthermore, the
* {@link org.springframework.core.annotation.Order @Order} annotation is not
* taken into account for {@code BeanFactoryPostProcessor} beans.
*
* @author Juergen Hoeller
* @author Sam Brannen
* @since 06.07.2003
* @see BeanPostProcessor
* @see PropertyResourceConfigurer
*/
@FunctionalInterface
public interface BeanFactoryPostProcessor {
/**
* Modify the application context's internal bean factory after its standard
* initialization. All bean definitions will have been loaded, but no beans
* will have been instantiated yet. This allows for overriding or adding
* properties even to eager-initializing beans.
* @param beanFactory the bean factory used by the application context
* @throws org.springframework.beans.BeansException in case of errors
*/
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
インターフェイスのコメントで機能と詳細が説明されていますが、おおよそ次のとおりです。
- このインターフェースを使用すると、ユーザーはファクトリ Bean の BeanDefinition をカスタマイズしたり、BeanDefinition のプロパティ値を調整したり、Bean を初期化したりすることができます。たとえば、組み込みのものは、
PropertyResourceConfigurer
beanDefinition のプロパティを構成ファイルのプロパティに変更するものです。 - このインターフェースは主に BeanDefinition を変更するために使用され、Bean を直接インスタンス化することもできますが、他の不明なエラーが発生する可能性があるため、お勧めできません。
(2) 実行プロセス分析
コアPostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors
メソッド (このメソッドのロジックの前の部分) は主に処理BeanDefinitionRegistryPostProcessor
メソッドですpostProcessBeanDefinitionRegistry
。つまり、新しい BeanDefinition を BeanDefinition 登録センターに追加します。
public static void invokeBeanFactoryPostProcessors(
ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
.......... 该部分是处理BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry相关逻辑,跳过, 可以看BeanDefinitionRegistryPostProcessor的解析
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let the bean factory post-processors apply to them!
// 获取所有实现了BeanFactoryPostProcessor接口的bean name列表,前提是在BeanFactory的BeanDefinitions列表中包含对应的bean定义信息。
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);
// Separate between BeanFactoryPostProcessors that implement PriorityOrdered,
// Ordered, and the rest.
// 存放实现了PriorityOrdered接口的processor
List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
// 存放实现了Ordered接口的processor
List<String> orderedPostProcessorNames = new ArrayList<>();
// 存放没有实现排序的processor
List<String> nonOrderedPostProcessorNames = new ArrayList<>();
// 遍历前面全量的bean name,将他们归类,放到上面的容器中
for (String ppName : postProcessorNames) {
// 如果在第一阶段已经被调用过,就不调用,第一阶段主要是BeanDefinitionRegistryPostProcessor,它继承了BeanFactoryPostProcessor,它会在第一阶段调用。
if (processedBeans.contains(ppName)) {
// skip - already processed in first phase above
}
else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
}
else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
orderedPostProcessorNames.add(ppName);
}
else {
nonOrderedPostProcessorNames.add(ppName);
}
}
// First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered.
// 首先执行实现了PriorityOrdered接口的processor,对它们进行排序
sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
// 真实执行processor中的逻辑。
invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);
// Next, invoke the BeanFactoryPostProcessors that implement Ordered.
// 其次执行实现了Ordered接口的processor,对它们进行排序后执行processor中的逻辑。
List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size());
for (String postProcessorName : orderedPostProcessorNames) {
orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
}
sortPostProcessors(orderedPostProcessors, beanFactory);
invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);
// 最后执行,没有顺序要求的processor
List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size());
for (String postProcessorName : nonOrderedPostProcessorNames) {
nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
}
invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);
// Clear cached merged bean definitions since the post-processors might have
// modified the original metadata, e.g. replacing placeholders in values...
beanFactory.clearMetadataCache();
}
//调用processors中的postProcessBeanFactory方法
private static void invokeBeanFactoryPostProcessors(
Collection<? extends BeanFactoryPostProcessor> postProcessors, ConfigurableListableBeanFactory beanFactory) {
for (BeanFactoryPostProcessor postProcessor : postProcessors) {
StartupStep postProcessBeanFactory = beanFactory.getApplicationStartup().start("spring.context.bean-factory.post-process")
.tag("postProcessor", postProcessor::toString);
postProcessor.postProcessBeanFactory(beanFactory);
postProcessBeanFactory.end();
}
}
参考記事リンク
- Baeldung: 「Spring の BeanFactoryPostProcessor の概要」 - この記事では、BeanFactoryPostProcessor の概要を示し、その役割、使用法、およびサンプル コードを説明します。この記事は Baeldung Web サイトでご覧いただけます。
- DZone: 「Spring Framework における BeanFactoryPostProcessor の役割を理解する」 - この記事では、BeanFactoryPostProcessor の役割と使用法を詳細に説明し、その使用方法を示すサンプル コードを提供します。この記事は DZone Web サイトでご覧いただけます。
- Spring Framework リファレンス ドキュメント - Spring の公式ドキュメントには、BeanFactoryPostProcessor に関する詳細な説明と使用手順が記載されています。Spring 公式 Web サイトにアクセスし、公式ドキュメントで BeanFactoryPostProcessor に関する部分を探してください。
- 説明: 「Spring BeanFactoryPostProcessor チュートリアル」 - このチュートリアルでは、BeanFactoryPostProcessor を使用して Bean 定義を変更し、新しい Bean 定義を登録する方法の完全な例を提供します。このチュートリアルは、Baeldung Web サイトにあります。
- Spring Boot 拡張機能 BeanFactoryPostProcessor の完全な理解 - Nuggets