blog

Springインジェクションのプロセス

"\n実際、人生のすべての甌穴は自分自身で掘ったものであり、混乱も同様です。あなたの心が強く、愛を失いませんように。\n"\n依存の取り扱い\n\nオートアセンブリ vs @オートワイヤード\n長い間...

Oct 29, 2020 · 15 min. read
シェア
"

人生は自分で掘った落とし穴であり、混乱もまた真実です。愛情を失わず、しっかりとした心でありますように。

"

依存関係の処理

public interface AutowireCapableBeanFactory{ //自動アセンブルは必要ない int AUTOWIRE_NO = 0; //名前によるビーン属性の自動組立て int AUTOWIRE_BY_NAME = 1; //型によるビーン属性の自動組立て int AUTOWIRE_BY_TYPE = 2; //コンストラクタによる自動アセンブリ int AUTOWIRE_CONSTRUCTOR = 3; //廃止されたメソッド,Spring3.0今後、@Autowiredアノテーション付きコンストラクタはサポートしない。 @Deprecated int AUTOWIRE_AUTODETECT = 4; ... } 前回の記事で、ようやくメソッドを通してBeanWrapperオブジェクトを取得しました。このオブジェクトを取得した後、Springでの依存関係の処理は、BeanWrapperを通して行われます。

自動アセンブリと@Autowired

というのも、私は長い間@Autowiredが自動配線だと勘違いしていたからです。そのため、Springの自動配線はまずbyTypeで、次にbyNameだと思い込んでいました。この間違いに気づいたのは、ソースコードを読んでからでした。

Beanの依存関係を自動アセンブルする場合、Springは4つの自動アセンブル戦略を提供します。

public interface AutowireCapableBeanFactory{
 
 //自動アクセサリーは必要である!
 int AUTOWIRE_NO = 0;
 
 //名前の前にある属性の自動構成
 int AUTOWIRE_BY_NAME = 1;
 
 //型によるビーイング属性の自動構成
 int AUTOWIRE_BY_TYPE = 2;
 
 //コンストラクターにおける自動アセンブリー
 int AUTOWIRE_CONSTRUCTOR = 3;
 
 //廃されたメソッド,Spring3.0 @AutowiredアノテーションコンストラクタはSpringオブジェクトのサービス部分である。
 @Deprecated
 int AUTOWIRE_AUTODETECT = 4;
 ...
}

自動アセンブリ

xmlでBeanを定義する場合、以下の方法で自動アセンブリの種類を指定できます。

<bean id="demoServiceOne" class="DemoServiceOne" autowire="byName"/>
<bean id="userService" class="UserService" autowire="byType"/>
<bean id="user" class="User" autowire="constructor"></bean>

型に基づく自動アセンブリが使用される場合、IOC コンテナにはそのような型は 1 つしか存在できません!

1.2 自動アセンブリのためのアノテーションの使用

クラスのメンバ変数、メソッド、コンストラクタにアノテーションを付ける@Autowiredアノテーションは、Springが自動配線を実装するために使用します。もちろん、SpringはJSR-330の@InjectアノテーションやJSR-250の@Resourceアノテーションなど、自動配線を実装する他の方法もサポートしています。

アノテーションによってBeanのプロパティを自動アセンブルすることで、よりきめ細かい自動アセンブルが可能になります。

「シナリオ1:@Autowiredでオブジェクトに注釈をつける

@Service
public class DemoServiceTwo {
 @Autowired
 DemoServiceThree demoServiceThree;
}

「シナリオ2:@Autowiredによるコンストラクタの注釈付け

@Service
public class DemoServiceTwo {
 
 DemoServiceOne demoServiceOne;
 
 @Autowired
 public DemoServiceTwo(DemoServiceOne demoServiceOne){
 this.demoServiceOne = demoServiceOne;
 } 
}

「シナリオ3:@Resourceでオブジェクトに注釈を付ける

@Service
public class DemoServiceTwo {
 @Resource
 DemoServiceOne demoServiceOne;
}

「シナリオ4:@Autowiredでメソッドに注釈をつける

@Service
public class DemoServiceTwo {
 DemoServiceOne demoServiceOne;
 
 @Autowired 
 public void prepare(DemoServiceOne demoServiceOne){
 this.demoServiceOne = demoServiceOne;
 }
}

2.2 オブジェクト間の依存処理プロセス

  1. 上図は、 2.1で 説明した4つのシナリオの処理を表したもので 青い線は@Resourceアノテーションの処理を表しています。

  2. 赤線は@Autowiredアノテーションの処理を表し、3つのサブシナリオに分かれています。

    • AutowiredFieldElement 注釈付き属性の場合を示します。
    • AutowiredMethodElement 注釈付きメソッドの表現
    • 緑色の線はコンストラクタのメソッドに対するアノテーションを示します。

上記のフローチャートを通して、以下の点がわかりました。また、@Resourceと@Autowiredの使い分けは、すでに以下のポイントで可能です。

  1. getBeanPostProcessors() どちらのアノテーションも、処理を完了させるためにポストプロセッサによって処理されます。 拡張機能を使わなければ、Springには5つしかありません。もし忘れていたら、"Container Initialisation Preemptive Five Tigers"をチェックしてください;

  2. CommonAnnotationBeanPostProcessor Resourceの処理は.NET Frameworkで行います。

  3. AutowiredAnnotationBeanPostProcessorAutowiredの処理は.

  4. Autowired アノテーション付きコンストラクタでは、アノテーション付き要素が NULL の場合、その要素が直接返されます。populateBean() プロシージャを終了します。

  5. getBean()のハイライト ここではおなじみのgetBean()...

  6. field.set() は、オブジェクト間の依存関係を保持します。

依存性の注入

ソース・コード分析で最初に思い浮かぶのは、 オブジェクトのラッパーBeanWrapperが作成された後、 オブジェクト間の依存関係を処理するメソッド・エントリ、 populateBean()です:

protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
 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 (BeanPostProcessor bp : getBeanPostProcessors()) {
 if (bp instanceof InstantiationAwareBeanPostProcessor) {
 InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
 /**
 * InstantiationAwareBeanPostProcessor ResourceアノテーションのpostProcessAfterInstantiation()メソッドの適用。
 * 属性を埋めるかどうかを制御する
 */
 if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
 return;
 }
 }
 }
 }
 /**
 * BeanDefinitionに設定されたProperty値を取得する,
 * これらのプロパティは、BeanDefinitionのパースから得られる。,
 * BeanDefinitionのロードの分析を見る。
 * 以下は、Springによって内部的に設定され、通常は設定されないプロパティの値である。
 */
 PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
 /**
 * 自動アセンブリーの処理では、xmlメソッドに自動アセンブリー型のコンフィギュレーションを指定することができる。
 * あるいは、setAutowireMode() で自動配線モードを設定することもできる。
 */
 int resolvedAutowireMode = mbd.getResolvedAutowireMode();
 // Spring デフォルト byTypeでもbyNameでもなく、デフォルトではnullである。
 // このように判断する理由は、オートインジェクション・モデルが変更される特殊なシナリオがあるからだ。
 if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
 MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
 /**
 * byName  
 * リフレクションによって現在のBeanから注入されるプロパティの名前を取得する。,
 * 次に、プロパティ名を使用して、コンテナから同じ名前の Bean を要求する。,
 * これは実際には、ビーン生成と依存性注入の別のプロセスをトリガーする。
 */
 if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
 autowireByName(beanName, mbd, bw, newPvs);
 }
 // byType  
 if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
 autowireByType(beanName, mbd, bw, newPvs);
 }
 pvs = newPvs;
 }
 // ポストプロセッサーは初期化される
 boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
 // 依存関係のチェック
 boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);
 PropertyDescriptor[] filteredPds = null;
 if (hasInstAwareBpps) {
 if (pvs == null) {
 // コンストラクタの処理と同じで、オブジェクトは存在するが、オブジェクト内部のプロパティは存在しない。
 pvs = mbd.getPropertyValues();
 }
 for (BeanPostProcessor bp : getBeanPostProcessors()) {
 if (bp instanceof InstantiationAwareBeanPostProcessor) {
 InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
 // ポストプロセッサーによる処理
 PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
 if (pvsToUse == null) {
 if (filteredPds == null) {
 filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
 }
 pvsToUse = ibp.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);
 }
 if (pvs != null) {
 // 属性の注入
 applyPropertyValues(beanName, mbd, bw, pvs);
 }
}

postProcessProperties()異なるポストプロセッサの方法は、異なる処理ロジックに対応します。

4. @Autowired アノテーション・プロパティ

4.1 処理

次に、object() メソッドで適切な処理を行います。

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()) {
 // 注入された要素をループし、inject メソッドを呼び出す。
 for (InjectedElement element : elementsToIterate) {
 if (logger.isTraceEnabled()) {
 logger.trace("Processing injected element of bean '" + beanName + "': " + element);
 }
 // 注入されたメソッドを呼び出す
 element.inject(target, beanName, pvs);
 }
 }
 }

その他のすべての @Autowired アノテーションの使用では、最後に element.inject(target, beanName, pvs);

このように、アノテートされたメソッドとアノテートされたアトリビュートは区別されており、今回は例としてアノテートされたアトリビュートのアプローチを引き続き分析します。

AutowiredFieldElement次のステップは resolveDependency() メソッドを分析することです。doXXXX()メソッドを見ると、Springの通常のルーチンに戻ります。 beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter) ここでは、...で行われていることを見てみましょう。

public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
 @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
 InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
 try {
 Object shortcut = descriptor.resolveShortcut(this);
 if (shortcut != null) {
 return shortcut;
 }
 /**
 * 型によるオブジェクトの取得,@Autowired デフォルトでは、コンストラクタは型
 */
 Class<?> type = descriptor.getDependencyType();
 /**
 *  @value  
 */
 Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
 if (value != null) {
 if (value instanceof String) {
 String strVal = resolveEmbeddedValue((String) value);
 BeanDefinition bd = (beanName != null && containsBean(beanName) ?
 getMergedBeanDefinition(beanName) : null);
 value = evaluateBeanDefinitionString(strVal, bd);
 }
 TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
 try {
 /** コンバータでBeanの値を型に変換する*/
 return converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor());
 }
 catch (UnsupportedOperationException ex) {
 // A custom TypeConverter which does not support TypeDescriptor resolution...
 return (descriptor.getField() != null ?
 converter.convertIfNecessary(value, type, descriptor.getField()) :
 converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));
 }
 }
 Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
 if (multipleBeans != null) {
 return multipleBeans;
 }
 /**
 * 属性タイプに基づいて、beanFactoryで一致するすべてのbeanを見つける。
 * 戻り値の構造は以下の通りである:
 * keyResourceアノテーションと@Autowiredアノテーションメソッド;
 * valuebeanNameに対応するインスタンス化されたBeanは、getBeanによって返される。
 */
 Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
 if (matchingBeans.isEmpty()) {
 /**
 * autowireのrequire属性がtrueの場合
 * 見つかったマッチが NULL の場合は例外をスローする。
 */
 if (isRequired(descriptor)) {
 raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
 }
 return null;
 }
 String autowiredBeanName;
 Object instanceCandidate;
 // 型によるマッチ数が1より多い
 if (matchingBeans.size() > 1) {
 // オートインジェクションのためのビーン名の決定
 autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
 if (autowiredBeanName == null) {
 if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
 return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans);
 }
 else {
 return null;
 }
 }
 instanceCandidate = matchingBeans.get(autowiredBeanName);
 }
 else {
 // We have exactly one match.
 Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
 autowiredBeanName = entry.getKey();
 instanceCandidate = entry.getValue();
 }
 if (autowiredBeanNames != null) {
 autowiredBeanNames.add(autowiredBeanName);
 }
 if (instanceCandidate instanceof Class) {
 //オブジェクトのインスタンス化
 instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
 }
 Object result = instanceCandidate;
 if (result instanceof NullBean) {
 if (isRequired(descriptor)) {
 raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
 }
 result = null;
 }
 if (!ClassUtils.isAssignableValue(type, result)) {
 throw new BeanNotOfRequiredTypeException(autowiredBeanName, type, instanceCandidate.getClass());
 }
 return result;
 }
 finally {
 ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
 }
}

上記のコードを分析すると、主に以下のことを行っていることがわかります:

パース時の @value アノテーションのサポート;

メソッドを用いて,属性型に従った型が一致する BeanFactory 内の全ての Bean を見つけ,それらを Map に格納して返します。このメソッドでは,与えられた型に従って,すべてのビーンズの名前が Map のキーとして取得されます。

    • まず、@Primaryアノテーションのオブジェクトに基づき、存在すればそれを返します;
    • そして、@Priorityでアノテーションされたオブジェクトで、利用可能であればそれを返します。
  • これが 1 に等しい場合、返される Map のキーは beanName です;

上記の説明では、@Autowired は、対応する型のオブジェクトが複数存在する場合に、byType および byName で属性をアノテートすることを示しています。

処理

findAutowireCandidates() メソッドによって依存オブジェクトを取得した後、オブジェクト間の依存関係はメソッドによって維持されます。

private void registerDependentBeans(@Nullable String beanName, Set<String> autowiredBeanNames) {
 if (beanName != null) {
 for (String autowiredBeanName : autowiredBeanNames) {
 if (this.beanFactory != null && this.beanFactory.containsBean(autowiredBeanName)) {
 this.beanFactory.registerDependentBean(autowiredBeanName, beanName);
 }
 if (logger.isTraceEnabled()) {
 logger.trace("Autowiring by type from bean name '" + beanName +
 "' to bean named '" + autowiredBeanName + "'");
 }
 }
 }
}
public void registerDependentBean(String beanName, String dependentBeanName) {
 String canonicalName = canonicalName(beanName);
 /**
 * dependentBeanMapResourceアノテーションは、現在登録されていてこのBeanに依存しているすべてのBeanを@Resourceアノテーションに格納するために使用される。,
 * ここに,beanNameに依存するすべての登録済みBeanの集合を示す。,
 * 次に、コレクションにdependentBeanNameが含まれているかどうか、つまり登録されているかどうかを確認する。,
 * これが含まれていれば、すでに登録されていることになり、その場合は;
 * その他、Beanの依存関係を2つのマップキャッシュに追加することで、登録は完了する。.
 */
 synchronized (this.dependentBeanMap) {
 Set<String> dependentBeans =
 this.dependentBeanMap.computeIfAbsent(canonicalName, k -> new LinkedHashSet<>(8));
 if (!dependentBeans.add(dependentBeanName)) {
 return;
 }
 }
 synchronized (this.dependenciesForBeanMap) {
 Set<String> dependenciesForBean =
 this.dependenciesForBeanMap.computeIfAbsent(dependentBeanName, k -> new LinkedHashSet<>(8));
 dependenciesForBean.add(canonicalName);
 }
 }

上記のコードには2つのマップがあります:

/** 指定されたBeanと,指定されたBeanに依存する現在登録されているBeanとの間のすべての依存関係をキャッシュする。*/
private final Map<String, Set<String>> dependentBeanMap = new ConcurrentHashMap<>(64);
/** 指定されたBeanと、そのBeanの生成が依存する現在登録されているすべてのBeanとの間の依存関係(dependencies)のキャッシュ。*/
private final Map<String, Set<String>> dependenciesForBeanMap = new ConcurrentHashMap<>(64);

最後の注意点として、コンテナ内のすべてのオブジェクト間のリレーションシップは、コンテナの破棄時に削除されるまで、この2つのマップに保存されます。

@Autowired アノテーション属性

6.まとめ

この記事では、Springでオブジェクト間の依存関係を処理するプロセスを紹介します。Resourceアノテーションと@Autowiredアノテーションをフローチャートで説明します。

この記事では、@Autowiredアノテーション付きプロパティの扱い、Javaオブジェクトとプロパティ間の関係の維持、Springオブジェクト間の依存関係の維持について詳しく説明します。

ここでは、@Autowired アノテーションのコンストラクタ処理の構成について簡単に説明します。

Resourceアノテーションと@Autowiredアノテーション・メソッドの処理については、後で詳しく分析します。

Read next

ElementUIのフォーム検証でよく使われる正規表現

1.文書タイプによる選択的検証 HTML部分:2.携帯電話番号 3.小数を含む正の数

Oct 29, 2020 · 3 min read