基於XML的DI
DI 是ioc(控制反轉)的技術實現
ioc技術實現使用的DI(Dependency Injection) :依賴注入, 只需要在程式中提供要使用的對象名稱就可以, 至於對象如何在容器中創建,賦值,查找都由容器內部實現。
spring是使用的di實現了ioc的功能, spring底層創建對象,使用的是反射機制。
spring是一個容器,管理對象,給屬性賦值, 底層是反射創建對象
一、注入分類
bean 實例在調用無參構造器創建對象後,就要對 bean 對象的屬性進行初始化。
初始化是由容器自動完成的,稱為注入
根據注入方式的不同,常用的有兩類:set 注入、構造注入
二、set注入
set 注入也叫設值注入,是指通過 setter 方法傳入被調用者的實例,這種注入方式簡單、直觀,因而在 Spring 的依賴注入中大量使用
1. 簡單類型
項目的具體創建看上一篇就可以了,這裡直接寫重點
首先聲明一個Studnet的類
package com.md.b1;
/**
* @author MD
* @create 2020-08-07 19:55
*/
public class Student {
private String name;
private int age;
public Student() {
System.out.println("我是Student類的無參構造方法");
}
public void setName(String name) {
System.out.println("setName:"+name);
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
然後寫對應的配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="//www.springframework.org/schema/beans"
xmlns:xsi="//www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="//www.springframework.org/schema/beans //www.springframework.org/schema/beans/spring-beans.xsd">
<!--
聲明Student的對象
簡單類型:spring中java的基本數據類型和String都是簡單數據類型
di:給屬性賦值也就是注入
1. set注入 :spring來調用類的set方法,在set方法中完成屬性的賦值
1. 簡單類型的注入
<bean id="xx" class="yyy">
<property name="屬性名字" value="此屬性的值"/>
一個property只能給一個屬性賦值
<property....>
</bean>
必須要有屬性對應的set方法,沒有的話就報錯
但是set方法裡面的內容是你能控制,除了賦值,你還可以在set里多寫幾條java語句
-->
<bean id="student" class="com.md.b1.Student">
<property name="name" value="張三" /> <!-- setName("張三")-->
<property name="age" value="20"/>
</bean>
</beans>
結構圖
測試類
注意:此時由於這個文件不是直接在resources下面,而是在下面的b1包的下面,所以指定的路徑得加上
@Test
public void test01(){
String config = "b1/applicationContext.xml";
ApplicationContext ac = new ClassPathXmlApplicationContext(config);
// 從容器中獲取Student的對象
Student student = (Student) ac.getBean("student");
System.out.println(student);
// 我是Student類的無參構造方法
// setName:張三
// Student{name='張三', age=20}
}
注意一:沒有屬性但有set方法
還可以在Student的類中加入這個方法,
public void setEmail(String eamil) {
System.out.println("setEmail:"+eamil);
}
對應的配置文件
<bean id="student" class="com.md.b1.Student">
<property name="name" value="張三" /> <!-- setName("張三")-->
<property name="age" value="20"/>
<property name="email" value="[email protected]"/>
</bean>
此時在Student類中沒有email屬性,但是有setEmail方法,能順利執行不?
能,只要有對應的set方法都是正確的,無論屬性名是否存在
測試:
@Test
public void test01(){
// 注意:此時由於這個文件不是直接在resources下面,而是在下面的b1包的下面,所以指定的路徑得加上
String config = "b1/applicationContext.xml";
ApplicationContext ac = new ClassPathXmlApplicationContext(config);
// 從容器中獲取Student的對象
Student student = (Student) ac.getBean("student");
System.out.println(student);
// 我是Student類的無參構造方法
// setName:張三
// setEmail:[email protected]
// Student{name='張三', age=20}
}
注意二:對於非自定義的類
在配置文件中
<!--
非自定義類設置屬性
只有這個類中有setXXX(),就可以
-->
<bean id="mydate" class="java.util.Date">
<!--setTime(993462034956)-->
<property name="time" value="9348362034" />
</bean>
測試:
@Test
public void test02(){
String config = "b1/applicationContext.xml";
ApplicationContext ac = new ClassPathXmlApplicationContext(config);
Date mydate = (Date) ac.getBean("mydate");
System.out.println(mydate);
}
2. 引用類型
當指定 bean 的某屬性值為另一 bean 的實例時,通過 ref 指定它們間的引用關係
ref的值必須為某 bean 的 id 值
如下:
先創建一個School類
package com.md.b2;
/**
* @author MD
* @create 2020-08-07 20:40
*/
public class School {
private String name;
private String address;
public void setName(String name) {
this.name = name;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "School{" +
"name='" + name + '\'' +
", address='" + address + '\'' +
'}';
}
}
再創建一個Student類,在裡面引用School的類的對象
package com.md.b2;
/**
* @author MD
* @create 2020-08-07 19:55
*/
public class Student {
private String name;
private int age;
// 聲明一個引用數據類型
private School school;
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public void setSchool(School school) {
System.out.println("setSchool:"+school);
this.school = school;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", school=" + school +
'}';
}
}
寫對應的配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="//www.springframework.org/schema/beans"
xmlns:xsi="//www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="//www.springframework.org/schema/beans //www.springframework.org/schema/beans/spring-beans.xsd">
<!--
2. 引用數據類型的注入:spring來調用類的set方法,
<property name="屬性名稱" ref="bean的id也就是對象的名稱"/>
-->
<!--
聲明school對象
-->
<bean id="school" class="com.md.b2.School">
<property name="name" value="清華"/>
<property name="address" value="北京"/>
</bean>
<bean id="student" class="com.md.b2.Student">
<property name="name" value="張三"/>
<property name="age" value="40"/>
<!--
引用數據類型,調用的是setSchool(school),就是上面的
-->
<property name="school" ref="school"/>
</bean>
</beans>
測試:
@Test
public void test02(){
// 注意:此時由於這個文件不是直接在resources下面,而是在下面的b2包的下面,所以指定的路徑得加上
String config = "b2/applicationContext.xml";
ApplicationContext ac = new ClassPathXmlApplicationContext(config);
Student student = (Student) ac.getBean("student");
System.out.println(student);
// setSchool:School{name='清華', address='北京'}
// Student{name='張三', age=40, school=School{name='清華', address='北京'}}
}
三、構造注入
構造注入是指,spring在調用類的有參構造方法,在創建對象的同時,在構造方法中進行屬性的賦值
語法:使用<constructor-arg />標籤,具體看下面的使用
首先還在Student類中寫有參構造器
public Student(String name, int age, School school) {
System.out.println("我是Student類的有參構造方法");
this.name = name;
this.age = age;
this.school = school;
}
然後在配置文件中
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="//www.springframework.org/schema/beans"
xmlns:xsi="//www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="//www.springframework.org/schema/beans //www.springframework.org/schema/beans/spring-beans.xsd">
<!--
構造注入
spring在調用類的有參構造方法,在創建對象的同時,在構造方法中進行屬性的賦值
構造注入使用 <constructor-arg> 標籤
<constructor-arg> 標籤:一個<constructor-arg>表示構造方法一個參數。
<constructor-arg> 標籤屬性:
name:表示構造方法的形參名
index:表示構造方法的參數的位置,參數從左往右位置是 0 , 1 ,2的順序
value:構造方法的形參類型是簡單類型的,使用value
ref:構造方法的形參類型是引用類型的,使用ref
-->
<!--
聲明school對象
-->
<bean id="school" class="com.md.b3.School">
<property name="name" value="清華"/>
<property name="address" value="北京"/>
</bean>
<!--推薦用name-->
<!--調用類的有參構造方法-->
<!--<bean id="student" class="com.md.b3.Student">-->
<!--<constructor-arg name="name" value="張三"/>-->
<!--<constructor-arg name="age" value="30"/>-->
<!--<constructor-arg name="school" ref="school"/>-->
<!--</bean>-->
<!-- 或者這樣也是可以的,根據參數的位置-->
<!--<bean id="student" class="com.md.b3.Student">-->
<!--<constructor-arg index="0" value="張三"/>-->
<!--<constructor-arg index="1" value="30"/>-->
<!--<constructor-arg index="2" ref="school"/>-->
<!--</bean>-->
<!---->
<!--或者直接省略-->
<bean id="student" class="com.md.b3.Student">
<constructor-arg value="張三"/>
<constructor-arg value="30"/>
<constructor-arg ref="school"/>
</bean>
</beans>
測試:
@Test
public void test01(){
// 注意:此時由於這個文件不是直接在resources下面,而是在下面的b3包的下面,所以指定的路徑得加上
String config = "b3/applicationContext.xml";
ApplicationContext ac = new ClassPathXmlApplicationContext(config);
Student student = (Student) ac.getBean("student");
System.out.println(student);
// 我是Student類的有參構造方法
// Student{name='張三', age=30, school=School{name='清華', address='北京'}}
}
四、引用類型屬性自動注入
對於引用類型屬性的注入,也可不在配置文件中顯示的注入。可以通過為<bean/>標籤設置 autowire 屬性值,為引用類型屬性進行隱式自動注入(默認是不自動注入引用類型屬性)
根據自動注入判斷標準的不同,可以分為兩種:
- byName:根據名稱自動注入
- byType: 根據類型自動注入
1. byName 方式自動注入
當配置文件中被調用者 bean 的 id 值與程式碼中調用者 bean 類的屬性名相同時,可使用byName 方式,讓容器自動將被調用者 bean 注入給調用者 bean。容器是通過調用者的 bean類的屬性名與配置文件的被調用者 bean 的 id 進行比較而實現自動注入的
語法:
byName(按名稱注入) : java類中引用類型的屬性名和spring容器中(配置文件)<bean>的id名稱一樣,
且數據類型是一致的,這樣的容器中的bean,spring能夠賦值給引用類型。
語法:
<bean id="xx" class="yyy" autowire="byName">
簡單類型屬性賦值
</bean>
例子:
public class School {
private String name;
private String address;
// 省略set
}
//---------------------------------
public class Student {
private String name;
private int age;
// 聲明一個引用數據類型
private School school;
// 省略set
}
在配置文件中
<bean id="school" class="com.md.b4.School">
<property name="name" value="北大"/>
<property name="address" value="北京"/>
</bean>
<!--/////////////////////////////-->
<bean id="student" class="com.md.b4.Student" autowire="byName">
<property name="name" value="張三"/>
<property name="age" value="20"/>
<!--自動賦值引用數據類型-->
</bean>
如上:
java類中引用類型的屬性名school 和 spring容器中(配置文件)<bean>的id名稱一樣,且數據類型一致,這樣就能自動注入
2. byType 方式自動注入
使用 byType 方式自動注入,要求:配置文件中被調用者 bean 的 class 屬性指定的類,要與程式碼中調用者 bean 類的某引用類型屬性類型同源。即要麼相同,要麼有 is-a 關係(子類,或是實現類)
但這樣的同源的被調用 bean 只能有一個。多於一個,容器就不知該匹配哪一個了
語法:
byType(按類型注入) : java類中引用類型的數據類型和spring容器中(配置文件)<bean>的class屬性
是同源關係的,這樣的bean能夠賦值給引用類型
同源就是一類的意思:
1.java類中引用類型的數據類型和bean的class的值是一樣的。
2.java類中引用類型的數據類型和bean的class的值父子類關係的。
3.java類中引用類型的數據類型和bean的class的值介面和實現類關係的
語法:
<bean id="xx" class="yyy" autowire="byType">
簡單類型屬性賦值
</bean>
注意:在byType中, 在xml配置文件中聲明bean只能有一個符合條件的,
多餘一個是錯誤的
還是上面的例子:
五、指定多個 Spring 配置文件
在實際應用里,隨著應用規模的增加,系統中 Bean 數量也大量增加,導致配置文件變得非常龐大、臃腫。為了避免這種情況的產生,提高配置文件的可讀性與可維護性,可以將Spring 配置文件分解成多個配置文件,其中一個為主配置文件
total.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="//www.springframework.org/schema/beans"
xmlns:xsi="//www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="//www.springframework.org/schema/beans //www.springframework.org/schema/beans/spring-beans.xsd">
<!--
包含關係的配置文件:
total表示主配置文件 : 包含其他的配置文件的,主配置文件一般是不定義對象的。
語法:<import resource="其他配置文件的路徑" />
關鍵字:"classpath:" 表示類路徑(class文件所在的目錄),
在spring的配置文件中要指定其他文件的位置, 需要使用classpath,告訴spring到哪去載入讀取文件。
-->
<!--載入的是文件列表-->
<!--
<import resource="classpath:b4/spring-school.xml" />
<import resource="classpath:b4/spring-student.xml" />
-->
<!--/////////////////////////////-->
<!--
在包含關係的配置文件中,可以通配符(*:表示任意字元)
注意: 主的配置文件名稱不能包含在通配符的範圍內(不能叫做spring-total.xml)
-->
<import resource="classpath:b4/spring-*.xml" />
</beans>
六、總結
1. set注入
spring調用類的set方法實現屬性賦值
- 簡單類型的set注入:
<property name="屬性名" value="屬性的值"/>
- 引用類型的set注入:
<property name="屬性名" ref="bean的id"/>
2. 構造注入
spring調用有參數的構造方法
<constructor-arg>的name屬性,name表示構造方法的形參名
<constructor-arg>的index屬性,表示構造方法形參的位置,從0開始
3. 自動注入
由spring根據某些規則,給引用類型完成賦值,有byName、byType
- byName:按名稱注入,
java類中引用類型的屬性名和spring容器中bean的id一樣,數據類型一樣
- byType:按類型注入,
java類中引用類型的數據類型和spring容器中bean的class是同源關係