再談@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類中。