【Spring註解驅動開發】如何實現方法、構造器位置的自動裝配?我這樣回答讓面試官很滿意!

冰河團隊發表於2020-08-15

寫在前面

冰河技術 微信公眾號前面的文章中,我們介紹瞭如何使用註解來自動裝配Spring元件。之前將的都是在來的欄位上新增註解,那有沒有什麼方法可以實現方法、構造器位置的自動裝配嗎?今天我們就一起來探討下如何實現方法、構造器位置的自動裝配。

關注 冰河技術 技術微信公眾號,後臺回覆“spring註解”關鍵字,領取專案工程原始碼。

再談@Autowired註解

在我發表在 冰河技術 微信公眾號的《【Spring註解驅動開發】使用@Autowired@Qualifier@Primary三大註解自動裝配元件,你會了嗎?》一文中簡單介紹了下@Autowired註解註解的使用方法。下面,我們再來看下@Autowired註解的原始碼。

package org.springframework.beans.factory.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
	boolean required() default true;
}

我們通過@Autowired註解的原始碼可以看出,在@Autowired註解上標註有如下的註解資訊。

@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})

可以看出@Autowired註解不僅可以標註在欄位上。也可以標註在構造方法上,例項方法上,引數上。

專案案例

案例準備

接下來,我們在專案中新建一個Dog類,在Doc類中有一個Cat類的引用,並且我們使用@Component註解將Dog類載入到IOC容器中,如下所示。

package io.mykit.spring.plugins.register.bean;
import org.springframework.stereotype.Component;
/**
 * @author binghe
 * @version 1.0.0
 * @description 測試實體類
 */
@Component
public class Dog {

    private Cat cat;

    public Cat getCat() {
        return cat;
    }

    public void setCat(Cat cat) {
        this.cat = cat;
    }

    @Override
    public String toString() {
        return "Dog{" +  "cat=" + cat + '}';
    }
}

配置好之後,我們還需要在AutowiredConfig類的@ComponentScan註解中進行配置,使其能夠掃描io.mykit.spring.plugins.register.controller包下的類,如下所示。

@Configuration
@ComponentScan(value = {
        "io.mykit.spring.plugins.register.dao",
        "io.mykit.spring.plugins.register.service",
        "io.mykit.spring.plugins.register.controller",
        "io.mykit.spring.plugins.register.bean"})
public class AutowiredConfig {
}

此時,我們可以直接在Dog類中的cat欄位上新增@Autowired註解,使其自動裝配。這是我們在《【Spring註解驅動開發】使用@Autowired@Qualifier@Primary三大註解自動裝配元件,你會了嗎?》一文中得出的結論。那今天我們就使用其他的方式來實現cat的自動裝配。

標註在例項方法上

我們也可以將@Autowired註解標註在setter方法上,如下所示。

@Autowired
public void setCat(Cat cat) {
    this.cat = cat;
}

當@Autowired註解標註在方法上時,Spring容器在建立物件的時候,就會呼叫相應的方法為物件賦值。如果標註的方法存在引數時,則方法使用的引數和自定義型別的值,需要從IOC容器中獲取。

接下來,我們將AutowiredTest類的testAutowired01()方法中有關獲取和列印PersonService資訊的程式碼註釋,新增獲取和列印Dog資訊的程式碼,如下所示。

@Test
public void testAutowired01(){
    //建立IOC容器
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AutowiredConfig.class);
    //PersonService personService = context.getBean(PersonService.class);
    //System.out.println(personService);
    Dog dog = context.getBean(Dog.class);
    System.out.println(dog.toString());
    context.close();
}

執行AutowiredTest類的testAutowired01()方法進行測試,可以看到,結果資訊中輸出瞭如下一行資訊。

Dog{cat=io.mykit.spring.plugins.register.bean.Cat@6a400542}

說明已經獲取到cat的資訊,可以將@Autowired註解標註在方法上

為了驗證最終的輸出結果是否是從IOC容器中獲取的,我們可以在AutowiredTest類的testAutowired01()方法中直接獲取Cat的資訊,如下所示。

@Test
public void testAutowired01(){
    //建立IOC容器
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AutowiredConfig.class);
    //PersonService personService = context.getBean(PersonService.class);
    //System.out.println(personService);
    Dog dog = context.getBean(Dog.class);
    System.out.println(dog.toString());
    Cat cat = context.getBean(Cat.class);
    System.out.println(cat);
    context.close();
}

我們再次執行AutowiredTest類的testAutowired01()方法進行測試,可以在輸出的結果資訊看到如下兩行程式碼。

Dog{cat=io.mykit.spring.plugins.register.bean.Cat@6a400542}
io.mykit.spring.plugins.register.bean.Cat@6a400542

可以看出在Dog類中通過@Autowired註解獲取到的Cat物件和直接從IOC容器中獲取到Cat物件是同一個物件。

標註在構造方法上

在前面的案例中,我們在Dog類上使用了@Component註解,如下所示。

package io.mykit.spring.plugins.register.bean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
 * @author binghe
 * @version 1.0.0
 * @description 測試實體類
 */
@Component
public class Dog {

    private Cat cat;

    public Cat getCat() {
        return cat;
    }

    @Autowired
    public void setCat(Cat cat) {
        this.cat = cat;
    }

    @Override
    public String toString() {
        return "Dog{" +
                "cat=" + cat +
                '}';
    }
}

此時,Spring預設載入IOC容器中的元件,IOC容器啟動的時候預設會呼叫bean的無參構造器建立物件,然後再進行初始化賦值等操作。

接下來,我們為Dog類新增一個有參構造方法,然後去除setCat()方法上的@Autowired註解,將@Autowired註解標註在有參構造方法上,並在構造方法中列印資訊,如下所示。

package io.mykit.spring.plugins.register.bean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
 * @author binghe
 * @version 1.0.0
 * @description 測試實體類
 */
@Component
public class Dog {
    private Cat cat;
    @Autowired
    public Dog(Cat cat){
        this.cat = cat;
        System.out.println("呼叫了Dog的有參構造方法");
    }
    public Cat getCat() {
        return cat;
    }
    public void setCat(Cat cat) {
        this.cat = cat;
    }
    @Override
    public String toString() {
        return "Dog{" +
                "cat=" + cat +
                '}';
    }
}

接下來,我們執行AutowiredTest類的testAutowired01()方法進行測試,可以看到輸出結果資訊中存在如下一行資訊。

呼叫了Dog的有參構造方法

說明IOC容器在啟動的時候呼叫了Dog類的有參構造方法。並且可以從輸出的如下兩行資訊可以看出:通過Dog類的toString()方法列印出的Cat物件和直接從IOC容器中獲取的Cat物件是同一個物件。

Dog{cat=io.mykit.spring.plugins.register.bean.Cat@6a400542}
io.mykit.spring.plugins.register.bean.Cat@6a400542

這裡,需要大家注意的是:使用@Autowired註解標註在構造方法上時,構造方法中的引數物件也都是從IOC容器中獲取的。

標註在引數上

我們也可以將@Autowired註解標註在引數上,例如,在Dog類中我們將構造方法上的@Autowired註解標註在構造方法的引數上,如下所示。

public Dog(@Autowired Cat cat){
    this.cat = cat;
    System.out.println("呼叫了Dog的有參構造方法");
}

也可以將@Autowired註解標註在setter方法的引數上,如下所示。

public void setCat(@Autowired  Cat cat) {
    this.cat = cat;
}

這些效果與標註在欄位、例項方法和構造方法上的效果都是一樣的。

例如,我們將@Autowired註解標註在構造方法的引數上,執行AutowiredTest類的testAutowired01()方法進行測試,可以看到,輸出結果中,同樣包含如下三行資訊。

呼叫了Dog的有參構造方法
Dog{cat=io.mykit.spring.plugins.register.bean.Cat@6a400542}
io.mykit.spring.plugins.register.bean.Cat@6a400542

結論:無論Autowired註解標註在欄位上、例項方法上、構造方法上還是引數上,都是從IOC容器中獲取引數元件的值。

如果Spring的bean只有一個有參構造方法,並且這個有參構造方法只有一個引數,並且這個引數是IOC容器中的物件,當@Autowired註解標註在這個構造方法的引數上時,我們可以將@Autowired註解省略,如下所示。

public Dog(Cat cat){
    this.cat = cat;
    System.out.println("呼叫了Dog的有參構造方法");
}

接下來,我們執行AutowiredTest類的testAutowired01()方法進行測試,從輸出的結果資訊中,可以看出,同樣輸出了下面的三行資訊。

呼叫了Dog的有參構造方法
Dog{cat=io.mykit.spring.plugins.register.bean.Cat@6a400542}
io.mykit.spring.plugins.register.bean.Cat@6a400542

說明:如果Spring的bean只有一個有參構造方法,並且這個有參構造方法只有一個引數,並且這個引數是IOC容器中的物件,當@Autowired註解標註在這個構造方法的引數上時,我們可以將@Autowired註解省略。

標註在方法位置

@Autowired註解可以標註在某個方法的位置上。這裡,為了更好的演示效果,我們新建一個Fish類,在Fish類中有一個Cat型別的成員變數,如下所示。

package io.mykit.spring.plugins.register.bean;
/**
 * @author binghe
 * @version 1.0.0
 * @description 測試類
 */
public class Fish {
    private Cat cat;
    public void setCat(Cat cat) {
        this.cat = cat;
    }
    @Override
    public String toString() {
        return "Fish{" + "cat=" + cat + '}';
    }
}

接下來,我們在AutowiredConfig類中例項化Fish類,如下所示。

@Bean
public Fish fish(){
    return new Fish();
}

接下來,我們在AutowiredTest類中建立testAutowired02()方法,如下所示。

@Test
public void testAutowired02(){
    //建立IOC容器
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AutowiredConfig.class);
    Fish fish = context.getBean(Fish.class);
    System.out.println(fish);
    context.close();
}

執行testAutowired02()方法,在輸出的結果資訊中存在如下一行資訊。

Fish{cat=null}

說明此時的Fish類中的Cat物件為空。此時,我們可以將Cat物件作為一個引數傳遞到AutowiredConfig類的fish()方法中,並且將Cat物件設定到Fish中,如下所示。

@Bean
public Fish fish(Cat cat){
    Fish fish = new Fish();
    fish.setCat(cat);
    return fish;
}

當然,我們也可以使用@Autowired註解來標註fish()方法中的cat引數,如下所示。

@Bean
public Fish fish(@Autowired  Cat cat){
    Fish fish = new Fish();
    fish.setCat(cat);
    return fish;
}

接下來,我們再次執行testAutowired02()方法,在輸出的結果資訊中存在如下一行資訊。

Fish{cat=io.mykit.spring.plugins.register.bean.Cat@21de60b4}

說明Cat物件被成功建立並設定到了Fish類中。

結論:如果方法只有一個IOC容器中的物件作為引數,當@Autowired註解標註在這個方法的引數上時,我們可以將@Autowired註解省略。

重磅福利

關注「 冰河技術 」微信公眾號,後臺回覆 “設計模式” 關鍵字領取《深入淺出Java 23種設計模式》PDF文件。回覆“Java8”關鍵字領取《Java8新特性教程》PDF文件。回覆“限流”關鍵字獲取《億級流量下的分散式限流解決方案》PDF文件,三本PDF均是由冰河原創並整理的超硬核教程,面試必備!!

好了,今天就聊到這兒吧!別忘了點個贊,給個在看和轉發,讓更多的人看到,一起學習,一起進步!!

寫在最後

如果你覺得冰河寫的還不錯,請微信搜尋並關注「 冰河技術 」微信公眾號,跟冰河學習高併發、分散式、微服務、大資料、網際網路和雲原生技術,「 冰河技術 」微信公眾號更新了大量技術專題,每一篇技術文章乾貨滿滿!不少讀者已經通過閱讀「 冰河技術 」微信公眾號文章,吊打面試官,成功跳槽到大廠;也有不少讀者實現了技術上的飛躍,成為公司的技術骨幹!如果你也想像他們一樣提升自己的能力,實現技術能力的飛躍,進大廠,升職加薪,那就關注「 冰河技術 」微信公眾號吧,每天更新超硬核技術乾貨,讓你對如何提升技術能力不再迷茫!

相關文章