ApplicationContext の初期化プロセス
ApplicationContext の起動プロセスが第 11 段階まで到達すると、すべての準備が完了し、残りの Bean 初期化処理が実行されます。この段階では遅延初期化としてマークされていない Bean がいよいよインスタンス化されます。
public void refresh() throws BeansException, IllegalStateException {
// 非遅延 Bean の初期化処理を実行
finishBeanFactoryInitialization(beanFactory);
}
finishBeanFactoryInitialization メソッドの役割
このメソッドは、遅延初期化の対象外となっているすべての Bean をインスタンス化します。処理の流れは以下の通りです。
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// ConversionService の登録(Bean 名は正確に "conversionService" である必要あり)
if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
beanFactory.setConversionService(
beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
}
// LoadTimeWeaverAware インタフェースを実装した Bean の先行初期化
String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
for (String weaverAwareName : weaverAwareNames) {
getBean(weaverAwareName);
}
// 一時的な ClassLoader の使用を終了
beanFactory.setTempClassLoader(null);
// Bean 定義の凍結(以降は変更不可となる)
beanFactory.freezeConfiguration();
// 実際の Bean 初期化処理
beanFactory.preInstantiateSingletons();
}
ConversionService の設定
カスタム Converter の実装例
文字列から日付への変換を行う Converter を実装する場合、以下のように記述します。
public class StringToDateConverter implements Converter<String, LocalDate> {
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
@Override
public LocalDate convert(String source) {
return LocalDate.parse(source, FORMATTER);
}
}
XML による Bean 登録
ConversionServiceFactoryBean を使用して Converter を登録します。Bean 名は「conversionService」である必要があることに注意してください。
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="com.example.config.StringToDateConverter"/>
</set>
</property>
</bean>
動作確認用のテストコード
@Test
public void conversionServiceTest() {
DefaultConversionService service = new DefaultConversionService();
service.addConverter(new StringToDateConverter());
LocalDate result = service.convert("2024-01-15", LocalDate.class);
System.out.println("変換結果: " + result);
}
Bean 定義の凍結処理
freezeConfiguration メソッドを呼び出すことで、Bean 定義が確定します。これにより、以降の処理で Bean 定義が変更されることを防止できます。
public void freezeConfiguration() {
this.configurationFrozen = true;
this.frozenBeanDefinitionNames = StringUtils.toStringArray(this.beanDefinitionNames);
}
凍結された Bean 定義は読み取り専用となり、BeanFactoryPostProcessor による改変の対象外となります。
非遅延 Bean の実際の初期化
preInstantiateSingletons メソッドの処理内容
このメソッドでは、シングルトンスコープかつ遅延初期化でない Bean が順次インスタンス化されます。
public void preInstantiateSingletons() throws BeansException {
if (logger.isDebugEnabled()) {
logger.debug("シングルトン Bean の先行インスタンス化を開始します: " + this);
}
// すべての Bean 名を取得
List<String> definedNames = new ArrayList<>(this.beanDefinitionNames);
// フェーズ 1: 通常の Bean と FactoryBean の初期化
for (String beanName : definedNames) {
RootBeanDefinition beanDef = getMergedLocalBeanDefinition(beanName);
if (!beanDef.isAbstract() && beanDef.isSingleton() && !beanDef.isLazyInit()) {
if (isFactoryBean(beanName)) {
FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
boolean eagerInit;
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
eagerInit = AccessController.doPrivileged(
(PrivilegedAction<Boolean>) () ->
((SmartFactoryBean<?>) factory).isEagerInit(),
getAccessControlContext()
);
} else {
eagerInit = (factory instanceof SmartFactoryBean &&
((SmartFactoryBean<?>) factory).isEagerInit());
}
if (eagerInit) {
getBean(beanName);
}
} else {
getBean(beanName);
}
}
}
// フェーズ 2: SmartInitializingSingleton インタフェースのコールバック処理
for (String beanName : definedNames) {
Object singletonInstance = getSingleton(beanName);
if (singletonInstance instanceof SmartInitializingSingleton) {
final SmartInitializingSingleton initializer = (SmartInitializingSingleton) singletonInstance;
if (System.getSecurityManager() != null) {
AccessController.doPrivileged(
(PrivilegedAction<Object>) () -> {
initializer.afterSingletonsInstantiated();
return null;
},
getAccessControlContext()
);
} else {
initializer.afterSingletonsInstantiated();
}
}
}
}
初期化プロセスの詳細
処理は大きく 2 つのフェーズに分かれています。第一フェーズでは、通常の Bean と FactoryBean のインスタンス化が行われます。FactoryBean については、SmartFactoryBean インタフェースを実装している場合に限り、即時初期化の判断が行われます。
第二フェーズでは、SmartInitializingSingleton インタフェースを実装した Bean に対して、afterSingletonsInstantiated メソッドが呼び出されます。@EventListener アノテーションを使用して登録されたイベントリスナーも、この段階で初期化が完了します。