5. Spring IoCDI ★ ?

5. Spring IoC&DI

  • 1. IoC & DI ??
    • 1.1 Spring 是什么?★ (Spring 是包含了眾多?具?法的 IoC 容器)
      • 1.1.1 什么是容器?
      • 1.1.2 什么是 IoC?★ (IoC: Inversion of Control (控制反轉))
        • 總結:把傳統模式 被依賴對象創建依賴對象 改成 交給容器創建。用的時候注入即可。
    • 1.2 IoC 介紹
      • 需求: 造?輛?
      • 1.2.1 傳統程序開發 ★
        • car-framework-bottom-tire
      • 1.2.2 問題分析
      • 1.2.3 解決?案
      • 1.2.4 IoC程序開發 ★
      • 1.2.5 IoC 優勢
        • Tire -> Bottom -> Framework -> Car
          • 第?,資源集中管理,實現資源的可配置和易管理。
          • 第?,降低了使?資源雙?的依賴程度,也就是我們說的耦合度。
    • 1.3 DI 介紹
  • 2. IoC & DI 使?
        • 以下是一個基于Spring Boot的IoC和DI的實戰代碼案例:
        • ?標: 把BookDao, BookService 交給Spring管理, 完成Controller層, Service層, Dao層的解耦
  • 3. IoC 詳解
    • 3.1 Bean的存儲
      • 3.1.1 @Controller(控制器存儲)
        • 如何觀察這個對象已經存在Spring容器當中了呢?
          • 接下來我們學習如何從Spring容器中獲取對象 ★
            • ApplicationContext.getBean(手動獲取bean對象)
            • XML的方式注入的獲取 ClassPathXmlApplicationContext
          • 獲取bean對象的其他?式(beanFactory)
        • ApplicationContext VS BeanFactory(常??試題)
      • 3.1.2 @Service(服務存儲)
      • 3.1.3 @Repository(倉庫存儲)
      • 3.1.4 @Component(組件存儲)
      • 3.1.5 @Configuration(配置存儲)
    • 3.2 為什么要這么多類注解?
    • 3.3 ?法注解 @Bean
      • 3.3.1 ?法注解要配合類注解使?
      • 3.3.2 定義多個對象
      • 3.3.3 重命名 Bean
    • 3.4 掃描路徑
  • 4. DI 詳解
    • 4.1 屬性注?
    • 4.2 構造?法注?
    • 4.3 Setter 注?
    • 4.4 三種注?優缺點分析
    • 4.5 @Autowired存在問題
      • 當同?類型存在多個bean時, 使?@Autowired會存在問題
        • 使?@Primary注解:當存在多個相同類型的Bean注?時,加上@Primary注解,來確定默認的實現.
        • 使?@Qualifier注解:指定當前要注?的bean對象。 在@Qualifier的value屬性中,指定注?的bean的名稱。
        • 使?@Resource注解:是按照bean的名稱進?注?。通過name屬性指定要注?的bean的名稱。
        • @Autowird 與 @Resource的區別
  • 5. 總結
    • Spring, Spring Boot 和Spring MVC的關系以及區別

本節?標

  1. 了解Spring,Spring MVC, Spring Boot 之間的聯系及區別
  2. 掌握IoC&DI的概念以及寫法

1. IoC & DI ??

在前?的章節中, 我們學習了Spring Boot和Spring MVC的開發, 可以完成?些基本功能的開發了, 但是什么是Spring呢? Spring, Spring Boot 和SpringMVC?有什么關系呢? 咱們還是帶著問題去學習.

我們先看什么是Spring

1.1 Spring 是什么?★ (Spring 是包含了眾多?具?法的 IoC 容器)

通過前?的學習, 我們知道了Spring是?個開源框架, 他讓我們的開發更加簡單. 他?持?泛的應?場景, 有著活躍?龐?的社區, 這也是Spring能夠?久不衰的原因.但是這個概念相對來說, 還是?較抽象.
我們??句更具體的話來概括Spring, 那就是: Spring 是包含了眾多?具?法的 IoC 容器
那問題來了,什么是容器?什么是 IoC 容器?接下來我們?起來看

1.1.1 什么是容器?

容器是?來容納某種物品的(基本)裝置。?來?:百度百科
?活中的?杯, 垃圾桶, 冰箱等等這些都是容器.
我們想想,之前課程我們接觸的容器有哪些?

  • List/Map -> 數據存儲容器
  • Tomcat -> Web 容器

1.1.2 什么是 IoC?★ (IoC: Inversion of Control (控制反轉))

IoC 是Spring的核?思想, 也是常?的?試題, 那什么是IoC呢?
其實IoC我們在前?已經使?了, 我們在前?講到, 在類上?添加 @RestController 和 @Controller 注解, 就是把這個對象交給Spring管理, Spring 框架啟動時就會加載該類. 把對象交給Spring管理, 就是IoC思想

IoC: Inversion of Control (控制反轉), 也就是說 Spring 是?個"控制反轉"的容器.
什么是控制反轉呢? 也就是控制權反轉. 什么的控制權發?了反轉? 獲得依賴對象的過程被反轉了也就是說, 當需要某個對象時, 傳統開發模式中需要??通過 new 創建對象, 現在不需要再進?創建, 把創建對象的任務交給容器, 程序中只需要依賴注? (Dependency Injection,DI)就可以了.
這個容器稱為:IoC容器. Spring是?個IoC容器, 所以有時Spring 也稱為Spring 容器.

總結:把傳統模式 被依賴對象創建依賴對象 改成 交給容器創建。用的時候注入即可。

1.2 IoC 介紹

接下來我們通過案例來了解?下什么是IoC

需求: 造?輛?

1.2.1 傳統程序開發 ★

我們的實現思路是這樣的:
先設計輪?(Tire),然后根據輪?的??設計底盤(Bottom),接著根據底盤設計??(Framework),最后根據??設計好整個汽?(Car)。這?就出現了?個"依賴"關系:汽?依賴??,??依賴底盤,底盤依賴輪?

car-framework-bottom-tire

在這里插入圖片描述

最終程序的實現代碼如下

public class NewCarExample {public static void main(String[] args) {ar ar = ew ar );car.run();}/*** 汽?對象*/static class Car {private Framework framework;public Car() {framework = new Framework();System.out.println("Car init....");}public void run(){System.out.println("Car run...");}}/*** ??類*/static class Framework {private Bottom bottom;public Framework() {bottom = new Bottom();System.out.println("Framework init...");}}/*** 底盤類*/static class Bottom {private Tire tire;public Bottom() {this.tire = new Tire();System.out.println("Bottom init...");}}/*** 輪胎類*/static class Tire {// 尺?private int size;public Tire(){this.size = 17;System.out.println("輪胎尺?:" + size);}}
}

1.2.2 問題分析

這樣的設計看起來沒問題,但是可維護性卻很低.
接下來需求有了變更: 隨著對的?的需求量越來越?, 個性化需求也會越來越多,我們需要加?多種尺?的輪胎.
那這個時候就要對上?的程序進?修改了,修改后的代碼如下所?:
在這里插入圖片描述
修改之后, 其他調?程序也會報錯, 我們需要繼續修改
在這里插入圖片描述
在這里插入圖片描述
從以上代碼可以看出,以上程序的問題是:當最底層代碼改動之后,整個調?鏈上的所有代碼都需要修改.
程序的耦合度?常?(修改?處代碼, 影響其他處的代碼修改)

1.2.3 解決?案

在上?的程序中, 我們是根據輪?的尺?設計的底盤,輪?的尺??改,底盤的設計就得修改. 同樣因為我們是根據底盤設計的??,那么??也得改,同理汽?設計也得改, 也就是整個設計?乎都得改

我們嘗試換?種思路, 我們先設計汽?的?概樣?,然后根據汽?的樣?來設計??,根據??來設計底盤,最后根據底盤來設計輪?. 這時候,依賴關系就倒置過來了:輪?依賴底盤, 底盤依賴??,??依賴汽?

這就類似我們打造?輛完整的汽?, 如果所有的配件都是??造,那么當客?需求發?改變的時候,?如輪胎的尺?不再是原來的尺?了,那我們要??動?來改了,但如果我們是把輪胎外包出去,那么即使是輪胎的尺?發?變變了,我們只需要向代理??下訂單就?了,我們??是不需要出?的.

在這里插入圖片描述
如何來實現呢:
我們可以嘗試不在每個類中??創建下級類,如果??創建下級類就會出現當下級類發?改變操作,??也要跟著修改.
此時,我們只需要將原來由??創建的下級類,改為傳遞的?式(也就是注?的?式),因為我們不需要在當前類中創建下級類了,所以下級類即使發?變化(創建或減少參數),當前類本?也?需修改任何代碼,這樣就完成了程序的解耦.

1.2.4 IoC程序開發 ★

基于以上思路,我們把調?汽?的程序?例改造?下,把創建?類的?式,改為注?傳遞的?式.
具體實現代碼如下:

public class Main {public static void main(String[] args) {Tire tire = new Tire(19,"red");Bottom bottom = new Bottom(tire);Framework framework = new Framework(bottom);Car car = new Car(framework);car.run();}
}
public class Bottom {private Tire tire;public Bottom(Tire tire) {this.tire = tire;System.out.println("bottom init...");}}
public class Car {private Framework framework;public Car(Framework framework) {this.framework = framework;System.out.println("car init...");}public void run() {System.out.println("car run...");}
}
public class Framework {private Bottom bottom;public Framework(Bottom bottom) {this.bottom = bottom;System.out.println("framework init...");}
}
public class Tire {private int size;private String color;public Tire(int size,String color) {System.out.println("tire size:"+size+",color:"+color);}
}

代碼經過以上調整,?論底層類如何變化,整個調?鏈是不?做任何改變的,這樣就完成了代碼之間的解耦,從?實現了更加靈活、通?的程序設計了。

1.2.5 IoC 優勢

Tire -> Bottom -> Framework -> Car

在傳統的代碼中對象創建順序是:Car -> Framework -> Bottom -> Tire
改進之后解耦的代碼的對象創建順序是:Tire -> Bottom -> Framework -> Car


我們發現了?個規律,通?程序的實現代碼,類的創建順序是反的,傳統代碼是 Car 控制并創建了Framework,Framework 創建并創建了 Bottom,依次往下,?改進之后的控制權發?的反轉,不再是使??對象創建并控制依賴對象了,?是把依賴對象注?將當前對象中,依賴對象的控制權不再由當前類控制了.
這樣的話, 即使依賴類發?任何改變,當前類都是不受影響的,這就是典型的控制反轉,也就是 IoC 的實現思想。

學到這?, 我們?概就知道了什么是控制反轉了, 那什么是控制反轉容器呢, 也就是IoC容器
在這里插入圖片描述
這部分代碼, 就是IoC容器做的?作.
從上?也可以看出來, IoC容器具備以下優點:
資源不由使?資源的雙?管理,?由不使?資源的第三?管理,這可以帶來很多好處。

第?,資源集中管理,實現資源的可配置和易管理。
第?,降低了使?資源雙?的依賴程度,也就是我們說的耦合度。
  1. 資源集中管理: IoC容器會幫我們管理?些資源(對象等), 我們需要使?時, 只需要從IoC容器中去取就可以了
  2. 我們在創建實例的時候不需要了解其中的細節, 降低了使?資源雙?的依賴程度, 也就是耦合度.Spring 就是?種IoC容器, 幫助我們來做了這些資源管理.

1.3 DI 介紹

上?學習了IoC, 什么是DI呢?

DI: Dependency Injection(依賴注?)
容器在運?期間, 動態的為應?程序提供運?時所依賴的資源,稱之為依賴注?。

程序運?時需要某個資源,此時容器就為其提供這個資源
從這點來看, 依賴注?(DI)和控制反轉(IoC)是從不同的?度的描述的同?件事情,就是指通過引? IoC 容器,利?依賴關系注?的?式,實現對象之間的解耦。

上述代碼中, 是通過構造函數的?式, 把依賴對象注?到需要使?的對象中的
在這里插入圖片描述
IoC 是?種思想,也是"?標", ?思想只是?種指導原則,最終還是要有可?的落地?案,? DI 就屬于具體的實現。所以也可以說, DI 是IoC的?種實現.

?如說我今天?情?較好,吃?頓好的犒勞犒勞??,那么"吃?頓好的"是思想和?標(是IoC),但最后我是吃海底撈還是楊國福?這就是具體的實現,就是 DI。

2. IoC & DI 使?

對IoC和DI有了初步的了解, 我們接下來具體學習Spring IoC和DI的代碼實現.
依然是先使?, 再學習

既然 Spring 是?個 IoC(控制反轉)容器,作為容器, 那么它就具備兩個最基礎的功能:
? 存
? 取
Spring 容器 管理的主要是對象, 這些對象, 我們稱之為"Bean". 我們把這些對象交由Spring管理, 由Spring來負責對象的創建和銷毀. 我們程序只需要告訴Spring, 哪些需要存, 以及如何從Spring中取出對象

當涉及到Spring Boot的IoC(控制反轉)和DI(依賴注入)時,一個常見的實戰案例是創建一個簡單的RESTful API。

以下是一個基于Spring Boot的IoC和DI的實戰代碼案例:

首先,你需要創建一個簡單的Maven項目,并添加Spring Boot的依賴。然后創建一個Controller類來處理RESTful請求,并創建一個Service類來處理業務邏輯。最后,通過依賴注入將Service類注入到Controller類中。

// Service類
@Service
public class UserService {public String getUserInfo() {return "User information";}
}// Controller類
@RestController
public class UserController {private final UserService userService;@Autowiredpublic UserController(UserService userService) {this.userService = userService;}@GetMapping("/user")public String getUser() {return userService.getUserInfo();}
}// Spring Boot應用入口類
@SpringBootApplication
public class DemoApplication {public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args);}
}

在這個案例中,UserService類使用了@Service注解來告訴Spring它是一個Bean,而UserController類使用了@Autowired注解來告訴Spring需要將UserService注入到它的構造函數中。當Spring Boot應用啟動時,它會自動掃描并創建這些Bean,并且處理它們之間的依賴關系。

通過這個案例,你可以看到IoC和DI是如何在Spring Boot應用中發揮作用的。當你發送GET請求到/user時,UserController會調用UserService來獲取用戶信息,并返回給客戶端。這展示了IoC和DI如何幫助我們編寫松耦合、可測試的代碼。

?標: 把BookDao, BookService 交給Spring管理, 完成Controller層, Service層, Dao層的解耦

步驟:

  1. Service層及Dao層的實現類,交給Spring管理: 使?注解: @Component
  2. 在Controller層 和Service層 注?運?時依賴的對象: 使?注解 @Autowired實現:
  3. 把BookDao 交給Spring管理, 由Spring來管理對象

3. IoC 詳解

通過上?的案例, 我們已經知道了Spring IoC 和DI的基本操作, 接下來我們來系統的學習Spring IoC和DI的操作.
前?我們提到IoC控制反轉,就是將對象的控制權交給Spring的IOC容器,由IOC容器創建及管理對象。也就是bean的存儲

3.1 Bean的存儲

在之前的??案例中,要把某個對象交給IOC容器管理,需要在類上添加?個注解: @Component ?Spring框架為了更好的服務web應?程序, 提供了更豐富的注解.

共有兩類注解類型可以實現:

  1. 類注解:@Controller、@Service、@Repository@Component、@Configuration.
  2. ?法注解:@Bean.
    接下來我們分別來看

3.1.1 @Controller(控制器存儲)

使? @Controller 存儲 bean 的代碼如下所?:

@Controller // 將對象存儲到 Spring 中
public class UserController {public void sayHi(){System.out.println("hi,UserController...");}
}
如何觀察這個對象已經存在Spring容器當中了呢?
接下來我們學習如何從Spring容器中獲取對象 ★

在Spring框架中,你可以使用注解來實現對象的注入和獲取。首先,你需要在你的類中使用@Component或者其他相關的注解來標識這個類是一個Spring容器管理的Bean。然后,你可以使用@Autowired注解來實現對象的注入,或者使用@Resource注解來指定注入的對象。

舉個例子,假設你有一個名為UserService的類,你可以這樣標識它是一個Bean:

@Component
public class UserService {// ...
}

然后,在另一個類中,你可以使用@Autowired注解來注入UserService

@Component
public class UserController {@Autowiredprivate UserService userService;// ...
}

這樣,Spring容器會在啟動時自動將UserService注入到UserController中。

如果你想手動從Spring容器中獲取對象,你可以使用@Autowired或者@Resource注解來注入ApplicationContext,然后通過getBean方法來獲取對象。示例代碼如下:

ApplicationContext.getBean(手動獲取bean對象)
@Component
public class SomeOtherClass {@Autowiredprivate ApplicationContext context;public void doSomething() {UserService userService = context.getBean(UserService.class);// 使用userService對象進行操作}
}

這樣,你就可以通過注解的方式實現對象的注入和獲取。希望這能幫到你!

XML的方式注入的獲取 ClassPathXmlApplicationContext

在Spring容器中獲取對象通常需要使用ApplicationContext接口。以下是一個簡單的示例代碼,演示如何從Spring容器中獲取對象:

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class Main {public static void main(String[] args) {// 加載Spring配置文件ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");// 從容器中獲取對象YourObject yourObject = (YourObject) context.getBean("yourObjectBeanName");// 使用獲取到的對象yourObject.doSomething();}
}

在上面的示例中,假設你有一個名為"YourObject"的類,并且在Spring配置文件"applicationContext.xml"中定義了該類的bean。通過調用context.getBean("yourObjectBeanName")方法,你可以從Spring容器中獲取到該對象的實例。

獲取bean對象的其他?式(beanFactory)

上述代碼是根據類型來查找對象, 如果Spring容器中, 同?個類型存在多個bean的話, 怎么來獲取呢?
ApplicationContext 也提供了其他獲取bean的?式, ApplicationContext 獲取bean對象的功能, 是?類BeanFactory提供的功能

public interface BeanFactory {//以上省略...// 1. 根據bean名稱獲取beanObject getBean(String var1) throws BeansException;// 2. 根據bean名稱和類型獲取bean<T> T getBean(String var1, Class<T> var2) throws BeansException;// 3. 按bean名稱和構造函數參數動態創建bean,只適?于具有原型(prototype)作?域的beanObject getBean(String var1, Object... var2) throws BeansException;// 4. 根據類型獲取bean<T> T getBean(Class<T> var1) throws BeansException;// 5. 按bean類型和構造函數參數動態創建bean, 只適?于具有原型(prototype)作?域的bean<T> T getBean(Class<T> var1, Object... var2) throws BeansException;//以下省略...
}

常?的是上述1,2,4種, 這三種?式,獲取到的bean是?樣的
其中1,2種都涉及到根據名稱來獲取對象.

bean的名稱是什么呢?
Spring bean是Spring框架在運?時管理的對象, Spring會給管理的對象起?個名字.
?如學校管理學?, 會給每個學?分配?個學號, 根據學號, 就可以找到對應的學?.
Spring也是如此, 給每個對象起?個名字, 根據Bean的名稱(BeanId)就可以獲取到對應的對象.

Bean 命名約定
我們看下官??檔的說明: Bean Overview :: Spring Framework

程序開發?員不需要為bean指定名稱(BeanId), 如果沒有顯式的提供名稱(BeanId),Spring容器將為該bean?成唯?的名稱.
命名約定使?Java標準約定作為實例字段名. 也就是說,bean名稱以?寫字?開頭,然后使?駝峰式??寫

?如
類名: UserController, Bean的名稱為: userController
類名: AccountManager, Bean的名稱為: accountManager
類名: AccountService, Bean的名稱為: accountService

也有?些特殊情況, 當有多個字符并且第?個和第?個字符都是?寫時, 將保留原始的??寫. 這些規則與java.beans.Introspector.decapitalize (Spring在這?使?的)定義的規則相同.
?如
類名: UController, Bean的名稱為: UController
類名: AManager, Bean的名稱為: AManager

根據這個命名規則, 我們來獲取Bean

@SpringBootApplication
public class SpringIocDemoApplication {public static void main(String[] args) {//獲取Spring上下?對象ApplicationContext context = SpringApplication.run(SpringIocDemoApplicatio//從Spring上下?中獲取對象//根據bean類型, 從Spring上下?中獲取對象UserController userController1 = context.getBean(UserController.class);//根據bean名稱, 從Spring上下?中獲取對象UserController userController2 = (UserController) context.getBean("userCon//根據bean類型+名稱, 從Spring上下?中獲取對象UserController userController3 = context.getBean("userController",UserContSystem.out.println(userController1);System.out.println(userController2);System.out.println(userController3);}
}

在這里插入圖片描述
地址?樣, 說明對象是?個

獲取bean對象, 是?類BeanFactory提供的功能

ApplicationContext VS BeanFactory(常??試題)
  • 繼承關系和功能??來說:Spring 容器有兩個頂級的接?:BeanFactory 和 ApplicationContext。其中 BeanFactory 提供了基礎的訪問容器的能?,?ApplicationContext 屬于 BeanFactory 的?類,它除了繼承了 BeanFactory 的所有功能之外,它還擁有獨特的特性,還添加了對國際化?持、資源訪問?持、以及事件傳播等??的?持.
  • 從性能??來說:ApplicationContext 是?次性加載并初始化所有的 Bean 對象,?BeanFactory 是需要那個才去加載那個,因此更加輕量. (空間換時間)

3.1.2 @Service(服務存儲)

使? @Service 存儲 bean 的代碼如下所?

@Service
public class UserService {public void sayHi(String name) {System.out.println("Hi," + name);}
}

讀取 bean 的代碼:

@SpringBootApplication
public class SpringIocDemoApplication {public static void main(String[] args) {//獲取Spring上下?對象ApplicationContext context = SpringApplication.run(SpringIocDemoApplicatio//從Spring中獲取UserService對象UserService userService = context.getBean(UserService.class);//使?對象userService.sayHi();}}

在這里插入圖片描述

3.1.3 @Repository(倉庫存儲)

使? @Repository 存儲 bean 的代碼如下所?

@Repository
public class UserRepository {public void sayHi() {System.out.println("Hi, UserRepository~");}
}

讀取 bean 的代碼

@SpringBootApplication
public class SpringIocDemoApplication {public static void main(String[] args) {//獲取Spring上下?對象ApplicationContext context = SpringApplication.run(SpringIocDemoApplicatio//從Spring上下?中獲取對象UserRepository userRepository = context.getBean(UserRepository.class);//使?對象userRepository.sayHi();}
}

在這里插入圖片描述

3.1.4 @Component(組件存儲)

使? @Component 存儲 bean 的代碼如下所?:

@Component
public class UserComponent {public void sayHi() {System.out.println("Hi, UserComponent~");}
}

讀取

@SpringBootApplication
public class SpringIocDemoApplication {public static void main(String[] args) {//獲取Spring上下?對象ApplicationContext context = SpringApplication.run(SpringIocDemoApplicatio//從Spring上下?中獲取對象UserComponent userComponent = context.getBean(UserComponent.class);//使?對象userComponent.sayHi();}
}

3.1.5 @Configuration(配置存儲)

使? @Configuration 存儲 bean 的代碼如下所?:

@Configuration
public class UserConfiguration {public void sayHi() {System.out.println("Hi,UserConfiguration~");}
}

讀取

@SpringBootApplication
public class SpringIocDemoApplication {public static void main(String[] args) {//獲取Spring上下?對象ApplicationContext context = SpringApplication.run(SpringIocDemoApplicatio//從Spring上下?中獲取對象UserConfiguration userConfiguration = context.getBean(UserConfiguration.cl//使?對象userConfiguration.sayHi();}
}

3.2 為什么要這么多類注解?

這個也是和咱們前?講的應?分層是呼應的. 讓程序員看到類注解之后,就能直接了解當前類的?途.
? @Controller:控制層, 接收請求, 對請求進?處理, 并進?響應.
? @Servie:業務邏輯層, 處理具體的業務邏輯.
? @Repository:數據訪問層,也稱為持久層. 負責數據訪問操作
? @Configuration:配置層. 處理項?中的?些配置信息.

這和每個省/市都有??的?牌號是?樣的.
?牌號都是唯?的, 標識?個?輛的. 但是為什么還需要設置不同的?牌開頭呢.
?如陜西的?牌號就是:陜X:XXXXXX,北京的?牌號:京X:XXXXXX,甚??個省不同的縣區也是不同的,?如西安就是,陜A:XXXXX,咸陽:陜B:XXXXXX,寶雞,陜C:XXXXXX,?樣.這樣做的好處除了可以節約號碼之外,更重要的作?是可以直觀的標識?輛?的歸屬地.

程序的應?分層,調?流程如下:
在這里插入圖片描述
類注解之間的關系
查看 @Controller / @Service / @Repository / @Configuration 等注解的源碼發現:

在這里插入圖片描述
其實這些注解??都有?個注解 @Component ,說明它們本?就是屬于 @Component 的"?類".
@Component 是?個元注解,也就是說可以注解其他類注解,如 @Controller , @Service ,@Repository 等. 這些注解被稱為 @Component 的衍?注解.

@Controller , @Service 和 @Repository ?于更具體的?例(分別在控制層, 業務邏輯層, 持久化層), 在開發過程中, 如果你要在業務邏輯層使? @Component 或@Service,顯然@Service是更好的選擇

?如杯?有喝?杯, 刷?杯等, 但是我們更傾向于在?常喝?時使??杯, 洗漱時使?刷?杯.
更多資料參考:
https://docs.spring.io/spring-framework/reference/core/beans/classpathscanning.html#beans-stereotype-annotations

3.3 ?法注解 @Bean

類注解是添加到某個類上的, 但是存在兩個問題:

  1. 使?外部包?的類, 沒辦法添加類注解
  2. ?個類, 需要多個對象, ?如多個數據源

這種場景, 我們就需要使??法注解 @Bean

我們先來看看?法注解如何使?:

public class BeanConfig {@Beanpublic User user(){User user = new User();user.setName("zhangsan");user.setAge(18);return user;}
}

然?,當我們寫完以上代碼,嘗試獲取 bean 對象中的 user 時卻發現,根本獲取不到:

3.3.1 ?法注解要配合類注解使?

在 Spring 框架的設計中,?法注解 @Bean 要配合類注解才能將對象正常的存儲到 Spring 容器中,如下代碼所?

@Component
public class BeanConfig {@Beanpublic User user(){User user = new User();user.setName("zhangsan");user.setAge(18);return user;}
}

3.3.2 定義多個對象

對于同?個類, 如何定義多個對象呢?

?如多數據源的場景, 類是同?個, 但是配置不同, 指向不同的數據源.

我們看下@Bean的使?

@Component
public class BeanConfig {@Beanpublic User user1(){User user = new User();user.setName("zhangsan");user.setAge(18);return user;}@Beanpublic User user2(){User user = new User();user.setName("lisi");user.setAge(19);return user;}
}

定義了多個對象的話, 我們根據類型獲取對象, 獲取的是哪個對象呢?

@SpringBootApplication
public class SpringIocDemoApplication {public static void main(String[] args) {//獲取Spring上下?對象ApplicationContext context = SpringApplication.run(SpringIocDemoApplicatio//從Spring上下?中獲取對象User user = context.getBean(User.class);//使?對象System.out.println(user);}
}

報錯信息顯?: 期望只有?個匹配, 結果發現了兩個, user1, user2
從報錯信息中, 可以看出來, @Bean 注解的bean, bean的名稱就是它的?法名

接下來我們根據名稱來獲取bean對象

@SpringBootApplication
public class SpringIocDemoApplication {public static void main(String[] args) {//獲取Spring上下?對象ApplicationContext context = SpringApplication.run(SpringIocDemoApplicatio//根據bean名稱, 從Spring上下?中獲取對象User user1 = (User) context.getBean("user1");User user2 = (User) context.getBean("user2");System.out.println(user1);System.out.println(user2);}
}

在這里插入圖片描述
運?結果:
可以看到, @Bean 可以針對同?個類, 定義多個對象.

3.3.3 重命名 Bean

可以通過設置 name 屬性給 Bean 對象進?重命名操作,如下代碼所?:

@Bean(name = {"u1","user1"})
public User user1(){User user = new User();user.setName("zhangsan");user.setAge(18);return user;
}

此時我們使? u1 就可以獲取到 User 對象了,如下代碼所?:

@SpringBootApplication
public class SpringIocDemoApplication {public static void main(String[] args) {//獲取Spring上下?對象ApplicationContext context = SpringApplication.run(SpringIocDemoApplicatio//從Spring上下?中獲取對象User u1 = (User) context.getBean("u1");//使?對象System.out.println(u1);}
}

name={} 可以省略,如下代碼所?

@Bean({"u1","user1"})
public User user1(){User user = new User();user.setName("zhangsan");user.setAge(18);return user;
}

只有?個名稱時, {}也可以省略, 如

@Bean("u1")
public User user1(){User user = new User();user.setName("zhangsan");user.setAge(18);return user;
}

3.4 掃描路徑

Q: 使?前?學習的四個注解聲明的bean,?定會?效嗎?
A: 不?定(原因:bean想要?效,還需要被Spring掃描)

下?我們通過修改項??程的?錄結構,來測試bean對象是否?效:

在這里插入圖片描述
再運?代碼:

@SpringBootApplication
public class SpringIocDemoApplication {public static void main(String[] args) {//獲取Spring上下?對象ApplicationContext context = SpringApplication.run(SpringIocDemoApplicatio//從Spring上下?中獲取對象User u1 = (User) context.getBean("u1");//使?對象System.out.println(u1);}
}

在這里插入圖片描述
解釋: 沒有bean的名稱為u1
為什么沒有找到bean對象呢?
使?五?注解聲明的bean,要想?效, 還需要配置掃描路徑, 讓Spring掃描到這些注解
也就是通過 @ComponentScan 來配置掃描路徑.

@ComponentScan({"com.example.demo"})
@SpringBootApplication
public class SpringIocDemoApplication {public static void main(String[] args) {//獲取Spring上下?對象ApplicationContext context = SpringApplication.run(SpringIocDemoApplicatio//從Spring上下?中獲取對象User u1 = (User) context.getBean("u1");//使?對象System.out.println(u1);}
}

{} ?可以配置多個包路徑
這種做法僅做了解, 不做推薦使?

那為什么前?沒有配置 @ComponentScan注解也可以呢?
@ComponentScan 注解雖然沒有顯式配置,但是實際上已經包含在了啟動類聲明注解@SpringBootApplication 中了

默認掃描的范圍是SpringBoot啟動類所在包及其?包

在配置類上添加 @ComponentScan 注解, 該注解默認會掃描該類所在的包下所有的配置類

在這里插入圖片描述
推薦做法:
把啟動類放在我們希望掃描的包的路徑下, 這樣我們定義的bean就都可以被掃描到
在這里插入圖片描述

4. DI 詳解

上?我們講解了控制反轉IoC的細節,接下來呢,我們學習依賴注?DI的細節。

依賴注?是?個過程,是指IoC容器在創建Bean時, 去提供運?時所依賴的資源,?資源指的就是對象.在上?程序案例中,我們使?了 @Autowired 這個注解,完成了依賴注?的操作.
簡單來說, 就是把對象取出來放到某個類的屬性中.

在?些?章中, 依賴注?也被稱之為 “對象注?”, “屬性裝配”, 具體含義需要結合?章的上下?來理解

關于依賴注?, Spring也給我們提供了三種?式:

  1. 屬性注?(Field Injection)
  2. 構造?法注?(Constructor Injection)
  3. Setter 注?(Setter Injection)

接下來,我們分別來看。
下?我們按照實際開發中的模式,將 Service 類注?到Controller 類中。

4.1 屬性注?

屬性注?是使? @Autowired 實現的,將 Service 類注?到 Controller 類中.
Service 類的實現代碼如下:

import org.springframework.stereotype.Service;
@Service
public class UserService {public void sayHi() {System.out.println("Hi,UserService");}
}

Controller 類的實現代碼如下:

@Controller
public class UserController {//注??法1: 屬性注?@Autowiredprivate UserService userService;public void sayHi(){System.out.println("hi,UserController...");userService.sayHi();}
}

獲取 Controller 中的 sayHi?法:

@SpringBootApplication
public class SpringIocDemoApplication {public static void main(String[] args) {//獲取Spring上下?對象ApplicationContext context = SpringApplication.run(SpringIocDemoApplicatio//從Spring上下?中獲取對象UserController userController = (UserController) context.getBean("userCont//使?對象userController.sayHi();}
}

在這里插入圖片描述
去掉@Autowired , 再運??下程序看看結果

4.2 構造?法注?

構造?法注?是在類的構造?法中實現注?,如下代碼所?:

@Controller
public class UserController2 {//注??法2: 構造?法private UserService userService;@Autowiredpublic UserController2(UserService userService) {this.userService = userService;}public void sayHi(){System.out.println("hi,UserController2...");userService.sayHi();}
}

注意事項:如果類只有?個構造?法,那么 @Autowired 注解可以省略;如果類中有多個構造?法,
那么需要添加上 @Autowired 來明確指定到底使?哪個構造?法。

4.3 Setter 注?

Setter 注?和屬性的 Setter ?法實現類似,只不過在設置 set ?法的時候需要加上 @Autowired 注解 ,如下代碼所?

@Controller
public class UserController3 {//注??法3: Setter?法注?private UserService userService;@Autowiredpublic void setUserService(UserService userService) {this.userService = userService;}public void sayHi(){System.out.println("hi,UserController3...");userService.sayHi();}
}

練習?下:嘗試?下 set ?法如果不加 @Autowired 注解能注?成功嗎?

4.4 三種注?優缺點分析

  • 屬性注?

    • 優點: 簡潔,使??便;
    • 缺點:
      • 只能?于 IoC 容器,如果是? IoC 容器不可?,并且只有在使?的時候才會出現 NPE(空指針異常)
      • 不能注??個Final修飾的屬性
  • 構造函數注?(Spring 4.X推薦)

    • 優點:
      • 可以注?final修飾的屬性
      • 注?的對象不會被修改
      • 依賴對象在使?前?定會被完全初始化,因為依賴是在類的構造?法中執?的,?構造?法是在類加載階段就會執?的?法.
      • 通?性好, 構造?法是JDK?持的, 所以更換任何框架,他都是適?的
      • 缺點:
        • 注?多個對象時, 代碼會?較繁瑣
  • Setter注?(Spring 3.X推薦)

    • 優點: ?便在類實例之后, 重新對該對象進?配置或者注?
    • 缺點:
      • 不能注??個Final修飾的屬性
      • 注?對象可能會被改變, 因為setter?法可能會被多次調?, 就有被修改的?險.

4.5 @Autowired存在問題

當同?類型存在多個bean時, 使?@Autowired會存在問題

@Component
public class BeanConfig {@Bean("u1")public User user1(){User user = new User();user.setName("zhangsan");user.setAge(18);return user;
}@Beanpublic User user2() {User user = new User();user.setName("lisi");user.setAge(19);return user;}
}
@Controller
public class UserController {@Autowiredprivate UserService userService;//注?user@Autowiredprivate User user;public void sayHi(){System.out.println("hi,UserController...");userService.sayHi();System.out.println(user);}
}

在這里插入圖片描述
報錯的原因是,?唯?的 Bean 對象。
如何解決上述問題呢?Spring提供了以下?種解決?案:

  • @Primary
  • @Qualifier
  • @Resource
使?@Primary注解:當存在多個相同類型的Bean注?時,加上@Primary注解,來確定默認的實現.
@Component
public class BeanConfig {@Primary //指定該bean為默認bean的實現@Bean("u1")public User user1(){User user = new User();user.setName("zhangsan");user.setAge(18);return user;}@Beanpublic User user2() {User user = new User();user.setName("lisi");user.setAge(19);return user;}
}
使?@Qualifier注解:指定當前要注?的bean對象。 在@Qualifier的value屬性中,指定注?的bean的名稱。
  • @Qualifier注解不能單獨使?,必須配合@Autowired使?
@Controller
public class UserController {@Qualifier("user2") //指定bean名稱@Autowiredprivate User user;public void sayHi(){System.out.println("hi,UserController...");System.out.println(user);}
}
使?@Resource注解:是按照bean的名稱進?注?。通過name屬性指定要注?的bean的名稱。
@Controller
public class UserController {@Resource(name = "user2")private User user;public void sayHi(){System.out.println("hi,UserController...");System.out.println(user);}
}

常??試題:

@Autowird 與 @Resource的區別

? @Autowired 是spring框架提供的注解,?@Resource是JDK提供的注解
? @Autowired 默認是按照類型注?,?@Resource是按照名稱注?. 相?于 @Autowired 來說,@Resource ?持更多的參數設置,例如 name 設置,根據名稱獲取 Bean

5. 總結

Spring, Spring Boot 和Spring MVC的關系以及區別

Spring: 簡單來說,== Spring 是?個開發應?框架==,什么樣的框架呢,有這么?個標簽:輕量級、?站式、模塊化,其?的是?于簡化企業級應?程序開發.

Spring的主要功能: 管理對象,以及對象之間的依賴關系, ?向切?編程, 數據庫事務管理, 數據訪問, web框架?持等.

但是Spring具備?度可開放性, 并不強制依賴Spring, 開發者可以?由選擇Spring的部分或者全部, Spring可以?縫繼承第三?框架, ?如數據訪問框架(Hibernate 、JPA), web框架(如Struts、JSF)

Spring MVC: Spring MVC是Spring的?個?框架, Spring誕?之后, ?家覺得很好?, 于是按照MVC模式設計了?個 MVC框架(?些?Spring 解耦的組件), 主要?于開發WEB應?和?絡接?,所以,Spring MVC 是?個Web框架.
Spring MVC基于Spring進?開發的, 天?的與Spring框架集成. 可以讓我們更簡潔的進?Web層開發, ?持靈活的 URL 到??控制器的映射, 提供了強?的約定?于配置的契約式編程?持, ?常容易與其他視圖框架集成,如 Velocity、FreeMarker等

Spring Boot: Spring Boot是對Spring的?個封裝, 為了簡化Spring應?的開發?出現的,中?型企業,沒有成本研究??的框架, 使?Spring Boot 可以更加快速的搭建框架, 降級開發成本, 讓開發?員更加專注于Spring應?的開發,??需過多關注XML的配置和?些底層的實現.

Spring Boot 是個腳?架, 插拔式搭建項?, 可以快速的集成其他框架進來?如想使?SpringBoot開發Web項?, 只需要引?Spring MVC框架即可, Web開發的?作是pringMVC完成的, ?不是SpringBoot, 想完成數據訪問, 只需要引?Mybatis框架即可.Spring Boot只是輔助簡化項?開發的, 讓開發變得更加簡單, 甚?不需要額外的web服務器, 直接?成jar包執?即可.

最后?句話總結: Spring MVC和Spring Boot都屬于Spring,Spring MVC 是基于Spring的?個MVC 框架,?Spring Boot 是基于Spring的?套快速開發整合包.

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/37269.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/37269.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/37269.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

都江堰操作系統(DJYOS)和安卓操作系統(Android)的區別

- 開發團隊&#xff1a;都江堰操作系統由深圳市秦簡計算機系統有限公司開發和運營&#xff1b;而安卓由谷歌及其他公司帶領的開放手機聯盟開發。 - 開源情況&#xff1a;都江堰操作系統是開源操作系統&#xff0c;采取了類BSD許可證&#xff1b;安卓是基于Linux的開放源代碼軟…

AGI大模型的門檻是什么?怎么學

AGI&#xff08;Artificial General Intelligence&#xff0c;通用人工智能&#xff09;大模型&#xff0c;即具備類似人類智能的、能夠理解、學習、應用知識和技能并解決各種復雜問題的通用型人工智能模型&#xff0c;目前仍然是人工智能研究的前沿領域&#xff0c;具有以下門…

深度學習 --- stanford cs231學習筆記七(訓練神經網絡之梯度下降優化器)

5&#xff0c;梯度下降優化器 5&#xff0c;1 梯度下降在深度學習中的作用 在深度學習中&#xff0c;權重W的值是否合理是由損失函數L來判斷的。L越小&#xff0c;表示W的設置越happy。L越大&#xff0c;表示W的值越unhappy。 為了讓L越來越小&#xff0c;常用的方法是梯度下降…

centos7安裝zabbix6.4-mysql8

CentOS 7 安裝 Zabbix 6.4 步驟 關閉防火墻和服務 systemctl stop firewalld.service setenforce 0安裝 MySQL 配置 MySQL 源 wget https://dev.mysql.com/get/mysql80-community-release-el7-5.noarch.rpm yum -y install mysql80-community-release-el7-5.noarch.rpm安裝 M…

句法分析概述

第1關&#xff1a;句法分析概述 任務描述 本關任務&#xff1a;通過對句法分析基本概念的學習&#xff0c;完成相應的選擇題。 相關知識 為了完成本關任務&#xff0c;你需要掌握&#xff1a; 句法分析的基礎概念&#xff1b; 句法分析的數據集和評測方法。 句法分析簡介…

應用及安全

目錄 一、PAM 安全認證及配置 1.1配置 su 命令的認證 1.2PAM 配置文件結構二、賬號和密碼安全管理 2.1賬號管理 2.2系統賬號清理 2.3密碼安全控制 2.4密碼重設示例 2.5參考命令三、命令歷史限制 3.1設置命令歷史記錄…

【問chatgpt】

這采用的是什么腳手架初始化的&#xff1f;這樣問詢chatgpt 從文件結構來看&#xff0c;這個項目很可能是使用 Express 框架初始化的。這些文件夾和命名約定常見于 Express 項目結構&#xff0c;特別是&#xff1a; controllers: 控制器文件夾&#xff0c;通常用于處理請求邏…

同城小商城貨到付款系統源碼小程序

&#xff1a;便捷購物新選擇 &#x1f680; 一、快速便捷&#xff0c;同城直達 在這個快節奏的時代&#xff0c;時間就是金錢。你是否曾因為等待快遞而焦慮不安&#xff1f;現在&#xff0c;有了“同城商城貨到付款小程序”&#xff0c;這一切都變得不再是問題。它專注于同城…

<商務世界>《79 微課堂 <客戶是核心:2 客戶溝通的技巧《揚長避短進行溝通》(全文原創首發)>》

1 說明 符合表示★關鍵信息▲必要信息●一般信息 2 規則 等級信息★1、無準備&#xff0c;不溝通。▲2、做最準備充分&#xff0c;準備不設上限★3、掌握自己的節奏&#xff0c;不在他人節參中被動●4、準備充分、心態平穩、揚長避短。●5、溝通就是對消息的處理。發送、接收…

谷歌如何進行失效鏈接建設?

失效鏈接建設是一種高效的外鏈建設策略&#xff0c;通過發現并利用失效鏈接來提升自己網站的SEO。以下是詳細的步驟&#xff1a; 尋找失效頁面&#xff1a;你需要使用SEO工具&#xff0c;如Ahrefs&#xff0c;來查找與你的網站內容相關的失效頁面。這些頁面可能是競爭對手的失…

傳神社區|數據集合集第4期|中文NLP數據集合集

自從ChatGPT等大型語言模型&#xff08;Large Language Model, LLM&#xff09;出現以來&#xff0c;其類通用人工智能&#xff08;AGI&#xff09;能力引發了自然語言處理&#xff08;NLP&#xff09;領域的新一輪研究和應用浪潮。尤其是ChatGLM、LLaMA等普通開發者都能運行的…

基于深度學習的相機內參標定

基于深度學習的相機內參標定 相機內參標定&#xff08;Camera Intrinsic Calibration&#xff09;是計算機視覺中的關鍵步驟&#xff0c;用于確定相機的內部參數&#xff08;如焦距、主點位置、畸變系數等&#xff09;。傳統的標定方法依賴于已知尺寸的標定板&#xff0c;通常…

一文弄懂線性回歸模型

1、引言 今天&#xff0c;我們將深入探討機器學習中的三個關鍵概念&#xff1a;線性回歸、代價函數和梯度下降。這些概念構成了許多機器學習算法的基礎。起初&#xff0c;我決定不寫一篇關于這些主題的文章&#xff0c;因為它們已經被廣泛涉及。不過&#xff0c;我改變了主意&…

[圖解]SysML和EA建模住宅安全系統-02-現有運營領域-塊定義圖

1 00:00:00,840 --> 00:00:02,440 首先我們來看畫在哪里 2 00:00:02,570 --> 00:00:08,310 你看&#xff0c;這是圖的類型&#xff0c;圖里面內容 3 00:00:08,320 --> 00:00:10,780 這是元素類型 4 00:00:10,790 --> 00:00:14,900 這是位置&#xff0c;哪個包 …

Halcon 文本文件操作,形態學

一文件的讀寫 *******************************************************向文本文件寫入字符串內容*************************************************************read_image (Image, fabrik)threshold (Image, Region, 0, 120)area_center (Region, Area, Row, Column)open_…

【前端面試題】vue2.0與vue3.0不同處大全

Vue 3.0與Vue 2.0在細節上存在多個顯著的不同之處&#xff0c;以下是對這些區別的詳細歸納和解釋&#xff1a; 性能提升&#xff1a; Vue 3.0的性能相比Vue 2.x快了1.2至2倍。這主要得益于內部對虛擬DOM的完全重寫&#xff0c;mounting和patching的提速&#xff0c;以及基于Pr…

嘉立創學習

1.兩個設置&#xff0c;一般用左邊那個 2.焊盤分類 基本焊盤 熱風盤&#xff1a;也叫花焊盤&#xff08;負片&#xff09; 隔離焊盤&#xff1a;外面那圈黑色&#xff0c;用作隔離&#xff08;負片&#xff09; 鋼網層&#xff1a;&#xff08;錫膏&#xff09; 阻焊層&…

【php】【mysql】【layui】 原生初級簡易留言簿系統成品代碼動態網站開發網頁WEB瀏覽器端B/S結構

更多項目點擊&#x1f446;&#x1f446;&#x1f446;完整項目成品專欄 【php】【mysql】【layui】 原生初級簡易留言簿系統成品代碼動態網站開發網頁WEB瀏覽器端B/S結構 獲取源碼方式項目說明&#xff1a;文件包含&#xff1a;項目運行環境項目運行截圖 獲取源碼方式 加Q群…

妙解設計模式之橋接模式

橋接模式的概念 橋接模式&#xff08;Bridge Pattern&#xff09;是一種結構型設計模式&#xff0c;用于將抽象部分和實現部分分離&#xff0c;使它們可以獨立變化。這種模式通過組合而不是繼承來實現這個目標&#xff0c;從而提高系統的靈活性和可擴展性。 抽象部分&#xf…

如何使用C++進行文件讀寫操作

在C中&#xff0c;我們可以使用標準庫中的 <fstream>&#xff08;文件流&#xff09;來進行文件的讀寫操作。以下是一些基本的文件讀寫操作的示例。 讀取文件 cpp復制代碼 #include <fstream> #include <iostream> #include <string> int main() { s…