各1
ロス:そこ並列ドラフトピックもありませんが、まったくMVPも
序文
では前回の記事(@LoadBalanced負荷分散を説明する)最後に、私は非常に重要な問題を投げ、私たちは小さなパートナー自身がそれについて深く考えるようにすることをお勧めします。この問題については、この論文の統一応答や説明をします。
私はに属している知識のこの作品だと思いますのでSpring Framework
コアコンテンツのいずれかが非常に重要であるので、単一私はあなたの助けを期待する、程度の特別な記事のために彼女を行っています。
ケースの背景
いえば@Qualifier
、このコメントはよく知られている:それは「完全一致」するために使用されBean
、一般に豆の異なるインスタンスの数が存在する場合、同じタイプの下で使用される、注釈を識別し、マッチングすることによってこれを行うことができます。
私は考えた@Qualifier
私が表示されるまで、十分に識別するために使用されるタイプ、プロパティに注釈を使用するLoadBalancerAutoConfiguration
ので、多くのアプリケーションがあります。
@LoadBalanced
@Autowired(required = false)
private List<RestTemplate> restTemplates = Collections.emptyList();
これは、コンテナ内のすべての可能RestTemplate
なタイプとは、と注釈を付け@LoadBalanced
ビーンフルショットのノートが入ってきます。
この用法はので、私は私がフレームワークプレー以上にしましょう、それは私の思考の余分なラインを与える、非常にうれしい驚きでした。それを習得し、それらは私ではないピットを行う使用して回避しようとするために、それが唯一の基本原理の場所からその使用を理解するために、それを発見します。
QualifierAnnotationAutowireCandidateResolver
詳しいです
これは、インタフェース候補プロセッサ注入に依存したAutowireCandidateResolver
実装クラスから継承するGenericTypeAwareAutowireCandidateResolver
ので、これらは(ほとんど完全な、最も強力な一つのプロセッサですContextAnnotationAutowireCandidateResolver
最初のを除いて)、Spring
デフォルトの候補者のためにそれを使用してプロセス。
それはほとんど呼び出すことができます@Qualifier
このコメントを解決するために設計された、注釈付きの「実装クラス」。
上記の質問には、原則だったと、次のとおりです。
// @since 2.5
public class QualifierAnnotationAutowireCandidateResolver extends GenericTypeAwareAutowireCandidateResolver {
// 是个List,可以知道它不仅仅只支持org.springframework.beans.factory.annotation.Qualifier
private final Set<Class<? extends Annotation>> qualifierTypes = new LinkedHashSet<>(2);
private Class<? extends Annotation> valueAnnotationType = Value.class;
// 空构造:默认支持的是@Qualifier以及JSR330标准的@Qualifier
public QualifierAnnotationAutowireCandidateResolver() {
this.qualifierTypes.add(Qualifier.class);
try {
this.qualifierTypes.add((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Qualifier", QualifierAnnotationAutowireCandidateResolver.class.getClassLoader()));
} catch (ClassNotFoundException ex) {
// JSR-330 API not available - simply skip.
}
}
// 非空构造:可自己额外指定注解类型
// 注意:如果通过构造函数指定qualifierType,上面两种就不支持了,因此不建议使用
// 而建议使用它提供的addQualifierType() 来添加~~~
public QualifierAnnotationAutowireCandidateResolver(Class<? extends Annotation> qualifierType) {
... // 省略add/set方法
// 这是个最重要的接口方法~~~ 判断所提供的Bean-->BeanDefinitionHolder 是否是候选的
// (返回true表示此Bean符合条件)
@Override
public boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
// 1、先看父类:ean定义是否允许依赖注入、泛型类型是否匹配
boolean match = super.isAutowireCandidate(bdHolder, descriptor);
// 2、若都满足就继续判断@Qualifier注解~~~~
if (match) {
// 3、看看标注的@Qualifier注解和候选Bean是否匹配~~~(本处的核心逻辑)
// descriptor 一般封装的是属性写方法的参数,即方法参数上的注解
match = checkQualifiers(bdHolder, descriptor.getAnnotations());
// 4、若Field/方法参数匹配,会继续去看看参数所在的方法Method的情况
// 若是构造函数/返回void。 进一步校验标注在构造函数/方法上的@Qualifier限定符是否匹配
if (match) {
MethodParameter methodParam = descriptor.getMethodParameter();
// 若是Field,methodParam就是null 所以这里是需要判空的
if (methodParam != null) {
Method method = methodParam.getMethod();
// method == null表示构造函数 void.class表示方法返回void
if (method == null || void.class == method.getReturnType()) {
// 注意methodParam.getMethodAnnotations()方法是可能返回空的
// 毕竟构造方法/普通方法上不一定会标注@Qualifier等注解呀~~~~
// 同时警示我们:方法上的@Qualifier注解可不要乱标
match = checkQualifiers(bdHolder, methodParam.getMethodAnnotations());
}
}
}
}
return match;
}
...
}
私はそれに合わせて、ステップ実行ロジックをマーク手順に従って、ソースコード内のコメント。次の点に注意してください。
qualifierTypes
それは(のみデフォルトサポートし、独自に指定するには、発信者をサポートしている@Qualifier
タイプ)- 試合の唯一の種類は、豆は、すべての一般的な[OK]を一致、一致を定義し、使用され
@Qualifier
、より正確に一致させます descriptor.getAnnotations()
ロジックは次のとおりです
-場合はDependencyDescriptor
(フィールドの説明Field
)、その後、彼らはメモを取るフィールドに移動
-メソッドのパラメータを(記述する場合MethodParameter
)、それは、アノテーション付きメソッドのパラメータを返します。ステップ3
match = true
表しフィールド/メソッドのパラメータ修飾子が一致しました-説明:あなたが行くことができます
isAutowireCandidate()
への方法を、それはでマークされなければならない@Autowired
(することができ、注釈AutowiredAnnotationBeanPostProcessor
後処理)、そのdescriptor.getAnnotations()
配列の長さは、少なくとも1を返します。
checkQualifiers()
方法:
QualifierAnnotationAutowireCandidateResolver:
// 将给定的限定符注释与候选bean定义匹配。命名中你发现:这里是负数形式,表示多个注解一起匹配
// 此处指的限定符,显然默认情况下只有@Qualifier注解
protected boolean checkQualifiers(BeanDefinitionHolder bdHolder, Annotation[] annotationsToSearch) {
// 很多人疑问为何没标注注解返回的还是true?
// 请参照上面我的解释:methodParam.getMethodAnnotations()方法是可能返回空的,so...可以理解了吧
if (ObjectUtils.isEmpty(annotationsToSearch)) {
return true;
}
SimpleTypeConverter typeConverter = new SimpleTypeConverter();
// 遍历每个注解(一般有@Autowired+@Qualifier两个注解)
// 本文示例的两个注解:@Autowired+@LoadBalanced两个注解~~~(@LoadBalanced上标注有@Qualifier)
for (Annotation annotation : annotationsToSearch) {
Class<? extends Annotation> type = annotation.annotationType();
boolean checkMeta = true; // 是否去检查元注解
boolean fallbackToMeta = false;
// isQualifier方法逻辑见下面:是否是限定注解(默认的/开发自己指定的)
// 本文的org.springframework.cloud.client.loadbalancer.LoadBalanced是返回true的
if (isQualifier(type)) {
// checkQualifier:检查当前的注解限定符是否匹配
if (!checkQualifier(bdHolder, annotation, typeConverter)) {
fallbackToMeta = true; // 没匹配上。那就fallback到Meta去吧
} else {
checkMeta = false; // 匹配上了,就没必要校验元数据了喽~~~
}
}
// 开始检查元数据(如果上面匹配上了,就不需要检查元数据了)
// 比如说@Autowired注解/其它自定义的注解(反正就是未匹配上的),就会进来一个个检查元数据
// 什么时候会到checkMeta里来:如@A上标注有@Qualifier。@B上标注有@A。这个时候限定符是@B的话会fallback过来
if (checkMeta) {
boolean foundMeta = false;
// type.getAnnotations()结果为元注解们:@Documented、@Retention、@Target等等
for (Annotation metaAnn : type.getAnnotations()) {
Class<? extends Annotation> metaType = metaAnn.annotationType();
if (isQualifier(metaType)) {
foundMeta = true; // 只要进来了 就标注找到了,标记为true表示从元注解中找到了
// Only accept fallback match if @Qualifier annotation has a value...
// Otherwise it is just a marker for a custom qualifier annotation.
// fallback=true(是限定符但是没匹配上才为true)但没有valeu值
// 或者根本就没有匹配上,那不好意思,直接return false~
if ((fallbackToMeta && StringUtils.isEmpty(AnnotationUtils.getValue(metaAnn))) || !checkQualifier(bdHolder, metaAnn, typeConverter)) {
return false;
}
}
}
// fallbackToMeta =true你都没有找到匹配的,就返回false的
if (fallbackToMeta && !foundMeta) {
return false;
}
}
}
// 相当于:只有所有的注解都木有返回false,才会认为这个Bean是合法的~~~
return true;
}
// 判断一个类型是否是限定注解 qualifierTypes:表示我所有支持的限定符
// 本文的关键在于下面这个判断语句:类型就是限定符的类型 or @Qualifier标注在了此注解上(isAnnotationPresent)
protected boolean isQualifier(Class<? extends Annotation> annotationType) {
for (Class<? extends Annotation> qualifierType : this.qualifierTypes) {
// 类型就是限定符的类型 or @Qualifier标注在了此注解上(isAnnotationPresent)
if (annotationType.equals(qualifierType) || annotationType.isAnnotationPresent(qualifierType)) {
return true;
}
}
return false;
}
checkQualifiers()
以下のように(1つのチェックループスルー)マークされたすべてのコメントを調べる方法は、ルールは以下のとおりです。
- 修飾子アノテーションは(彼がある場合
@Qualifier
またはisAnnotationPresent
)、一致し、ノートを見て継続すること
-それは言った@Qualifier
(マークされたノートが修飾子とみなすことができますisQualifier() = true
) - 修飾子はなく、試合にはコメントしなかった場合は、フォールバック。マッチがあまりにに入った場合は、どうか試合に(もしあれば)、その上にマークされ修飾子ノートを見続けます
- それは修飾子注釈ですが、また、フォールバックロジックを行けば
要するに:それはなかった場合は、直接修飾子アノテーションは無視されます。複数の修飾子注釈を有効にした場合、上のすべての試合が決勝戦をするために考慮されなければなりません。
ヒント:修飾子は効果になりません効果を必ずしも注入の故障ではありませんが、それは、単一の単語または注入された成功である場合。〜それは効果的な豆区別することはできませんので、失敗した注入することだけよりも存在する場合
そのfallback
戦略は、唯一の(死ぬくらいの)最も上のレベルを見つけなければなりませんでした。例えば、例が顕著に再生することができますまた、@Bを使用して@Qualifier
効果を、しかしと組み合わせた場合に@C
階層修飾子発効しません。
注意:
Class.isAnnotationPresent(Class<? extends Annotation> annotationClass)
指示annotationClass
(このタイプは、任意の型クラスすることができます)このタイプのラベルかどうか。
この方法は推移していません。例えば、Aにマーク@Qualifierノートがあり、Bノートは、あなたが@Bであっ@Qualifierかどうかを判断するために、このメソッドを使用して、ノートに@Aマークを持っている它是返回false的
(でも書かれている@Inherited
ノート、それは問題ではありませんので)
この記事では、なぜ実際には、これにはまだ説明できない@LoadBalanced
、依存性注入を参加の本質の本質を見て続けなければならないcheckQualifier()
:(メソッド名は、単一のコメントの正確な検査を表す単一の番号である)方法
QualifierAnnotationAutowireCandidateResolver:
// 检查某一个注解限定符,是否匹配当前的Bean
protected boolean checkQualifier(BeanDefinitionHolder bdHolder, Annotation annotation, TypeConverter typeConverter) {
// type:注解类型 bd:当前Bean的RootBeanDefinition
Class<? extends Annotation> type = annotation.annotationType();
RootBeanDefinition bd = (RootBeanDefinition) bdHolder.getBeanDefinition();
// ========下面是匹配的关键步骤=========
// 1、Bean定义信息的qualifiers字段一般都无值了(XML时代的配置除外)
// 长名称不行再拿短名称去试了一把。显然此处 qualifier还是为null的
AutowireCandidateQualifier qualifier = bd.getQualifier(type.getName());
if (qualifier == null) {
qualifier = bd.getQualifier(ClassUtils.getShortName(type));
}
//这里才是真真有料的地方~~~请认真看步骤
if (qualifier == null) {
// First, check annotation on qualified element, if any
// 1、词方法是从bd标签里拿这个类型的注解声明,非XML配置时代此处targetAnnotation 为null
Annotation targetAnnotation = getQualifiedElementAnnotation(bd, type);
// Then, check annotation on factory method, if applicable
// 2、若为null。去工厂方法里拿这个类型的注解。这方法里标注了两个注解@Bean和@LoadBalanced,所以此时targetAnnotation就不再为null了~~
if (targetAnnotation == null) {
targetAnnotation = getFactoryMethodAnnotation(bd, type);
}
// 若本类木有,还会去父类去找一趟
if (targetAnnotation == null) {
RootBeanDefinition dbd = getResolvedDecoratedDefinition(bd);
if (dbd != null) {
targetAnnotation = getFactoryMethodAnnotation(dbd, type);
}
}
// 若xml、工厂方法、父里都还没找到此方法。那好家伙,回退到还去类本身上去看
// 也就是说,如果@LoadBalanced标注在RestTemplate上,也是阔仪的
if (targetAnnotation == null) {
// Look for matching annotation on the target class
...
}
// 找到了,并且当且仅当就是这个注解的时候,就return true了~
// Tips:这里使用的是equals,所以即使目标的和Bean都标注了@Qualifier属性,value值相同才行哟~~~~
// 简单的说:只有value值相同,才会被选中的。否则这个Bean就是不符合条件的
if (targetAnnotation != null && targetAnnotation.equals(annotation)) {
return true;
}
}
// 赞。若targetAnnotation还没找到,也就是还没匹配上。仍旧还不放弃,拿到当前这个注解的所有注解属性继续尝试匹配
Map<String, Object> attributes = AnnotationUtils.getAnnotationAttributes(annotation);
if (attributes.isEmpty() && qualifier == null) {
return false;
}
... // 详情不描述了。这就是为什么我们吧@Qualifier标注在某个类上面都能生效的原因 就是这里做了非常强大的兼容性~
}
// =================它最重要的两个判断=================
if (targetAnnotation != null && targetAnnotation.equals(annotation));
// Fall back on bean name (or alias) match
if (actualValue == null && attributeName.equals(AutowireCandidateQualifier.VALUE_KEY) &&
expectedValue instanceof String && bdHolder.matchesName((String) expectedValue));
checkQualifier()
十分な実装が見られるようにSpring
包括性、互換性、まだ場所の柔軟性の考慮、その場合のための良い枠組みとして。そのためのSpring
強力なサポートと柔軟な拡張を提供し、唯一放棄するSpringBoot、SpringCloud
フレームレベルの設計上のより多くの可能性を〜
---
@Qualifier
高度な使用
@Autowired
それはタイプによってコンテナ春豆複数回の同じタイプは、あなたが必要とするときに応じて自動組立で@Qualifier
一緒に使用します。
例1:
@Configuration
public class WebMvcConfiguration {
@Qualifier("person1")
@Autowired
public Person person;
@Bean
public Person person1() {
return new Person("fsx01", 16);
}
@Bean
public Person person2() {
return new Person("fsx02", 18);
}
}
(以下同じ)を次のように単一のテストコードがあります:
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(WebMvcConfiguration.class);
WebMvcConfiguration bean = context.getBean(WebMvcConfiguration.class);
// 打印字段的值
System.out.println(bean.person);
}
印刷を実行した後Person(name=fsx01, age=16)
、完全に予想に沿ったもので、。これはまた、私たちで@Qualifier
、使用するのが最も簡単で最も通常のノート。
例2:
あなたが注意している場合、あなたは気づいているかもしれません@Qualifier
継承(ことができますコメント@Inherited
、)、フィールド、メソッド、メソッドパラメータ、クラスにマークすることができます注解上
。
:それはまた、そうでありません
@Configuration
public class WebMvcConfiguration {
@MyAnno // 会把所有标注有此注解的Bean都收入囊中,请List装(因为会有多个)
@Autowired
public List<Person> person;
@MyAnno
@Bean
public Person person1() {
return new Person("fsx01", 16);
}
@MyAnno
@Bean
public Person person2() {
return new Person("fsx02", 18);
}
// 自定义注解:上面标注有@Qualifier注解
@Target({FIELD, METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
@interface MyAnno {
}
}
単一の測定、印刷を実行し、[Person(name=fsx01, age=16), Person(name=fsx02, age=18)]
期待に沿って、。
三つの例:
あなたが直接使用して注釈をカスタマイズしたくない場合は@Qualifier
、注釈の分類注射を、それは、次の場合可能です。
@Configuration
public class WebMvcConfiguration {
@Qualifier("person2")
@Autowired
public List<Person> person;
@Qualifier("person2")
@Bean
public Person person1() {
return new Person("fsx01", 16);
}
@Qualifier
@Bean
public Person person2() {
return new Person("fsx02", 18);
}
@Qualifier
@Bean
public Person person3() {
return new Person("fsx03", 20);
}
}
最終的な結果は、実行されています。
[Person(name=fsx01, age=16), Person(name=fsx02, age=18)]
@Qualifier
来た同じに注入され、同じ値指定された値またはのbeanName(または別名)。このマッチングコードの一部:
checkQualifier方法:
1、头上标注的注解完全equals(类型和value值都一样,算作匹配成功)
targetAnnotation != null && targetAnnotation.equals(annotation)
2、Fall back on bean name (or alias) match。若@Qualifier没匹配上,回退到BeanName的匹配,规则为:
取头上注解的`value`属性(必须有此属性),如果beanName/alias能匹配上次名称,也算最终匹配成功了
actualValue == null && attributeName.equals(AutowireCandidateQualifier.VALUE_KEY) &&
expectedValue instanceof String && bdHolder.matchesName((String) expectedValue)
注意:人参の使用が比較的簡単である上デモンストレーションを行うにはない、ここで、クラスで使用してください。設計の詳細を見ることができ、注釈のプロパティが必要とされていない、それは米国の注釈シーンで活用することができます。
@Qualifier
value
注入との依存関係@Qualifier
を使用することも、次の詳細に注意してください。
@Autowired
内側容器が見つかったNの複数与えることができるので、それは、注入オブジェクトタイプフィールド中に書き込むことがないかもしれません。しかし、List<Object>
可能である(すべてのBeanに相当することは〜引き継ぐことがあります)- 活用することができます
@Qualifier
(入力しない)、カテゴリによって、オンデマンドで、高度な機能依存性注入を、この能力は、二次開発フレームワークは、より多くの可能性を設計者に提供して与えられ、非常に立派です
指定された値は、キー/マッチに従って定義されている場合と同様@LoadBalanced
、この達成のMoのクラス、および自由度に従って分類画定その注釈マッチングを理解すべきです。
推奨読書
@LoadBalanced注釈はRestTemplateは、負荷分散機能を持たせることができますなぜ?[春の雲を楽しむことを学びます]
概要
この資料に記載されて@Qualifier
組み合わせることで、高度なアプリケーションシナリオやケーススタディを@LoadBalanced
この注釈が使用されている、それは指摘しておかなければ見て新しい視点を開くあなたを与えることである@Qualifier
、とさえ見てSpring
理解のフォローアップである依存性注入、カスタム拡張/使用することは非常にまだあります有意義。
== 春、SpringBoot、MyBatisのソースコード解析およびその他の利害私にWXを追加することができため場合:fsx641385712、手動でグループに離陸することを勧め ==
== 春、SpringBoot、MyBatisのソースコード解析および他のために興味が私を追加WXことができる場合:fsx641385712、手動で離陸したグループにあなたを招待 ==