從源碼分析@Qualifier,@Primary,@Priority的候選順序

註解分析

@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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章