前言
對于任何一個(gè) Java 開發(fā)人員钞支,Spring 的大名一定如雷貫耳督弓,在行業(yè)中可謂是無人不知芦圾、無人不曉骤竹,說它是 Java 領(lǐng)域第一框架毫不為過帝牡。
(圖片來自 Spring 官網(wǎng))
Spring 概念誕生于 2002 年,創(chuàng)始人 Rod Jahnson 在其著作《Expert One-on-One J2EE Design and Development》中第一次提出了 Spring 的核心思想瘤载,于 2003 年正式發(fā)布第一個(gè)版本 Spring Framework 0.9否灾。
經(jīng)過十幾年的優(yōu)化迭代,Spring Framework 已經(jīng)從最初的取代 EJB 框架逐步發(fā)展為一套完整的生態(tài)鸣奔,最新的版本是 5.X墨技,支持現(xiàn)代 Java 開發(fā)的各個(gè)技術(shù)領(lǐng)域,家族兩大核心成員 Spring Boot 和 Spring Cloud 更是當(dāng)下 Java 領(lǐng)域最為熱門的技術(shù)棧挎狸。
毋庸置疑扣汪,Spring 已經(jīng)成為 Java 開發(fā)的行業(yè)標(biāo)準(zhǔn),無論你是初級(jí)程序員還是架構(gòu)師锨匆,只要是做 Java 開發(fā)的崭别,工作中或多或少一定會(huì)接觸到 Spring 相關(guān)技術(shù)棧。
我們所說的 Spring 全家桶各個(gè)模塊都是基于 Spring Framework 衍生而來恐锣,通常所說的 Spring 框架一般泛指 Spring Framework茅主,它包含 IoC 控制反轉(zhuǎn)、DI 依賴注入土榴、AOP 面向切面編程诀姚、Context 上下文、bean 管理玷禽、Spring Web MVC 等眾多功能模塊赫段,其他的 Spring 家族成員都需要依賴 Spring Framework呀打。
可以簡單理解 Spring Framework 是一個(gè)設(shè)計(jì)層面的框架,通過分層思想來實(shí)現(xiàn)組件之間的解耦合糯笙,開發(fā)者可以根據(jù)需求選擇不同的組件贬丛,并且可以非常方便的進(jìn)行集成,Spring Framework 的這一特性使得企業(yè)級(jí)項(xiàng)目開發(fā)變得更加簡單方便给涕。
Spring 的兩大核心機(jī)制是 IoC(控制反轉(zhuǎn))和 AOP(面向切面編程)豺憔,對于初學(xué)者來講,搞清楚這兩個(gè)核心機(jī)制就掌握了 Spring 的基本應(yīng)用够庙。這兩大核心機(jī)制也是 Java 設(shè)計(jì)模式的典型代表焕阿,其中 IoC 是工廠模式,AOP 是代理模式首启。
什么是 IoC 和 AOP
下面來詳細(xì)了解 IoC暮屡,IoC 是 Spring 框架的靈魂,非常重要毅桃,理解了 IoC 才能真正掌握 Spring 框架的使用褒纲。
IoC 也叫控制反轉(zhuǎn),首先從字面意思理解钥飞,什么叫控制反轉(zhuǎn)莺掠?反轉(zhuǎn)的是什么?
在傳統(tǒng)的程序開發(fā)中读宙,需要獲取對象時(shí)彻秆,通常由開發(fā)者來手動(dòng)創(chuàng)建實(shí)例化對象,但是在 Spring 框架中創(chuàng)建對象的工作不再由開發(fā)者完成结闸,而是交給 IoC 容器來創(chuàng)建唇兑,我們直接獲取即可,整個(gè)流程完成反轉(zhuǎn)桦锄,因此是控制反轉(zhuǎn)扎附。
舉個(gè)例子:超市購物。
- 傳統(tǒng)方式:你去超市買東西结耀,需要自己拿著袋子去超市購買商品留夜,然后自己把袋子提回來。
- IoC 容器:你只需要把袋子放在家門口图甜,袋子里面會(huì)自動(dòng)裝滿你需要的商品碍粥,直接取出來用就可以了。
我們通過創(chuàng)建一個(gè) Student 對象的例子來對比兩種方式的區(qū)別黑毅。
傳統(tǒng)方式
(1)創(chuàng)建 Student 類
public class Student {
private int id;
private String name;
private int age;
}
(2)測試方法中調(diào)用構(gòu)造函數(shù)創(chuàng)建對象
Student student = new Student();
IoC 容器
實(shí)現(xiàn)步驟
- 在 pom.xml 中添加 Spring 依賴
- 創(chuàng)建配置文件嚼摩,可以自定義文件名 spring.xml
- 在 spring.xml 中配置 bean 標(biāo)簽,IoC 容器通過加載 bean 標(biāo)簽來創(chuàng)建對象
- 調(diào)用 API 獲取 IoC 創(chuàng)建的對象
IoC 容器可以調(diào)用無參構(gòu)造或者有參構(gòu)造來創(chuàng)建對象,我們先來看無參構(gòu)造的方式低斋。
無參構(gòu)造
<!-- 配置 student 對象-->
<bean id="stu" class="com.southwind.entity.Student"</bean>
配置一個(gè) bean 標(biāo)簽:
- id,對象名
- class匪凡,對象的模板類
接下來調(diào)用 API 獲取對象膊畴,Spring 提供了兩種方式來獲取對象:id 或者運(yùn)行時(shí)類。
(1)通過 id 獲取對象
//1.加載 spring.xml 配置文件
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
//2.通過 id 值獲取對象
Student stu = (Student) applicationContext.getBean("stu");
System.out.println(stu);
第一步:加載 spring.xml 配置文件病游,生成 ApplicationContext 對象唇跨。
第二步:調(diào)用 ApplicationContext 的 getBean 方法獲取對象,參數(shù)為配置文件中的 id 值衬衬。程序在加載 spring.xml 時(shí)創(chuàng)建 stu 對象买猖,通過反射機(jī)制調(diào)用無參構(gòu)造函數(shù),所有要求交給 IoC 容器管理的類必須有無參構(gòu)造函數(shù)滋尉。
運(yùn)行結(jié)果如下圖所示玉控。
<img src="https://images.gitbook.cn/512e7870-96e4-11e8-9f54-b3cc9167c22b" width = "70%" />
可以看到,此時(shí) stu 對象的屬性全部為空狮惜,因?yàn)檎{(diào)用無參構(gòu)造只會(huì)創(chuàng)建對象而不會(huì)進(jìn)行賦值高诺,如何賦值呢?只需要在 spring.xml 中進(jìn)行相關(guān)配置即可碾篡,如下所示虱而。
<!-- 配置 student 對象 -->
<bean id="stu" class="com.southwind.entity.Student">
<property name="id" value="1"></property>
<property name="name" value="張三"></property>
<property name="age" value="23"></property>
</bean>
添加 property 標(biāo)簽:name 對應(yīng)屬性名,value 是屬性的值开泽。若包含特殊字符牡拇,比如 name="<張三>",使用 <![CDATA[<張三>]]> 進(jìn)行配置穆律,如下所示惠呼。
<!-- 配置 student 對象 -->
<bean id="stu" class="com.southwind.entity.Student">
<property name="id" value="1"></property>
<property name="name">
<value><![CDATA[<張三>]]></value>
</property>
<property name="age" value="23"></property>
</bean>
運(yùn)行結(jié)果如下圖所示。
<img src="https://images.gitbook.cn/5fcf9670-96e4-11e8-9e0c-8bfb55c56242" width = "70%" />
Spring 通過調(diào)用每個(gè)屬性的 setter 方法來完成屬性的賦值峦耘,因此實(shí)體類必須有 setter 方法罢杉,否則加載時(shí)報(bào)錯(cuò),getter 方法可省略贡歧。
(2)通過運(yùn)行時(shí)類獲取對象
//1.加載 spring.xml 配置文件
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
//2.通過運(yùn)行時(shí)類獲取對象
Student stu = applicationContext.getBean(Student.class);
System.out.println(stu);
此方法有一個(gè)弊端滩租,當(dāng) spring.xml 中配置兩個(gè) Student 的 bean 時(shí)程序會(huì)拋出異常,因?yàn)榇藭r(shí)兩個(gè) bean 都是由 Student 類生成的利朵,IoC 容器無法將兩個(gè) bean 都返回律想,必須指定一個(gè)唯一的 bean。
<bean id="stu" class="com.hzit.entity.Student">
<property name="id" value="1"></property>
<property name="name">
<value><![CDATA[<張三>]]></value>
</property>
<property name="age" value="23"></property>
</bean>
<bean id="stu2" class="com.hzit.entity.Student">
<property name="id" value="1"></property>
<property name="name" value="李四"></property>
<property name="age" value="23"></property>
</bean>
異常信息如下圖所示绍弟。
<img src="https://images.gitbook.cn/6764cf90-96e4-11e8-9f54-b3cc9167c22b" width = "70%" />
以上是 IoC 容器通過無參構(gòu)造創(chuàng)建對象的方式技即,同時(shí) IoC 容器也可以調(diào)用有參構(gòu)造來創(chuàng)建對象。
有參構(gòu)造
(1)在實(shí)體類中創(chuàng)建有參構(gòu)造
public Student(int id, String name, int age) {
super();
this.id = id;
this.name = name;
this.age = age;
}
(2)spring.xml 中進(jìn)行配置
<!-- 通過有參構(gòu)造函數(shù)創(chuàng)建對象 -->
<bean id="stu3" class="com.hzit.entity.Student">
<constructor-arg name="id" value="3"></constructor-arg>
<constructor-arg name="name" value="小明"></constructor-arg>
<constructor-arg name="age" value="22"></constructor-arg>
</bean>
(3)此時(shí) IoC 容器會(huì)根據(jù) constructor-arg 標(biāo)簽去加載對應(yīng)的有參構(gòu)造函數(shù)樟遣,創(chuàng)建對象并完成屬性賦值而叼。name 的值需要與有參構(gòu)造的形參名對應(yīng)身笤,value 是對應(yīng)的值。除了使用 name 對應(yīng)參數(shù)外葵陵,還可以通過下標(biāo) index 對應(yīng)液荸,如下所示。
<!-- 通過有參構(gòu)造函數(shù)創(chuàng)建對象 -->
<bean id="stu3" class="com.hzit.entity.Student">
<constructor-arg index="0" value="3"></constructor-arg>
<constructor-arg index="1" value="小明"></constructor-arg>
<constructor-arg index="2" value="22"></constructor-arg>
</bean>
以上是 IoC 容器通過有參構(gòu)造創(chuàng)建對象的方式脱篙,獲取對象同樣有兩種方式可以選擇:id 和運(yùn)行時(shí)類娇钱。
如果 IoC 容器管理多個(gè)對象,并且對象之間有級(jí)聯(lián)關(guān)系绊困,如何實(shí)現(xiàn)文搂?
(1)創(chuàng)建 Classes 類
public class Classes {
private int id;
private String name;
}
(2)在 Student 類中添加 Classes 屬性
public class Student {
private int id;
private String name;
private int age;
private Classes classes;
}
(3)spring.xml 中配置 classes 對象,然后將該對象賦值給 stu 對象
<!-- 創(chuàng)建 classes 對象 -->
<bean id="classes" class="com.hzit.entity.Classes">
<property name="id" value="1"></property>
<property name="name" value="Java班"></property>
</bean>
<!-- 創(chuàng)建 stu 對象 -->
<bean id="stu" class="com.hzit.entity.Student">
<property name="id" value="1"></property>
<property name="name">
<value><![CDATA[<張三>]]></value>
</property>
<property name="age" value="23"></property>
<!-- 將 classes 對象賦給 stu 對象 -->
<property name="classes" ref="classes"></property>
</bean>
再次獲取 Student 對象秤朗,結(jié)果如下圖所示煤蹭。
<img src="https://images.gitbook.cn/0d91b450-96e5-11e8-ab3d-7b3c8b8e2dff" width = "70%" />
在 spring.xml 中,通過 ref 屬性將其他 bean 賦給當(dāng)前 bean 對象取视,這種方式叫做依賴注入(DI)疯兼,是 Spring 非常重要的機(jī)制,DI 是將不同對象進(jìn)行關(guān)聯(lián)的一種方式贫途,是 IoC 的具體實(shí)現(xiàn)方式吧彪,通常 DI 和 IoC 是緊密結(jié)合在一起的,因此一般說的 IoC 包括 DI丢早。
如果是集合屬性如何依賴注入姨裸?
(1)Classes 類中添加 List<Student> 屬性。
public class Classes {
private int id;
private String name;
private List<Student> students;
}
(2)spring.xml 中配置 2 個(gè) student 對象怨酝、1 個(gè) classes 對象傀缩,并將 2 個(gè) student 對象注入到 classes 對象中。
<!-- 配置 classes 對象 -->
<bean id="classes" class="com.hzit.entity.Classes">
<property name="id" value="1"></property>
<property name="name" value="Java班"></property>
<property name="students">
<!-- 注入 student 對象 -->
<list>
<ref bean="stu"/>
<ref bean="stu2"/>
</list>
</property>
</bean>
<bean id="stu" class="com.hzit.entity.Student">
<property name="id" value="1"></property>
<property name="name">
<value><![CDATA[<張三>]]></value>
</property>
<property name="age" value="23"></property>
</bean>
<bean id="stu2" class="com.hzit.entity.Student">
<property name="id" value="2"></property>
<property name="name" value="李四"></property>
<property name="age" value="23"></property>
</bean>
運(yùn)行結(jié)果如下圖所示农猬。
<img src="https://images.gitbook.cn/1d27e0b0-96e5-11e8-ab3d-7b3c8b8e2dff" width = "70%" />
集合屬性通過 list 標(biāo)簽和 ref 標(biāo)簽完成注入赡艰,ref 的 bean 屬性指向需要注入的 bean 對象。
總結(jié)
這一講我們講解了 Spring IoC 的基本概念以及如何使用斤葱,IoC 是 Spring 的核心慷垮,這很重要。使用 Spring 開發(fā)項(xiàng)目時(shí)揍堕,控制層料身、業(yè)務(wù)層、DAO 層都是通過 IoC 來完成依賴注入的衩茸。
Spring 交流
我們?yōu)?a target="_blank">本課程付費(fèi)讀者創(chuàng)建了微信交流群芹血,以方便更有針對性地討論課程相關(guān)問題。入群方式請到第 1-4 課末尾添加小助手的微信號(hào),并注明「全家桶」幔烛。
閱讀文章過程中有任何疑問隨時(shí)可以跟其他小伙伴討論啃擦,或者直接向作者提問(作者看到后抽空回復(fù))。你的分享不僅幫助他人饿悬,更會(huì)提升自己令蛉。
溫馨提示:需購買才可入群哦,加小助手微信后需要截已購買的圖來驗(yàn)證~