目次
- 序文
- 授業内容
-
- 1. 依存性注入の方法(事前知識)
- 2. 依存性注入プロセス
- 3. [注入点を探す]方法説明
-
- 3.1 AbstractAutowireCapableBeanFactory#applyMergedBeanDefinitionPostProcessors: インジェクションポイントコードエントリを探しています
- 3.2 AutowiredAnnotationBeanPostProcessor#postProcessMergedBeanDefinition
- 3.3 AutowiredAnnotationBeanPostProcessor#findAutowiringMetadata: インジェクションポイントを見つける
- *3.4 AutowiredAnnotationBeanPostProcessor#buildAutowiringMetadata: ビルドインジェクションポイント
- 3.5 ReflectionUtils#doWithLocalFields: リフレクションを使用してクラスの [フィールド] を走査します
- 3.6 AutowiredAnnotationBeanPostProcessor#findAutowiredAnnotation: フィールド上の autowired アノテーションを検索します
- 3.7 修飾子.isStatic
- 3.8 残りの手順
- 4. [注入点を探す]ロジックフローチャート
- 4 ポイント 5、== 特別なステートメント ==
- 5. [プロパティ充填]ロジックフローチャート
- 6. [プロパティ充填]メソッドの説明
-
- 6.1 AbstractAutowireCapableBeanFactory#populateBean
- 6.2 InstantiationAwareBeanPostProcessor#postProcessProperties: 処理プロパティ
- 6.3 InjectionMetadata#inject: 注入ポイントに従って注入
- 6.4 AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject: フィールド クラス属性の注入 [エントリ]
- 6.5 AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#resolveFieldValue: フィールド属性インジェクションの解決 [エントリ]
- 6.6 DefaultListableBeanFactory#resolveDependency: 属性インジェクションの解決 [エントリ]
- 6.7 DefaultListableBeanFactory#doResolveDependency: 属性インジェクションの解決。ここで実際の作業が行われます。
- 要約する
序文
読書の準備
Springのソースコード解析は前後関係が強い処理であり、ここでの解析もコード順に説明しているため、事前知識を理解していないと、現在の内容を理解することができなくなります。したがって、私の以前の記事を (上から下まで) 読むことを特にお勧めします。
- Spring の根底にある核原理の分析[学習難易度: ★★☆☆☆ ]
- 手書き簡単スプリングコンテナ工程解析【学習難易度:★★☆☆☆】
- Springの基盤となるアーキテクチャの中核概念の分析[学習難易度:★★★☆☆、重要度:★★★★★ ]
- Beanのライフサイクルフローチャート【学習難易度: ☆☆☆☆☆、重要度: ★★★★★】
- Spring の Bean ライフサイクルのソースコード解析 —— フェーズ 1 (スキャンして BeanDefinition を生成) [学習難易度: ★★☆☆☆、重要度: ★★★☆☆ ]
- Spring Bean ライフサイクル ソースコード解析 - フェーズ 2 (IoC インスタンス化) [学習難易度: ★★★★★、重要度: ★★★☆☆ ]
(追記: 特に「Bean のライフ サイクル フローチャート」は、最初に [目を開いて] プロセスを理解するのに役立ちます。結局のところ、[ビジネスを通じてコードを理解することは、コードを通じてビジネスを理解するよりもはるかに簡単です]!!!! )
(追記: 特に「Bean のライフ サイクル フローチャート」は、最初に [目を開いて] プロセスを理解するのに役立ちます。結局のところ、[ビジネスを通じてコードを理解することは、コードを通じてビジネスを理解するよりもはるかに簡単です]!!!! )
(追記: 特に「Bean のライフ サイクル フローチャート」は、最初に [目を開いて] プロセスを理解するのに役立ちます。結局のところ、[ビジネスを通じてコードを理解することは、コードを通じてビジネスを理解するよりもはるかに簡単です]!!!! )
読書ガイド
前回のレッスンですでに説明しましたが、この Spring ソース コード分析の一般的なエントリ ポイントは であるnew AnnotationConfigApplicationContext("org.tuling.spring");
ため、ここでは説明を繰り返しません。このレッスンで話したいのは Spring IOC のプロパティ充填/依存性注入です。ここではエントリに直接与えてみましょう。呼び出しチェーンは次のとおりです: (呼び出しチェーンは比較的深いので、絡まらないように注意してください)詳細)
- AbstractApplicationContext#refresh: Refresh メソッド、気にしない
- AbstractApplicationContext#finishBeanFactoryInitialization: 残りのすべての (遅延初期化ではない) シングルトンはここでインスタンス化されます
- DefaultListableBeanFactory#preInstantiateSingletons: ここで残りのすべての (遅延初期化ではない) シングルトンをインスタンス化します (上記のメソッド、コアの作業メソッドはここにあります)
- DefaultListableBeanFactory#getBean: Beanを取得するメソッド
- AbstractBeanFactory#doGetBean: 指定された Bean のインスタンスを返します。これは共有または独立できます。
- 上記の
AbstractBeanFactory#doGetBean
部分コードで記述されたコールバック メソッドは次のとおりです。
// 如果是单例创建bean实例
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
});
beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
- AbstractAutowireCapableBeanFactory#createBean: このクラスの中心的なメソッド: Bean インスタンスの作成、Bean インスタンスの設定、ポストプロセッサの適用など。
- AbstractAutowireCapableBeanFactory#doCreateBean: [インスタンス化] および後続のステートメント サイクルの呼び出し場所。
- 【入口一】: AbstractAutowireCapableBeanFactory#applyMergedBeanDefinitionPostProcessors: マージされた BeanDefinition ポストプロセッサを指定された BeanDefinition に適用します。
- 【入口2】AbstractAutowireCapableBeanFactory#populateBean: 指定された BeanWrapper の Bean インスタンスに Bean 定義のプロパティ値を設定します。
上記の呼び出しチェーンに示されているように、最後の 2 つのメソッドが私たちの研究の中核となるメソッドです。なぜここには入り口が2つあると言われているのでしょうか?本章では主に「プロパティ充填・依存性注入」を2部に分けて分析していきます。[入口 1] は [パート 1: 注入ポイントの検索] に対応し、[入口 2] は [パート 2: 属性の充填と充填後] に対応します。
読書のアドバイス
- ソースコードを見て、細部に絡むことを忘れないでください。そうしないと、簡単に行き詰ってしまいます。通常はメインプロセスだけを見てください
- 理解できない場合は、クラスのアノテーションまたはメソッドのアノテーションを見てください。Spring は非常に優れたソース コードであり、注釈が適切に配置されています
- アイデアユーザーの方はF11のブックマーク機能をもっと活用してください。
- Ctrl + F11 ファイル/フォルダーを選択し、ニーモニックを使用してブックマークを設定/解除します (必須)
- Shift + F11 ポップアップブックマーク表示レイヤー (必須)
- Ctrl +1、2、3...9 対応する値のブックマーク位置に移動します (必須)
授業内容
1. 依存性注入の方法(事前知識)
Springでは属性インジェクションには「手動インジェクション」と「自動インジェクション」の2つの方法があります。
1.1 手動注入
Bean が XML で定義されている場合、プログラマが手動で属性に値を割り当てるため、Bean は手動で挿入されます。次のように:
<bean name="userService" class="com.luban.service.UserService">
<property name="orderService" ref="orderService"/>
</bean>
経験豊富な学生は、その上の最下層がsetXxx
メソッドを通じて注入されていることを知っておく必要があります。また、工法で注入する方法としては、次のような方法もあります。
<bean name="userService" class="com.luban.service.UserService">
<constructor-arg index="0" ref="orderService"/>
</bean>
そのため、手動インジェクションの最下層は「セットメソッドインジェクション」と「構築メソッドインジェクション」の2種類に分かれます。
1.2 自動注入
自動インジェクションには「XML autowire自動インジェクション」と「@Autowiredアノテーション自動インジェクション」の2種類があります。
1.2.1 XML オートワイヤーの自動インジェクション
XML では、Bean を定義するときにこの Bean の自動注入モードを指定できます。これには次のメソッドがあります。
1.2.1.1 byType: タイプごとに注入
byTypeインジェクション方式では最下層をsetXxx
メソッドベースで実装するため、setterメソッドが必須となります。ここでいう型とは、[入力パラメータ]の型です。
byType の自動入力プロパティを渡すときの Spring のプロセスは次のとおりです。
- set メソッド内の唯一のパラメータのパラメータ タイプを取得し、コンテナに移動してタイプに応じて Bean を取得します。
- 複数見つかった場合はエラーが報告されます
使用例は次のとおりです。
<bean id="userService" class="com.luban.service.UserService" autowire="byType"/>
public void setOrderService(OrderService orderService) {
this.orderService = orderService;
}
上記の例の型は、入力パラメータorderService
の型を指します。OrderService
1.2.1.2 byName: 名前による注入
byTypeインジェクション方式では最下層をsetXxx
メソッドベースで実装するため、setterメソッドが必須となります。ここで言う[名前]はsetXxx
後半Xxx
部分を指します。
したがって、byName の autofill 属性を渡すときの Spring のプロセスは次のようになります。
- すべての設定メソッドに対応する Xxx 部分の名前を検索します。
- Xxx部分の名前に従ってBeanを取得します
使用例は次のとおりです。
<bean id="userXmlBean" class="org.tuling.spring.xml.bean.UserXmlBean" autowire="byName"/>
<bean id="walletXmlBean" class="org.tuling.spring.xml.bean.WalletXmlBean"/>
上記のように、定義したuserXmlBean
自動注入タイプはであり、 という名前のBeanbyName
が定義されています。walletXmlBean
public class UserXmlBean {
private WalletXmlBean wallet;
public void printProperty() {
System.out.println(wallet);
}
public void setWalletXmlBean(WalletXmlBean param) {
this.wallet = param;
}
}
上記のように、UserXmlBean
メンバー変数を持つ1 つを定義しましたWalletXmlBean wallet
。同時に、printProperty()
メンバー プロパティのアドレスを出力するメンバー メソッドが宣言されます。
テストコード:
public class MyXmlApplicationContextTest {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
UserXmlBean userXmlBean = (UserXmlBean)context.getBean("userXmlBean");
userXmlBean.printProperty();
}
// 系统输出:
// org.tuling.spring.xml.bean.WalletXmlBean@1d16f93d
}
上記のように、型の属性には 2 つの名前があり、1 つはメンバー変数UserXmlBean
、もう 1 つはセッター メソッドの入力パラメータですが、注入がまったく妨げられるわけではありません。のルールによれば、検索されるのは後半だからです。十分に納得できませんね?内部の setter メソッドを次のように変更しましょう。WalletXmlBean
wallet
param
byName
byName
setXxx
Xxx
UserXmlBean
public void setWalletXmlBean123(WalletXmlBean param) {
this.wallet = param;
}
このとき、再度呼び出して を出力しますnull
。
1.2.1.3 コンストラクター: 構築メソッドに従って注入
コンストラクターとは構築メソッドによる注入を意味しますが、実際、この状況は比較的単純で、byType や byName ほど複雑ではありません。
コンストラクターの場合、set メソッドを記述する必要はありません。コンストラクター メソッドを通じて Bean が注入されると、Spring はコンストラクター メソッドのパラメーター情報を使用して Spring コンテナーから Bean を検索し、これにより、Bean オブジェクトがインスタンス化され、属性の割り当てが完了します (属性割り当てのコードはプログラマが記述する必要があります)。
(追記: ここではクラスに複数のコンストラクターがある場合は考慮しません。コンストラクターの推論については後ほど個別に説明します。ここではパラメーターを持つコンストラクターが 1 つだけであることのみを考慮します。)
実際、コンストラクターの注入は byType+byName と同等です。Spring が byName の auto-fill プロパティを渡すときのフローは次のようになります。
- 構築メソッドのパラメータ タイプを通じて Bean を検索します。対応するタイプの Bean が 1 つだけある場合は、それだけです。
- 複数見つかった場合はパラメータ名に従って決定されます
- 最終的にパラメータ名から判断できない場合はエラーとなります。
使用例は次のとおりです。
<bean id="userXmlBean" class="org.tuling.spring.xml.bean.UserXmlBean" autowire="constructor"/>
<bean id="walletXmlBean123" class="org.tuling.spring.xml.bean.WalletXmlBean"/>
<bean id="walletXmlBean" class="org.tuling.spring.xml.bean.WalletXmlBean"/>
Bean の例:
public class UserXmlBean {
private WalletXmlBean wallet;
public void printProperty() {
System.out.println(wallet);
}
public UserXmlBean(WalletXmlBean walletXmlBean) {
this.wallet = walletXmlBean;
}
}
具体的な呼び出しメソッドとエラーメソッドはここでは紹介しません。戻って自分で試してみましょう
1.2.1.4 その他
その他、次のようなものがあります。
- デフォルト: デフォルト値、私たちがデモしてきた特定の Bean のオートワイヤーを示します、また、<beans> タグで直接オートワイヤーを設定することもできます。設定されている場合、<bean> タグで設定されたオートワイヤーがデフォルトであるかどうかを示しますの場合、<beans> タグに設定された autowire が使用されます。
- no: オートワイヤをオフにし、自動インジェクションを行わないことを意味します
1.2.1.5 AutowireによるXMLの自動挿入方法の概要
XML の自動挿入の最下層は実際には次のようになります。
- セットメソッドインジェクション
- コンストラクターインジェクション
1.2.2 @Autowired アノテーションの自動挿入
@Autowired アノテーションは基本的に byType と byName の組み合わせです。まず byType で、複数見つかった場合は次に byName です。これはXML構築メソッドの注入原理と全く同じです。あれは:
- まず、タイプに従って Bean を見つけます。対応するタイプの Bean が 1 つしかない場合、それが Bean です。
- 複数見つかった場合は属性名に従って決定されます
- 最終的に属性名から判断できない場合はエラーとなります
@Autowired アノテーションは次のように記述できます。
- 属性: 最初に属性タイプに従って Bean を検索し、複数の Bean が見つかった場合は、属性名に従って 1 つを決定します (属性インジェクション)
- 構築方法について: まず、メソッドのパラメータのタイプに従って Bean を検索し、複数の Bean が見つかった場合は、パラメータ名に従って 1 つを決定します (構築メソッドのインジェクション)。
- set メソッドの場合: まず、メソッド パラメータのタイプに従って Bean を検索し、複数の Bean が見つかった場合は、パラメータ名に従って 1 つを決定します (set メソッド インジェクション)。
1.2.3 自動注入の概要
XML での自動インジェクションは非常に強力であることがわかりますが、問題は、なぜ通常 @Autowired アノテーションを使用するのかということです。上記の自動注入方法を使用する代わりに?
実際、@Autowired アノテーションは、XML の autowire 属性のアノテーション メソッドの置き換えに相当します。基本的に、 @Autowired アノテーションは autowire と同じ機能を提供しますが、よりきめ細かい制御そしてより広い適用範囲。
XML の autowire は Bean 全体のすべてのプロパティを制御しますが、 @Autowired アノテーションはプロパティ、set メソッド、または構築メソッドに直接書き込まれます。
別の例として、クラスに複数のコンストラクターがある場合、XML の autowire=constructor を使用すると、どのコンストラクターを使用するかを制御できませんが、 @Autowired アノテーションを使用して、使用するコンストラクターを直接指定できます。
同時に、 @Autowired アノテーションを使用すると、どの属性を自動的に挿入するか、どの属性を挿入したくないかを制御することもできます。これもきめ細かい制御です。
2. 依存性注入プロセス
2.1 簡単なレビュー
依存関係注入のプロセスは、次の 3 つのステップに大別できます。[射出点の検索]、[充填属性]、[充填後属性]。しかし実際には、[注入ポイントを見つける] プロセスは 2 か所で呼び出されます。1つ目は矢印の方向、[インスタンス化]ステージの[BeanDefinition後処理]を行う場所です。どうやって理解しますか?なぜなら、「注入ポイントの検索」の実装クラスは「BeanDefinition後処理」の一つだからです。。
上記の概念はやや複雑です。[インジェクションポイントの検索]とは、簡単に言うと、 、 、 、 アノテーションによって変更された属性やメソッドなどを探し、@Autowird
[@Value
属性@Inject
の@Resource
埋め込み] の際に、これらの検索されたインジェクションポイントを処理して、対応するBeanのプロパティに設定することです。 。
2.2 コンセプトレビュー
この[インスタンス化]プロセスには、Spring の基礎となる設計の概念がいくつか含まれており、前回のメモで Spring の基礎となる概念についての説明を大まかに紹介しました。
含まれる主な概念は次のとおりです。
- BeanDefinition (設計図): BeanDefinition は Bean の定義を表し、BeanDefinition には Bean の特性を記述する多くの属性があります。
- MergedBeanDefinitionPostProcessor: BeanDefinition ポストプロセッサをマージします。
AutowiredAnnotationBeanPostProcessor
ただし、ここでは主に次のことについて話しますCommonAnnotationBeanPostProcessor
。彼らの役割は何でしょうか?前者はSpring 内で定義された自動注入アノテーションを処理するもの@Autowired
であり@Value
、後者は jdk によって定義されたアノテーションを処理するものです@Resource
。MergedBeanDefinitionPostProcessor
これは主にpostProcessMergedBeanDefinition
[注入ポイントの検索]の操作を完了するために使用されます。 - InstantiationAwareBeanPostProcessor: インスタンス化を認識する Bean ポストプロセッサ。これは上記と同じですが、実際、重要な点は
AutowiredAnnotationBeanPostProcessor
に従うことですCommonAnnotationBeanPostProcessor
。これら2つのクラスも継承され、InstantiationAwareBeanPostProcessor
その中でpostProcessProperties
対応するアノテーション[自動インジェクション]の操作が完了します
CommonAnnotationBeanPostProcessor
インターフェースは次のように定義されます。
/**
* 这个后置处理器通过继承InitDestroyAnnotationBeanPostProcessor和InstantiationAwareBeanPostProcessor注解,
* 获得了对@PostConstruct和@PreDestroy的支持。
* 另外,这个类的核心处理元素是@Resource注解
*/
public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBeanPostProcessor
implements InstantiationAwareBeanPostProcessor, BeanFactoryAware, Serializable {
// 具体代码就不贴了。学到这里大家应该知道如何通过【接口】继承、实现来猜测类能力了吧
}
AutowiredAnnotationBeanPostProcessor
インターフェースは次のように定義されます。
// 继承类跟CommonAnnotationBeanPostProcessor 如出一辙,唯一不同的是,继承了功能更强大的
// SmartInstantiationAwareBeanPostProcessor(InstantiationAwareBeanPostProcessor子类)
// 实现这个类,是为了实现里面的推断构造方法
public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor,
MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware {
2.3 コアメソッドの説明
このセクション [プロパティの注入] は 2 つの部分に分かれています。最初の部分は [注入ポイントを見つける]、残りは 2 番目の部分です。
まず最初の部分について説明します。最初の部分には主に [3 つのクラス、7 つのコア メソッド] が含まれます。
パート 2、未定…
3. [注入点を探す]方法説明
上で述べたように、[注入ポイントを探す]は実際には 2 か所で呼び出されます。1 つはpopulateBean()
[属性充填] の前の [BeanDefinition のマージ] でありapplyMergedBeanDefinitionPostProcessors()
、もう 1 つは [属性充填] 内にあります。([注入ポイントを探す] ソース コードでは AutowiredAnnotationBeanPostProcessor を例にしています)
3.1 AbstractAutowireCapableBeanFactory#applyMergedBeanDefinitionPostProcessors: インジェクションポイントコードエントリを探しています
フルパス: org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyMergedBeanDefinitionPostProcessors
MergedBeanDefinitionPostProcessors を指定された Bean 定義に適用し、その postProcessMergedBeanDefinition メソッドを呼び出します。
ソースコードは次のとおりです。
protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class<?> beanType, String beanName) {
for (MergedBeanDefinitionPostProcessor processor : getBeanPostProcessorCache().mergedDefinition) {
processor.postProcessMergedBeanDefinition(mbd, beanType, beanName);
}
}
このサイクルのクラス オブジェクトをクリックすると、AutowiredAnnotationBeanPostProcessor
と の2 つの実装クラスが見つかりますCommonAnnotationBeanPostProcessor
。便宜上、ここでは例のみを示しますAutowiredAnnotationBeanPostProcessor
が、前者によって処理される Spring アノテーションが Spring 自体によって記述され、後者によって処理される JDK アノテーションが異なる点を除いて、この 2 つの実装方法は基本的に同じです。
3.2 AutowiredAnnotationBeanPostProcessor#postProcessMergedBeanDefinition
メソッド呼び出しチェーン: 3.2 の applyMergedBeanDefinitionPostProcessors() によって呼び出されます
。 フルパス: org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessMergedBeanDefinition
メソッド アノテーション: 指定された Bean の指定されたマージ Bean 定義を後処理します。
ソースコードは次のとおりです。
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
metadata.checkConfigMembers(beanDefinition);
}
メソッドの解釈: コードは非常にシンプルで、プロセス全体の実際の作業は実際には内部のfindAutowiringMetadata()
メソッドです。
3.3 AutowiredAnnotationBeanPostProcessor#findAutowiringMetadata: インジェクションポイントを見つける
メソッド呼び出しチェーン: 3.2 の applyMergedBeanDefinitionPostProcessors() によって呼び出されます
。 フルパス: org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#findAutowiringMetadata
メソッド アノテーション: インジェクション ポイントの検索
** private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
// 设置缓存key
String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
// 先看缓存里面有没有
InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
synchronized (this.injectionMetadataCache) {
metadata = this.injectionMetadataCache.get(cacheKey);
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
if (metadata != null) {
metadata.clear(pvs);
}
metadata = buildAutowiringMetadata(clazz);
this.injectionMetadataCache.put(cacheKey, metadata);
}
}
}
return metadata;
}**
メソッドの解釈: ここでキャッシュを使っていますが、端的に言えば「注入ポイントが見つかったかどうか」を判断するためのマップであり、その後の注入の便宜のためでもあります。ここでの中心的な操作は、buildAutowiringMetadata
現在のクラスを呼び出すことによってその注入ポイント情報を構築し、それをラップすることですInjectionMetadata
。
*3.4 AutowiredAnnotationBeanPostProcessor#buildAutowiringMetadata: ビルドインジェクションポイント
メソッド呼び出しチェーン: 3.3 の findAutowiringMetadata() によって呼び出されます
。 フルパス: org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#buildAutowiringMetadata
メソッド アノテーション: ビルド インジェクション ポイント
ソースコードは次のとおりです。
private InjectionMetadata buildAutowiringMetadata(Class<?> clazz) {
// 第一步:判断当前类是否候选类(是否需要【寻找注入点】)
if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) {
return InjectionMetadata.EMPTY;
}
List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
Class<?> targetClass = clazz;
do {
final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
// 第二步:利用反射,寻找【字段】上是否有【自动注入】的注解
ReflectionUtils.doWithLocalFields(targetClass, field -> {
MergedAnnotation<?> ann = findAutowiredAnnotation(field);
if (ann != null) {
if (Modifier.isStatic(field.getModifiers())) {
if (logger.isInfoEnabled()) {
logger.info("Autowired annotation is not supported on static fields: " + field);
}
return;
}
boolean required = determineRequiredStatus(ann);
currElements.add(new AutowiredFieldElement(field, required));
}
});
// 第三步:利用反射,寻找【方法】上是否有【自动注入】的注解
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
return;
}
MergedAnnotation<?> ann = findAutowiredAnnotation(bridgedMethod);
if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
if (Modifier.isStatic(method.getModifiers())) {
if (logger.isInfoEnabled()) {
logger.info("Autowired annotation is not supported on static methods: " + method);
}
return;
}
if (method.getParameterCount() == 0) {
if (logger.isInfoEnabled()) {
logger.info("Autowired annotation should only be used on methods with parameters: " +
method);
}
}
boolean required = determineRequiredStatus(ann);
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
currElements.add(new AutowiredMethodElement(method, required, pd));
}
});
elements.addAll(0, currElements);
targetClass = targetClass.getSuperclass();
}
while (targetClass != null && targetClass != Object.class);
return InjectionMetadata.forElements(elements, clazz);
}
方法の解釈: 上記の方法は非常に長く感じますが、全体としては 3 つのステップに分かれており、特に理解するのが難しいものはありません。特に、前回の【春の手書きガイド記事】を読んでくださった方はぜひ。
最初のステップは、現在のクラスが候補クラスであるかどうか ([注入ポイントを見つける] 必要があるかどうか) を判断することです。正直、ここで何を意味するのかよくわかりません。コードを見ると、java.
最初にクラスとアノテーションをフィルタリングしています(Java の始まりである Baidu。一般的には JDK のオープン API であり、メタ アノテーションが宣言されています)内部 (@Retention、@Target など)。したがって、私の理解では、ここでの判断ロジックは、Spring クラスまたはカスタム クラスが @Resource などの JDK の [自動アセンブリ] アノテーションを使用できるようにすることですが、JDK が使用できるのは JDK 独自の [自動アセンブリ] アノテーションのみです。
2 番目のステップ: フィールドとメソッドのアノテーションを変更できることは誰もが知っています@Autowired
。2 番目のステップは、[フィールド] タイプのアノテーションを処理することです。
3 番目のステップ: [メソッド] タイプのアノテーションを処理します。
2 番目と 3 番目のステップのソース コードの実装は実際には同じですが、処理オブジェクトが異なるため、ここでは [フィールド] の処理ロジックについてのみ説明します。それは次のとおりです。
ReflectionUtils.doWithLocalFields(targetClass, field -> {
MergedAnnotation<?> ann = findAutowiredAnnotation(field);
if (ann != null) {
if (Modifier.isStatic(field.getModifiers())) {
if (logger.isInfoEnabled()) {
logger.info("Autowired annotation is not supported on static fields: " + field);
}
return;
}
boolean required = determineRequiredStatus(ann);
currElements.add(new AutowiredFieldElement(field, required));
}
});
しかし、最初に一言言わせてください。プロセス全体がdo-while
ループ本体で実行されることに気づいたかもしれませんが、なぜでしょうか? 実はここのループはdo-while
「継承関係のあるBean」のインジェクションを処理するためのものです。
3.5 ReflectionUtils#doWithLocalFields: リフレクションを使用してクラスの [フィールド] を走査します
メソッド呼び出しチェーン: 3.4 の buildAutowiringMetadata() によって呼び出されます。
フル パス: org.springframework.util.ReflectionUtils#doWithLocalFields
メソッド アノテーション: 指定されたクラス内のローカルに宣言されたすべてのフィールドに対して指定されたコールバックを呼び出します。
リフレクション ツール メソッドは次のように実装されます。
public static void doWithLocalFields(Class<?> clazz, FieldCallback fc) {
for (Field field : getDeclaredFields(clazz)) {
try {
fc.doWith(field);
}
catch (IllegalAccessException ex) {
throw new IllegalStateException("Not allowed to access field '" + field.getName() + "': " + ex);
}
}
}
(追記: 上記のコードは、関数型インターフェイスやラムダ式の使用方法がよくわからない場合、理解できない可能性があるため、すぐに復習する必要があります)
3.6 AutowiredAnnotationBeanPostProcessor#findAutowiredAnnotation: フィールド上の autowired アノテーションを検索します
メソッド呼び出しチェーン: 3.5 の doWithLocalFields() によって呼び出されます。
フルパス: org.springframework.util.ReflectionUtils#doWithLocalFields
メソッド アノテーション: 指定されたクラス内のローカルに宣言されたすべてのフィールドに対して指定されたコールバックを呼び出します。
次に、リフレクション [コールバック関数] で呼び出して、findAutowiredAnnotation
現在のフィールドとメソッドに [自動アセンブリ] アノテーションがあるかどうかを判断します。次のように:
@Nullable
private MergedAnnotation<?> findAutowiredAnnotation(AccessibleObject ao) {
MergedAnnotations annotations = MergedAnnotations.from(ao);
for (Class<? extends Annotation> type : this.autowiredAnnotationTypes) {
MergedAnnotation<?> annotation = annotations.get(type);
if (annotation.isPresent()) {
return annotation;
}
}
return null;
}
this.autowiredAnnotationTypes
慎重な友人は、価値とは何ですかと尋ねるかもしれません。はい、はい@Autowired
と@Value
注釈は知っていますが、値はどこに割り当てられるのでしょうか? ああ、これについては今は話しません。Spring コンテナーの起動に関する章で説明します。ただし、最初に言っておきますが、これはAutowiredAnnotationBeanPostProcessor
コンストラクターで初期化されます。次のように:
public AutowiredAnnotationBeanPostProcessor() {
this.autowiredAnnotationTypes.add(Autowired.class);
this.autowiredAnnotationTypes.add(Value.class);
try {
this.autowiredAnnotationTypes.add((Class<? extends Annotation>)
ClassUtils.forName("javax.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader()));
logger.trace("JSR-330 'javax.inject.Inject' annotation found and supported for autowiring");
}
catch (ClassNotFoundException ex) {
// JSR-330 API not available - simply skip.
}
}
3.7 修飾子.isStatic
メソッド呼び出しチェーン: 3.5 の doWithLocalFields() によって呼び出されます。
フル パス: java.lang.reflect.Modifier#isStatic
メソッド アノテーション: 指定されたクラス内のローカルに宣言されたすべてのフィールドに対して指定されたコールバックを呼び出します。
static
そして、@Autowired と @Value のアノテーションが付けられたフィールドまたはメソッドが見つかった場合、そのフィールドまたはメソッドが変更されているか、つまり静的であるかどうかも判断されます。静的なものは処理されません。ソースコードは次のとおりです。
if (Modifier.isStatic(field.getModifiers())) {
if (logger.isInfoEnabled()) {
logger.info("Autowired annotation is not supported on static fields: " + field);
}
return;
}
if (Modifier.isStatic(method.getModifiers())) {
if (logger.isInfoEnabled()) {
logger.info("Autowired annotation is not supported on static methods: " + method);
}
return;
}
説明する?理由は非常に単純で、プロトタイプ Bean を見ればわかります。static がオブジェクトではなくクラスに属していることはわかっています。そのため、static を注入するたびに static を処理しなければならない場合、繰り返し上書きされることになりませんか? 例:
@Component
@Scope("prototype")
public class OrderService {
}
@Component
@Scope("prototype")
public class UserService {
@Autowired
private static OrderService orderService;
public void test() {
System.out.println("test123");
}
}
上記のコードを見ると、UserService と OrderService は両方ともプロトタイプ Bean であることがわかります。Spring が静的フィールドの自動挿入をサポートしていると仮定すると、今度はそれを 2 回呼び出します。
UserService userService1 = context.getBean("userService")
UserService userService2 = context.getBean("userService")
Q この時、userService1のorderServiceの値は何でしょうか?それともそれ自体が注入する値ですか? 答えは「いいえ」です。userService2 が作成されると、静的な orderService フィールドの値が変更され、バグが発生します。
3.8 残りの手順
残りのステップでは、次の 3 つの作業が行われます。
@Autowired(required = false)
このプロパティを設定します- 結果として得られるビルド ポイントをラップします。
InjectionMetadata.InjectedElement
- 取得したすべての注入ポイントをカプセル化し、
InjectionMetadata
キャッシュします。
4. [注入点を探す]ロジックフローチャート
過程説明:
- すべての属性フィールドを走査する 現在のクラスのフィールド
- フィールドに @Autowired、@Value、@Inject のいずれかがあるかどうかを確認し、存在する場合、そのフィールドは注入ポイントとみなされます。
- フィールドが静的な場合は注入なし
- @Autowired で必要な属性の値を取得します
- フィールド情報を AutowiredFieldElement オブジェクトに構築し、それを注入ポイント オブジェクトとして currElements コレクションに追加します。
- 現在のクラスのすべてのメソッドを走査する メソッド
- 元のメソッドが見つかった場合、現在のメソッドがブリッジ メソッドであるかどうかを判断します。
- メソッドに @Autowired、@Value、@Inject のいずれかが存在するかどうかを確認し、存在する場合、そのメソッドはインジェクション ポイントとみなされます。
- メソッドが静的である場合、注入は行われません。
- @Autowired で必要な属性の値を取得します
- メソッド情報を AutowiredMethodElement オブジェクトに構築し、それを注入ポイント オブジェクトとして currElements コレクションに追加します。
- 現在のクラスのフィールドとメソッドを走査した後、親クラスがなくなるまで親クラスを走査します。
- 最後に、currElements コレクションは InjectionMetadata オブジェクトにカプセル化されます。このオブジェクトは、現在の Bean の注入ポイント コレクション オブジェクトとして使用され、キャッシュされます。
四時五分特別な声明
兄弟の皆さん、以下の内容は個人的に非常に複雑であり、Spring のソースコードを順番に読んだのでこれまでで最も複雑な部分であることを事前に宣言しておきます。ただし、2部に分かれているので、やはり階層感はありますので、続きを見る気がない方は、次回、続きをご覧ください。[/狗头][/狗头]
そこで、以下の説明では別の書き方をしてみようと思いました。
- まずはフローチャートを先にあげてツリー構造で書いていきます
- 次に、ソースコードの呼び出し順序に従って[上から下、左から右]の方法でツリー構造にマッピングします。
- 最後に、全員が Spring のソースコードを開いて記事を読み、同時にソースコードを読む必要があると感じました。
- すべてのソース コードについては説明しませんが、馴染みのないソース コードまたは重要なソース コードのみをクリックします。
5. [プロパティ充填]ロジックフローチャート
全体的なソースコードのロジックフローチャートは以下の通りです(描きながら血を吐きました)
クリックすると画像が鮮明ではありません。右クリックして新しいウィンドウを作成して開くことをお勧めします。その後、ズームインして見ることができます
6. [プロパティ充填]メソッドの説明
[入り口 2] が であることは、[読書の提案] ですでに述べましたAbstractAutowireCapableBeanFactory#populateBean
。このメソッドでは、上記の属性フローチャートの第 1 層と第 2 層でこのメソッドについて説明します。
6.1 AbstractAutowireCapableBeanFactory#populateBean
フルパス: org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean
メソッドの説明: Bean 定義のプロパティ値を使用して、指定された BeanWrapper に Bean インスタンスを設定します。
対応するフローチャート:
ソース コードは次のとおりです。 (マークされた手順 1、2、および 3 は、この章の研究内容です)
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
// 空判断,有属性,但是bean为空,则报错
if (bw == null) {
if (mbd.hasPropertyValues()) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
}
else {
// Skip property population phase for null instance.
return;
}
}
// 这个在之前的【实例化阶段】的【实例化后】讲过了,不在本次研究范围内
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
if (!bp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
return;
}
}
}
// 下面才是我们本节课要研究的起点
// 下面才是我们本节课要研究的起点
// 下面才是我们本节课要研究的起点
PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
// 步骤一:
// 处理beanDefinition的autowire属性。比如@Bean标签就可以设置这个属性;xml也可以设置这个属性
int resolvedAutowireMode = mbd.getResolvedAutowireMode();
if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
// Add property values based on autowire by name if applicable.
if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
autowireByName(beanName, mbd, bw, newPvs);
}
// Add property values based on autowire by type if applicable.
if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
autowireByType(beanName, mbd, bw, newPvs);
}
pvs = newPvs;
}
// 步骤二:
// 处理@Autowired、@Value、@Resource等【自动注入】的属性填充(注意,之前是寻找注入点,这里才是真正赋值的地方)
boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);
PropertyDescriptor[] filteredPds = null;
if (hasInstAwareBpps) {
if (pvs == null) {
pvs = mbd.getPropertyValues();
}
for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
if (filteredPds == null) {
filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
}
pvsToUse = bp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
return;
}
}
pvs = pvsToUse;
}
}
// 依赖检查,【细枝末节】,不看了
if (needsDepCheck) {
if (filteredPds == null) {
filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
}
checkDependencies(beanName, mbd, filteredPds, pvs);
}
// 步骤三:
// 处理BeanDefinition里面的propertyValues。比如:我们在操作beanDefinition的时候会修改;
// 或者,在第一步处理@Bean的autowire属性的时候,实际上也是把结果跟旧有BeanDefinition的propertyValues合并。
// 最后在这里处理注入操作
if (pvs != null) {
applyPropertyValues(beanName, mbd, bw, pvs);
}
}
メソッドの解釈: このメソッドは非常に長く見えますが、実際にはロジックは比較的明確です。ただ、コードのスタイルが急に変わっていて、これまでの研究方法とは明らかに違う人のものになっているのが残念です。たとえば、以前はpopulateBean()
ドイツの[インスタンス化後の処理]段階のソースコードはメソッドでカプセル化されていましたが、resolveAfterInstantiation()
ステップ1もメソッドとして記述されresolveAutowireMode()
、セマンティクスがより明確になります。ああ、こうやって後から書くと、ソースコードを読むのが少々不快になります。
あまりナンセンスではないので、内部のプロセスを分析してみましょう。
- ステップ 1: 主に
autowire
@Bean タグの属性を扱います。実際、厳密に言えば、 beanDefinition の下のプロパティを処理することになりますautowire
。以下のとおり、あまり使用されていないと推定されます。
@Bean(autowire = Autowire.BY_NAME)
public OrderService orderService1() {
return new OrderService();
}
またはこれ:
<bean id="userService" class="org.example.spring.bean.UserService" autowire="byType"/>
- ステップ 2: @Autowired、@Value、@Resource およびその他の [自動注入] 属性の入力を処理します (注入ポイントを探す前に、ここに実際の値が割り当てられることに注意してください)。これはこのレッスンの中核となる内容であり、後で詳しく説明します。
- ステップ 3: BeanDefinition の propertyValues を処理します。たとえば、beanDefinition を操作するときに変更します。または、最初のステップで @Bean の autowire 属性を処理するときに、結果を古い BeanDefinition の propertyValues と実際にマージし、最後にここで注入操作を処理します。これについては本当に何も言うことはありません
以下では、ステップ 2 [アノテーションを自動的に挿入する] の属性の入力に焦点を当てます。
6.2 InstantiationAwareBeanPostProcessor#postProcessProperties: 処理プロパティ
前回の【コンセプトレビュー】でも触れましたが、実際に使用しているものは同じですAutowiredAnnotationBeanPostProcessor
ので、簡単な例CommonAnnotationBeanPostProcessor
を示します。AutowiredAnnotationBeanPostProcessor
メソッド呼び出しチェーン: 6.1 の PopulateBean() によって呼び出されます
。 フルパス: org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessProperties
メソッド アノテーション: ファクトリが指定されたプロパティ値を指定された Bean に適用する前に、それらは後処理用です。処理する場合、属性記述子は必要ありません。
対応するフローチャート:
ソースコードは次のとおりです。
@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
try {
metadata.inject(bean, beanName, pvs);
}
catch (BeanCreationException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
}
return pvs;
}
メソッドの解釈: ここでは 2 つのことを行います。1 つ目は [注入ポイントの検索] の入り口です。以前にも紹介しましたが、[注入ポイントの検索]は2か所で呼び出されると言いましたが、こちらは2か所目です。しかし、ここでは、キャッシュにあるものは直接取得されることが多く、再び[注入ポイントを見つける]ケースは多くありません。これらは[詳細]に属し、結局のところ、見えるかどうかは私たちの作業にはほとんど影響しません。全体的な文脈の把握。
したがって、ここで最も重要なことは、この方法を見ることですmetadata.inject()
。注射の意味が一目でわかります。
6.3 InjectionMetadata#inject: 注入ポイントに従って注入
メソッド呼び出しチェーン: 6.2 の postProcessProperties() によって呼び出されます
。 フルパス: org.springframework.beans.factory.annotationInjectionMetadata#inject
メソッド アノテーション: ターゲット クラスのプロパティを注入します。
ソースコードは次のとおりです。
public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
Collection<InjectedElement> checkedElements = this.checkedElements;
Collection<InjectedElement> elementsToIterate =
(checkedElements != null ? checkedElements : this.injectedElements);
if (!elementsToIterate.isEmpty()) {
for (InjectedElement element : elementsToIterate) {
element.inject(target, beanName, pvs);
}
}
}
メソッドの解釈: メソッドは非常に単純で、this.injectedElements
[注入ポイントの検索] ステージでキャッシュした注入ポイントの情報です。それで、ここの注入ポイントの情報をまだ覚えていますか?へー、フィールドクラスとメソッドクラスでカプセル化されたオブジェクトですね。ここでは、メソッドクラスであってもフィールドクラスであっても、すべての注入ポイントをたどって、相手の注入メソッドを順番に呼び出します。
そうは言っても、[単一の責任] を維持するために、Spring は異なる注入オブジェクトに対して 2 つのクラスを設計しました。彼らです:
- AutowiredFieldElement: アノテーションが付けられた [フィールド] に関するインジェクション情報を示します。
- AutowiredMethodElement: アノテーション付きの [メソッド] に関する注入情報を表します。
これら 2 つはAutowiredAnnotationBeanPostProcessor
定義された 内部クラスです
。いつものように、これら 2 つは似ています。例としてAutowiredFieldElement
見てみましょう。element.inject()
6.4 AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject: フィールド クラス属性の注入 [エントリ]
メソッド呼び出しチェーン: 6.3 の inject() によって呼び出されます。
フルパス: org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject
メソッドのアノテーション: シンプルです。要素注入ロジックを実行します。
ソースコードは次のとおりです。
@Override
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
Field field = (Field) this.member;
Object value;
if (this.cached) {
try {
value = resolvedCachedArgument(beanName, this.cachedFieldValue);
}
catch (NoSuchBeanDefinitionException ex) {
// Unexpected removal of target bean for cached argument -> re-resolve
value = resolveFieldValue(field, bean, beanName);
}
}
else {
value = resolveFieldValue(field, bean, beanName);
}
if (value != null) {
ReflectionUtils.makeAccessible(field);
field.set(bean, value);
}
}
メソッドの解釈: 明らかに、最初に呼び出すときは、理論的にはキャッシュがないため、else
ロジックを直接確認します。
6.5 AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#resolveFieldValue: フィールド属性インジェクションの解決 [エントリ]
メソッド呼び出しチェーン: 6.4 の inject() によって呼び出されます。
フルパス: org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject
ソースコードは次のとおりです。
@Nullable
private Object resolveFieldValue(Field field, Object bean, @Nullable String beanName) {
// 步骤一:属性注入准备工作
DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
desc.setContainingClass(bean.getClass());
Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
Assert.state(beanFactory != null, "No BeanFactory available");
TypeConverter typeConverter = beanFactory.getTypeConverter();
Object value;
try {
// 步骤二:属性注入
value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
}
catch (BeansException ex) {
throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
}
// 步骤三:设置bean依赖信息
synchronized (this) {
if (!this.cached) {
Object cachedFieldValue = null;
if (value != null || this.required) {
cachedFieldValue = desc;
registerDependentBeans(beanName, autowiredBeanNames);
if (autowiredBeanNames.size() == 1) {
String autowiredBeanName = autowiredBeanNames.iterator().next();
if (beanFactory.containsBean(autowiredBeanName) &&
beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
cachedFieldValue = new ShortcutDependencyDescriptor(
desc, autowiredBeanName, field.getType());
}
}
}
this.cachedFieldValue = cachedFieldValue;
this.cached = true;
}
}
return value;
}
}
この方法の解釈: 全体として、3 つのステップに分けることができます。最初のステップは、以前に設定された属性、どのような型コンバーターが存在するか、Bean ファクトリの存在を確認するなど、
属性インジェクションの準備です。2番目のステップが核心で、その後が実際のステップです。属性インジェクションが処理されます; 3 番目 最初のステップは、Bean 依存関係情報を設定することです。これは何ですか?簡単に言うと、後のメンテナンスを容易にするために、Bean 間の相互依存性を記録する 2 つのマップが追加されました。2 つのマップは次のとおりです。required
- dependentBeanMap: Bean がどの Bean に依存しているかを記録します。beanName で記録する
- dependencyForBeanMap: どの Bean が Bean に依存しているかを記録します。beanName によっても記録されます
6.6 DefaultListableBeanFactory#resolveDependency: 属性インジェクションの解決 [エントリ]
メソッド呼び出しチェーン: 6.5 のsolveFieldValue() より
フルパス: org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveDependency
メソッド アノテーション: このファクトリで定義された Bean に従って、指定された依存関係を解決します。
対応するフローチャート:
ソースコードは次のとおりです。
@Override
@Nullable
public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
if (Optional.class == descriptor.getDependencyType()) {
return createOptionalDependency(descriptor, requestingBeanName);
}
else if (ObjectFactory.class == descriptor.getDependencyType() ||
ObjectProvider.class == descriptor.getDependencyType()) {
return new DependencyObjectProvider(descriptor, requestingBeanName);
}
else if (javaxInjectProviderClass == descriptor.getDependencyType()) {
return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName);
}
else {
Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
descriptor, requestingBeanName);
if (result == null) {
result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
}
return result;
}
}
メソッドの解釈: 最初にこの属性をどのように注入するか、注入するかどうかの判断がたくさんあります。最初の 3 つは特に気にしませif-elseif-elseif
んが、Spring を使用する場合、基本的にこれらの型は使用しません。興味のある友達は行って自分の目で確かめることができます。最後の部分を見てみましょうelse
。
ここが細かいですが、注入する必要がある箇所があると判断したら[プロキシオブジェクト]を直接返し、それを直接返す(ここの書き方はがあるかどうかを@Lazy
判断するという書き方です)。誰かがそれについて考えたことがあるかどうかはわかりませんが、なぜプロキシ オブジェクトを使用するのでしょうか? ああ、前回の記事で触れた【エージェントパラダイム】をまだ覚えていますか?result==null
@Lazy
// 代理对象
public class ProxyModel extends ProxyTarget {
private ProxyTarget proxyTarget;
public void setProxyTarget(ProxyTarget proxyTarget) {
this.proxyTarget = proxyTarget;
}
@Override
public void run() {
System.out.println("我代理对象可以在这里做加强---1");
super.run();
System.out.println("我代理对象也可以在这里做加强---2");
}
}
このすぐ上です。次に、遅延読み込みの特性を思い出してください。遅延読み込みは使用時に挿入されるだけではありません。したがって、プロキシモードでは、proxyTarge==null
遅延ロードされた各属性の呼び出しメソッドで判断すれば十分です。そのため、プロキシ オブジェクトが返されます。
6.7 DefaultListableBeanFactory#doResolveDependency: 属性インジェクションの解決。ここで実際の作業が行われます。
メソッド呼び出しチェーン: 6.6 のsolveDependency() より
フルパス: org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency
メソッドのコメント: 属性インジェクションを解決します。実際の作業はここで行われます。
対応するフローチャート:
ここではソース コードが少し長いため、スクリーンショットは撮りません。メソッドの長さに関して言えば、以前のコードスタイルと異なり、作成者が明らかに怠けているため、まだ文句を言いたくなります。