1、問題引入
在說Spring之前,首先來回顧一下我們之前學習的javaSE部分的知識,假如我們現在有一個需求:現在有兩個類(A類和B類),我們需要在第二個類(B)中調用第一個類(A)的某些方法,用我們以前學過的知識,肯定是在第二個類中實例化一個A類的對象,然後用對象調用對應的方法,具體操作如下:
但是這麼做並不是一個好的方法,因爲這樣做我們可以理解爲是A類 ‘污染’ 了B類,或者說是A類 ‘ 侵入 ’ 了B類。我們可以這麼理解,假如現在一個項目中有很多個類都用這種方式調用了A類的方法,但是隨着技術的更新有一天我們發現A類現在已經毫無存在的價值了,我們有了更好的方式去代替它,但是如果這個時候要刪掉A類,那麼每一個調用了A類這個方法的類都需要手動去修改源代碼。這樣就會給我們帶來很大的麻煩。因此,我們應該用另一種更好的方法區代替上面的操作。
這個時候我們可能會想到使用接口,讓A、B兩個類實現同樣的一個接口,然後覆寫接口裏面的方法,這樣看起來似乎也沒有多大的問題:
這樣看起來是和A類沒有多大的關係,但是使用這種還是擺脫不了new一個對象的命運。和這個比起來,我們更加希望:假如現在有這樣一個 ‘接口’ ,當我們需要哪一個類的對象的時候,由這個特殊的接口直接給我們返回這個對象的實例,而不用讓我們自己手動去new一個對象,增加代碼的耦合度。比如:
其實上面這種思想就是Spring中的IOC/DI思想。
2、IOC 和 DI
2.1 什麼是IOC和DI
IOC的全稱爲 Inversion of Control ,顧名思義就是控制反轉。
在傳統方法中,當一個類中需要另一個類的實例化對象,通常都是自己手動去new一個,而有了IOC之後,IOC中有一個容器,這個容器就相當於上面提到的“特殊的接口”(注意:容器不等於接口,我這裏說它是一個特殊的接口,只是舉個例子方便描述它的功能而已)。這個容器負責創建實例化對象,然後再在適當的時候主動將對象給需要依賴這個對象的類。
所謂的控制反轉:
- 控制的就是上面說的創建對象的過程(也就是new的過程);誰控制誰 —— IOC容器控制對象的創建
- 反轉:有反轉就有正轉,像我們的傳統方法,自己需要什麼就自己動手去創建什麼,這個就是正轉;而通過容器控制對象的創建以及依賴對象的注入,這個過程就是反轉。因爲是容器幫我們查找並注入依賴對象,所以對象只是被動的接收依賴對象,所以叫做反轉。反轉的就是依賴對象獲取的過程。
DI的全稱爲Dependency Injection,即“依賴注入”:是組件之間依賴關係由容器在運行期決定,形象的說,即由容器動態的將某個依賴關係注入到組件之中。依賴注入的目的並非爲軟件系統帶來更多功能,而是爲了提升組件重用的頻率,併爲系統搭建一個靈活、可擴展的平臺。通過依賴注入機制,我們只需要通過簡單的配置,而無需任何代碼就可指定目標需要的資源,完成自身的業務邏輯,而不需要關心具體的資源來自何處,由誰實現。
依賴注入和控制反轉之間的關係:
依賴注入和控制反轉其實描述的是同一個東西,只是從不同的角度來描述而已。控制反轉就是爲一個對象動態的提供它所需要的另一個對象,而這個過程是通過依賴注入來實現的。比如:B類需要A類的一個對象,然後容器new一個A的對象,在適當的時候,就像打針一樣,把A的對象注射到B裏面,這樣就實現了依賴注入。
IOC不是什麼新技術,而是一種思想。下面在通過一個簡單的例子去理解IOC和傳統方法的區別;
就拿找女朋友來說,剛開始,我們是在茫茫人海中自己去尋找,我們自己知道我們需要的是怎麼樣一個人,當我們發現一個目標之後,開始自己私下去打聽她的qq,微型,手機號......製造巧合去認識她,然後投其所好......這一系列的過程都需要我們自己去操作,然而,現在有了IOC,就相當於是一個婚介所,他們那裏存了整個村子的男男女女的資料,這個時候我們只需要將我們的要求告訴他們,然後讓他們找到合適的對象之後再主動給我,如果她給我的這個對象合適了,我們就談談戀愛、然後結婚......如果不合適,就拋出異常。這整個過程不用我們自己去操作,而是交給了婚介所這樣的一個容器去控制整個過程。Spring也就是這個思想:所有的類都會在spring容器中登記,告訴spring你是個什麼東西,你需要什麼東西,然後spring會在系統運行到適當的時候,把你要的東西主動給你,同時也把你交給其他需要你的東西。所有的類的創建、銷燬都由 spring來控制,也就是說控制對象生存週期的不再是引用它的對象,而是spring。對於某個具體的對象而言,以前是它控制其他對象,現在是所有對象都被spring控制,所以這叫控制反轉。
3、在代碼中簡單體驗IOC和DI
繼續使用上面的A,B兩個類:
A類:
在配置文件中配置A類
在B類中獲取A類的實例對象
運行結果:
如果是新手,看來這個可能還是一臉茫然,不知道是怎麼回事,下面會有完整的Spring項目創建以及IOC和DI的實例;
4、Spring屬性實例化案例
使用工具:IDEA+maven
首先,創建一個maven項目(前提是你電腦中已經安裝好了maven)
早maven項目的pom文件中導入依賴的jar包:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
</dependencies>
在resource文件下創建一個配置文件(.xml) (如 applicationContext.xml):
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--在這裏配置類、屬性信息-->
</beans>
這是下面例子的目錄結構:
幾個類結構:
import java.util.*;
/**
* 測試各種數據類型的屬性注入
*/
public class MyTest {
private Integer intVal ; // 整形數據
private String strVal ; // String 數據
private String[] strArr ; // 數組類型
private List list ; // List集合
private Set set ; // Set集合
private Map map; // Map集合
private User user; //自定義類型
private Date date; // 日期類型
// 爲每一個屬性添加一個get和set方法 通過set方法實現屬性注入,所以必須要有set方法
public void setIntVal(Integer intVal) {
this.intVal = intVal;
}
public void setStrVal(String strVal) {
this.strVal = strVal;
}
public void setStrArr(String[] strArr) {
this.strArr = strArr;
}
public void setList(List list) {
this.list = list;
}
public void setSet(Set set) {
this.set = set;
}
public void setMap(Map map) {
this.map = map;
}
public void setUser(User user) {
this.user = user;
}
public void setDate(Date date) {
this.date = date;
}
// 覆寫toString方法用於打印屬性信息
@Override
public String toString() {
return " intVal = " + intVal +
"\r\n strVal = '" + strVal + '\'' +
"\r\n strArr = " + Arrays.toString(strArr) +
"\r\n list = " + list +
"\r\n set = " + set +
"\r\n map = " + map +
"\r\n date = " + date +
"\r\n user = " + user ;
}
}
/**
* 用於測試自定義屬性注入的User類
*/
public class User {
private Integer age ;
private String name ;
public void setAge(Integer age) {
this.age = age;
}
public void setName(String name) {
this.name = name;
}
// 覆寫toString方法用於打印信息
@Override
public String toString() {
return "User{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
4.1 通過set方法實例化:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--在這裏配置類、屬性信息-->
<bean id="test" class="per.fei._3SpringProjectTest.MyTest">
<!--整形的注入-->
<property name="intVal">
<value>18</value>
</property>
<!--整形也可以這麼弄-->
<!--這裏寫的是String ("18"),而類中的類型是Integer ( = 18),spring內部實現了從String到Integer的轉換-->
<!--<property name="intVal" value="18"/>-->
<!--String類型-->
<property name="strVal" value="hahaha"/>
<!--數組類型-->
<property name="strArr">
<list>
<value>strArr_1</value>
<value>strArr_2</value>
<value>strArr_3</value>
</list>
</property>
<!--list就是一個動態數組,所以和數組基本一樣-->
<property name="list">
<list>
<value>list_1</value>
<value>list_2</value>
<value>list_3</value>
</list>
</property>
<!--set類型的數據-->
<property name="set">
<set>
<value>set_01</value>
<value>set_02</value>
<value>set_03</value>
</set>
</property>
<!--map類型-->
<property name="map" >
<map>
<entry key="1" value="no_1"/>
<entry key="2" value="no_2"/>
<entry key="3" value="no_3"/>
</map>
</property>
<!--Date類型不能直接像下面這麼弄,要先寫一個類型轉換器(MyConvert.java),
因爲Spring雖然在上面說實現了從String到Integer的類型轉換,-->
<!--但是沒有實現從String到Date的類型轉換,需要我們自己實現一個轉換器-->
<property name="date" value="2019-5-21"/>
<!--自定義類型-可以先配置對應的自定義類型的bean,然後通過ref(反射)直接獲取對應的自定義類型-->
<property name="user" ref="user"/>
</bean>
<!--配置User類-->
<bean id="user" class="per.fei._3SpringProjectTest.User">
<property name="name" value="張三"/>
<property name="age" value="22"/>
</bean>
<!--自定義屬性編輯器到Spring中-->
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="customEditors">
<map >
<entry key="java.util.Date" value="per.fei._3SpringProjectTest.MyConvert"/>
</map>
</property>
</bean>
</beans>
測試結果:
4.2 通過構造方法實例化:
(這裏需要把上面的MyTest類中的set方法換成構造方法,其餘類結構的不變)
MyTest.java
import java.util.*;
/**
* 測試各種數據類型的屬性注入
*/
public class MyTest {
private Integer intVal ; // 整形數據
private String strVal ; // String 數據
private String[] strArr ; // 數組類型
private List list ; // List集合
private Set set ; // Set集合
private Map map; // Map集合
private User user; //自定義類型
private Date date; // 日期類型
// 添加構造方法
public MyTest(Integer intVal, String strVal, String[] strArr, List list, Set set, Map map, User user, Date date) {
this.intVal = intVal;
this.strVal = strVal;
this.strArr = strArr;
this.list = list;
this.set = set;
this.map = map;
this.user = user;
this.date = date;
}
// 覆寫toString方法用於打印屬性信息
@Override
public String toString() {
return " intVal = " + intVal +
"\r\n strVal = '" + strVal + '\'' +
"\r\n strArr = " + Arrays.toString(strArr) +
"\r\n list = " + list +
"\r\n set = " + set +
"\r\n map = " + map +
"\r\n date = " + date +
"\r\n user = " + user ;
}
}
配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--在這裏配置類、屬性信息-->
<bean id="test" class="per.fei._3SpringProjectTest.MyTest">
<!--int型-->
<constructor-arg name="intVal" value="18"/>
<!--String類型-->
<constructor-arg name="strVal" value="hahaha"/>
<!--數組類型-->
<constructor-arg name="strArr">
<list>
<value>strArr_01</value>
<value>strArr_02</value>
<value>strArr_03</value>
</list>
</constructor-arg>
<!--list類-->
<constructor-arg name="list">
<list>
<value>list_01</value>
<value>list_02</value>
<value>list_03</value>
</list>
</constructor-arg>
<!--set類型-->
<constructor-arg name="set">
<set>
<value>set_01</value>
<value>set_02</value>
<value>set_03</value>
</set>
</constructor-arg>
<!--map類型-->
<constructor-arg name="map">
<map>
<entry key="mapKey_01" value="mapVal_01"/>
<entry key="mapKey_02" value="mapVal_02"/>
<entry key="mapKey_03" value="mapVal_03"/>
</map>
</constructor-arg>
<!--自定義類User-->
<constructor-arg name="user" ref="user"/>
<!--Date類-->
<constructor-arg name="date" value="2019-5-21"/>
</bean>
<!--配置User類-->
<bean id="user" class="per.fei._3SpringProjectTest.User">
<property name="name" value="張三"/>
<property name="age" value="22"/>
</bean>
<!--自定義屬性編輯器到Spring中-->
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="customEditors">
<map >
<entry key="java.util.Date" value="per.fei._3SpringProjectTest.MyConvert"/>
</map>
</property>
</bean>
</beans>
Test類運行結果: