Spring框架概述及核心設(shè)計(jì)思想分享
一. Spring框架概述
1. 什么是Spring框架
我們通常所說的 Spring 指的是 Spring Framework(Spring 框架),它是?個(gè)開源框架,有著活躍而龐大的社區(qū),這就是它之所以能長久不衰的原因;Spring 支持廣泛的應(yīng)用場景,它可以讓 Java 企業(yè)級(jí)的應(yīng)用程序開發(fā)起來更簡單。
用?句話概括 Spring:Spring 框架是包含了眾多工具方法的 IoC 容器。
2. 為什么要學(xué)習(xí)框架?
因?yàn)閷W(xué)習(xí)框架相當(dāng)于從“小作坊”到“工廠”的升級(jí),小作坊什么都要自己做,工廠是組件式裝配,特點(diǎn)就是高效。
框架更加易?、簡單且高效。
Servlet有以下痛點(diǎn):
- 添加外部 jar 不?便,容易出錯(cuò),比如添加了?個(gè)不匹配的外部 jar 版本。
- 運(yùn)行和調(diào)試的時(shí)候需要配置 Tomcat 不?便。
- 發(fā)布不方便,Servlet 項(xiàng)目必須依靠外置的 Tomcat(外置的 Web 容器)運(yùn)行。
- 路由配置不方便,?個(gè)訪問地址對(duì)應(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ā)布。
- 對(duì)象自動(dòng)裝配。
- 添加路由更加方便,無需每個(gè)訪問地址都添加?個(gè)類。
- …
3. Spring框架學(xué)習(xí)的難點(diǎn)
- 配置比較多。
- 需要?量的外部 jar 包,在下載時(shí)容易出錯(cuò)。
- 會(huì)涉及簡單的軟件?程的設(shè)計(jì)思想(分層思想:前后端的分層思想;后端工程的分層思想)。
- 知識(shí)點(diǎn)相對(duì)來說比之前的知識(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ī)代碼中,對(duì)象的生命周期,是由當(dāng)前代碼(程序員自己)控制的,而控制權(quán)反轉(zhuǎn)就是對(duì)象的生命周期,不再由當(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(); } }
但是,但上面的代碼中,輪胎的大小是固定寫死的, 然而隨著對(duì)的車的需求量越來越大,個(gè)性化需求也會(huì)越來越多,這 時(shí)候我們就需要加?多種尺寸的輪胎,那這個(gè)時(shí)候就要對(duì)上面的程序進(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
對(duì)象傳入的參數(shù)就可以了;但是,如果再加上加上一個(gè)需求,還要定制輪胎的顏色,那我們又要加參數(shù),此時(shí)就意味著像上面一樣修改最底層的代碼, 整個(gè)調(diào)?鏈上的所有代碼都需要修改。
這樣的代碼耦合性就太高了,為了解決這個(gè)問題,我們可以使用loC
的思想來實(shí)現(xiàn)代碼,將控制權(quán)交出去,也就是說,IoC
模式下,我們不再自己構(gòu)造創(chuàng)建對(duì)象,當(dāng)我們需要輪胎Tire
類時(shí),你就給我傳一個(gè)Tire
對(duì)象,我們不去new
一個(gè)Tire
對(duì)象了,這樣的話,就算在Tire
類加參數(shù)也只需要改動(dòng)Tire
類的構(gòu)造方法與相關(guān)執(zhí)行方法與屬性,頂多再改一下Tire
對(duì)象的創(chuàng)建,同理其他類也一樣,將對(duì)象作為參數(shù)傳入到上級(jí)類的構(gòu)造方法中去就行了,但此時(shí)其他類是不需要修改的,這個(gè)過程也叫做傳入或注入。
由于我們創(chuàng)建Car
時(shí)需要Framework
,所以先要實(shí)例一個(gè)Framework
對(duì)象,同理實(shí)例一個(gè)Framework
對(duì)象需要Bottom
對(duì)象,那么需先實(shí)例一個(gè)Bottom
對(duì)象,一樣,在實(shí)例Bottom
對(duì)象之前需要實(shí)例一個(gè)Tire
對(duì)象,于是需要先后創(chuàng)建Tire
對(duì)象,Bottom
對(duì)象,Framework
對(duì)象后才能創(chuàng)建一個(gè)Car
對(duì)象,我們可以得到如下的代碼:
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 容器做的事情 // 對(duì)象的生命周期控制權(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)的代碼中對(duì)象創(chuàng)建順序是:Car -> Framework -> Bottom -> Tire
- 改進(jìn)之后解耦的代碼的對(duì)象創(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),是下層將對(duì)象注入到當(dāng)前對(duì)象當(dāng)中,下級(jí)的控制權(quán)不再由上級(jí)控制了,下級(jí)在發(fā)生改變時(shí)會(huì)將改變完成后的對(duì)象注入上級(jí),這樣上級(jí)就是不受影響的,這就是 IoC 的實(shí)現(xiàn)思想。
所以 IoC 有以下的優(yōu)點(diǎn):對(duì)象(Bean)的生命周期交給 IoC 框架維護(hù),作為程序員無需關(guān)注,說白了就是程序員不需要關(guān)注對(duì)象創(chuàng)建、銷毀時(shí)機(jī)以及對(duì)象的依賴關(guān)系,這些工作加個(gè) IoC 框架(也就是 Spring)做就行,實(shí)現(xiàn)了代碼的解耦,對(duì)象的使用更加方便高效。
3. Spring是IoC容器
Spring 框架就是包含了多個(gè)工具方法的 IoC 容器,既然是容器,那它就有存和取的功能,這也是 Spring 最核心的兩個(gè)功能:
- 將 Bean(對(duì)象)存儲(chǔ)到 Spring 容器中。
- 將 Bean(對(duì)象)從 Spring 容器中取出來。
將對(duì)象存放到容器有什么好處呢?
將對(duì)象存儲(chǔ)到 IoC 容器相當(dāng)于我們將所有可能用到的工具制作好都放到倉庫,當(dāng)我們需要使用時(shí)直接取即可,用完歸還倉庫;而 new 對(duì)象的方式相當(dāng)于我們每次需要用工具的時(shí)候現(xiàn)場制作,制作完了扔掉,下次需要的時(shí)候重新做。
Spring 是?個(gè) IoC 容器,說的是對(duì)象的創(chuàng)建和銷毀的權(quán)利都交給 Spring 來管理了,它本身?具備了存儲(chǔ)對(duì)象和獲取對(duì)象的能力。
4. DI(依賴注入)
DI,即Dependency Injection
,依賴注入。
所謂依賴注?,就是由 IoC 容器在運(yùn)行期間,動(dòng)態(tài)地將某種依賴關(guān)系注入到對(duì)象之中,在pom.xml
有一個(gè)依賴項(xiàng),就是用來導(dǎo)入外部的資源,而這里的依賴注入,導(dǎo)入的不是外部的資源,而是對(duì)象;當(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)對(duì)象之間的解耦。
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)系委托給容器,由容器來管理對(duì)象之間的依賴關(guān)系,容器外部是不關(guān)心這種依賴關(guān)系的,需要時(shí)由容器判斷提供什么;而依賴查找是由對(duì)象自己來查找它所依賴的對(duì)象,容器只負(fù)責(zé)管理對(duì)象的生命周期,也就是說此時(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)前控制器注冊(cè)一個(gè)屬性編輯器,用于對(duì)WebDataBinder進(jìn)行初始化,且只對(duì)當(dāng)前的Controller有效,需要的朋友可以參考下2023-10-10SpringBoot整合EasyCaptcha實(shí)現(xiàn)圖形驗(yàn)證碼功能
這篇文章主要介紹了SpringBoot整合EasyCaptcha實(shí)現(xiàn)圖形驗(yàn)證碼功能,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2024-02-02IDEA中Javaweb項(xiàng)目圖片加載不出來解決方案
在IDEA中能夠正常的預(yù)覽到圖片,但是在生成項(xiàng)目的war包時(shí),項(xiàng)目的目錄結(jié)構(gòu)卻會(huì)發(fā)生變化,所以無法訪問圖片,本文主要介紹了IDEA中Javaweb項(xiàng)目圖片加載不出來解決方案,感興趣的可以了解一下2023-10-10MybatisPlus自動(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