Spring框架概述及核心設(shè)計(jì)思想分享
一. Spring框架概述
1. 什么是Spring框架
我們通常所說的 Spring 指的是 Spring Framework(Spring 框架),它是?個(gè)開源框架,有著活躍而龐大的社區(qū),這就是它之所以能長久不衰的原因;Spring 支持廣泛的應(yīng)用場景,它可以讓 Java 企業(yè)級的應(yīng)用程序開發(fā)起來更簡單。
用?句話概括 Spring:Spring 框架是包含了眾多工具方法的 IoC 容器。
2. 為什么要學(xué)習(xí)框架?
因?yàn)閷W(xué)習(xí)框架相當(dāng)于從“小作坊”到“工廠”的升級,小作坊什么都要自己做,工廠是組件式裝配,特點(diǎn)就是高效。
框架更加易?、簡單且高效。
Servlet有以下痛點(diǎn):
- 添加外部 jar 不?便,容易出錯(cuò),比如添加了?個(gè)不匹配的外部 jar 版本。
- 運(yùn)行和調(diào)試的時(shí)候需要配置 Tomcat 不?便。
- 發(fā)布不方便,Servlet 項(xiàng)目必須依靠外置的 Tomcat(外置的 Web 容器)運(yùn)行。
- 路由配置不方便,?個(gè)訪問地址對應(yīng)?個(gè) Servlet 類。
- …
而 Spring Boot 相比于 Servlet 具備以下優(yōu)點(diǎn):
- 快速添加外部 jar 包。
- 調(diào)試項(xiàng)目更方便,無需配置 Tomcat,點(diǎn)擊“運(yùn)行”按鈕就可以直接運(yùn)行項(xiàng)目,因?yàn)?Spring Boot 內(nèi)置了 Tomcat 容器可直接運(yùn)行,但是 Servlet 需外掛 Tomcat。
- 發(fā)布項(xiàng)目更加方便,無需配置 Tomcat,使用 java -jar 方式就可以發(fā)布。
- 對象自動(dòng)裝配。
- 添加路由更加方便,無需每個(gè)訪問地址都添加?個(gè)類。
- …
3. Spring框架學(xué)習(xí)的難點(diǎn)
- 配置比較多。
- 需要?量的外部 jar 包,在下載時(shí)容易出錯(cuò)。
- 會(huì)涉及簡單的軟件?程的設(shè)計(jì)思想(分層思想:前后端的分層思想;后端工程的分層思想)。
- 知識(shí)點(diǎn)相對來說比之前的知識(shí)更加的分散,要仔細(xì)聽才能搞懂各個(gè)知識(shí)點(diǎn)的邏輯關(guān)系。
- 要記的東西很多,所以要大量地重復(fù)練習(xí)才能記住,比如各種注解。
Spring框架基本學(xué)習(xí)應(yīng)用路線:Spring全家桶(Spring/Spring Boot/Spring MVC) -> MyBatis -> Redis 等。
二. Spring核心設(shè)計(jì)思想
Spring 核心就是這么一句話:Spring 框架是包含了眾多工具方法的 IoC 容器。
那么這句話怎么理解呢?什么是容器?什么又是 IoC?
1. 容器是什么?
容器是用來容納某種物品的(基本)裝置。 ——來?:百度百科
Java 中也有一些容器,比如 List,Map,Set 等這些集合,是屬于數(shù)據(jù)儲(chǔ)存的容器,它把我們常用的操作都封裝到集合中了,我們只需要知道集合為我們提供的方法就可以使用各種集合了;還有 Tomcat 是屬于 Web 容器,同理 Spring 是就一個(gè) IoC 容器,它包含了許多的工具和方法。
2. IoC是什么?
IoC 即 Inversion of Control,直譯過來就是控制反轉(zhuǎn)的意思,這是一種思想,控制權(quán)反轉(zhuǎn),在 Java 的常規(guī)代碼中,對象的生命周期,是由當(dāng)前代碼(程序員自己)控制的,而控制權(quán)反轉(zhuǎn)就是對象的生命周期,不再由當(dāng)前代碼片段來控制,而是由 Spring(IoC 容器)來控制 。
這種思想還比較抽象,我們來通過一個(gè)例子來了解它。
我們都知道汽車,它包含輪胎,底座,車身等,現(xiàn)在我們要造一輛汽車時(shí),需要有車身,而車身需要有底座和輪胎,最傳統(tǒng)的思想就是造車時(shí),需要車身,于是就new一個(gè)車身,而車身需要底座,于是就再new一個(gè)底座,同理底座需要輪胎,那就造底座前new一個(gè)輪胎。

我們可以得到以下代碼:
package old;
/**
* 傳統(tǒng)開發(fā)方式, 耦合性問題
*/
// 汽車類
public class Car {
// 車身
private Framework framework;
public Car() {
framework = new Framework();
}
public void init() {
System.out.println("do car");
// 汽車的組建依賴于車身
framework.init();
}
}
package old;
// 車身類
public class Framework {
private Bottom bottom;
public Framework() {
bottom = new Bottom();
}
public void init() {
System.out.println("do framework");
// 車身的組建依賴于底盤
bottom.init();
}
}
package old;
// 底盤類
public class Bottom {
private Tire tire;
public Bottom() {
tire = new Tire();
}
public void init() {
System.out.println("do bottom");
// 底盤的組建依賴于輪胎
tire.init();
}
}
package old;
// 輪胎類
public class Tire {
private int size = 17; // 輪胎的尺寸
public void init() {
System.out.println("size -> " + size);
}
}
package old;
public class Test {
public static void main(String[] args) {
Car car = new Car();
car.init();
}
}但是,但上面的代碼中,輪胎的大小是固定寫死的, 然而隨著對的車的需求量越來越大,個(gè)性化需求也會(huì)越來越多,這 時(shí)候我們就需要加?多種尺寸的輪胎,那這個(gè)時(shí)候就要對上面的程序進(jìn)行修改了,根據(jù)我們上面寫的代碼,我們需要往輪胎Tire類的構(gòu)造方法加上一個(gè)參數(shù),由于底盤Bottom類控制著Tire類,那么底盤類的構(gòu)造方法也需要加上一個(gè)參數(shù),以此類推,我們的車身Framework類與汽車Car類都需要為構(gòu)造方法加上參數(shù),于是我們得到了如下的代碼:
package old;
/**
* 傳統(tǒng)開發(fā)方式, 耦合性問題
*/
// 汽車類
public class Car {
// 車身
private Framework framework;
public Car(int size) {
framework = new Framework(size);
}
public void init() {
System.out.println("do car");
// 汽車的組建依賴于車身
framework.init();
}
}
package old;
// 車身類
public class Framework {
private Bottom bottom;
public Framework(int size) {
bottom = new Bottom(size);
}
public void init() {
System.out.println("do framework");
// 車身的組建依賴于底盤
bottom.init();
}
}
package old;
// 底盤類
public class Bottom {
private Tire tire;
public Bottom(int size) {
tire = new Tire(size);
}
public void init() {
System.out.println("do bottom");
// 底盤的組建依賴于輪胎
tire.init();
}
}
package old;
// 輪胎類
public class Tire {
private int size = 17; // 輪胎的尺寸
public Tire(int size) {
this.size = size;
}
public void init() {
System.out.println("size -> " + size);
}
}
package old;
public class Test {
public static void main(String[] args) {
Car car = new Car(20);
car.init();
}
}此時(shí),如果需要個(gè)性化定制輪胎的大小,就可以只改動(dòng)構(gòu)造Car對象傳入的參數(shù)就可以了;但是,如果再加上加上一個(gè)需求,還要定制輪胎的顏色,那我們又要加參數(shù),此時(shí)就意味著像上面一樣修改最底層的代碼, 整個(gè)調(diào)?鏈上的所有代碼都需要修改。
這樣的代碼耦合性就太高了,為了解決這個(gè)問題,我們可以使用loC的思想來實(shí)現(xiàn)代碼,將控制權(quán)交出去,也就是說,IoC模式下,我們不再自己構(gòu)造創(chuàng)建對象,當(dāng)我們需要輪胎Tire類時(shí),你就給我傳一個(gè)Tire對象,我們不去new一個(gè)Tire對象了,這樣的話,就算在Tire類加參數(shù)也只需要改動(dòng)Tire類的構(gòu)造方法與相關(guān)執(zhí)行方法與屬性,頂多再改一下Tire對象的創(chuàng)建,同理其他類也一樣,將對象作為參數(shù)傳入到上級類的構(gòu)造方法中去就行了,但此時(shí)其他類是不需要修改的,這個(gè)過程也叫做傳入或注入。

由于我們創(chuàng)建Car時(shí)需要Framework,所以先要實(shí)例一個(gè)Framework對象,同理實(shí)例一個(gè)Framework對象需要Bottom對象,那么需先實(shí)例一個(gè)Bottom對象,一樣,在實(shí)例Bottom對象之前需要實(shí)例一個(gè)Tire對象,于是需要先后創(chuàng)建Tire對象,Bottom對象,Framework對象后才能創(chuàng)建一個(gè)Car對象,我們可以得到如下的代碼:
package ioc;
public class Car {
// 汽車的組建依賴于車身的組建
private Framework franmework;
public Car(Framework franmework) {
this.franmework = franmework;
}
public void init() {
System.out.println("do car...");
franmework.init();
}
}
package ioc;
public class Framework {
// 車身的組建依賴于底盤
private Bottom bottom;
public Framework(Bottom bottom) {
this.bottom = bottom;
}
public void init() {
System.out.println("do franmework");
bottom.init();
}
}
package ioc;
public class Bottom {
// 底盤的組建依賴于輪胎
private Tire tire;
public Bottom(Tire tire) {
this.tire = tire;
}
public void init() {
System.out.println("do bottom...");
tire.init();
}
}
package ioc;
public class Tire {
private int size = 17;
private String color = "黑色";
public Tire(int size, String color) {
this.size = size;
this.color = color;
}
public void init() {
System.out.println("size -> " + size);
System.out.println("color->" + color);
}
}
package ioc;
public class IOCDemo {
// 這里的內(nèi)容包含就相當(dāng)于是 IoC 容器做的事情
// 對象的生命周期控制權(quán)就翻轉(zhuǎn)給 IoC 容器了, 不再由程序員控制
private Tire tire;
private Bottom bottom;
private Framework framework;
public Car car;
public IOCDemo() {
tire = new Tire(17, "紅色");
bottom = new Bottom(tire);
framework = new Framework(bottom);
car = new Car(framework);
}
}
package ioc;
/**
* 模擬 IoC 容器
*/
public class Test {
public static void main(String[] args) {
// 直接使用, 創(chuàng)建就交給IoC了
IOCDemo ioc = new IOCDemo();
Car car = ioc.car;
car.init();
}
}此時(shí)如果要變需求,需要加參數(shù)或減少參數(shù),IoC 的代碼只需改動(dòng)圖中的兩處代碼即可, 整個(gè)調(diào)用鏈?zhǔn)遣挥米鋈魏胃淖兊模?達(dá)到了解耦的目的。


- 在傳統(tǒng)的代碼中對象創(chuàng)建順序是:Car -> Framework -> Bottom -> Tire
- 改進(jìn)之后解耦的代碼的對象創(chuàng)建順序是:Tire -> Bottom -> Framework -> Car
到這里我們就可以發(fā)現(xiàn),傳統(tǒng)的代碼類創(chuàng)建順序是反著的,Car 控制 FrameWork,F(xiàn)rameWork 控制著 Bottom,Bottom 控制著 Tire;而改進(jìn)之后的控制權(quán)發(fā)生了反轉(zhuǎn),是下層將對象注入到當(dāng)前對象當(dāng)中,下級的控制權(quán)不再由上級控制了,下級在發(fā)生改變時(shí)會(huì)將改變完成后的對象注入上級,這樣上級就是不受影響的,這就是 IoC 的實(shí)現(xiàn)思想。
所以 IoC 有以下的優(yōu)點(diǎn):對象(Bean)的生命周期交給 IoC 框架維護(hù),作為程序員無需關(guān)注,說白了就是程序員不需要關(guān)注對象創(chuàng)建、銷毀時(shí)機(jī)以及對象的依賴關(guān)系,這些工作加個(gè) IoC 框架(也就是 Spring)做就行,實(shí)現(xiàn)了代碼的解耦,對象的使用更加方便高效。
3. Spring是IoC容器
Spring 框架就是包含了多個(gè)工具方法的 IoC 容器,既然是容器,那它就有存和取的功能,這也是 Spring 最核心的兩個(gè)功能:
- 將 Bean(對象)存儲(chǔ)到 Spring 容器中。
- 將 Bean(對象)從 Spring 容器中取出來。
將對象存放到容器有什么好處呢?
將對象存儲(chǔ)到 IoC 容器相當(dāng)于我們將所有可能用到的工具制作好都放到倉庫,當(dāng)我們需要使用時(shí)直接取即可,用完歸還倉庫;而 new 對象的方式相當(dāng)于我們每次需要用工具的時(shí)候現(xiàn)場制作,制作完了扔掉,下次需要的時(shí)候重新做。
Spring 是?個(gè) IoC 容器,說的是對象的創(chuàng)建和銷毀的權(quán)利都交給 Spring 來管理了,它本身?具備了存儲(chǔ)對象和獲取對象的能力。
4. DI(依賴注入)
DI,即Dependency Injection,依賴注入。
所謂依賴注?,就是由 IoC 容器在運(yùn)行期間,動(dòng)態(tài)地將某種依賴關(guān)系注入到對象之中,在pom.xml有一個(gè)依賴項(xiàng),就是用來導(dǎo)入外部的資源,而這里的依賴注入,導(dǎo)入的不是外部的資源,而是對象;當(dāng)某個(gè) Java 實(shí)例需要另一個(gè) Java 實(shí)例時(shí),創(chuàng)建被調(diào)用者的工作不是由調(diào)用者實(shí)現(xiàn),而是由 Spring 容器來完成,然后注入調(diào)用者,因此稱為依賴注入。
IoC 與 DI 的區(qū)別是什么?
依賴注入(DI)和控制反轉(zhuǎn)(IoC)是從不同的角度的描述的同?件事情,就是指通過引入 IoC 容器,利用依賴關(guān)系注入的方式,實(shí)現(xiàn)對象之間的解耦。
IoC 是“目標(biāo)”也是?種思想,而目標(biāo)和思想只是?種指導(dǎo)原則,最終還是要有可行的落地方案,而 DI 就屬于具體的實(shí)現(xiàn);也就是說,IoC 是一種思想,而 DI 是 IoC 的一種實(shí)現(xiàn)。
5. DL(依賴查找)
DL,即Dependency Lookup,依賴查找,也是 IoC的一種實(shí)現(xiàn)。
依賴查找和依賴注入的區(qū)別在于,依賴注入是將依賴關(guān)系委托給容器,由容器來管理對象之間的依賴關(guān)系,容器外部是不關(guān)心這種依賴關(guān)系的,需要時(shí)由容器判斷提供什么;而依賴查找是由對象自己來查找它所依賴的對象,容器只負(fù)責(zé)管理對象的生命周期,也就是說此時(shí)需要容器外部自己確定要容器提供哪種依賴關(guān)系;兩者之間是主動(dòng)和被動(dòng)的區(qū)別。
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java設(shè)計(jì)模塊系列之書店管理系統(tǒng)單機(jī)版(一)
這篇文章主要為大家詳細(xì)介紹了Java單機(jī)版的書店管理系統(tǒng)設(shè)計(jì)模塊和思想第一章,感興趣的小伙伴們可以參考一下2016-08-08
深入了解SpringBoot中@InitBinder注解的使用
這篇文章主要介紹了深入了解SpringBoot中@InitBinder注解的使用,@InitBinder注解可以作用在被@Controller注解的類的方法上,表示為當(dāng)前控制器注冊一個(gè)屬性編輯器,用于對WebDataBinder進(jìn)行初始化,且只對當(dāng)前的Controller有效,需要的朋友可以參考下2023-10-10
SpringBoot整合EasyCaptcha實(shí)現(xiàn)圖形驗(yàn)證碼功能
這篇文章主要介紹了SpringBoot整合EasyCaptcha實(shí)現(xiàn)圖形驗(yàn)證碼功能,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2024-02-02
IDEA中Javaweb項(xiàng)目圖片加載不出來解決方案
在IDEA中能夠正常的預(yù)覽到圖片,但是在生成項(xiàng)目的war包時(shí),項(xiàng)目的目錄結(jié)構(gòu)卻會(huì)發(fā)生變化,所以無法訪問圖片,本文主要介紹了IDEA中Javaweb項(xiàng)目圖片加載不出來解決方案,感興趣的可以了解一下2023-10-10
MybatisPlus自動(dòng)填充創(chuàng)建(更新)時(shí)間問題
在開發(fā)數(shù)據(jù)庫相關(guān)應(yīng)用時(shí),手動(dòng)設(shè)置創(chuàng)建和更新時(shí)間會(huì)導(dǎo)致代碼冗余,MybatisPlus提供了自動(dòng)填充功能,通過實(shí)現(xiàn)MetaObjectHandler接口并重寫insertFill、updateFill方法,可以自動(dòng)維護(hù)創(chuàng)建時(shí)間、更新時(shí)間等字段,極大簡化了代碼,這不僅提高了開發(fā)效率,也保證了數(shù)據(jù)的可追溯性2024-09-09

