Spring 中接口的 bean 是如何注入的?

每一個優秀的人,都有一段沉默的時光。 那段時光,是付出了不少努力,卻得不到結果的日子,咱們把它叫做紮根。面試

Question:

這個問題困擾了我很久,一直疑問這個接口的bean是怎麼注入進去的?由於只看到使用@Service注入了實現類serviceImpl,使用時怎麼能獲取的接口,並且還能調用到實現類的方法,難道這個接口是在何時自動注入了進去,且和實現類關聯上了?spring

接口微信

public interface TestService {
    public String test();
}

實現類implapp

@Service
public class TestServiceImpl implements TestService {
    @Override
    public String test() {
        return "TestServiceImpl";
    }
}

Controller的調用:ide

@RestController
public class TestCtl {
    @Autowired
    private TestService testService;

    @RequestMapping("/test")
    public String test() {
        return testService.test();
    }
}

請求結果:
微信圖片_20200609111229.jpgspa

Answwer:

後來才知道,並無注入接口的bean,只注入了實現類serviceImpl的bean,接口只是用來接收的。code

這裏就要說到@Autowired/@Resource的注入原理了:@Autowired是Spring的註解,Autowired默認先按byType,若是發現找到多個bean,則,又按照byName方式比對,若是還有多個,則報出異常;@Resource 是JDK1.6支持的註解,默認按照名稱(Byname)進行裝配, 若是沒有指定name屬性,當註解寫在字段上時,默認取字段名,按照名稱查找,若是註解寫在setter方法上默認取屬性名進行裝配。當找不到與名稱匹配的bean時才按照類型進行裝配。可是須要注意的是,若是name屬性一旦指定,就只會按照名稱進行裝配。blog

再來講Controller獲取實例的過程:使用@Autowired,程序在spring的容器中查找類型是TestService的bean,恰好找到有且只有一個此類型的bean,即testServiceImpl,因此就把testServiceImpl自動裝配到了controller的實例testService中,testService其實就是TestServiceImpl實現類;繼承

若是使用的是@Resource,則是先在容器中查找名字爲testService的bean,但並無找到,由於容器中的bean名字是TestServiceImpl(若是@Service沒指定bean的value屬性,則注入bean的名字就是類名,若是指定了則是指定的名字),而後再經過類型查找TestService類型的bean,找到惟一的了個TestService類型bean(即TestServiceImpl),因此就自動裝配實例成功了。更多面試題,歡迎關注公衆號Java面試題精選接口

Note:

byName 經過參數名 自動裝配,若是一個bean的name 和另一個bean的 property 相同,就自動裝配。

byType 經過參數的數據類型自動自動裝配,若是一個bean的數據類型和另一個bean的property屬性的數據類型兼容,就自動裝配

效率上來講@Autowired/@Resource差很少,不過推薦使用@Resource一點,由於當接口有多個實現時@Resource直接就能經過name屬性來指定實現類,而@Autowired還要結合@Qualifier註解來使用,且@Resource是jdk的註釋,可與Spring解耦。

Question:

若是一個接口有多個實現類時,經過註解獲取實例時怎麼知道應該獲取的是哪個實現類serviceImpl呢?

再增長了一個實現類TestServiceImpl2

@Service
public class TestServiceImpl2 implements TestService {
    @Override
    public String test() {
        return "TestServiceImpl2";
    }
}

Answer:

多個實現類的話可經過如下2種方式來指定具體要使用哪個實現:

一、 經過指定bean的名字來明確到底要實例哪個類

@Autowired 須要結合@Qualifier來使用,以下:

@Autowired
@Qualifier("testServiceImpl")
private TestService testService;

@Resource可直接經過指定name屬性的值便可,不過也能夠使用@Qualifier(有點畫蛇添足了…)

@Resource(name = "testServiceImpl")
private TestService testService;

@Resource若是不顯示的指定name值,就會自動把實例變量的名稱做爲name的值的,因此也能夠直接這樣寫:

@Resource
private TestService testServiceImpl;

二、 經過在實現類上添加@Primary註解來指定默認加載類

@Service
@Primary
public class TestServiceImpl2 implements TestService {
    @Override
    public String test() {
        return "TestServiceImpl2";
    }
}

這樣若是在使用@Autowired/@Resource獲取實例時若是不指定bean的名字,就會默認獲取TestServiceImpl2的bean,若是指定了bean的名字則以指定的爲準。

Question:

爲何非要調用接口來畫蛇添足,而不直接調用實現類serviceImpl的bean來得簡單明瞭呢?

Answer:

一、 直接獲取實現類serviceImpl的bean也是能夠的;

二、 至於加一層接口的緣由:一是AOP程序設置思想指導,給別人調用的接口,調用者只想知道方法和功能,而對於這個方法內部邏輯怎麼實現的並不關心;二是能夠下降各個模塊間的關聯,實現鬆耦合、程序分層、高擴展性,使程序更加靈活,他除了在規範上有卓越貢獻外,最精髓的是在多態上的運用;繼承只能單一繼承,接口卻能夠多實現

三、 當業務邏輯簡單,變動較少,項目自用時,省略掉接口直接使用實現類更簡單明瞭;反之則推薦使用接口;

相關文章
相關標籤/搜索