天天看點

【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注解,使其自動裝配。這是我們在《

》一文中得出的結論。那今天我們就使用其他的方式來實作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類中。