Spring系列第十一講 bean中的autowire-candidate又是幹什麼的?

qwer1030274531發表於2020-11-02

autowire-candidate做什麼事情的?

上一篇文章Spring系列第10篇:primary可以解決什麼問題?中遇到的問題我們再來回顧一下,當容器中某種型別的bean存在多個的時候,此時如果我們從容器中查詢這種型別的bean的時候,會報下面這個異常:

org.springframework.beans.factory.NoUniqueBeanDefinitionException1

原因:當從容器中按照型別查詢一個bean物件的時候,容器中卻找到了多個匹配的bean,此時spring不知道如何選擇了,處於懵逼狀態,就會報這個異常。

這種異常主要出現在2種場景中:

場景1:

從容器容器中查詢符合指定型別的bean,對應BeanFactory下面的方法:

<T> T getBean(Class<T> requiredType) throws BeansException;1

場景2:

自動注入方式設定為byType的時候,如下:

package com.javacode2018.lesson001.demo8;public class SetterBean {
    public interface IService{} //@1
    public static class ServiceA implements IService{} //@2
    public static class ServiceB implements IService{} //@3
    private IService service;
    public void setService(IService service) {
        this.service = service;
    }}<?xml version="1.0" encoding="UTF-8"?><beans xmlns="
       xmlns:xsi="
       xsi:schemaLocation="
    /spring-beans-4.3.xsd">
    <bean id="serviceA" class="com.javacode2018.lesson001.demo8.SetterBean$ServiceA"/>
    <bean id="serviceB" class="com.javacode2018.lesson001.demo8.SetterBean$ServiceB"/>
    <bean id="setterBean" class="com.javacode2018.lesson001.demo8.SetterBean" autowire="byType" /></beans>123456789101112131415161718192021222324

setterBean的autowire設定的是byType,即按setter方法的引數型別自動注入,SetterBean的setService的型別是IService,而IService類有2個實現類:ServiceA和ServiceB,而容器容器中剛好有這2個實現類的bean:serviceA和serviceB,所以上面程式碼會報錯,不知道注入的時候選擇那個物件注入。

我們可以透過primary屬性來指定一個主要的bean,當從容器中查詢的時候,如果有多個候選的bean符合查詢的型別,此時容器將返回primary="true"的bean物件。

spring還有一種方法也可以解決這個問題,可以設定某個bean是否在自動注入的時候是否為作為候選bean,透過bean元素的autowire-candidate屬性類配置,如下:

<bean id="serviceA" class="com.javacode2018.lesson001.demo8.SetterBean$ServiceA" autowire-candidate="false"/>1

autowire-candidate:設定當前bean在被其他物件作為自動注入物件的時候,是否作為候選bean,預設值是true。

來舉例說明一下,以上面的setter注入的案例先來說一下注入的過程:

容器在建立setterBean的時候,發現其autowire為byType,即按型別自動注入,此時會在SetterBean類中查詢所有setter方法列表,其中就包含了setService方法,setService方法引數型別是IService,然後就會去容器中按照IService型別查詢所有符合條件的bean列表,此時容器中會返回滿足IService這種型別並且autowire-candidate="true"的bean,剛才有說過bean元素的autowire-candidate的預設值是true,所以容器中符合條件的候選bean有2個:serviceA和serviceB,setService方法只需要一個滿足條件的bean,此時會再去看這個列表中是否只有一個主要的bean(即bean元素的primary=“ture”的bean),而bean元素的primary預設值都是false,所以沒有primary為true的bean,此時spring容器懵了,不知道選哪個了,此時就報錯了,丟擲NoUniqueBeanDefinitionException異常

從上面過程中可以看出將某個候選bean的primary置為true就可以解決問題了。

或者只保留一個bean的autowire-candidate為true,將其餘的滿足條件的bean的autowire-candidate置為false,此時也可以解決這個問題,下面我們使用autowire-candidate來解決上面問題看一下效果:

SetterBean.java

package com.javacode2018.lesson001.demo9;public class SetterBean {
    public interface IService {} //@1
    public static class ServiceA implements IService {} //@2
    public static class ServiceB implements IService {} //@3
    private IService service;
    public void setService(IService service) {
        this.service = service;
    }
    @Override
    public String toString() {
        return "SetterBean{" +
                "service=" + service +
                '}';
    }}12345678910111213141516171819202122

autowireCandidateBean.xml

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="
       xmlns:xsi="
       xsi:schemaLocation="
    /spring-beans-4.3.xsd">
    <bean id="serviceA" class="com.javacode2018.lesson001.demo9.SetterBean$ServiceA" autowire-candidate="false"/>
    <bean id="serviceB" class="com.javacode2018.lesson001.demo9.SetterBean$ServiceB"/>
    <bean id="setterBean" class="com.javacode2018.lesson001.demo9.SetterBean" autowire="byType" /></beans>1234567891011

上面我們將serviceA的autowire-candidate置為false了,serviceA在被其他bean自動按照型別注入的時候,將不再放入候選名單中

測試用例 nanchang/

package com.javacode2018.lesson001.demo9;import com.javacode2018.lesson001.demo5.IocUtils;import com.javacode2018.lesson001.demo8.NormalBean;import com.javacode2018.lesson001.demo8.PrimaryBean;import org.junit.Test;import org.springframework.context.support.ClassPathXmlApplicationContext;import java.util.Arrays;import java.util.Map;/**
 * bean元素的autowire-candidate可以設定當前bean是否作為其他bean自動注入的候選bean
 */public class AutowireCandidateTest {
    @Test
    public void setterBean() {
        String beanXml = "classpath:/com/javacode2018/lesson001/demo9/autowireCandidateBean.xml";
        ClassPathXmlApplicationContext context = IocUtils.context(beanXml);
        System.out.println(context.getBean(SetterBean.class)); //@1
        SetterBean.IService service = context.getBean(SetterBean.IService.class); //@2
        System.out.println(service);
    }}123456789101112131415161718192021222324252627

@1:查詢容器中SetterBean型別的bean物件

@2:查詢容器中SetterBean.IService介面型別的bean,實際上面容器中serviceA和serviceB都是這種型別的

下面我們執行一下,看看輸出:

SetterBean{service=com.javacode2018.lesson001.demo9.SetterBean$ServiceB@29176cc1}com.javacode2018.lesson001.demo9.SetterBean$ServiceB@29176cc112

注意一下輸出,2行輸出中都是ServiceB,因為serviceB的autowire-candidate是預設值true,自動注入的時候作為候選bean,而serviceA的autowire-candidate是false,自動注入的時候不作為候選bean,所以上面輸出的都是serviceB。

autowire-candidates屬性解析原始碼

beans元素是xml中定義bean的根元素,beans元素有個default-autowire-candidates屬性,用於定義哪些bean可以作為候選者,default-autowire-candidates的值是個萬用字元如: hangzhou/

default-autowire-candidates="*Service"1

再來說一下bean元素的autowire-candidate屬性,這個屬性有3個可選值:

  • default:這個是預設值,autowire-candidate如果不設定,其值就是default

  • true:作為候選者

  • false:不作為候選者

spring中由beans元素的default-autowire-candidates和bean元素的autowire-candidate來決定最終bean元素autowire-candidate的值,我們來看一下bean元素autowire-candidates的解析原始碼:

原始碼位置:org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parseBeanDefinitionAttributes1

主要程式碼如下: wuhan/

//獲取bean元素的autowire-candidate元素,autowire-candidate如果不設定,其值就是defaultString autowireCandidate = ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE);//判斷bean元素的autowire-candidate元素是否等於"default"或者是否等於""if (isDefaultValue(autowireCandidate)) { 
    //獲取beans元素default-autowire-candidates屬性值
    String candidatePattern = this.defaults.getAutowireCandidates();
    //判斷獲取beans元素default-autowire-candidates屬性值是否為空,default-autowire-candidates預設值就是null
    if (candidatePattern != null) {
        //判斷bean的名稱是否和default-autowire-candidates的值匹配,如果匹配就將bean的autowireCandidate置為true,否則置為false
        String[] patterns = StringUtils.commaDelimitedListToStringArray(candidatePattern);
        bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName));
    }}else {
    //判斷bean的autowire-candidate的值是否等於"true"
    bd.setAutowireCandidate(TRUE_VALUE.equals(autowireCandidate));}12345678910111213141516

如果上面判斷都沒有進去,autowireCandidate屬性預設值就是true,這個在下面定義的:

org.springframework.beans.factory.support.AbstractBeanDefinition#autowireCandidateprivate boolean autowireCandidate = true;123

所有的bean元素最後都會被解析為spring中的org.springframework.beans.factory.config.BeanDefinition物件,關於BeanDefinition以後我們會細說。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/30239065/viewspace-2731543/,如需轉載,請註明出處,否則將追究法律責任。

相關文章