寫在前面
在 冰河技術 微信公眾號前面的文章中,我們介紹瞭如何使用註解來自動裝配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均是由冰河原創並整理的超硬核教程,面試必備!!
好了,今天就聊到這兒吧!別忘了點個贊,給個在看和轉發,讓更多的人看到,一起學習,一起進步!!
寫在最後
如果你覺得冰河寫的還不錯,請微信搜尋並關注「 冰河技術 」微信公眾號,跟冰河學習高併發、分散式、微服務、大資料、網際網路和雲原生技術,「 冰河技術 」微信公眾號更新了大量技術專題,每一篇技術文章乾貨滿滿!不少讀者已經通過閱讀「 冰河技術 」微信公眾號文章,吊打面試官,成功跳槽到大廠;也有不少讀者實現了技術上的飛躍,成為公司的技術骨幹!如果你也想像他們一樣提升自己的能力,實現技術能力的飛躍,進大廠,升職加薪,那就關注「 冰河技術 」微信公眾號吧,每天更新超硬核技術乾貨,讓你對如何提升技術能力不再迷茫!