国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Spring高手之路——深入理解與實(shí)現(xiàn)IOC依賴查找與依賴注入

這篇具有很好參考價(jià)值的文章主要介紹了Spring高手之路——深入理解與實(shí)現(xiàn)IOC依賴查找與依賴注入。希望對大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

本文從xml開始講解,注解后面給出

1. 一個(gè)最基本的 IOC 依賴查找實(shí)例

??首先,我們需要明白什么是IOC(控制反轉(zhuǎn))和依賴查找。在Spring Framework中,控制反轉(zhuǎn)是一種設(shè)計(jì)模式,可以幫助我們解耦模塊間的關(guān)系,這樣我們就可以把注意力更多地集中在核心的業(yè)務(wù)邏輯上,而不是在對象的創(chuàng)建和管理上。

??依賴查找(Dependency Lookup)是一種技術(shù)手段,使得我們的對象可以從一個(gè)管理它們的容器(例如SpringApplicationContext)中獲得它所需要的資源或者依賴。這種查找過程通常是通過類型、名稱或者其他的標(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>

??然后,我們就可以在我們的主程序中通過SpringApplicationContext來獲取并使用這個(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)行的。

  1. 根據(jù)名稱查找

??在這種方式中,你需要知道你要查找的beanID,然后使用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)行查找操作。

  1. 根據(jù)類型查找

??在這種方式中,你不需要知道beanID,只需要知道它的類型。然后,你可以使用ApplicationContext.getBean(Class<T> requiredType)方法查找bean

??還是剛剛的Printer類和Ink類,還需要在XML配置文件中定義你的bean。以下面的方式定義InkPrinter類:

這里有多個(gè)相同類型的bean,如果按照類型注入,則編譯器會報(bào)錯(cuò),如下:

Spring高手之路——深入理解與實(shí)現(xiàn)IOC依賴查找與依賴注入

??我們得按照名稱注入,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è)類型為Inkbean,ink1ink2,他們的顏色屬性分別為“Black”和“Blue”。另外,我們還定義了一個(gè)類型為Printerbean,注入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...
}
Spring高手之路——深入理解與實(shí)現(xiàn)IOC依賴查找與依賴注入

??在這個(gè)例子中,context.getBean(Ink.class)會按照類型查找Inkbean。但是這里有多個(gè)Ink類型的bean(如本例所示),這種方式會拋出異常。對于這種情況,你可以使用context.getBeansOfType(Ink.class)方法獲取所有類型為Inkbean,然后自行決定如何使用這些bean。

2.2 依賴注入(Dependency Injection)

  1. 通過類型進(jìn)行依賴注入

還是和之前一樣的PrinterInk類:

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 注解,而是使用了 Javasetter 方法。你可以通過在 Spring 配置文件中使用 <bean><property> 標(biāo)簽來配置這種依賴關(guān)系

??在SpringXML配置文件中,我們可以如下配置:

	<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í),會查找idprinterbean對象,Printer對象會使用被Spring容器注入的Ink對象來打印信息。

Using ink...
Printing...

在這個(gè)例子中,依賴查找實(shí)際上應(yīng)用于兩個(gè)地方:

  1. 當(dāng)使用context.getBean("printer")時(shí),實(shí)際上正在查找名為"printer"的Bean對象,即Printer對象,這是一個(gè)明顯的依賴查找的例子。

  2. 當(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)了PrinterInk之間的解耦。

  1. 通過名稱進(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> 元素中,nameref 屬性有不同的作用:

??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 屬性指的是要注入的源 beanID。這個(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)行依賴注入的含義是:我們通過指定beanid(在這里是"blackInk"),來明確地告訴Spring我們想要注入哪一個(gè)bean。這是與通過類型進(jìn)行依賴注入的一個(gè)主要區(qū)別:通過類型進(jìn)行依賴注入時(shí),Spring會自動選擇一個(gè)與目標(biāo)屬性類型匹配的bean進(jìn)行注入,而不需要我們明確指定beanid。


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í)例,你需要做兩件事情:

  1. 創(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 配置文件的路徑。

  2. 調(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類獲取PrinterBean并調(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è)同類型的 beanSpring 會拋出一個(gè) NoUniqueBeanDefinitionException 異常,表示無法確定要注入哪個(gè) bean。為了避免這種錯(cuò)誤,可以使用以下方法之一:

  • 使用 @Qualifier 注解:

??@Qualifier 注解可以與 @Autowired 一起使用,通過指定 bean 的名稱來解決歧義。

??下面的例子中創(chuàng)建了兩個(gè)Ink Bean,分別是blueInkredInk。然后在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)方式上有所不同。

  1. 依賴查找(Dependency Lookup

??依賴查找是一種主動的依賴解決方式。在實(shí)際的編程中,我們需要顯式地調(diào)用API(如ApplicationContext.getBean())來獲取所需要的依賴。如果查找的對象里面還有其他對象,則會進(jìn)行隱式依賴查找,也就是說,查找的對象里面的依賴也會被Spring自動注入。

??依賴查找的一個(gè)缺點(diǎn)是它會使代碼和Spring框架緊密耦合。這意味著如果我們想要改變依賴解決方式或者更換其他的IoC容器,我們可能需要改動大量的代碼,比如改變要查找的bean名稱。

  1. 依賴注入(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查找對象。

如果看完本篇還有疑問,下篇: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)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • Spring高手之路3——揭秘Spring依賴注入和SpEL表達(dá)式

    Spring高手之路3——揭秘Spring依賴注入和SpEL表達(dá)式

    本篇會給大家舉出各種 Spring 屬性依賴注入的例子,方便大家理解。 我們在前面的文章中已經(jīng)使用過 XML 進(jìn)行 setter 方法的屬性注入了,下面讓我們再來回顧一下: 我們在前面的文章中也學(xué)習(xí)過如何在 bean 創(chuàng)建時(shí)通過編程方式設(shè)置屬性: 使用XML進(jìn)行setter方法注入 首先,我們需

    2024年02月08日
    瀏覽(32)
  • 深入理解Spring IOC

    深入理解Spring IOC

    ??歡迎來到 Spring 專欄:深入理解Spring IOC ??其他專欄:java面試?數(shù)據(jù)結(jié)構(gòu)?源碼解讀?故障分析?系統(tǒng)設(shè)計(jì) ??作者簡介:大家好,我是小徐?? ??博客首頁:CSDN主頁 小徐的博客 ??每日一句: 好學(xué)而不勤非真好學(xué)者 ?? 歡迎大家關(guān)注! ?? IOC 全稱控制反轉(zhuǎn),英文名為?

    2024年01月21日
    瀏覽(22)
  • Spring高手之路14——深入淺出:SPI機(jī)制在JDK與Spring Boot中的應(yīng)用

    Spring高手之路14——深入淺出:SPI機(jī)制在JDK與Spring Boot中的應(yīng)用

    ?? SPI ( Service Provider Interface ) 是一種服務(wù)發(fā)現(xiàn)機(jī)制,它允許第三方提供者為核心庫或主框架提供實(shí)現(xiàn)或擴(kuò)展。這種設(shè)計(jì)允許核心庫/框架在不修改自身代碼的情況下,通過第三方實(shí)現(xiàn)來增強(qiáng)功能。 JDK原生的SPI : 定義和發(fā)現(xiàn) : JDK 的 SPI 主要通過在 META-INF/services/ 目錄下放置

    2024年02月09日
    瀏覽(26)
  • Spring高手之路15——掌握Spring事件監(jiān)聽器的內(nèi)部邏輯與實(shí)現(xiàn)

    Spring高手之路15——掌握Spring事件監(jiān)聽器的內(nèi)部邏輯與實(shí)現(xiàn)

    在閱讀本文之前需要你已經(jīng)對事件監(jiān)聽器有了簡單的了解,或去閱讀前面的文章《 Spring高手之路7——事件機(jī)制與監(jiān)聽器的全面探索 》 ??在 Spring 中, ApplicationContext 可以形成一個(gè)層次結(jié)構(gòu),通常由主容器和多個(gè)子容器組成。一個(gè)常見的疑問是:當(dāng)一個(gè)事件在其中一個(gè)容器

    2024年02月06日
    瀏覽(28)
  • 【Spring教程十】Spring框架實(shí)戰(zhàn):全面深入詳解IOC/DI之--純注解開發(fā)模式下的依賴注入&&注解讀取properties配置文件

    【Spring教程十】Spring框架實(shí)戰(zhàn):全面深入詳解IOC/DI之--純注解開發(fā)模式下的依賴注入&&注解讀取properties配置文件

    歡迎大家回到《 Java教程之Spring30天快速入門》,本教程所有示例均基于Maven實(shí)現(xiàn),如果您對Maven還很陌生,請移步本人的博文《 如何在windows11下安裝Maven并配置以及 IDEA配置Maven環(huán)境》,本文的上一篇為《 全面深入詳解IOC/DI注解開發(fā)》 Spring為了使用注解簡化開發(fā),并沒有提供

    2024年02月04日
    瀏覽(25)
  • 后端進(jìn)階之路——深入理解Spring Security配置(二)

    后端進(jìn)階之路——深入理解Spring Security配置(二)

    「作者主頁」 :雪碧有白泡泡 「個(gè)人網(wǎng)站」 :雪碧的個(gè)人網(wǎng)站 「推薦專欄」 : ★ java一站式服務(wù) ★ ★ 前端炫酷代碼分享 ★ ★ uniapp-從構(gòu)建到提升 ★ ★ 從0到英雄,vue成神之路 ★ ★ 解決算法,一個(gè)專欄就夠了 ★ ★ 架構(gòu)咱們從0說 ★ ★ 數(shù)據(jù)流通的精妙之道★ ★后端進(jìn)

    2024年02月14日
    瀏覽(31)
  • 什么是Spring的IoC,依賴注入又該怎么實(shí)現(xiàn)?

    什么是Spring的IoC,依賴注入又該怎么實(shí)現(xiàn)?

    什么是IoC 所謂的控制反轉(zhuǎn)(Inversion of Control),縮寫為IoC,其實(shí)就是把對象的創(chuàng)建和對象之間的依賴關(guān)系處理,交給 Spring容器來管理,不用程序員自己創(chuàng)建和維護(hù)的一種技術(shù)。我們使用IoC的目的,主要是為了降低類之間的耦合。 IoC的底層原理 IoC的底層原理主要是基于xml解析+工

    2024年02月12日
    瀏覽(22)
  • Spring-2-深入理解Spring 注解依賴注入(DI):簡化Java應(yīng)用程序開發(fā)

    Spring-2-深入理解Spring 注解依賴注入(DI):簡化Java應(yīng)用程序開發(fā)

    ? 掌握純注解開發(fā)依賴注入(DI)模式 學(xué)習(xí)使用純注解進(jìn)行第三方Bean注入 問題導(dǎo)入 思考:如何使用注解方式將Bean對象注入到類中 1.1 使用@Autowired注解開啟自動裝配模式(按類型) 說明:不管是使用配置文件還是配置類,都必須進(jìn)行對應(yīng)的Spring注解包掃描才可以使用。@Autowired默

    2024年02月14日
    瀏覽(31)
  • Spring-1-深入理解Spring XML中的依賴注入(DI):簡化Java應(yīng)用程序開發(fā)

    Spring-1-深入理解Spring XML中的依賴注入(DI):簡化Java應(yīng)用程序開發(fā)

    前兩篇文章我們介紹了什么是Spring,以及Spring的一些核心概念,并且快速快發(fā)一個(gè)Spring項(xiàng)目,以及詳細(xì)講解IOC,今天詳細(xì)介紹一些DI(依賴注入) 能夠配置setter方式注入屬性值 能夠配置構(gòu)造方式注入屬性值 能夠理解什么是自動裝配 思考:向一個(gè)類中傳遞數(shù)據(jù)的方式有幾種?(給類

    2024年02月13日
    瀏覽(27)
  • Spring6-IoC(Inversion of Control)控制反轉(zhuǎn)和DI(Dependency Injection)依賴注入,手動實(shí)現(xiàn)IOC

    Java 反射機(jī)制是在運(yùn)行狀態(tài)中,對于任意一個(gè)類,都能夠知道這個(gè)類的所有屬性和方法;對于任意一個(gè)對象,都能夠調(diào)用它的任意方法和屬性;這種動態(tài)獲取信息以及動態(tài)調(diào)用對象方法的功能稱為 Java 語言的 反射機(jī)制 。簡單來說, 反射機(jī)制指的是程序在運(yùn)行時(shí)能夠獲取自身

    2024年02月09日
    瀏覽(43)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包