はじめに
実際の開発では、システム基盤の開発に傾倒している場合、FactoryBeanの使用は特に馴染みがないはずですが、FactoryBeanとBeanFactoryこの2つはよく比較され、その理由は、この2つの名前は非常に人々を混乱させやすいようですが、これらの2つの原理と役割は全く異なります。この記事では、FactoryBeanに焦点を当ててソースコード解析を開始します。
FactoryBean
public interface FactoryBean<T> {
// 作成されたBeanオブジェクトに戻る
T getObject() throws Exception;
// beanオブジェクトのクラス型
Class<?> getObjectType();
// beanオブジェクトがシングルトンであろうとなかろうと、デフォルトはシングルトンである。
default boolean isSingleton() {
return true;
}
}
これをテストするクラスを作成し、コンテナから MyDemoFactoryBean と MyDemoService を取得できるかどうかを確認してみましょう:
@Configuration
@ComponentScan("com.leon.factorybean")
public class Config {
}
public class MyApplication {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Config.class);
String[] myDemoTactoryBeanNames = applicationContext.getBeanNamesForType(MyDemoFactoryBean.class);
String[] myDemoServicBeanNames = applicationContext.getBeanNamesForType(MyDemoService.class);
System.out.println(JsonUtil.convert(myDemoTactoryBeanNames));
System.out.println(JsonUtil.convert(myDemoServicBeanNames));
}
}
FactoryBeanソースコードのトレース
1.エントランス
このようにSpringでは、ソースコードをトレースする最も直接的な方法は、applicatonContext.getBeanの方法です。
MyDemoFactoryBean myDemoFactoryBean = applicationContext.getBean(MyDemoFactoryBean.class);
2. AbstractApplicationContext#getBeanにデリゲートします。
@Override
public <T> T getBean(Class<T> requiredType) throws BeansException {
assertBeanFactoryActive();
return getBeanFactory().getBean(requiredType);
}
DefaultListableBeanFactoryへの再委任#getBean(java.lang.Class)
getBeanFactory()は実際にはDefaultListableBeanFactoryインスタンスを返します。ここで、DefaultListableBeanFactoryがSpringのBeanFactoryのデフォルト実装であることがわかります。したがって、デフォルトでは、当該IOCコンテナはそれです。この記事では、コンテナの分析はあまり行わず、まずFactoryBeanの助けを借りて、その外観を探ります。
DefaultListableBeanFactoryへの再委任#getBean(java.lang.Class, java.lang.Object
上記のメソッドはまだ実際には何もしておらず、getBeanに委譲されたオーバーロードされたメソッドのままです。ソースコードは以下の通りです:
public <T> T getBean(Class<T> requiredType, @Nullable Object... args) throws BeansException {
Assert.notNull(requiredType, "Required type must not be null");
Object resolved = resolveBean(ResolvableType.forRawClass(requiredType), args, false);
if (resolved == null) {
throw new NoSuchBeanDefinitionException(requiredType);
}
return (T) resolved;
}
DefaultListableBeanFactoryへの再委任#resolveBean
最後にようやく山の本当の顔を参照してください、内部のresolveBean()メソッドに委譲することにより、メソッドを変更すると、ビーンメソッドの最終的な解決です。ソースコードは次のとおりです:
private <T> T resolveBean(ResolvableType requiredType, @Nullable Object[] args, boolean nonUniqueAsNull) {
// Beanを構文解析し,戻り結果が空でなければ,構文解析されたBeanを既に得たことを意味し,それを直接返す。このメソッドは重要な構文解析メソッドである!
NamedBeanHolder<T> namedBean = resolveNamedBean(requiredType, args, nonUniqueAsNull);
if (namedBean != null) {
return namedBean.getBeanInstance();
}
// returnがnullの場合,beanFactoryを通してbeanを生成する。
BeanFactory parent = getParentBeanFactory();
if (parent instanceof DefaultListableBeanFactory) {
return ((DefaultListableBeanFactory) parent).resolveBean(requiredType, args, nonUniqueAsNull);
}
else if (parent != null) {
ObjectProvider<T> parentProvider = parent.getBeanProvider(requiredType);
if (args != null) {
return parentProvider.getObject(args);
}
else {
return (nonUniqueAsNull ? parentProvider.getIfUnique() : parentProvider.getIfAvailable());
}
}
return null;
}
注目すべきは、getBeanNamesForType メソッドと getBean メソッドです。
DefaultListableBeanFactory#getBeanNamesForType
public String[] getBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) {
Class resolved = type.resolve();
// ここで、クラスの解決を得る
// MyDemoFactoryBean他の属性は定義されていないので、ここではifブランチに入る。
if (resolved != null && !type.hasGenerics()) {
// これはgetBeanNamesForTypeのオーバーロードされたメソッドであり、doGetBeanNamesForTypeメソッドを呼び出すことになる。
return getBeanNamesForType(resolved, includeNonSingletons, allowEagerInit);
}
else {
return doGetBeanNamesForType(type, includeNonSingletons, allowEagerInit);
}
}
private String[] doGetBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) {
List<String> result = new ArrayList<>();
// すべてのbeanDefinitionNamesをチェックする.beanDefinitionnew" AnnotationConfigApplicationContext(Config.class)解析されるときは解析される。つまり、ここではループを直接たどることができる。
for (String beanName : this.beanDefinitionNames) {
// エイリアスがあるかどうかを判断する。一般に、エイリアスを使用するケースはほとんどない。この場合、エイリアスは存在しないので、if分岐に進む。
if (!isAlias(beanName)) {
try {
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
// Only check bean definition if it is complete.
if (!mbd.isAbstract() && (allowEagerInit ||
(mbd.hasBeanClass() || !mbd.isLazyInit() || isAllowEagerClassLoading()) && !requiresEagerInitForType(mbd.getFactoryBeanName()))) {
// !!! 重要である!ここでは,beanNameとbeanDefinitionに基づいて,現在の型がFactoryBean型であるかどうかを判断する。このパラメタは,通常のBeanとFactoryBeanとを区別する核となる。
// isFactoryBean()メソッドの分析については、次のサブセクション7を参照のこと。
boolean isFactoryBean = isFactoryBean(beanName, mbd);
BeanDefinitionHolder dbd = mbd.getDecoratedDefinition();
// !!! 重要型が一致するかどうか、デフォルトはfalseである.
boolean matchFound = false;
// FactoryBeanの初期化を許可するかどうか、デフォルトは許可されている
boolean allowFactoryBeanInit = allowEagerInit || containsSingleton(beanName);
boolean isNonLazyDecorated = dbd != null && !mbd.isLazyInit();
// 次に、matchFoundパラメーターを設定することである。
// ファクトリBean型でない場合は,直接isTypeMatchメソッドを呼び出してmatchFoundパラメタを設定する。
if (!isFactoryBean) {
if (includeNonSingletons || isSingleton(beanName, mbd, dbd)) {
matchFound = isTypeMatch(beanName, type, allowFactoryBeanInit);
}
}
// FactoryBeanであれば、次のelseブランチを入力する。
else {
// まず第一に、matchFoundを設定しようとする通常のbeanNameによると、trueの場合、それはFactoryBeanで生成されたbeanNameを探している。
if (includeNonSingletons || isNonLazyDecorated || (allowFactoryBeanInit && isSingleton(beanName, mbd, dbd))) {
matchFound = isTypeMatch(beanName, type, allowFactoryBeanInit);
}
// !重要である! matchFoundがまだfalseの場合、FactoryBean自体を探している可能性が高いことを意味し、次にbeanNameに接頭辞を追加する:&FactoryBean自体を見つけようとする。
if (!matchFound) {
// In case of FactoryBean, try to match FactoryBean instance itself next.
beanName = FACTORY_BEAN_PREFIX + beanName;
matchFound = isTypeMatch(beanName, type, allowFactoryBeanInit);
}
}
// matchFoundが真の場合,beanNameが結果に追加される。FactoryBean型を探す場合、beanNameはすでに追加されていることに注意。&接頭辞
if (matchFound) {
result.add(beanName);
}
}
}
catch (CannotLoadBeanClassException | BeanDefinitionStoreException ex) {
if (allowEagerInit) {
throw ex;
}
// Probably a placeholder: let's ignore it for type matching purposes.
LogMessage message = (ex instanceof CannotLoadBeanClassException) ?
LogMessage.format("Ignoring bean class loading failure for bean '%s'", beanName) :
LogMessage.format("Ignoring unresolvable metadata in bean definition '%s'", beanName);
logger.trace(message, ex);
onSuppressedException(ex);
}
}
}
// ...他のコードを省略する...
// 見つかったBeanNameの結果を返す
return StringUtils.toStringArray(result);
}
AbstractBeanFactory#isFactoryBean
それを知った上で、FactoryBean型かどうかを判断する方法は、isFactoryBean()メソッドによって解決されます:
protected boolean isFactoryBean(String beanName, RootBeanDefinition mbd) {
Boolean result = mbd.isFactoryBean;
// beanDefinitionのisFactoryBean属性がNULLの場合、bdのbeanTypeを直接取得し、FactoryBeanかどうかを判断する。
// 次に、判定結果をbdのisFactoryBeanプロパティに代入し、結果を直接返す。
if (result == null) {
Class<?> beanType = predictBeanType(beanName, mbd, FactoryBean.class);
result = (beanType != null && FactoryBean.class.isAssignableFrom(beanType));
mbd.isFactoryBean = result;
}
return result;
}
AbstractBeanFactory#getBean(java.lang.String, java.lang.Class, java.lang.Object...)
ApplicationContextのgetBeaメソッドには、多くのオーバーロードがありますが、実際には、究極は、特定のBeanを見つけるために、beanNameに基づいて、beanNameを解決することです。その理由は、ApplicationContextでは、beanDefinition、beanType、IOCコンテナなどは、すべてbeanNameに基づいてマッピング関係を行うからです。したがって、ソースコードを読むと、多くの場合、最初にbeanNameを解決してから、さらに操作を行うことがわかります。beanNameの定義方法が多様であるため、解析処理も複雑になり、ソースコードを解析するときは静かにする必要があります。getBeanメソッド自体は、Beanのライフサイクル、循環依存関係、および他の複雑なシナリオを含むため、分析のためにgetBeanメソッドを別途取り出します。この記事だけgetBeanメソッドを知っている必要があります実際のBeanを取得することができます。
まとめ
上記の分析により、FactoryBeanがどのように動作するかの一般的なアイデアが得られます。ソースコードから、いくつかの結論を引き出すこともできます:





