註解分析
@Qualifier和@Primary都是屬於spring框架下的註解,@Priority屬於javax.annotation,JSR250規範。
@Primary官方文檔的大概意思是,如果在自動注入的多個候選bean中,有一個primary bean的話,那麼這個bean就會是自動注入的目標bean。可以作用在類和方法上
@Qualifier官方的大概意思是,自動注入的時候,使用了這個註解,那麼在候選bean中,就會用這個註解指定的那個bean。作用在字段,方法,類,參數,註解上。
@Priority大概意思是說明使用的順序,作用在參數和類上。
代碼測試
新建幾個類:
待注入的接口:
public interface WhichSelected {
String beanName();
}
三個實現類:
@Component
@Primary
public class PrimarySelected implements WhichSelected{
@Override
public String beanName() {
return "primarySelected";
}
}
@Component
@Priority(1)
public class PrioritySelected implements WhichSelected{
@Override
public String beanName() {
return "prioritySelected";
}
}
@Component
@Priority(2)
public class PriorityNextSelected implements WhichSelected{
@Override
public String beanName() {
return "priorityNextSelected";
}
}
注入的類:
@Component
public class TargetInject {
@Autowired
private WhichSelected whichSelected;
public void whichBean() {
System.out.println(whichSelected.beanName());
}
}
包掃描類:
@Configuration
@ComponentScan("spring.postprocessor.choseorder")
public class PackageScannerConfig {
}
測試類:
public class OrderTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(PackageScannerConfig.class);
TargetInject targetInject = context.getBean(TargetInject.class);
targetInject.whichBean();
}
}
運行結果:
primarySelected
修改代碼:
@Autowired
@Qualifier("priorityNextSelected")
private WhichSelected whichSelected;
結果:
priorityNextSelected
注掉//@Primary
@Component
//@Primary
public class PrimarySelected implements WhichSelected{
@Override
public String beanName() {
return "primarySelected";
}
}
結果:
prioritySelected
修改代碼,去掉所有的@Qualifier,@Priority,@Primary,運行:
很明顯會報錯,找不到要注入的bean
再次修改:
將private WhichSelected whichSelected;改爲private WhichSelected prioritySelected; 運行結果爲prioritySelected
代碼小結
從上面五種情況可以看出,第一優先候選bean是@Qualifier註解指定的bean,第二優先是@Primary註解的bean,第三是@Priority(1)以及其後面的順序,最後是bean名稱。
源碼調試
先將代碼恢復到上面最開始的狀態。
我們都知道這一行代碼:
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(PackageScannerConfig.class);
是獲取IOC容器的,點到AbstractApplicationContext類裏面的refresh()方法,可以看到其實創建的是一個ConfigurableListableBeanFactory,
這是一個接口,我們來到DefaultListableBeanFactory,這個類實現了這個接口。
這裏討論的注入是用@Autowired注入的,其實很多註解都是通過後置處理器實現的,@Autowired註解也是,對應的後置處理器是:AutowiredAnnotationBeanPostProcessor,
這個類實現了PriorityOrdered接口,可以自行看一下後置處理器的加載順序,這裏不討論。注入的時機和其他創建bean初始化屬性的時機都是在populateBean的時候進行的。
來到AutowiredAnnotationBeanPostProcessor的postProcessPropertyValues方法
繼續F8會來到AbstractAutowireCapableBeanFactory的populateBean方法,可以看到這個方法裏面也是一系列的後置處理器的處理。這裏只需要看一下就可以,一直按F9,到beanName是targetInject的時候停下
按F8來到AutowiredAnnotationBeanPostProcessor的inject方法,來看那一下調用棧:
繼續向下走,會來到容器解決依賴這裏DefaultListableBeanFactory的doResolveDependency方法,走到這裏是關鍵
step into進去
這裏就能看到候選bean的名字了,走到下一個for循環
這裏是重點,第一輪淘汰,待會說到@Qualifier再點進去,看字面意思就是判斷一下不是自我引用和是候選bean,其實就是這個意思,滿足條件就加進去,在上面的代碼情況下,會全部加進去
繼續向下走,走到doResolveDependency的這裏
就開始從多個候選bean裏面篩選。繼續向下
進到determineAutowireCandidate方法裏面,其實在這個方法裏面就可以看出順序了
先是primary,然後priority,最後beanName。
繼續進入方法determinePrimaryCandidate
可以看到最主要的判斷方法是isPrimary,進去
點進去isPrimary
可以看到是AbstractBeanDefinition下面的一個Boolean屬性,此時看一下bean定義信息:
可以看到primarySelected是一個ScannedGenericBeanDefinition,其中ScannedGenericBeanDefinition又是AbstractBeanDefinition的子類
所以肯定有這個屬性信息,果然有,爲true
還可以看到一些常見的屬性,單例,懶加載等等。
爲true,所以可以通過,來到下一步:
可以看到一種類型的bean,只能有一個primary。最後返回bean名稱,將這個bean作爲選中的bean注入。
點進determineHighestPriorityCandidate看是一樣的,不能有同等級優先級。
這樣候選bean的選取就結束了,現在加一個@Qualifier。
現在來到上面說的DefaultListableBeanFactory-》findAutowireCandidates的這個for循環:
繼續深入:isAutowireCandidate(candidate, descriptor) -》isAutowireCandidate(beanName, descriptor, getAutowireCandidateResolver()) -》isAutowireCandidate(beanName, getMergedLocalBeanDefinition(beanDefinitionName), descriptor, resolver)-》QualifierAnnotationAutowireCandidateResolver的isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor)
進入:checkQualifiers
這裏就是檢查@Qualifier這個註解
如果是@Qualifier,繼續下一步
如果beanName和@Qualifier一致,就會返回true,上面的那個for循環就能加進去,最終的result只有一個元素:
因爲只有一個元素,所以就不會來到後面的檢查,因爲:
大於1纔會後面的檢查。
至此,整個過程就結束了。
驗證
可以看到容器DefaultListableBeanFactory的determineAutowireCandidate方法是protected,所以我們可以繼承這個容器,自己來寫選取順序,來驗證上面的調試。
把代碼考過來,交換priorityCandidate和primaryCandidate的順序:
public class MyListableBeanFactory extends DefaultListableBeanFactory {
@Override
protected String determineAutowireCandidate(Map<String, Object> candidates, DependencyDescriptor descriptor) {
Class<?> requiredType = descriptor.getDependencyType();
String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType);
if (priorityCandidate != null) {
return priorityCandidate;
}
String primaryCandidate = determinePrimaryCandidate(candidates, requiredType);
if (primaryCandidate != null) {
return primaryCandidate;
}
return null;
}
}
去掉@Qualifier,修改測試方法:
public class OrderTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(new MyListableBeanFactory());
context.register(PackageScannerConfig.class);
context.refresh();
TargetInject targetInject = context.getBean(TargetInject.class);
targetInject.whichBean();
}
}
運行,可以看到輸出的結果是:
prioritySelected
和之前的結果不一樣,和我們改完的代碼邏輯一致,說明調試都是正確的。
補充一點,AutowiredAnnotationBeanPostProcessor會作用於三種註解:@Autowired,@Value和JSR330的@Inject:
個人博客:https://robinted.github.io/
csdn 地址:https://blog.csdn.net/you_and_dream
原創鏈接:https://blog.csdn.net/you_and_dream/article/details/103247238