詳解SpringBoot依賴注入和使用配置文件
前言
在上一篇文章中,講訴了SpringIoC的Bean裝配,但是對(duì)于如何進(jìn)行獲取,也就是Bean之間的依賴還未講訴,下面開(kāi)始講訴依賴注入(Dependency Injection,DI)以及如何使用屬性文件。涉及主要注解@Autowired、@Primary、@Quelifier、@PropertySource和@ConfigurationProperties。
一、??依賴注入
例:人類(Person)有時(shí)候利用一些動(dòng)物(Animal)去完成一些事情,比方說(shuō)狗(Dog)是用來(lái)看門的,貓(Cat)是用來(lái)抓老鼠的, 鸚鵡(Paηot)是用來(lái)迎客的……于是做一些事情就依賴于那些可愛(ài)的動(dòng)物。假設(shè)現(xiàn)在需要用狗狗來(lái)看門。
//定義人類接口 public interface Person { void service(); void setAnimal(Animal animal); } //定義動(dòng)物接口 public interface Animal { void user(); } //定義狗 @Component public class Dog implements Animal { @Override public void user() { System.out.println("狗【" + Dog.class.getSimpleName() + "】是用來(lái)看門的"); } } //定義年輕人 @Component public class YoungPerson implements Person { @Autowired private Animal animal = null; @Override public void service() { this.animal.user(); } @Override public void setAnimal(Animal animal) { this.animal = animal; } } //定義配置類 @Configuration @ComponentScan("com.dragon.restart")//所有的包和類都在restart下 public class AppConfig { }
測(cè)試類:
ApplicationContext ctx =new AnnotationConfigApplicationContext(AppConfig.class) ; Person person= ctx.getBean(YoungPerson.class) ; person.service() ;
測(cè)試是成功的,這個(gè)時(shí)候SpringIoC 容器己經(jīng)通過(guò)注解@Autowired成功地將Dog注入到了 YoungPerson 實(shí)例中。 但是這只是一個(gè)比較簡(jiǎn)單的例子,我們有必要繼續(xù)探討@Autowired。
??注解@Autowired
@Autowired 是我們使用得最多的注解之一, 因此在這里需要進(jìn)一步地探討它。 它注入的機(jī)制最基本的一條是根據(jù)類型 (bytype), 我們回顧IoC 容器的頂級(jí)接口 BeanFactory,就可以知道 IoC 容器是通過(guò)getBean 方法獲取對(duì)應(yīng)Bean 的,而 getBean 又支持根據(jù)類型(by type)或者根據(jù)名稱(by name)。這里明顯根據(jù)類型注入,將狗狗實(shí)例注入Animal對(duì)象。
再回到上面的例子,我們只是創(chuàng)建了一個(gè)動(dòng)物一一狗,而實(shí)際上動(dòng)物還可以有貓 (Cat),貓可以為我們抓老鼠, 于是我們又創(chuàng)建了一個(gè)貓的類。
@Component public class Cat implements Animal{ @Override public void user() { System.out.println("貓【" + Cat.class.getSimpleName() + "】是抓老鼠的"); } }
好了,如果我們還使用著YoungPerson類,那么麻煩來(lái)了,因?yàn)檫@個(gè)類只是定義了一個(gè)動(dòng)物屬性(Animal),而我們卻有兩個(gè)動(dòng)物,一個(gè)狗, 一個(gè)貓, SpringIoC 如何注入呢?
運(yùn)行測(cè)試,可以看到IoC容器拋出異常,如下:
Description:
Field animal in com.dragon.restart.pojo.impl.YoungPerson required a single bean, but 2 were found:
- cat: defined in file [E:\IDEA_projects\restart\target\classes\com\dragon\restart\pojo\Cat.class]
- dog: defined in file [E:\IDEA_projects\restart\target\classes\com\dragon\restart\pojo\impl\Dog.class]
那么使用@Autowired 能處理這個(gè)問(wèn)題嗎?答案是肯定的。假設(shè)我們目前需要的是狗提供服務(wù),那么可以把屬性名稱轉(zhuǎn)化為dog,也就是原來(lái)YoungPerson的private Animal animal = null;
改為private An工mal dog = null;
@Component public class YoungPerson implements Person { @Autowired private Animal dog = null; @Override public void service() { this.dog.user(); } @Override public void setAnimal(Animal animal) { this.dog = animal; } }
這里, 我們只是將屬性的名稱從animal 修改為了 dog,那么我們?cè)贉y(cè)試的時(shí)候,你可以看到是采用狗來(lái)提供服務(wù)的。那是因?yàn)椋繟utowired提供這樣的規(guī)則:首先它會(huì)根據(jù)類型找到對(duì)應(yīng)的Bean,如果對(duì)應(yīng)類型的 Bean 不是唯一的,那么它會(huì)根據(jù)其屬性名稱和 Bean 的名稱進(jìn)行匹配。如果匹配得上,就會(huì)使用該Bean:如果還無(wú)法匹配,就會(huì)拋出異常。這里還要注意的是@Autowired 是一個(gè)默認(rèn)必須找到對(duì)應(yīng) Bean 的注解,如果不能確定其標(biāo)注屬性一定會(huì)存在并且允許這個(gè)被標(biāo)注的屬性為null, 那么你可以配置@Autowired屬性 required 為 false如@Autowired(required = false)
@Autowired除了可以標(biāo)注屬性外,還可以標(biāo)注方法, 如setAnimal方法,如下所示:
@Override @Autowired public void setAnimal (Animal animal) { this.animal = animal;
這樣它也會(huì)使用setAnimal 方法從 IoC 容器中找到對(duì)應(yīng)的動(dòng)物進(jìn)行注入,甚至我們還可以使用在方法的參數(shù)上,后續(xù)文章會(huì)再談到它。
??消除歧義性——@Quelifier和@Primary
在上面我們發(fā)現(xiàn)有貓有狗的時(shí)候, 為了使@Autowired 能夠繼續(xù)使用,我們做了一個(gè)決定,將YoungPerson 的屬性名稱從 animal 修改為 dog。顯然這是一個(gè)憋屈的做法,好好的一個(gè)動(dòng)物,卻被我們定義為了狗,畢竟不能每次換個(gè)對(duì)象就改一次,這樣太麻煩了。產(chǎn)生注入失敗的問(wèn)題根本是按類型(bytype) 查找, 正如動(dòng)物可以有多種類型,這樣會(huì)造成 Spring IoC 容器注入的困擾,我們把這樣的一個(gè)問(wèn)題稱為歧義性。知道這個(gè)原因后, 那么這兩個(gè)注解是從哪個(gè)角度去解決這些問(wèn)題的呢?
首先是一個(gè)注解@Primary,它是一個(gè)修改優(yōu)先權(quán)的注解,當(dāng)我們有貓有狗的時(shí)候,假設(shè)這次需要使用貓, 那么只需要在貓類的定義上加入@Primarγ就可以了,如下:
@Component @Primary public class Dog implements Animal { @Override public void user() { System.out.println("狗【" + Dog.class.getSimpleName() + "】是用來(lái)看門的"); } }
這里的@Primary 的含義告訴 Spring IoC 容器, 當(dāng)發(fā)現(xiàn)有多個(gè)同樣類型的 Bean 時(shí),請(qǐng)優(yōu)先使用我進(jìn)行注入,于是再進(jìn)行測(cè)試時(shí)會(huì)發(fā)現(xiàn),系統(tǒng)將用狗狗為你提供服務(wù)。 因?yàn)楫?dāng) Spring 進(jìn)行注入的時(shí)候,雖然它發(fā)現(xiàn)存在多個(gè)動(dòng)物, 但因?yàn)镈og被標(biāo)注為了@Primarγ,所以優(yōu)先采用Dog的實(shí)例進(jìn)行了注入,這樣就通過(guò)優(yōu)先級(jí)的變換使得IoC容器知道注入哪個(gè)具體的實(shí)例來(lái)滿足依賴注入。然后,有時(shí)候@Primary 也可以使用在多個(gè)類上,也許無(wú)論是貓還是狗狗都可能帶上@Primary 注解, 其結(jié)果是IoC容器還是無(wú)法區(qū)分采用哪個(gè)Bean的實(shí)例進(jìn)行注入, 又或者說(shuō)我們需要更加靈活的機(jī)制來(lái)實(shí)現(xiàn)注入,那么**@Quelifier** 可以滿足你的這個(gè)愿望。
它將與@Autowired 組合在一起,通過(guò)類型和名稱一起找到Bean。我們知道Bean 名稱在 Spring IoC 容器中是唯一的標(biāo)識(shí),通過(guò)這個(gè)就可以消除歧義性了。此時(shí)你是否想起了BeanFactory接口中的這個(gè)方法呢?<T> T getBean(String name, Class<T> requiredType) throws BeansException;
代碼:
@Component public class YoungPerson implements Person { @Autowired @Qualifier("dog") private Animal animal = null; @Override public void service() { this.animal.user(); } @Override public void setAnimal(Animal animal) { this.animal = animal; } }
一旦這樣聲明, Spring IoC 將會(huì)以類型和名稱去尋找對(duì)應(yīng)的Bean進(jìn)行注入。根據(jù)類型和名稱,顯然也只能找到狗狗為我們服務(wù)了。
??帶有參數(shù)的構(gòu)造方法類裝配
在上面,我們都基于一個(gè)默認(rèn)的情況,那就是不帶參數(shù)的構(gòu)造方法下實(shí)現(xiàn)依賴注入。但事實(shí)上,有些類只有帶有參數(shù)的構(gòu)造方法,于是上述的方法都不能再使用了。為了滿足這個(gè)功能,我們可以使用@Autowired 注解對(duì)構(gòu)造方法的參數(shù)進(jìn)行注入,例如,修改類YoungPerson來(lái)滿足這個(gè)功能。
database.driverName=com.mysql.jdbc.Driver database.url=jdbc:mysql://localhost:3306/my database.username=root database.password=root
@Autowired 和@Qualifier 注解,使其注入進(jìn)來(lái)。這里使用@Qualifier 是為了避免歧義性。當(dāng)然如果你的環(huán)境中不是有貓有狗,則可以完全不使用@Qualifier,而單單使用@Autowired就可以了。
二、??使用屬性文件
Java 開(kāi)發(fā)使用屬性文件已經(jīng)十分普遍,所以這里談?wù)勥@方面的內(nèi)容。在Spring Boot 中使用屬性文件,可以采用其默認(rèn)為我們準(zhǔn)備的application.properties,也可以使用自定義的配置文件。 應(yīng)該說(shuō)讀取配置文件的方法很多, 這里沒(méi)有必要面面俱到地介紹每一個(gè)細(xì)節(jié),只是介紹那些最常用的方法。
在SpringBoot項(xiàng)目的application.properties:
/** * @Version: 1.0.0 * @Author: Dragon_王 * @ClassName: DataBaseProperties * @Description: TODO描述 * @Date: 2024/1/16 16:07 */ @Component public class DataBaseProperties { @Value("${database.driverName}") private String driverName = null; @Value("${database.url}") private String url = null; private String username = null; private String password = null; public void setDriverName(String driverName) { System.out.println(driverName); this.driverName = driverName; } public void setUrl(String url) { System.out.println(url); this.url = url; } @Value("${database.username}") public void setUsername(String username) { System.out.println(username); this.username = username; } @Value("${database.password}") public void setPassword(String password) { System.out.println(password); this.password = password; } public String getDriverName() { return driverName; } public String getUrl() { return url; } public String getUsername() { return username; } public String getPassword() { return password; } }
創(chuàng)建個(gè)DataBaseProperties類:
/** * @Version: 1.0.0 * @Author: Dragon_王 * @ClassName: DataBaseProperties * @Description: TODO描述 * @Date: 2024/1/16 16:07 */ @Component public class DataBaseProperties { @Value("${database.driverName}") private String driverName = null; @Value("${database.url}") private String url = null; private String username = null; private String password = null; public void setDriverName(String driverName) { System.out.println(driverName); this.driverName = driverName; } public void setUrl(String url) { System.out.println(url); this.url = url; } @Value("${database.username}") public void setUsername(String username) { System.out.println(username); this.username = username; } @Value("${database.password}") public void setPassword(String password) { System.out.println(password); this.password = password; } public String getDriverName() { return driverName; } public String getUrl() { return url; } public String getUsername() { return username; } public String getPassword() { return password; } }
通過(guò)@Value注解, 使用${… }這樣的占位符讀取配置在屬性文件的內(nèi)容。這里的@Value 注解,既可以加載屬性, 也可以加在方法上。
這樣就能成功將屬性文件內(nèi)容成功注入了。但是我們可以使用過(guò)注解@ConfigurationProperties來(lái)簡(jiǎn)化一下,同樣能實(shí)現(xiàn)注入,如下:
/** * @Version: 1.0.0 * @Author: Dragon_王 * @ClassName: DataBaseProperties * @Description: TODO描述 * @Date: 2024/1/16 16:07 */ @Component @ConfigurationProperties("database") public class DataBaseProperties { private String driverName = null; private String url = null; private String username = null; private String password = null; public void setDriverName(String driverName) { System.out.println(driverName); this.driverName = driverName; } public void setUrl(String url) { System.out.println(url); this.url = url; } public void setUsername(String username) { System.out.println(username); this.username = username; } public void setPassword(String password) { System.out.println(password); this.password = password; } public String getDriverName() { return driverName; } public String getUrl() { return url; } public String getUsername() { return username; } public String getPassword() { return password; } }
這里@ConfigurationProperties注解內(nèi)的database將會(huì)與屬性名組成屬性的全限定名去配置文件里查找,如屬性driveName就會(huì)和database組成database.driverName去查找,這里注意一下不需要管字母的大小寫,不影響。
如果我不用默認(rèn)的application.properties文件怎么辦呢?現(xiàn)在我們重新創(chuàng)建一個(gè)jdbc.properties文件,將原配置文件內(nèi)容移入其中。
DataBaseProperties 類不變?nèi)缟?,啟?dòng)類配置如下:
@SpringBootApplication @PropertySource(value = {"classpath:jdbc.properties"},ignoreResourceNotFound = true) public class RestartApplication { public static void main(String[] args) { SpringApplication.run(RestartApplication.class, args); } }
value 可以配置多個(gè)配置文件。使用 classpath 前綴, 意味著去類文件路徑下找到屬性文件;ignoreResourceNotFound 則是是否忽略配置文件找不到的問(wèn)題。 ignoreResourceNotFound 的默認(rèn)值為false,也就是沒(méi)有找到屬性文件, 就會(huì)報(bào)錯(cuò);這里配置為true,也就是找不到就忽略掉,不會(huì)報(bào)錯(cuò)。通過(guò)運(yùn)行日志,可以看出成功注入。
總結(jié)
以上就是依賴注入和使用配置配置文件的講解,歡迎大家一起討論。
到此這篇關(guān)于詳解SpringBoot依賴注入和使用配置文件的文章就介紹到這了,更多相關(guān)SpringBoot依賴注入和配置文件內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot jackson 精度處理問(wèn)題解決
由于JavaScript處理的最大數(shù)值限制,較大的雪花ID在JS中容易溢出,為解決此問(wèn)題,可在SpringMVC或SpringBoot中使用@RequestBody注解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-10-10springboot2.0以上調(diào)度器配置線程池的實(shí)現(xiàn)
這篇文章主要介紹了springboot2.0以上調(diào)度器配置線程池的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12解決Mybatis中foreach嵌套使用if標(biāo)簽對(duì)象取值的問(wèn)題
這篇文章主要介紹了解決Mybatis中foreach嵌套使用if標(biāo)簽對(duì)象取值的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-02-02SpringBoot?Security使用MySQL實(shí)現(xiàn)驗(yàn)證與權(quán)限管理
安全管理是軟件系統(tǒng)必不可少的的功能。根據(jù)經(jīng)典的“墨菲定律”——凡是可能,總會(huì)發(fā)生。如果系統(tǒng)存在安全隱患,最終必然會(huì)出現(xiàn)問(wèn)題,這篇文章主要介紹了SpringBoot安全管理Spring?Security基本配置2022-11-11現(xiàn)代高效的java構(gòu)建工具gradle的快速入門
和Maven一樣,Gradle只是提供了構(gòu)建項(xiàng)目的一個(gè)框架,真正起作用的是Plugin,本文主要介紹了gradle入門,文中通過(guò)示例代碼介紹的非常詳細(xì),感興趣的小伙伴們可以參考一下2021-11-11springboot多文件上傳實(shí)現(xiàn)使用postman測(cè)試多文件上傳接口
這篇文章主要介紹了springboot多文件上傳實(shí)現(xiàn)使用postman測(cè)試多文件上傳接口,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08SpringBoot之spring.factories的使用方式
這篇文章主要介紹了SpringBoot之spring.factories的使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01springboot 整合EhCache實(shí)現(xiàn)單服務(wù)緩存的操作方法
這篇文章主要介紹了springboot 整合EhCache實(shí)現(xiàn)單服務(wù)緩存的操作方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-07-07