"人生は自分で掘った落とし穴であり、混乱もまた真実です。愛情を失わず、しっかりとした心でありますように。
"
依存関係の処理
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 オブジェクト間の依存処理プロセス
上図は、 2.1で 説明した4つのシナリオの処理を表したもので 、 青い線は@Resourceアノテーションの処理を表しています。
赤線は@Autowiredアノテーションの処理を表し、3つのサブシナリオに分かれています。
AutowiredFieldElement
注釈付き属性の場合を示します。AutowiredMethodElement
注釈付きメソッドの表現- 緑色の線はコンストラクタのメソッドに対するアノテーションを示します。
上記のフローチャートを通して、以下の点がわかりました。また、@Resourceと@Autowiredの使い分けは、すでに以下のポイントで可能です。
getBeanPostProcessors()
どちらのアノテーションも、処理を完了させるためにポストプロセッサによって処理されます。 拡張機能を使わなければ、Springには5つしかありません。もし忘れていたら、"Container Initialisation Preemptive Five Tigers"をチェックしてください;CommonAnnotationBeanPostProcessor
Resourceの処理は.NET Frameworkで行います。AutowiredAnnotationBeanPostProcessor
Autowiredの処理は.Autowired アノテーション付きコンストラクタでは、アノテーション付き要素が NULL の場合、その要素が直接返されます。populateBean() プロシージャを終了します。
getBean()のハイライト ここではおなじみのgetBean()...
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アノテーション・メソッドの処理については、後で詳しく分析します。