二、Spring
1、Spring簡介
1.1、Spring概述
官網(wǎng)地址:https://spring.io/
Spring 是最受歡迎的企業(yè)級 Java 應(yīng)用程序開發(fā)框架,數(shù)以百萬的來自世界各地的開發(fā)人員使用
Spring 框架來創(chuàng)建性能好、易于測試、可重用的代碼。
Spring 框架是一個(gè)開源的 Java 平臺(tái),它最初是由 Rod Johnson 編寫的,并且于 2003 年 6 月首
次在 Apache 2.0 許可下發(fā)布。
Spring 是輕量級的框架,其基礎(chǔ)版本只有 2 MB 左右的大小。
Spring 框架的核心特性是可以用于開發(fā)任何 Java 應(yīng)用程序,但是在 Java EE 平臺(tái)上構(gòu)建 web 應(yīng)
用程序是需要擴(kuò)展的。 Spring 框架的目標(biāo)是使 J2EE 開發(fā)變得更容易使用,通過啟用基于 POJO
編程模型來促進(jìn)良好的編程實(shí)踐。
1.2、Spring家族
項(xiàng)目列表:https://spring.io/projects
1.3、Spring Framework
Spring 基礎(chǔ)框架,可以視為 Spring 基礎(chǔ)設(shè)施,基本上任何其他 Spring 項(xiàng)目都是以 Spring Framework為基礎(chǔ)的。
1.3.1、Spring Framework特性
- 非侵入式:使用 Spring Framework 開發(fā)應(yīng)用程序時(shí),Spring 對應(yīng)用程序本身的結(jié)構(gòu)影響非常
小。對領(lǐng)域模型可以做到零污染;對功能性組件也只需要使用幾個(gè)簡單的注解進(jìn)行標(biāo)記,完全不會(huì)
破壞原有結(jié)構(gòu),反而能將組件結(jié)構(gòu)進(jìn)一步簡化。這就使得基于 Spring Framework 開發(fā)應(yīng)用程序
時(shí)結(jié)構(gòu)清晰、簡潔優(yōu)雅。
- 控制反轉(zhuǎn):IOC——Inversion of Control,翻轉(zhuǎn)資源獲取方向。把自己創(chuàng)建資源、向環(huán)境索取資源
變成環(huán)境將資源準(zhǔn)備好,我們享受資源注入。
- 面向切面編程:AOP——Aspect Oriented Programming,在不修改源代碼的基礎(chǔ)上增強(qiáng)代碼功
能。
- 容器:Spring IOC 是一個(gè)容器,因?yàn)樗⑶夜芾斫M件對象的生命周期。組件享受到了容器化
的管理,替程序員屏蔽了組件創(chuàng)建過程中的大量細(xì)節(jié),極大的降低了使用門檻,大幅度提高了開發(fā)
效率。
- 組件化:Spring 實(shí)現(xiàn)了使用簡單的組件配置組合成一個(gè)復(fù)雜的應(yīng)用。在 Spring 中可以使用 XML
和 Java 注解組合這些對象。這使得我們可以基于一個(gè)個(gè)功能明確、邊界清晰的組件有條不紊的搭
建超大型復(fù)雜應(yīng)用系統(tǒng)。
-
聲明式:很多以前需要編寫代碼才能實(shí)現(xiàn)的功能,現(xiàn)在只需要聲明需求即可由框架代為實(shí)現(xiàn)。
-
一站式:在 IOC 和 AOP 的基礎(chǔ)上可以整合各種企業(yè)應(yīng)用的開源框架和優(yōu)秀的第三方類庫。而且
Spring 旗下的項(xiàng)目已經(jīng)覆蓋了廣泛領(lǐng)域,很多方面的功能性需求可以在 Spring Framework 的基
礎(chǔ)上全部使用 Spring 來實(shí)現(xiàn)。
1.3.2、Spring Framework五大功能模塊
功能模塊 | 功能介紹 |
---|---|
Core Container | 核心容器,在 Spring 環(huán)境下使用任何功能都必須基于 IOC 容器。 |
AOP&Aspects | 面向切面編程 |
Testing | 提供了對 junit 或 TestNG 測試框架的整合。 |
Data Access/Integration | 提供了對數(shù)據(jù)訪問/集成的功能。 |
Spring MVC | 提供了面向Web應(yīng)用程序的集成功能。 |
2、IOC
2.1、IOC容器
2.1.1、IOC思想
IOC:Inversion of Control,翻譯過來是反轉(zhuǎn)控制。
①獲取資源的傳統(tǒng)方式
自己做飯:買菜、洗菜、擇菜、改刀、炒菜,全過程參與,費(fèi)時(shí)費(fèi)力,必須清楚了解資源創(chuàng)建整個(gè)過程中的全部細(xì)節(jié)且熟練掌握。
在應(yīng)用程序中的組件需要獲取資源時(shí),傳統(tǒng)的方式是組件主動(dòng)的從容器中獲取所需要的資源,在這樣的
模式下開發(fā)人員往往需要知道在具體容器中特定資源的獲取方式,增加了學(xué)習(xí)成本,同時(shí)降低了開發(fā)效率。
②反轉(zhuǎn)控制方式獲取資源
點(diǎn)外賣:下單、等、吃,省時(shí)省力,不必關(guān)心資源創(chuàng)建過程的所有細(xì)節(jié)。
反轉(zhuǎn)控制的思想完全顛覆了應(yīng)用程序組件獲取資源的傳統(tǒng)方式:反轉(zhuǎn)了資源的獲取方向——改由容器主動(dòng)的將資源推送給需要的組件,開發(fā)人員不需要知道容器是如何創(chuàng)建資源對象的,只需要提供接收資源的方式即可,極大的降低了學(xué)習(xí)成本,提高了開發(fā)的效率。這種行為也稱為查找的被動(dòng)形式。
③DI
DI:Dependency Injection,翻譯過來是依賴注入。
DI 是 IOC 的另一種表述方式:即組件以一些預(yù)先定義好的方式(例如:setter 方法)接受來自于容器
的資源注入。相對于IOC而言,這種表述更直接。
所以結(jié)論是:IOC 就是一種反轉(zhuǎn)控制的思想, 而 DI 是對 IOC 的一種具體實(shí)現(xiàn)。
2.1.2、IOC容器在Spring中的實(shí)現(xiàn)
Spring 的 IOC 容器就是 IOC 思想的一個(gè)落地的產(chǎn)品實(shí)現(xiàn)。IOC 容器中管理的組件也叫做 bean。在創(chuàng)建bean 之前,首先需要?jiǎng)?chuàng)建 IOC 容器。Spring 提供了 IOC 容器的兩種實(shí)現(xiàn)方式:
①BeanFactory
這是 IOC 容器的基本實(shí)現(xiàn),是 Spring 內(nèi)部使用的接口。面向 Spring 本身,不提供給開發(fā)人員使用。
②ApplicationContext
BeanFactory 的子接口,提供了更多高級特性。面向 Spring 的使用者,幾乎所有場合都使用
ApplicationContext 而不是底層的 BeanFactory。
③ApplicationContext的主要實(shí)現(xiàn)類
類型名 | 簡介 |
---|---|
ClassPathXmlApplicationContext | 通過讀取類路徑下的 XML 格式的配置文件創(chuàng)建 IOC 容器對象 |
FileSystemXmlApplicationContext | 通過文件系統(tǒng)路徑讀取 XML 格式的配置文件創(chuàng)建 IOC 容器對象 |
ConfigurableApplicationContext | ApplicationContext 的子接口,包含一些擴(kuò)展方法refresh() 和 close() ,讓 ApplicationContext 具有啟動(dòng)、關(guān)閉和刷新上下文的能力。 |
WebApplicationContext | 專門為 Web 應(yīng)用準(zhǔn)備,基于 Web 環(huán)境創(chuàng)建 IOC 容器對象,并將對象引入存入 ServletContext 域中。 |
2.2、基于XML管理bean
2.2.1、實(shí)驗(yàn)一:入門案例
①創(chuàng)建Maven Module
②引入依賴
<dependencies>
<!-- 基于Maven依賴傳遞性,導(dǎo)入spring-context依賴即可導(dǎo)入當(dāng)前所需所有jar包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.1</version>
</dependency>
<!-- junit測試 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
③創(chuàng)建類HelloWorld
public class HelloWorld {
public void sayHello(){
System.out.println("helloworld");
}
}
⑤在Spring的配置文件中配置bean
<!--
配置HelloWorld所對應(yīng)的bean,即將HelloWorld的對象交給Spring的IOC容器管理
通過bean標(biāo)簽配置IOC容器所管理的bean
屬性:
id:設(shè)置bean的唯一標(biāo)識
class:設(shè)置bean所對應(yīng)類型的全類名
-->
<bean id="helloworld" class="com.atguigu.spring.bean.HelloWorld"></bean>
⑥創(chuàng)建測試類測試
@Test
public void testHelloWorld(){
ApplicationContext ac = newClassPathXmlApplicationContext("applicationContext.xml");
HelloWorld helloworld = (HelloWorld) ac.getBean("helloworld");
helloworld.sayHello();
}
⑦思路
⑧注意
Spring 底層默認(rèn)通過反射技術(shù)調(diào)用組件類的無參構(gòu)造器來創(chuàng)建組件對象,這一點(diǎn)需要注意。如果在需要無參構(gòu)造器時(shí),沒有無參構(gòu)造器,則會(huì)拋出下面的異常:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name
‘helloworld’ defined in class path resource [applicationContext.xml]: Instantiation of bean
failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed
to instantiate [com.atguigu.spring.bean.HelloWorld]: No default constructor found; nested
exception is java.lang.NoSuchMethodException: com.atguigu.spring.bean.HelloWorld.
()
2.2.2、實(shí)驗(yàn)二:獲取bean
①方式一:根據(jù)id獲取
由于 id 屬性指定了 bean 的唯一標(biāo)識,所以根據(jù) bean 標(biāo)簽的 id 屬性可以精確獲取到一個(gè)組件對象。
上個(gè)實(shí)驗(yàn)中我們使用的就是這種方式。
②方式二:根據(jù)類型獲取
@Test
public void testHelloWorld(){
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
HelloWorld bean = ac.getBean(HelloWorld.class);
bean.sayHello();
}
③方式三:根據(jù)id和類型
@Test
public void testHelloWorld(){
ApplicationContext ac = newClassPathXmlApplicationContext("applicationContext.xml");
HelloWorld bean = ac.getBean("helloworld", HelloWorld.class);
bean.sayHello();
}
④注意
當(dāng)根據(jù)類型獲取bean時(shí),要求IOC容器中指定類型的bean有且只能有一個(gè)
當(dāng)IOC容器中一共配置了兩個(gè):
<bean id="helloworldOne" class="com.atguigu.spring.bean.HelloWorld"></bean>
<bean id="helloworldTwo" class="com.atguigu.spring.bean.HelloWorld"></bean>
根據(jù)類型獲取時(shí)會(huì)拋出異常:
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean
of type ‘com.atguigu.spring.bean.HelloWorld’ available: expected single matching bean but
found 2: helloworldOne,helloworldTwo
⑤擴(kuò)展
如果組件類實(shí)現(xiàn)了接口,根據(jù)接口類型可以獲取 bean 嗎?
可以,前提是bean唯一
如果一個(gè)接口有多個(gè)實(shí)現(xiàn)類,這些實(shí)現(xiàn)類都配置了 bean,根據(jù)接口類型可以獲取 bean 嗎?
不行,因?yàn)閎ean不唯一
⑥結(jié)論
根據(jù)類型來獲取bean時(shí),在滿足bean唯一性的前提下,其實(shí)只是看:『對象 instanceof 指定的類
型』的返回結(jié)果,只要返回的是true就可以認(rèn)定為和類型匹配,能夠獲取到。
2.2.3、實(shí)驗(yàn)三:依賴注入之setter注入
①創(chuàng)建學(xué)生類Student
public class Student {
private Integer id;
private String name;
private Integer age;
private String sex;
public Student() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
'}';
}
}
②配置bean時(shí)為屬性賦值
<bean id="studentOne" class="com.atguigu.spring.bean.Student">
<!-- property標(biāo)簽:通過組件類的setXxx()方法給組件對象設(shè)置屬性 -->
<!-- name屬性:指定屬性名(這個(gè)屬性名是getXxx()、setXxx()方法定義的,和成員變量無關(guān))-->
<!-- value屬性:指定屬性值 -->
<property name="id" value="1001"></property>
<property name="name" value="張三"></property>
<property name="age" value="23"></property>
<property name="sex" value="男"></property>
</bean>
③測試
@Test
public void testDIBySet(){
ApplicationContext ac = new ClassPathXmlApplicationContext("springdi.xml");
Student studentOne = ac.getBean("studentOne", Student.class);
System.out.println(studentOne);
}
2.2.4、實(shí)驗(yàn)四:依賴注入之構(gòu)造器注入
①在Student類中添加有參構(gòu)造
public Student(Integer id, String name, Integer age, String sex) {
this.id = id;
this.name = name;
this.age = age;
this.sex = sex;
}
②配置bean
<bean id="studentTwo" class="com.atguigu.spring.bean.Student">
<constructor-arg value="1002"></constructor-arg>
<constructor-arg value="李四"></constructor-arg>
<constructor-arg value="33"></constructor-arg>
<constructor-arg value="女"></constructor-arg>
</bean>
注意:
constructor-arg標(biāo)簽還有兩個(gè)屬性可以進(jìn)一步描述構(gòu)造器參數(shù):
- index屬性:指定參數(shù)所在位置的索引(從0開始)
- name屬性:指定參數(shù)名
③測試
@Test
public void testDIBySet(){
ApplicationContext ac = new ClassPathXmlApplicationContext("springdi.xml");
Student studentOne = ac.getBean("studentTwo", Student.class);
System.out.println(studentOne);
}
2.2.5、實(shí)驗(yàn)五:特殊值處理
①字面量賦值
什么是字面量?
int a = 10;
聲明一個(gè)變量a,初始化為10,此時(shí)a就不代表字母a了,而是作為一個(gè)變量的名字。當(dāng)我們引用a
的時(shí)候,我們實(shí)際上拿到的值是10。
而如果a是帶引號的:‘a(chǎn)’,那么它現(xiàn)在不是一個(gè)變量,它就是代表a這個(gè)字母本身,這就是字面
量。所以字面量沒有引申含義,就是我們看到的這個(gè)數(shù)據(jù)本身。
<!-- 使用value屬性給bean的屬性賦值時(shí),Spring會(huì)把value屬性的值看做字面量 -->
<property name="name" value="張三"/>
②null值
<property name="name">
<null />
</property>
注意:
<property name="name" value="null"></property>
以上寫法,為name所賦的值是字符串null
③xml實(shí)體
<!-- 小于號在XML文檔中用來定義標(biāo)簽的開始,不能隨便使用 -->
<!-- 解決方案一:使用XML實(shí)體來代替 -->
<property name="expression" value="a < b"/>
④CDATA節(jié)
<property name="expression">
<!-- 解決方案二:使用CDATA節(jié) -->
<!-- CDATA中的C代表Character,是文本、字符的含義,CDATA就表示純文本數(shù)據(jù) -->
<!-- XML解析器看到CDATA節(jié)就知道這里是純文本,就不會(huì)當(dāng)作XML標(biāo)簽或?qū)傩詠斫馕?-->
<!-- 所以CDATA節(jié)中寫什么符號都隨意 -->
<value><![CDATA[a < b]]></value>
</property>
2.2.6、實(shí)驗(yàn)六:為類類型屬性賦值
①創(chuàng)建班級類Clazz
public class Clazz {
private Integer clazzId;
private String clazzName;
public Integer getClazzId() {
return clazzId;
}
public void setClazzId(Integer clazzId) {
this.clazzId = clazzId;
}
public String getClazzName() {
return clazzName;
}
public void setClazzName(String clazzName) {
this.clazzName = clazzName;
}
@Override
public String toString() {
return "Clazz{" +
"clazzId=" + clazzId +
", clazzName='" + clazzName + '\'' +
'}';
}
public Clazz() {
}
public Clazz(Integer clazzId, String clazzName) {
this.clazzId = clazzId;
this.clazzName = clazzName;
}
}
②修改Student類
在Student類中添加以下代碼:
private Clazz clazz;
public Clazz getClazz() {
return clazz;
}
public void setClazz(Clazz clazz) {
this.clazz = clazz;
}
③方式一:引用外部已聲明的bean
配置Clazz類型的bean:
<bean id="clazzOne" class="com.atguigu.spring.bean.Clazz">
<property name="clazzId" value="1111"></property>
<property name="clazzName" value="財(cái)源滾滾班"></property>
</bean>
為Student中的clazz屬性賦值:
<bean id="studentFour" class="com.atguigu.spring.bean.Student">
<property name="id" value="1004"></property>
<property name="name" value="趙六"></property>
<property name="age" value="26"></property>
<property name="sex" value="女"></property>
<!-- ref屬性:引用IOC容器中某個(gè)bean的id,將所對應(yīng)的bean為屬性賦值 -->
<property name="clazz" ref="clazzOne"></property>
</bean>
錯(cuò)誤演示:
<bean id="studentFour" class="com.atguigu.spring.bean.Student">
<property name="id" value="1004"></property>
<property name="name" value="趙六"></property>
<property name="age" value="26"></property>
<property name="sex" value="女"></property>
<property name="clazz" value="clazzOne"></property>
</bean>
如果錯(cuò)把ref屬性寫成了value屬性,會(huì)拋出異常: Caused by: java.lang.IllegalStateException:
Cannot convert value of type ‘java.lang.String’ to required type
‘com.atguigu.spring.bean.Clazz’ for property ‘clazz’: no matching editors or conversion
strategy found
意思是不能把String類型轉(zhuǎn)換成我們要的Clazz類型,說明我們使用value屬性時(shí),Spring只把這個(gè)
屬性看做一個(gè)普通的字符串,不會(huì)認(rèn)為這是一個(gè)bean的id,更不會(huì)根據(jù)它去找到bean來賦值
④方式二:內(nèi)部bean
<bean id="studentFour" class="com.atguigu.spring.bean.Student">
<property name="id" value="1004"></property>
<property name="name" value="趙六"></property>
<property name="age" value="26"></property>
<property name="sex" value="女"></property>
<property name="clazz">
<!-- 在一個(gè)bean中再聲明一個(gè)bean就是內(nèi)部bean -->
<!-- 內(nèi)部bean只能用于給屬性賦值,不能在外部通過IOC容器獲取,因此可以省略id屬性 -->
<bean id="clazzInner" class="com.atguigu.spring.bean.Clazz">
<property name="clazzId" value="2222"></property>
<property name="clazzName" value="遠(yuǎn)大前程班"></property>
</bean>
</property>
</bean>
③方式三:級聯(lián)屬性賦值
<bean id="studentFour" class="com.atguigu.spring.bean.Student">
<property name="id" value="1004"></property>
<property name="name" value="趙六"></property>
<property name="age" value="26"></property>
<property name="sex" value="女"></property>
<!-- 一定先引用某個(gè)bean為屬性賦值,才可以使用級聯(lián)方式更新屬性 -->
<property name="clazz" ref="clazzOne"></property>
<property name="clazz.clazzId" value="3333"></property>
<property name="clazz.clazzName" value="最強(qiáng)王者班"></property>
</bean>
2.2.7、實(shí)驗(yàn)七:為數(shù)組類型屬性賦值
①修改Student類
在Student類中添加以下代碼:
private String[] hobbies;
public String[] getHobbies() {
return hobbies;
}
public void setHobbies(String[] hobbies) {
this.hobbies = hobbies;
}
②配置bean
<bean id="studentFour" class="com.atguigu.spring.bean.Student">
<property name="id" value="1004"></property>
<property name="name" value="趙六"></property>
<property name="age" value="26"></property>
<property name="sex" value="女"></property>
<!-- ref屬性:引用IOC容器中某個(gè)bean的id,將所對應(yīng)的bean為屬性賦值 -->
<property name="clazz" ref="clazzOne"></property>
<property name="hobbies">
<array>
<value>抽煙</value>
<value>喝酒</value>
<value>燙頭</value>
</array>
</property>
</bean>
2.2.8、實(shí)驗(yàn)八:為集合類型屬性賦值
①為List集合類型屬性賦值
在Clazz類中添加以下代碼:
private List<Student> students;
public List<Student> getStudents() {
return students;
}
public void setStudents(List<Student> students) {
this.students = students;
}
配置bean:
<bean id="clazzTwo" class="com.atguigu.spring.bean.Clazz">
<property name="clazzId" value="4444"></property>
<property name="clazzName" value="Javaee0222"></property>
<property name="students">
<list>
<ref bean="studentOne"></ref>
<ref bean="studentTwo"></ref>
<ref bean="studentThree"></ref>
</list>
</property>
</bean>
若為Set集合類型屬性賦值,只需要將其中的list標(biāo)簽改為set標(biāo)簽即可
②為Map集合類型屬性賦值
創(chuàng)建教師類Teacher:
public class Teacher {
private Integer teacherId;
private String teacherName;
public Integer getTeacherId() {
return teacherId;
}
public void setTeacherId(Integer teacherId) {
this.teacherId = teacherId;
}
public String getTeacherName() {
return teacherName;
}
public void setTeacherName(String teacherName) {
this.teacherName = teacherName;
}
public Teacher(Integer teacherId, String teacherName) {
this.teacherId = teacherId;
this.teacherName = teacherName;
}
public Teacher() {
}
@Override
public String toString() {
return "Teacher{" +
"teacherId=" + teacherId +
", teacherName='" + teacherName + '\'' +
'}';
}
}
在Student類中添加以下代碼:
private Map<String, Teacher> teacherMap;
public Map<String, Teacher> getTeacherMap() {
return teacherMap;
}
public void setTeacherMap(Map<String, Teacher> teacherMap) {
this.teacherMap = teacherMap;
}
配置bean:
<bean id="teacherOne" class="com.atguigu.spring.bean.Teacher">
<property name="teacherId" value="10010"></property>
<property name="teacherName" value="大寶"></property>
</bean>
<bean id="teacherTwo" class="com.atguigu.spring.bean.Teacher">
<property name="teacherId" value="10086"></property>
<property name="teacherName" value="二寶"></property>
</bean>
<bean id="studentFour" class="com.atguigu.spring.bean.Student">
<property name="id" value="1004"></property>
<property name="name" value="趙六"></property>
<property name="age" value="26"></property>
<property name="sex" value="女"></property>
<!-- ref屬性:引用IOC容器中某個(gè)bean的id,將所對應(yīng)的bean為屬性賦值 -->
<property name="clazz" ref="clazzOne"></property>
<property name="hobbies">
<array>
<value>抽煙</value>
<value>喝酒</value>
<value>燙頭</value>
</array>
</property>
<property name="teacherMap">
<map>
<entry>
<key>
<value>10010</value>
</key>
<ref bean="teacherOne"></ref>
</entry>
<entry>
<key>
<value>10086</value>
</key>
<ref bean="teacherTwo"></ref>
</entry>
</map>
</property>
</bean>
③引用集合類型的bean
<!--list集合類型的bean-->
<util:list id="students">
<ref bean="studentOne"></ref>
<ref bean="studentTwo"></ref>
<ref bean="studentThree"></ref>
</util:list>
<!--map集合類型的bean-->
<util:map id="teacherMap">
<entry>
<key>
<value>10010</value>
</key>
<ref bean="teacherOne"></ref>
</entry>
<entry>
<key>
<value>10086</value>
</key>
<ref bean="teacherTwo"></ref>
</entry>
</util:map>
<bean id="clazzTwo" class="com.atguigu.spring.bean.Clazz">
<property name="clazzId" value="4444"></property>
<property name="clazzName" value="Javaee0222"></property>
<property name="students" ref="students"></property>
</bean>
<bean id="studentFour" class="com.atguigu.spring.bean.Student">
<property name="id" value="1004"></property>
<property name="name" value="趙六"></property>
<property name="age" value="26"></property>
<property name="sex" value="女"></property>
<!-- ref屬性:引用IOC容器中某個(gè)bean的id,將所對應(yīng)的bean為屬性賦值 -->
<property name="clazz" ref="clazzOne"></property>
<property name="hobbies">
<array>
<value>抽煙</value>
<value>喝酒</value>
<value>燙頭</value>
</array>
</property>
<property name="teacherMap" ref="teacherMap"></property>
</bean>
使用util:list、util:map標(biāo)簽必須引入相應(yīng)的命名空間,可以通過idea的提示功能選擇
2.2.9、實(shí)驗(yàn)九:p命名空間
引入p命名空間后,可以通過以下方式為bean的各個(gè)屬性賦值
<bean id="studentSix" class="com.atguigu.spring.bean.Student"
p:id="1006" p:name="小明" p:clazz-ref="clazzOne" p:teacherMap-ref="teacherMap"></bean>
2.2.10、實(shí)驗(yàn)十:引入外部屬性文件
①加入依賴
<!-- MySQL驅(qū)動(dòng) -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>
<!-- 數(shù)據(jù)源 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.31</version>
</dependency>
②創(chuàng)建外部屬性文件
jdbc.user=root
jdbc.password=atguigu
jdbc.url=jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC
jdbc.driver=com.mysql.cj.jdbc.Driver
③引入屬性文件
<!-- 引入外部屬性文件 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
④配置bean
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="url" value="${jdbc.url}"/>
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="username" value="${jdbc.user}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
⑤測試
@Test
public void testDataSource() throws SQLException {
ApplicationContext ac = new ClassPathXmlApplicationContext("spring-datasource.xml");
DataSource dataSource = ac.getBean(DataSource.class);
Connection connection = dataSource.getConnection();
System.out.println(connection);
}
2.2.11、實(shí)驗(yàn)十一:bean的作用域
①概念
在Spring中可以通過配置bean標(biāo)簽的scope屬性來指定bean的作用域范圍,各取值含義參加下表:
取值 | 含義 | 創(chuàng)建對象的時(shí)機(jī) |
---|---|---|
singleton(默認(rèn)) | 在IOC容器中,這個(gè)bean的對象始終為單實(shí)例 | IOC容器初始化時(shí) |
prototype | 這個(gè)bean在IOC容器中有多個(gè)實(shí)例 | 獲取bean時(shí) |
如果是在WebApplicationContext環(huán)境下還會(huì)有另外兩個(gè)作用域(但不常用):
取值 | 含義 |
---|---|
request | 在一個(gè)請求范圍內(nèi)有效 |
session | 在一個(gè)會(huì)話范圍內(nèi)有效 |
②創(chuàng)建類User
public class User {
private Integer id;
private String username;
private String password;
private Integer age;
public User() {
}
public User(Integer id, String username, String password, Integer age) {
this.id = id;
this.username = username;
this.password = password;
this.age = age;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", _password='" + password + '\'' +
", age=" + age +
'}';
}
}
③配置bean
<!-- scope屬性:取值singleton(默認(rèn)值),bean在IOC容器中只有一個(gè)實(shí)例,IOC容器初始化時(shí)創(chuàng)建
對象 -->
<!-- scope屬性:取值prototype,bean在IOC容器中可以有多個(gè)實(shí)例,getBean()時(shí)創(chuàng)建對象 -->
<bean class="com.atguigu.bean.User" scope="prototype"></bean>
④測試
@Test
public void testBeanScope(){
ApplicationContext ac = new ClassPathXmlApplicationContext("spring-scope.xml");
User user1 = ac.getBean(User.class);
User user2 = ac.getBean(User.class);
System.out.println(user1==user2);
}
2.2.12、實(shí)驗(yàn)十二:bean的生命周期
①具體的生命周期過程
- bean對象創(chuàng)建(調(diào)用無參構(gòu)造器)
- 給bean對象設(shè)置屬性
- bean對象初始化之前操作(由bean的后置處理器負(fù)責(zé))
- bean對象初始化(需在配置bean時(shí)指定初始化方法)
- bean對象初始化之后操作(由bean的后置處理器負(fù)責(zé))
- bean對象就緒可以使用
- bean對象銷毀(需在配置bean時(shí)指定銷毀方法)
- IOC容器關(guān)閉
②修改類User
public class User {
private Integer id;
private String username;
private String password;
private Integer age;
public User() {
System.out.println("生命周期:1、創(chuàng)建對象");
}
public User(Integer id, String username, String password, Integer age) {
this.id = id;
this.username = username;
this.password = password;
this.age = age;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
System.out.println("生命周期:2、依賴注入");
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public void initMethod(){
System.out.println("生命周期:3、初始化");
}
public void destroyMethod(){
System.out.println("生命周期:5、銷毀");
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", _password='" + password + '\'' +
", age=" + age +
'}';
}
}
注意其中的initMethod()和destroyMethod(),可以通過配置bean指定為初始化和銷毀的方法
③配置bean
<!-- 使用init-method屬性指定初始化方法 -->
<!-- 使用destroy-method屬性指定銷毀方法 -->
<bean class="com.atguigu.bean.User" scope="prototype" init-method="initMethod"destroy-method="destroyMethod">
<property name="id" value="1001"></property>
<property name="username" value="admin"></property>
<property name="password" value="123456"></property>
<property name="age" value="23"></property>
</bean>
④測試
@Test
public void testLife(){
//ConfigurableApplication是Application的子接口,其中擴(kuò)展了刷新和關(guān)閉容器的方法
ClassPathXmlApplicationContext ac = newClassPathXmlApplicationContext("spring-lifecycle.xml");
User bean = ac.getBean(User.class);
System.out.println("生命周期:4、通過IOC容器獲取bean并使用");
ac.close();
}
⑤bean的后置處理器
bean的后置處理器會(huì)在生命周期的初始化前后添加額外的操作,需要實(shí)現(xiàn)BeanPostProcessor接口,
且配置到IOC容器中,需要注意的是,bean后置處理器不是單獨(dú)針對某一個(gè)bean生效,而是針對IOC容器中所有bean都會(huì)執(zhí)行
創(chuàng)建bean的后置處理器:
package com.atguigu.spring.process;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class MyBeanProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
System.out.println("☆☆☆" + beanName + " = " + bean);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
System.out.println("★★★" + beanName + " = " + bean);
return bean;
}
}
在IOC容器中配置后置處理器:
<bean id="myBeanProcessor"class=“com.atguigu.spring.process.MyBeanProcessor”/>
2.2.13、實(shí)驗(yàn)十三:FactoryBean
①簡介
FactoryBean是Spring提供的一種整合第三方框架的常用機(jī)制。和普通的bean不同,配置一個(gè)
FactoryBean類型的bean,在獲取bean的時(shí)候得到的并不是class屬性中配置的這個(gè)類的對象,而是
getObject()方法的返回值。通過這種機(jī)制,Spring可以幫我們把復(fù)雜組件創(chuàng)建的詳細(xì)過程和繁瑣細(xì)節(jié)都屏蔽起來,只把最簡潔的使用界面展示給我們。
將來我們整合Mybatis時(shí),Spring就是通過FactoryBean機(jī)制來幫我們創(chuàng)建SqlSessionFactory對象的。
package org.springframework.beans.factory;
import org.springframework.lang.Nullable;
String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";
@Nullable
T getObject() throws Exception;
@Nullable
Class<?> getObjectType();
default boolean isSingleton() {
return true;
}
}
②創(chuàng)建類UserFactoryBean
public class UserFactoryBean implements FactoryBean<User> {
@Override
public User getObject() throws Exception {
return new User();
}
@Override
public Class<?> getObjectType() {
return User.class;
}
}
③配置bean
<bean id="user" class="com.atguigu.bean.UserFactoryBean"></bean>
④測試
@Test
public void testUserFactoryBean(){
//獲取IOC容器
ApplicationContext ac = new ClassPathXmlApplicationContext("spring-factorybean.xml");
User user = (User) ac.getBean("user");
System.out.println(user);
}
2.2.14、實(shí)驗(yàn)十四:基于xml的自動(dòng)裝配
自動(dòng)裝配:
根據(jù)指定的策略,在IOC容器中匹配某一個(gè)bean,自動(dòng)為指定的bean中所依賴的類類型或接口類
型屬性賦值
①場景模擬
創(chuàng)建類UserController
public class UserController {
private UserService userService;
public void setUserService(UserService userService) {
this.userService = userService;
}
public void saveUser(){
userService.saveUser();
}
}
創(chuàng)建接口UserService
public interface UserService {
void saveUser();
}
創(chuàng)建類UserServiceImpl實(shí)現(xiàn)接口UserService
public class UserServiceImpl implements UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void saveUser() {
userDao.saveUser();
}
}
創(chuàng)建接口UserDao
public interface UserDao {
void saveUser();
}
創(chuàng)建類UserDaoImpl實(shí)現(xiàn)接口UserDao
public class UserDaoImpl implements UserDao {
@Override
public void saveUser() {
System.out.println("保存成功");
}
}
②配置bean
使用bean標(biāo)簽的autowire屬性設(shè)置自動(dòng)裝配效果
自動(dòng)裝配方式:byType
byType:根據(jù)類型匹配IOC容器中的某個(gè)兼容類型的bean,為屬性自動(dòng)賦值
若在IOC中,沒有任何一個(gè)兼容類型的bean能夠?yàn)閷傩再x值,則該屬性不裝配,即值為默認(rèn)值
null
若在IOC中,有多個(gè)兼容類型的bean能夠?yàn)閷傩再x值,則拋出異常
NoUniqueBeanDefinitionException
<bean id="userController"class="com.atguigu.autowire.xml.controller.UserController" autowire="byType">
</bean>
<bean id="userService"class="com.atguigu.autowire.xml.service.impl.UserServiceImpl" autowire="byType">
</bean>
<bean id="userDao" class="com.atguigu.autowire.xml.dao.impl.UserDaoImpl"></bean>
自動(dòng)裝配方式:byName
byName:將自動(dòng)裝配的屬性的屬性名,作為bean的id在IOC容器中匹配相對應(yīng)的bean進(jìn)行賦值
<bean id="userController"class="com.atguigu.autowire.xml.controller.UserController" autowire="byName">
</bean>
<bean id="userService"class="com.atguigu.autowire.xml.service.impl.UserServiceImpl" autowire="byName">
</bean>
<bean id="userServiceImpl"class="com.atguigu.autowire.xml.service.impl.UserServiceImpl" autowire="byName">
</bean>
<bean id="userDao" class="com.atguigu.autowire.xml.dao.impl.UserDaoImpl">
</bean>
<bean id="userDaoImpl" class="com.atguigu.autowire.xml.dao.impl.UserDaoImpl">
</bean>
③測試
@Test
public void testAutoWireByXML(){
ApplicationContext ac = new ClassPathXmlApplicationContext("autowire-xml.xml");
UserController userController = ac.getBean(UserController.class);
userController.saveUser();
}
2.3、基于注解管理bean
2.3.1、實(shí)驗(yàn)一:標(biāo)記與掃描
①注解
和 XML 配置文件一樣,注解本身并不能執(zhí)行,注解本身僅僅只是做一個(gè)標(biāo)記,具體的功能是框架檢測
到注解標(biāo)記的位置,然后針對這個(gè)位置按照注解標(biāo)記的功能來執(zhí)行具體操作。
本質(zhì)上:所有一切的操作都是Java代碼來完成的,XML和注解只是告訴框架中的Java代碼如何執(zhí)行。
舉例:元旦聯(lián)歡會(huì)要布置教室,藍(lán)色的地方貼上元旦快樂四個(gè)字,紅色的地方貼上拉花,黃色的地方貼上氣球。
班長做了所有標(biāo)記,同學(xué)們來完成具體工作。墻上的標(biāo)記相當(dāng)于我們在代碼中使用的注解,后面同學(xué)們做的工作,相當(dāng)于框架的具體操作。
②掃描
Spring 為了知道程序員在哪些地方標(biāo)記了什么注解,就需要通過掃描的方式,來進(jìn)行檢測。然后根據(jù)注解進(jìn)行后續(xù)操作。
③新建Maven Module
<dependencies>
<!-- 基于Maven依賴傳遞性,導(dǎo)入spring-context依賴即可導(dǎo)入當(dāng)前所需所有jar包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.1</version>
</dependency>
<!-- junit測試 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
④創(chuàng)建Spring配置文件
⑤標(biāo)識組件的常用注解
@Component:將類標(biāo)識為普通組件 @Controller:將類標(biāo)識為控制層組件 @Service:將類標(biāo)
識為業(yè)務(wù)層組件 @Repository:將類標(biāo)識為持久層組件
問:以上四個(gè)注解有什么關(guān)系和區(qū)別?
通過查看源碼我們得知,@Controller、@Service、@Repository這三個(gè)注解只是在@Component注解的基礎(chǔ)上起了三個(gè)新的名字。
對于Spring使用IOC容器管理這些組件來說沒有區(qū)別。所以@Controller、@Service、@Repository這
三個(gè)注解只是給開發(fā)人員看的,讓我們能夠便于分辨組件的作用。
注意:雖然它們本質(zhì)上一樣,但是為了代碼的可讀性,為了程序結(jié)構(gòu)嚴(yán)謹(jǐn)我們肯定不能隨便胡亂標(biāo)記。
⑥創(chuàng)建組件
創(chuàng)建控制層組件
@Controller
public class UserController {
}
創(chuàng)建接口UserService
public interface UserService {
}
創(chuàng)建業(yè)務(wù)層組件UserServiceImpl
@Service
public class UserServiceImpl implements UserService {
}
創(chuàng)建接口UserDao
public interface UserDao {
}
創(chuàng)建持久層組件UserDaoImpl
@Repository
public class UserDaoImpl implements UserDao {
}
⑦掃描組件
情況一:最基本的掃描方式
<context:component-scan base-package="com.atguigu">
</context:component-scan>
情況二:指定要排除的組件
<context:component-scan base-package="com.atguigu">
<!-- context:exclude-filter標(biāo)簽:指定排除規(guī)則 -->
<!--
type:設(shè)置排除或包含的依據(jù)
type="annotation",根據(jù)注解排除,expression中設(shè)置要排除的注解的全類名
type="assignable",根據(jù)類型排除,expression中設(shè)置要排除的類型的全類名
-->
<context:exclude-filter type="annotation"expression="org.springframework.stereotype.Controller"/>
<!--<context:exclude-filter type="assignable"expression="com.atguigu.controller.UserController"/>-->
</context:component-scan>
情況三:僅掃描指定組件
<context:component-scan base-package="com.atguigu" use-default-filters="false">
<!-- context:include-filter標(biāo)簽:指定在原有掃描規(guī)則的基礎(chǔ)上追加的規(guī)則 -->
<!-- use-default-filters屬性:取值false表示關(guān)閉默認(rèn)掃描規(guī)則 -->
<!-- 此時(shí)必須設(shè)置use-default-filters="false",因?yàn)槟J(rèn)規(guī)則即掃描指定包下所有類 -->
<!--
type:設(shè)置排除或包含的依據(jù)
type="annotation",根據(jù)注解排除,expression中設(shè)置要排除的注解的全類名
type="assignable",根據(jù)類型排除,expression中設(shè)置要排除的類型的全類名
-->
<context:include-filter type="annotation"expression="org.springframework.stereotype.Controller"/>
<!--<context:include-filter type="assignable"expression="com.atguigu.controller.UserController"/>-->
</context:component-scan>
⑧測試
@Test
public void testAutowireByAnnotation(){
ApplicationContext ac = new
ClassPathXmlApplicationContext("applicationContext.xml");
UserController userController = ac.getBean(UserController.class);
System.out.println(userController);
UserService userService = ac.getBean(UserService.class);
System.out.println(userService);
UserDao userDao = ac.getBean(UserDao.class);
System.out.println(userDao);
}
⑨組件所對應(yīng)的bean的id
在我們使用XML方式管理bean的時(shí)候,每個(gè)bean都有一個(gè)唯一標(biāo)識,便于在其他地方引用?,F(xiàn)在使用
注解后,每個(gè)組件仍然應(yīng)該有一個(gè)唯一標(biāo)識。
默認(rèn)情況
類名首字母小寫就是bean的id。例如:UserController類對應(yīng)的bean的id就是userController。
自定義bean的id
可通過標(biāo)識組件的注解的value屬性設(shè)置自定義的bean的id
@Service(“userService”)//默認(rèn)為userServiceImpl public class UserServiceImpl implements
UserService {}
2.3.2、實(shí)驗(yàn)二:基于注解的自動(dòng)裝配
①場景模擬
參考基于xml的自動(dòng)裝配
在UserController中聲明UserService對象
在UserServiceImpl中聲明UserDao對象
②@Autowired注解
在成員變量上直接標(biāo)記@Autowired注解即可完成自動(dòng)裝配,不需要提供setXxx()方法。以后我們在項(xiàng)
目中的正式用法就是這樣。
@Controller
public class UserController {
@Autowired
private UserService userService;
public void saveUser(){
userService.saveUser();
}
}
public interface UserService {
void saveUser();
}
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
public void saveUser() {
userDao.saveUser();
}
}
public interface UserDao {
void saveUser();
}
@Repository
public class UserDaoImpl implements UserDao {
@Override
public void saveUser() {
System.out.println("保存成功");
}
}
③@Autowired注解其他細(xì)節(jié)
@Autowired注解可以標(biāo)記在構(gòu)造器和set方法上
@Controller
public class UserController {
private UserService userService;
@Autowired
public UserController(UserService userService){
this.userService = userService;
}
public void saveUser(){
userService.saveUser();
}
}
@Controller
public class UserController {
private UserService userService;
@Autowired
public void setUserService(UserService userService){
this.userService = userService;
}
public void saveUser(){
userService.saveUser();
}
}
④@Autowired工作流程
- 首先根據(jù)所需要的組件類型到IOC容器中查找
- 能夠找到唯一的bean:直接執(zhí)行裝配
- 如果完全找不到匹配這個(gè)類型的bean:裝配失敗
- 和所需類型匹配的bean不止一個(gè)
- 沒有@Qualifier注解:根據(jù)@Autowired標(biāo)記位置成員變量的變量名作為bean的id進(jìn)行匹配
- 能夠找到:執(zhí)行裝配
- 找不到:裝配失敗
- 使用@Qualifier注解:根據(jù)@Qualifier注解中指定的名稱作為bean的id進(jìn)行匹配
- 能夠找到:執(zhí)行裝配
- 找不到:裝配失敗
@Controller
public class UserController {
@Autowired
@Qualifier("userServiceImpl")
private UserService userService;
public void saveUser(){
userService.saveUser();
}
}
@Autowired中有屬性required,默認(rèn)值為true,因此在自動(dòng)裝配無法找到相應(yīng)的bean時(shí),會(huì)裝
配失敗
可以將屬性required的值設(shè)置為false,則表示能裝就裝,裝不上就不裝,此時(shí)自動(dòng)裝配的屬性為
默認(rèn)值文章來源:http://www.zghlxwxcb.cn/news/detail-762456.html
但是實(shí)際開發(fā)時(shí),基本上所有需要裝配組件的地方都是必須裝配的,用不上這個(gè)屬性。文章來源地址http://www.zghlxwxcb.cn/news/detail-762456.html
到了這里,關(guān)于Spring簡介的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!