本文從
xml
開始講解,注解后面給出
1. 一個(gè)最基本的 IOC 依賴查找實(shí)例
??首先,我們需要明白什么是IOC
(控制反轉(zhuǎn))和依賴查找。在Spring Framework
中,控制反轉(zhuǎn)是一種設(shè)計(jì)模式,可以幫助我們解耦模塊間的關(guān)系,這樣我們就可以把注意力更多地集中在核心的業(yè)務(wù)邏輯上,而不是在對象的創(chuàng)建和管理上。
??依賴查找(Dependency Lookup
)是一種技術(shù)手段,使得我們的對象可以從一個(gè)管理它們的容器(例如Spring
的ApplicationContext
)中獲得它所需要的資源或者依賴。這種查找過程通常是通過類型、名稱或者其他的標(biāo)識進(jìn)行的。
??我們來看一個(gè)簡單的例子,通過這個(gè)例子,你可以理解在Spring
中如何實(shí)現(xiàn)IOC
依賴查找。這個(gè)例子的目標(biāo)是創(chuàng)建一個(gè)簡單的"Hello World
"應(yīng)用,通過依賴查找,我們將從Spring
的容器中獲取一個(gè)打印"Hello, World!
"的Bean
。
??第一步,我們需要創(chuàng)建一個(gè)簡單的Java
類,我們將其命名為"HelloWorld
":
public class HelloWorld {
public void sayHello() {
System.out.println("Hello, World!");
}
}
??接下來,我們需要在Spring
的配置文件中定義這個(gè)Bean
。這個(gè)配置文件通常命名為applicationContext.xml
,并放在項(xiàng)目的src/main/resources
目錄下:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 定義一個(gè)名為helloWorld的Bean -->
<bean id="helloWorld" class="com.example.demo.HelloWorld" />
</beans>
??然后,我們就可以在我們的主程序中通過Spring
的ApplicationContext
來獲取并使用這個(gè)Bean
:
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
public static void main(String[] args) {
// 創(chuàng)建一個(gè)ApplicationContext,加載spring配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
// 通過id從ApplicationContext中獲取Bean
HelloWorld obj = (HelloWorld) context.getBean("helloWorld");
// 調(diào)用方法
obj.sayHello();
}
}
可以在控制臺上看到"Hello, World!
"的輸出。
2. IOC 的兩種實(shí)現(xiàn)方式
??首先,讓我們開始了解什么是控制反轉(zhuǎn)(IoC
)。在傳統(tǒng)的程序設(shè)計(jì)中,我們常常會在需要的地方創(chuàng)建對象,然后使用這些對象來完成一些工作。這種方式存在一個(gè)問題,那就是對象之間的耦合度太高。如果我們要改變對象的創(chuàng)建方式或者使用不同的對象來完成同樣的工作,我們可能需要修改大量的代碼。
??為了解決這個(gè)問題,IoC
的概念被引入。在IoC
的設(shè)計(jì)模式中,對象的創(chuàng)建和維護(hù)不再由使用它們的代碼來控制,而是由一個(gè)容器來控制。這個(gè)容器會負(fù)責(zé)對象的創(chuàng)建、配置和生命周期管理,使得我們的代碼可以專注于核心的業(yè)務(wù)邏輯,而不需要關(guān)心對象是如何創(chuàng)建和管理的。這樣,當(dāng)我們需要改變對象的創(chuàng)建方式或者替換對象時(shí),我們只需要修改容器的配置,而不需要修改使用對象的代碼。
??接下來,讓我們來看看IoC
的兩種實(shí)現(xiàn)方式:依賴查找和依賴注入。
2.1 依賴查找(Dependency Lookup)
??在這種方式中,當(dāng)一個(gè)對象需要一個(gè)依賴(比如另一個(gè)對象)時(shí),它會主動從容器中查找這個(gè)依賴。通常,這個(gè)查找的過程是通過類型、名稱或者其他的標(biāo)識來進(jìn)行的。
- 根據(jù)名稱查找
??在這種方式中,你需要知道你要查找的bean
的ID
,然后使用ApplicationContext.getBean(String name)
方法查找bean
。
??例如,假設(shè)我們有一個(gè)Printer
類,它需要一個(gè)Ink
對象來打印信息。在沒有使用Spring
框架的情況下,Printer
可能會直接創(chuàng)建一個(gè)Ink
對象:
public class Printer {
private Ink ink = new Ink();
//...
}
??但是在Spring
框架中,我們可以將Ink
對象的創(chuàng)建交給Spring
的容器,然后在Printer
需要Ink
對象時(shí),從容器中查找獲取:
public class Ink {
private String color;
public void useInk() {
System.out.println("Using ink...");
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
public class Printer {
private Ink ink;
public void setInk(Ink ink) {
this.ink = ink;
}
public void print() {
ink.useInk();
System.out.println("Printing...");
}
}
<bean id="inkId" class="com.example.demo.Ink" />
<bean id="printer" class="com.example.demo.Printer">
<property name="ink" ref="inkId" />
</bean>
??在這個(gè)配置中,<bean>
元素定義了一個(gè)bean
,id
屬性給這個(gè)bean
定義了一個(gè)唯一的名字,class
屬性則指定了這個(gè)bean
的完全限定類名。這里“printer
”這個(gè)bean
依賴于"ink" bean
。當(dāng)Spring
容器初始化“printer
”這個(gè)bean
的時(shí)候,它需要查找到正確名稱的"ink" bean
,并將它們注入到“printer” bean
中。
??這里ref
按照名稱inkId
注入bean
,當(dāng)調(diào)用(Printer) context.getBean("printer")
時(shí),屬于按名稱依賴查找,Spring
檢查xml
配置根據(jù)名稱"printer
"和"inkId
"注入對象,關(guān)于注入,我們后面再說。
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Printer printer = (Printer) context.getBean("printer");
}
??依賴查找過程是隱含在Spring
框架的工作機(jī)制中的,開發(fā)者在編寫XML
配置的時(shí)候,并不需要顯式進(jìn)行查找操作。
- 根據(jù)類型查找
??在這種方式中,你不需要知道bean
的ID
,只需要知道它的類型。然后,你可以使用ApplicationContext.getBean(Class<T> requiredType)
方法查找bean
。
??還是剛剛的Printer
類和Ink
類,還需要在XML
配置文件中定義你的bean
。以下面的方式定義Ink
和Printer
類:
這里有多個(gè)相同類型的bean
,如果按照類型注入,則編譯器會報(bào)錯(cuò),如下:

??我們得按照名稱注入,autowire="byType"
會直接編譯報(bào)錯(cuò),就像有多個(gè)一樣類型的bean
,使用@Autowired
注解就會報(bào)錯(cuò)。所以這里在printer bean
里面指明ink bean
<bean id="ink1" class="com.example.demo.Ink">
<property name="color" value="Black"/>
</bean>
<bean id="ink2" class="com.example.demo.Ink">
<property name="color" value="Blue"/>
</bean>
<bean id="printer" class="com.example.demo.Printer">
<property name="ink" ref="ink1"/>
</bean>
??這里,我們定義了兩個(gè)類型為Ink
的bean
,ink1
和ink2
,他們的顏色屬性分別為“Black
”和“Blue
”。另外,我們還定義了一個(gè)類型為Printer
的bean
,注入ink1
依賴。
??然后在Java
代碼中,可以按照類型獲取Ink
類型的bean
:
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
// 按類型獲取bean
// 注意,這里存在多個(gè)Ink類型的bean,這行代碼會拋出異常
// Ink ink = context.getBean(Ink.class);
// 如果存在多個(gè)Ink類型的bean,你可以這樣獲取所有的Ink bean
Map<String, Ink> beans = context.getBeansOfType(Ink.class);
for (String id : beans.keySet()) {
System.out.println("Found ink with id: " + id);
Ink inkBean = beans.get(id);
// do something with inkBean...
}

??在這個(gè)例子中,context.getBean(Ink.class)
會按照類型查找Ink
的bean
。但是這里有多個(gè)Ink
類型的bean
(如本例所示),這種方式會拋出異常。對于這種情況,你可以使用context.getBeansOfType(Ink.class)
方法獲取所有類型為Ink
的bean
,然后自行決定如何使用這些bean
。
2.2 依賴注入(Dependency Injection)
- 通過類型進(jìn)行依賴注入
還是和之前一樣的Printer
和Ink
類:
public class Ink {
private String color;
public void useInk() {
System.out.println("Using ink...");
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
public class Printer {
private Ink ink;
public void setInk(Ink ink) {
this.ink = ink;
}
public void print() {
ink.useInk();
System.out.println("Printing...");
}
}
??在這個(gè)例子中,Printer
類有一個(gè)Ink
類型的屬性ink
,并且有一個(gè)setter
方法setInk
用于設(shè)置這個(gè)屬性的值。這就是我們通常說的依賴注入的setter
方法。
??這里我們沒有使用 Spring
的 @Autowired
注解,而是使用了 Java
的 setter
方法。你可以通過在 Spring
配置文件中使用 <bean>
和 <property>
標(biāo)簽來配置這種依賴關(guān)系
??在Spring
的XML
配置文件中,我們可以如下配置:
<bean id="ink" class="com.example.demo.Ink">
<property name="color" value="Blue"/>
</bean>
<bean id="printer" class="com.example.demo.Printer" autowire="byType">
</bean>
??注意,我們這里加上屬性autowire="byType"
,這樣Spring
就會自動根據(jù)類型來進(jìn)行依賴注入。雖然autowire="byType"
會啟用按類型的自動注入,但如果顯式配置了ink
屬性的值,Spring
會使用你的配置,而不是按類型自動注入。
??當(dāng)我們需要使用Printer
對象時(shí),我們可以從Spring
容器中獲取:
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Printer printer = (Printer) context.getBean("printer");
printer.print();
??當(dāng)我們調(diào)用context.getBean("printer")
時(shí),會查找id
為printer
的bean
對象,Printer
對象會使用被Spring
容器注入的Ink
對象來打印信息。
Using ink...
Printing...
在這個(gè)例子中,依賴查找實(shí)際上應(yīng)用于兩個(gè)地方:
-
當(dāng)使用
context.getBean("printer")
時(shí),實(shí)際上正在查找名為"printer
"的Bean
對象,即Printer
對象,這是一個(gè)明顯的依賴查找的例子。 -
當(dāng)
Spring
框架將Ink
對象注入Printer
對象時(shí),Printer
對象在內(nèi)部是通過依賴查找方式來找到Ink
對象的。在這種情況下,Spring
框架控制了這個(gè)查找過程,開發(fā)者并沒有直接進(jìn)行依賴查找。在這個(gè)過程中,Spring
框架根據(jù)配置文件中定義的依賴關(guān)系(這里是類型依賴),自動找到Ink
對象,并將它注入到Printer
對象中。這個(gè)查找過程是隱式的,對開發(fā)者是透明的。
所以總的來說,依賴查找可以發(fā)生在我們手動從容器中獲取Bean
的時(shí)候,也可以發(fā)生在Spring
自動注入依賴的過程中。
??通過這種方式,我們不再需要在Printer
類中直接創(chuàng)建Ink
對象,而是讓Spring
容器來管理Ink
對象的創(chuàng)建和注入,從而實(shí)現(xiàn)了Printer
和Ink
之間的解耦。
- 通過名稱進(jìn)行依賴注入
首先,我們修改Ink
類,添加構(gòu)造方法,使其可以有多種顏色:
public class Ink {
private String color;
public Ink(String color) {
this.color = color;
}
public void useInk() {
System.out.println("Using " + color + " ink...");
}
}
然后,在Spring的XML
配置文件中,我們定義兩種顏色的墨水,以及一個(gè)打印機(jī):
<bean id="blackInk" class="com.example.demo.Ink">
<constructor-arg value="black" />
</bean>
<bean id="blueInk" class="com.example.demo.Ink">
<constructor-arg value="blue" />
</bean>
<bean id="printer" class="com.example.demo.Printer">
<property name="ink" ref="blackInk" />
</bean>
??在這個(gè)配置中,我們有兩種顏色的墨水:黑色和藍(lán)色。在printer bean
的定義中,我們通過ref
屬性指定我們想要注入的墨水的id
為"blackInk
"。
在 <property>
元素中,name
和 ref
屬性有不同的作用:
??name
屬性指的是要注入的目標(biāo) bean
(這里是 “printer
”)的屬性名。這個(gè)屬性名應(yīng)該在目標(biāo) bean
的類(在這個(gè)例子中是 com.example.demo.Printer
類)中存在,且應(yīng)該有相應(yīng)的 setter
方法。比如,如果 name="ink"
,那么 com.example.demo.Printer
類中需要有一個(gè)名為 ink
的屬性,且有一個(gè)名為 setInk(...)
的方法。
??ref
屬性指的是要注入的源 bean
的 ID
。這個(gè) ID
應(yīng)該在同一份 Spring
配置文件中定義過。比如,如果 ref="blackInk"
,那么應(yīng)該在配置文件中有一個(gè) <bean id="blackInk" ... />
的定義。
??因此,當(dāng)你看到 <property name="ink" ref="blackInk" />
這樣的配置時(shí),你可以理解為:Spring
容器會找到 ID
為 “blackInk
” 的 bean
(在這個(gè)例子中是 com.example.demo.Ink
類的一個(gè)實(shí)例),然后調(diào)用 com.example.demo.Printer
類的 setInk(...)
方法,將這個(gè) Ink
實(shí)例注入到 Printer
實(shí)例的 ink
屬性中。
最后,我們可以在一個(gè)Java
類中獲取printer bean
并調(diào)用其print
方法:
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Printer printer = context.getBean("printer", Printer.class);
printer.print();
這將輸出以下結(jié)果:
Using black ink...
Printing...
??這里體現(xiàn)的通過名稱進(jìn)行依賴注入的含義是:我們通過指定bean
的id
(在這里是"blackInk
"),來明確地告訴Spring
我們想要注入哪一個(gè)bean
。這是與通過類型進(jìn)行依賴注入的一個(gè)主要區(qū)別:通過類型進(jìn)行依賴注入時(shí),Spring
會自動選擇一個(gè)與目標(biāo)屬性類型匹配的bean
進(jìn)行注入,而不需要我們明確指定bean
的id
。
3. 在三層架構(gòu)中的 service 層與 dao 層體會依賴查找與依賴注入的使用
在三層架構(gòu)中,我們通常會有以下三層:
- 表示層(
Presentation Layer
):與用戶進(jìn)行交互的層次,如前端頁面、命令行等,比如Controller
類里的邏輯。 - 業(yè)務(wù)邏輯層(
Business Logic Layer
),也叫作Service
層:實(shí)現(xiàn)業(yè)務(wù)邏輯的地方。 - 數(shù)據(jù)訪問層(
Data Access Layer
),也叫作DAO
層:直接操作數(shù)據(jù)庫或者調(diào)用API來獲取數(shù)據(jù)。
在本例中,我們將只關(guān)注 Service
層與 DAO
層,并且會使用到 Spring
框架。
首先,讓我們定義一個(gè)業(yè)務(wù)對象,比如一個(gè)用戶(User
):
public class User {
private String id;
private String name;
private String email;
// getters and setters omitted for brevity
}
??接著,我們定義 DAO
層。在 DAO
層中,我們通常會有一些方法來操作數(shù)據(jù)庫,如創(chuàng)建用戶,獲取用戶,更新用戶等。在本例中,我們簡化為一個(gè)接口:
public interface UserDao {
User getUser(String id);
}
??在實(shí)際項(xiàng)目中,我們可能有多種 UserDao
的實(shí)現(xiàn),比如 MySQLUserDao
、MongoDBUserDao
等。在這里,我們簡化為一個(gè)模擬實(shí)現(xiàn):
public class UserDaoImpl implements UserDao {
public User getUser(String id) {
// 為了簡化,我們在這里直接返回一個(gè)靜態(tài)的 User 對象
User user = new User();
user.setId(id);
user.setName("Test User");
user.setEmail("test@example.com");
return user;
}
}
??然后,我們定義 Service
層。在 Service
層中,我們會調(diào)用 DAO
層的方法來完成業(yè)務(wù)邏輯:
public class UserService {
private UserDao userDao;
public User getUser(String id) {
return userDao.getUser(id);
}
}
??這里的問題是,UserService
需要一個(gè) UserDao
的實(shí)例,但是 UserService
并不知道如何獲取 UserDao
的實(shí)例。這就是我們要解決的問題,我們可以使用依賴查找或者依賴注入來解決。
1.依賴查找:
我們可以修改 UserService
類,讓它從 Spring
容器中獲取 UserDao
的實(shí)例:
public class UserService {
private UserDao userDao;
public UserService() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
this.userDao = (UserDao) context.getBean("userDao");
}
public User getUser(String id) {
return userDao.getUser(id);
}
}
這樣,當(dāng)我們創(chuàng)建 UserService
的實(shí)例時(shí),它會自動從 Spring
容器中獲取 UserDao
的實(shí)例。
2.依賴注入:
??我們可以利用 Spring
框架的依賴注入特性,讓 Spring
容器自動將 UserDao
的實(shí)例注入到 UserService
中。為此,我們需要在 UserService
中添加一個(gè) setter
方法,并在 Spring
的配置文件中配置這個(gè)依賴關(guān)系:
UserService
類:
public class UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public User getUser(String id) {
return userDao.getUser(id);
}
}
Spring
配置文件(applicationContext.xml
):
<bean id="userDao" class="com.example.UserDaoImpl" />
<bean id="userService" class="com.example.UserService">
<property name="userDao" ref="userDao" />
</bean>
??這樣,當(dāng)我們從 Spring
容器中獲取 UserService
的實(shí)例時(shí),Spring
容器會自動將 UserDao
的實(shí)例注入到 UserService
中:
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) context.getBean("userService");
User user = userService.getUser("1");
如果你想在代碼中獲取這個(gè)bean
實(shí)例,你需要做兩件事情:
-
創(chuàng)建一個(gè)
Spring
應(yīng)用上下文(ApplicationContext
)對象。這個(gè)對象會讀取你的Spring
配置文件,然后根據(jù)配置文件的內(nèi)容創(chuàng)建和管理bean
實(shí)例。在你的代碼中,new ClassPathXmlApplicationContext("applicationContext.xml")
就是在創(chuàng)建一個(gè)Spring
應(yīng)用上下文對象。“applicationContext.xml
” 是你的Spring
配置文件的路徑。 -
調(diào)用
context.getBean("userDao")
來獲取 “userDao
” 這個(gè)bean
的實(shí)例。Spring
會根據(jù)你的配置創(chuàng)建一個(gè)UserDaoImpl
類的實(shí)例,并返回給你。
4. 使用注解時(shí),依賴查找在哪里查找?依賴注入在哪里注入?
當(dāng)我們?nèi)坑米⒔?,不?code>xml來實(shí)現(xiàn)的時(shí)候,會寫成如下形式:
Ink.java
@Component
public class Ink {
public void useInk() {
System.out.println("Using ink...");
}
}
Printer.java
@Component
public class Printer {
@Resource
private Ink ink;
public void print() {
ink.useInk();
System.out.println("Printing...");
}
}
??@Resource
注解告訴Spring
,請查找一個(gè)名為"ink
"的Bean
并注入到這個(gè)字段中。注意,@Resource
的名稱是大小寫敏感的,因此"ink
"和"Ink
"是兩個(gè)不同的名字。
??@Resource
注解的 name
屬性應(yīng)與 @Component
注解中指定的名稱相匹配,如果@Component
沒有指定name
屬性,那么bean
的名稱默認(rèn)是類名的小寫形式。
??如果省略了@Resource
注解的name
屬性,則默認(rèn)的注入規(guī)則是根據(jù)字段的名稱進(jìn)行推斷,并嘗試注入同名的資源,如果Ink
類上注解為@Component("inkComponent")
,那這個(gè)bean
的名稱就是inkComponent
,只能寫成如下形式
@Resource(name = "inkComponent")
private Ink ink;
或者
@Resource
private Ink inkComponent;
??為了使這些注解起作用,我們需要開啟注解掃描@ComponentScan("com.example")
:
@Configuration
@ComponentScan("com.example")
public class AppConfig {
// 可以在這里定義其他的配置或進(jìn)行其他的注解配置
}
??然后,你可以在你的Java
代碼中通過AnnotationConfigApplicationContext
類獲取Printer
的Bean
并調(diào)用它的print
方法:
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
Printer printer = context.getBean(Printer.class);
printer.print();
輸出以下結(jié)果:
Using ink...
Printing...
??這個(gè)例子中,我們使用了完全基于Java
的配置,沒有使用任何XML
。通過@Configuration
和@ComponentScan
注解,我們告訴了Spring
在哪里找到我們的Bean
。
這個(gè)例子中
-
context.getBean(Printer.class);
按照類型進(jìn)行了依賴查找 -
@Resource
按照名稱進(jìn)行了依賴注入 -
@Resource
在按名稱依賴注入的時(shí)候會隱式按名稱依賴查找
??有沒有人發(fā)現(xiàn)這次Printer
不用強(qiáng)制轉(zhuǎn)換了?之前還是(Printer) context.getBean("printer");
呢。這里把類型都傳進(jìn)去了,ApplicationContext
查找的時(shí)候當(dāng)然按照這個(gè)類型查找啊
??依賴注入中的按名稱和按類型兩種方式,主要體現(xiàn)在注入時(shí)如何選擇合適的bean
進(jìn)行注入。
-
按名稱進(jìn)行依賴注入: 是指在進(jìn)行依賴注入時(shí),根據(jù)名稱來查找合適的
bean
。比如在Java
代碼中使用@Resource(name = "beanName")
。這種方式的優(yōu)點(diǎn)是明確指定了注入的bean
。 -
按類型進(jìn)行依賴注入: 是指在進(jìn)行依賴注入時(shí),根據(jù)類型來查找合適的
bean
。比如在Java
代碼中使用@Autowired
。缺點(diǎn)是當(dāng)有多個(gè)相同類型的bean
存在時(shí),可能會導(dǎo)致選擇錯(cuò)誤的bean
。
??至于context.getBean()
方法,這是依賴查找的方式,而不是依賴注入。它也分為按名稱和按類型兩種方式,與依賴注入的按名稱和按類型是類似的。
-
使用
context.getBean("beanName")
,是按名稱進(jìn)行依賴查找。 -
使用
context.getBean(ClassType)
或者context.getBean("beanName", ClassType)
,是按類型進(jìn)行依賴查找。如果有多個(gè)同類型的bean
,則會拋出異常。
??對于依賴查找,除了可以使用
context.getBean
進(jìn)行顯示的查找外,Spring
容器在實(shí)例化Bean
的過程中也會進(jìn)行隱式的依賴查找。此外,Spring
還支持使用@Autowired
、@Resource
、@Inject
等注解在依賴注入之前隱式依賴查找。
??對于依賴注入,通過XML
配置文件中的<property>
和<constructor-arg>
進(jìn)行屬性和構(gòu)造器注入,以及通過@Autowired
和@Resource
注解進(jìn)行注入。但還要注意,Spring
還支持通過@Value
注解對簡單類型的值進(jìn)行注入,以及通過@Qualifier
注解對同一類型的不同實(shí)例進(jìn)行精確選擇。
5. @Autowired 進(jìn)行自動注入時(shí),如果存在多個(gè)同類型的 bean該如何解決?
??在使用 @Autowired
進(jìn)行自動裝配時(shí),如果存在多個(gè)同類型的 bean
,Spring
會拋出一個(gè) NoUniqueBeanDefinitionException
異常,表示無法確定要注入哪個(gè) bean
。為了避免這種錯(cuò)誤,可以使用以下方法之一:
- 使用 @Qualifier 注解:
??@Qualifier
注解可以與 @Autowired
一起使用,通過指定 bean
的名稱來解決歧義。
??下面的例子中創(chuàng)建了兩個(gè)Ink Bean
,分別是blueInk
和redInk
。然后在Printer
類中,我們通過@Qualifier
注解指定需要注入的Bean
的名稱。
@Component
public class BlueInk implements Ink {
//...
}
@Component
public class RedInk implements Ink {
//...
}
@Component
public class Printer {
@Autowired
@Qualifier("blueInk")
private Ink ink;
public void setInk(Ink ink) {
this.ink = ink;
}
public void print() {
ink.useInk();
System.out.println("Printing...");
}
}
注意:@Autowired
和@Qualifier("blueInk")
寫在setInk
方法上和寫在ink
字段定義上是一樣的。
- 使用 @Primary 注解:
??可以使用@Primary
注解來標(biāo)識優(yōu)先注入的Bean
。如果在容器中存在多個(gè)同類型的Bean
,Spring
會優(yōu)先注入被@Primary
注解標(biāo)記的Bean
。
@Component
@Primary
public class BlueInk implements Ink {
//...
}
@Component
public class RedInk implements Ink {
//...
}
@Component
public class Printer {
private Ink ink;
@Autowired
public void setInk(Ink ink) {
this.ink = ink;
}
public void print() {
ink.useInk();
System.out.println("Printing...");
}
}
??在上面的例子中,我們使用@Primary
注解標(biāo)記了BlueInk
,所以在注入Ink
類型的Bean
時(shí),Spring
會優(yōu)先選擇BlueInk
。
??需要注意的是,@Qualifier
注解的優(yōu)先級高于@Primary
注解,也就是說如果同時(shí)使用了@Qualifier
和@Primary
注解,Spring
會優(yōu)先考慮@Qualifier
注解。
6. 【面試題】依賴查找與依賴注入的對比
??依賴查找(Dependency Lookup
)和依賴注入(Dependency Injection
)都是在控制反轉(zhuǎn)(Inversion of Control
,IoC
)的背景下,解決對象間依賴關(guān)系的方式,但它們在實(shí)現(xiàn)方式上有所不同。
- 依賴查找(
Dependency Lookup
)
??依賴查找是一種主動的依賴解決方式。在實(shí)際的編程中,我們需要顯式地調(diào)用API
(如ApplicationContext.getBean()
)來獲取所需要的依賴。如果查找的對象里面還有其他對象,則會進(jìn)行隱式依賴查找,也就是說,查找的對象里面的依賴也會被Spring
自動注入。
??依賴查找的一個(gè)缺點(diǎn)是它會使代碼和Spring
框架緊密耦合。這意味著如果我們想要改變依賴解決方式或者更換其他的IoC
容器,我們可能需要改動大量的代碼,比如改變要查找的bean
名稱。
- 依賴注入(
Dependency Injection
)
??依賴注入是一種被動的依賴解決方式。與依賴查找相比,依賴注入不需要我們顯式地調(diào)用API
,而是由Spring
容器負(fù)責(zé)將依賴注入到需要它的Bean
中。
??依賴注入的主要優(yōu)點(diǎn)是它能夠減少代碼和Spring
框架的耦合度,使我們的代碼更加易于測試和維護(hù)。同時(shí),它也支持更加復(fù)雜的依賴關(guān)系,包括循環(huán)依賴和自動裝配等。
??依賴注入通常分為基于構(gòu)造器的依賴注入和基于setter的依賴注入,也可以通過使用注解(如@Autowired
, @Resource
等)來實(shí)現(xiàn)。
??總結(jié)來說,依賴查找和依賴注入各有優(yōu)缺點(diǎn)。選擇使用哪種方式,主要取決于實(shí)際的需求和場景。在實(shí)際的開發(fā)中,我們通常會更傾向于使用依賴注入,因?yàn)樗軌蛱峁└叩撵`活性和可維護(hù)性。比如使用對象時(shí)全都是加上@Autowired
或@Resource
注解后直接使用,調(diào)用被注入對象的方法,不會再去用API
查找對象。文章來源:http://www.zghlxwxcb.cn/news/detail-462335.html
如果看完本篇還有疑問,下篇:Spring高手之路——深入理解注解驅(qū)動配置與XML配置的融合與區(qū)別文章來源地址http://www.zghlxwxcb.cn/news/detail-462335.html
歡迎一鍵三連~
有問題請留言,大家一起探討學(xué)習(xí)
----------------------Talk is cheap, show me the code-----------------------
到了這里,關(guān)于Spring高手之路——深入理解與實(shí)現(xiàn)IOC依賴查找與依賴注入的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!