前回のブログでは、自動注入モデルの使用について説明しました。このメモは、自動注入モデルでautowireModeが0である状況を記録することを目的としています。これは、私たちがよく使用する@Autowiredおよび@Resourceアノテーションの原則です。
まず、これら2つの注釈では、ポストプロセッサを使用して分析を完了します。分析のタイミングは、6番目のポストプロセッサが実行されるときと同じですが、異なるポストプロセッサを使用して分析を完了します。
@Autowiredアノテーション:
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessPropertyValues
@リソースアノテーション:
org.springframework.context.annotation.CommonAnnotationBeanPostProcessor#postProcessPropertyValues
序文
1.これらの2つの注釈は、6番目のポストプロセッサが実行されるときに両方とも解析されますが、3番目のプロセッサでは解析されます
org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor#postProcessMergedBeanDefinition
実行時に、最初に現在のBeanのどの属性を注入する必要があるかを分析します.6番目のポストプロセッサが実行されると、解析する必要がなく、マップから直接注入される属性を取得できます。
2.これら2つの注釈は異なるポストプロセッサによって処理されますが、処理のアイデアと手順はほぼ同じです。
- 最初に、アノテーションが追加されたBeanのメソッドと属性(フィールド)を取得しようとします
- 見つかった後、対応する属性は、実装クラスの優先度や名前などに応じて、コンテナまたはbeanDefinitionMapから見つかります。
- filed.set()を介して属性の挿入を完了します
3.最初に説明するいくつかのポイントがあります。
- @Resourceアノテーションの場合、Beanは、属性の注入を完了するために注入される属性の名前に従って、単一インスタンスプールから取得されます。
- @Autowiredアノテーションの場合、通常、タイプに応じて注入されると言われます。複数が見つかった場合、または見つからなかった場合は、名前に従って注入します。説明したいのは、Springが@Autowiredアノテーションを解析するときです。シングルインスタンスプールから直接ではありませんBeanDefinitionMapでBeanを検索するには、最初にタイプに従って検索します。複数見つかった場合は、名前に従ってシングルインスタンスプールから検索します。見つかった場合のみどのクラスを注入するか、単一インスタンスプールから対応するBeanを見つけますか?完全な注入
- もちろん、@ Autowiredアノテーションと組み合わせて使用されるアノテーションがいくつかあり、以下で1つずつ分析されます。
@Autowired
これは、プロセス全体で解決可能な@Autowiredコメントであり、
@ Autowiredが使用されたいくつかのメモを含むいくつかのメモがあります:
@Qualifier
@Primary @priority
最初のアノテーションがフィールドで使用されます。タイプに応じてbeanDefinitionMapからBeanを見つけた後、どのbeanNameが修飾されているかを判別します。注入されるBeanに複数の実装クラスがある場合、@ Qualifierアノテーションで指定された実装クラスは最初に注入されます
2つ目は、クラスで直接使用されます。@ Qualifierアノテーションが追加されていないシナリオでは、複数の実装クラスがある場合、どの実装クラスに@Primaryアノテーションが追加されているかが判別され、追加されている場合は、実装クラスに注入されます
3番目のアノテーションもロードクラスにあります。@ Qualiferアノテーションも@Priorityアノテーションも追加されていない場合は、どちらの実装クラスの優先度が高いかが決定されます。値が小さいほど、優先度が高くなります。
これは
org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency
コードの一部
/**
* @Autowired注解源码 三
* 查找要注入的bean,按照type来查找,如果匹配到一个,就获取到这个实例,然后返回,返回之后,会调用field.set();注入进去
* 如果找到了多个,那就先判断哪个实现类有@primary注解,如果都没有注解,那就判断priority的优先级
*
* 这个方法中,对Qualifier注解进行了处理;如果一个接口有多个实现类,但是没有加@Qualifier注解,那么就会返回多个
*
* 如果@Qualifier指定的value在spring容器中没有,这里返回的map集合就是空
*
*/
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
if (matchingBeans.isEmpty()) {
if (isRequired(descriptor)) {
// 如果匹配到的要注入的bean是null,就报错
raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
}
return null;
}
String autowiredBeanName;
Object instanceCandidate;
if (matchingBeans.size() > 1) {
/**
* 如果根据类型,匹配到有多个bean,那就判断@Primary注解和@Priority注解;
* 所以:可以得出一个结论:
* 当一个接口有多个实现类的时候,添加了@Qualifier注解,同时在一个类上添加了@Primary注解,那么会注入@Qualifier注解对应的bean
*
* 在下面的这个推断方法中:
* 1.会先判断@Primary注解
* 2.再判断@Priority注解声明的优先级
* 3.如果两个注解都没有,那就根据controller中注入的service的name和matchingBeans中的beanName进行匹配
* 如果匹配上,就注入匹配上的beanName对应的beanClass
* 这里需要注意的是:根据name查询的时候,会先根据type从beanDefinitionMap中查询到符合类型的beanDefinitionNames
* 然后从beanDefinitionNames中依次匹配,是否和name一致
*/
autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
if (autowiredBeanName == null) {
if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
return descriptor.resolveNotUnique(type, matchingBeans);
}
else {
// In case of an optional Collection/Map, silently ignore a non-unique case:
// possibly it was meant to be an empty collection of multiple regular beans
// (before 4.3 in particular when we didn't even look for collection beans).
return null;
}
}
/**
* 如果能找到一个,那就根据name获取到对应的instance实例,然后再field.set();
*/
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) {
/**
* 根据获取到的beanName和type从容器中查找。如果没有找到,就创建
* 这里才是真正的从单实例池中获取对象,如果对象没有,就创建
*/
instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
}
コメント
では、どのコード行がどのコメントを解析するかが詳細に説明されています。メソッドdetermineAutowireCandidateに焦点を当てたいと思います。
/**
* 如果根据type获取到有多个符合的bean,那就来这个方法中,推断出其中的一个
* @param candidates:推断出来的多个bean
* @param descriptor:要注入的属性对应的类型,如果是接口,那这里就是接口的类型
* @return
*/
@Nullable
protected String determineAutowireCandidate(Map<String, Object> candidates, DependencyDescriptor descriptor) {
Class<?> requiredType = descriptor.getDependencyType();
/**
* 判断哪个bean有@Primary注解,返回注解对应的beanName
*/
String primaryCandidate = determinePrimaryCandidate(candidates, requiredType);
if (primaryCandidate != null) {
return primaryCandidate;
}
/**
* 如果都没有添加@Primary注解,那就判断bean是否有添加@Priority注解,注解值越小,优先级越高;返回优先级高的beanName
*/
String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType);
if (priorityCandidate != null) {
return priorityCandidate;
}
// Fallback
/**
* 这里的descriptor中保存的是注入的bean对应的名称(在controller注入service,这里就是controller中的service属性名)
* 判断 从容器中拿出来的beanName是否和controller中的service属性名一致,如果一致,就注入一致的这个
* 这也就是在controller注入service的时候,如果service有多个实现类,那么,如果注入的service属性名和实现类的beanName一致,也可以注入成功的原因
*
* class Controller{
* @Autowired
* Service service01
* }
* 如果service有多个实现类,其实注入的是service01,就是这里判断的
*
* 这里我觉得,也是我们平常所说的,如果根据type找到多个,或者没有找到,就根据name来注入,这里就是根据name来注入的这一步
* candidates:是根据类型从beanDefinitionMap中获取到的符合类型的beanDefinitionNames
* descriptor.getDependencyName():是@Autowired注解属性注入的属性name
*/
for (Map.Entry<String, Object> entry : candidates.entrySet()) {
String candidateName = entry.getKey();
Object beanInstance = entry.getValue();
if ((beanInstance != null && this.resolvableDependencies.containsValue(beanInstance)) ||
matchesBeanName(candidateName, descriptor.getDependencyName())) {
return candidateName;
}
}
return null;
}
これらの2つのコードを通して、問題を説明できると思います。
- @Autowiredアノテーションの場合、Springが処理するとき:最初に@Qualiferアノテーションを解析し、次に@Primaryアノテーションを解析し、次に@Priorityアノテーションを解析します。
- 解析後も一致するBeanが複数ある場合は、名前に基づいて検索します。ここでの検索は、beanDefinitionMapにある注入条件を満たすBeanに基づいていることに注意してください。
- 注入する実装クラスを確認した後でのみ、インスタンス化されたBeanが単一インスタンスプールから取得されます。
追加するポイントがあります。私はこのポイントについて話してきました。すべての検索は、beanDefinitionMapからの注入条件を満たすBeanの検索に基づいています。次のコードでこのポイントを確認できます。
ここでのコードを追跡することはもうありません。これは、ここの最下層がbeanDefinitionNamesコレクションをトラバースし、beanDefinitionNameに従ってbeanDefinitionを取得し、beanDefinitionが非抽象クラスであるかどうか、非遅延であるかどうかを判断するためです。ロードされ、それが単一インスタンスであるかどうか、タイプタイプであるかどうかなど。満たされている場合は、現在のbeanNameを返します。
@資源
これは単純なフローチャートです。この図は基本的に@Resourceアノテーションの分析を明確にしています。@ Resourceが名前に基づいて属性を挿入する理由を見てみましょう。図の最後のメソッドを見てください。
protected Object autowireResource(BeanFactory factory, LookupElement element, @Nullable String requestingBeanName)
throws NoSuchBeanDefinitionException {
Object resource;
Set<String> autowiredBeanNames;
String name = element.name;
if (this.fallbackToDefaultTypeMatch && element.isDefaultName &&
factory instanceof AutowireCapableBeanFactory && !factory.containsBean(name)) {
autowiredBeanNames = new LinkedHashSet<>();
/**
* 这里也可以佐证@Resource是根据name来进行注入的,这里代码追进去,可以看到,是也是调用的doGetBean()
* 所以:无论是走这里,还是下面的getBean,都是从单实例池中尝试获取bean的
*/
resource = ((AutowireCapableBeanFactory) factory).resolveDependency(
element.getDependencyDescriptor(), requestingBeanName, autowiredBeanNames, null);
if (resource == null) {
throw new NoSuchBeanDefinitionException(element.getLookupType(), "No resolvable resource object");
}
}
else {
/**
* 之所以说@Resource是按照name来注入的,就是这里的代码可以佐证这个观点
* 这里,直接根据name从单实例池中获取bean,这里是从单实例池中获取,如果存在就返回,不存在就初始化
* 这里的getBean()调用的就是org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
*/
resource = factory.getBean(name, element.lookupType);
autowiredBeanNames = Collections.singleton(name);
}
if (factory instanceof ConfigurableBeanFactory) {
ConfigurableBeanFactory beanFactory = (ConfigurableBeanFactory) factory;
for (String autowiredBeanName : autowiredBeanNames) {
if (requestingBeanName != null && beanFactory.containsBean(autowiredBeanName)) {
beanFactory.registerDependentBean(autowiredBeanName, requestingBeanName);
}
}
}
return resource;
}
したがって、@ Resourceアノテーションの場合、Beanに設定された名前に従って単一インスタンスプールから検索され、見つかった場合は注入され、見つからなかった場合はnullになります。
総括する
@Resourceアノテーションは言うまでもありません。名前に従って、単一インスタンスプールから一致するbeanDefinitionを取得することです。
@Autowiredアノテーションの場合
- まず、タイプに応じて、タイプに準拠するすべてのbeanDefinitionsに対応するbeanNameがbeanDefinitionMapから取得されます。
- 次に、対応する@Qualifierアノテーションを決定し、アノテーションで指定されたBeanを最初に注入します。Beanがアノテーションで指定されているが見つからない場合は、nullが返されます。
- @Qualiferアノテーションが追加されていないが、複数の実装クラスが見つかった場合、@ Primaryアノテーションが追加されているかどうかが判断され、アノテーションが追加されたBeanが最初に挿入されます。複数のクラスがアノテーションを追加した場合、Springはレポートします。春もどちらを注入するかわからないため、エラー
- @Primaryアノテーションが追加されていない場合、@ Priorityアノテーションが処理されます。アノテーションは優先度を宣言するためのものです。値が小さいほど、優先度が高くなります。
- これらの3つのアノテーションのいずれも追加されていない場合は、名前に従って、beanDefinitionMapで見つかったインジェクションタイプに一致するすべてのbeanNameがフィールドに対応する名前と同じであるかどうかを判断します。ある場合、Beanはインジェクションされ、そうでない場合は、@ Autowiredに従って注入されます。アノテーションに必要なアノテーションは、例外をスローするかnullを返すかを決定するためにtrueまたはfalseです。
- 注入されたBeanが検出されて決定されると、beanNameに対応するインスタンス化されたBeanオブジェクトが単一インスタンスのプールから取得され、注入されます。
最後のポイントは、説明する必要があります。@ Autowiredアノテーションと@Resourceアノテーションはすべてfield.set()で埋められます。