【Spring】Spring複習之基礎和IOC

如果對此文感興趣,可以繼續看下一篇博文,持續更新,新手學習,有問題歡迎指正:https://blog.csdn.net/renjingjingya0429/article/details/90138698

一、Spring概述

1. Spring是什麼?

Spring是分層的Java SE/EE應用 full-stack 輕量級開發框架,以IOC(Inverse Of Course:控制反轉)和AOP(Aspect Oriented Programming:面向切面編程)爲內核,提供了展現層Spring MVC和持久層Spring JDBC以及業務層事務管理等衆多的企業級應用技術,還能整合開源世界衆多著名的第三方框架和類庫,逐漸成爲使用最多的Java EE企業級應用開源框架。
2. Spring 的發展歷程

1997 年 IBM 提出了 EJB 的思想
1998 年,SUN 制定開發標準規範 EJB1.0
1999 年,EJB1.1 發佈
2001 年,EJB2.0 發佈
2003 年,EJB2.1 發佈
2006 年,EJB3.0 發佈
Rod Johnson(spring 之父)
Expert One-to-One J2EE Design and Development(2002)
闡述了 J2EE 使用 EJB 開發設計的優點及解決方案
Expert One-to-One J2EE Development without EJB(2004)
闡述了 J2EE 開發不使用 EJB 的解決方式(Spring 雛形)
== 2017 年 9 月份發佈了 spring 的最新版本 spring 5.0 通用版(GA) ==

3. Spring 的優勢

(1)方便解耦,簡化開發
通過 Spring 提供的IOC容器,可以將對象間的依賴關係交由Spring進行控制,避免硬編碼所造成的過度程序耦合。用戶也不必在位單例模式類、屬性文件解析等這些很底層的需求編寫代碼,可以更專注於上層的應用。
(2)AOP編程的支持
通過Spring 的AOP功能,方便進行面向切面的編程,許多不容易用傳統OOP實現的功能可以通過AOP輕鬆應付。
(3)聲明式事務的支持
可以將我們從單調煩悶的事務管理代碼中解脫出來,通過聲明式方式靈活的進行事務的管理,提高開發效率和質量。
(4)方便程序的測試
可以用非容器以來的編程方式進行幾乎所有的測試工作,測試不在是昂貴的操作,而是隨手可做的事情。
(5)方便集成各種優秀框架
Spring可以降低各種框架的使用難度,提供了對各種優秀框架(Struts、Hibernate、Hessian、Quartz等)的直接支持。
(6)降低JavaEE API的使用難度
Spring 對JavaEE API(如JDBC、JavaMail、遠程調用等)進行了薄薄的封裝層,使這些API的使用難度大爲降低。
(7)Java 源碼是經典學習範例
Spring 的源代碼設計精妙、結構清晰、匠心獨用,處處體現着大師對 Java 設計模式靈活運用以
及對 Java 技術的高深造詣。它的源代碼無意是 Java 技術的最佳實踐的範例。

4. Spring 的體系結構

在這裏插入圖片描述

二、IOC的概念和作用

1. 什麼是程序的耦合

==耦合性(Coupling),也叫耦合度,是對程序模塊間關聯程度的度量。==耦合的輕若取決於模塊間接口的複雜性、調用模塊的方式以及通過界面傳送數據的多少。模塊間的耦合度是指模塊之間的依賴關係,包括控制關係、調用關係、數據傳遞關係。模塊間聯繫越多,其耦合性越強,同時表明其獨立性越差(降低耦合度,可以提高其獨立性)。耦合性存在於各個領域,而非軟件設計中獨有的。

在軟件工程中,耦合指的就是對象之間的依賴性。對象之間的耦合越高,維護成本越高。因此對象的設計應使類和構件之間的耦合最小。軟件設計中通常用耦合度和內聚度作爲衡量模塊獨立程序的標準。劃分模塊的一個準則就是高內聚低耦合。

總結:耦合是影響軟件複雜程度和設計質量的一個重要因素,在設計上我們應採用以下原則:如果模塊間必須存在耦合,就儘量使用數據耦合,少用控制耦合,限制公共耦合的範圍,儘量避免使用內容耦合。

內聚與耦合
內聚標誌一個模塊內各個元素彼此結合的緊密程度,它是信息隱蔽和局部化概念的自然擴展。內聚是從
功能角度來度量模塊內的聯繫,一個好的內聚模塊應當恰好做一件事。它描述的是模塊內的功能聯繫。耦合是軟件結構中各模塊之間相互連接的一種度量,耦合強弱取決於模塊間接口的複雜程度、進入或訪問一個模塊的點以及通過接口的數據。 程序講究的是低耦合,高內聚。就是同一個模塊內的各個元素之間要高度緊密,但是各個模塊之間的相互依存度卻要不那麼緊密。
內聚和耦合是密切相關的,同其他模塊存在高耦合的模塊意味着低內聚,而高內聚的模塊意味着該模塊同其他模塊之間是低耦合。在進行軟件設計時,應力爭做到
高內聚,低耦合

//我們在開發中,有些依賴關係是必須的,有些依賴關係可以通過優化代碼來解決。
//請看下面的示例代碼
/**
* 賬戶的業務實現類
*/
public class AccountServiceImpl implements IAccountService {
	private IAccountDao accountDao = new AccountDaoImpl();
}

上面的代碼表示:
==業務層調用持久層,並且此時業務層在依賴持久層的接口和實現類。如果此時沒有持久層實現類,編譯將不能通過。==這種編譯期依賴關係,應該在我們開發中杜絕。我們需要優化代碼來解決。

再比如:
早期我們的JDBC操作,註冊驅動時,我們爲什麼不使用DriverManager的register方法,而是採用Class.forName的方式?

public class JdbcDemo1 {
/**
* @author 黑馬程序員
* @Company http://www.ithiema.com
* @Version 1.0
* @param args
* @throws Exception
*/
	public static void main(String[] args) throws Exception {
	//1.註冊驅動
	//DriverManager.registerDriver(new com.mysql.jdbc.Driver());
	Class.forName("com.mysql.jdbc.Driver");
	//2.獲取連接
	//3.獲取預處理 sql 語句對象
	//4.獲取結果集
	//5.遍歷結果集
	}
}

原因就是:
我們的類依賴了數據庫的具體驅動類(MySQL),如果這時更換了數據庫品牌,需要修改源代碼來重新數據庫驅動。這顯然不是我們想要的。

2. 解決耦合的思路

我們在學習JDBC時,是通過反射來註冊驅動的,代碼如下:

Class.forName("com.mysql.jdbc.Driver");//此時只是一個字符串

此時的好處是,我們的類中不再依賴具體的驅動類,此時就算刪除 MySQL 的驅動jar包,依然可以編譯(運行時肯定會報類找不到的異常)。
同時,產生了一個新的問題,MySQL 驅動的全限定類名字符串是在 java 類中寫死的,一旦要改還是要修改源碼。
解決這個問題也很簡單,使用配置文件配置

3. 工廠模式解耦

在實際開發中我們可以把三層的對象都使用配置文件配置起來,當啓動服務器應用加載的時候,讓一個類中的方法通過讀取配置文件,把這些對象創建出來並存起來。在接下來使用的時候,可以直接拿過來用
那麼,這個讀取配置文件,創建和獲取三層對象的類就是工廠

4. 控制反轉 - Inversion Of Control

解耦的思路還有2個問題:

(1)存到哪去?
分析:由於是很多對象,需要使用集合來存。但是我們還有查找需求,這時使用 Map 纔是最佳的選擇。
所以 ,我們可以在應用加載時,創建一個Map,用於存放三層對象。我們把 Map 稱之爲容器

(2)那工廠又是什麼呢?
工廠就是負責給我們從容器中獲取指定對象的類。這時候我們獲取對象的方式發生了改變。

原來,我們在獲取對象時,都是採用 new 的方式。是主動的。

在這裏插入圖片描述
現在:
我們獲取對象時,同時跟工廠要,有工廠爲我們查找獲取創建對象。是被動的。

在這裏插入圖片描述
這種被動接受的方式獲取對象的思想就是控制反轉,它是DSpring框架的核心之一。

明確IOC的作用:
削減計算機程序的耦合(解除我們代碼中的依賴關係)。

三、使用 Spring 的 IOC 解決程序耦合

我們的案例是,賬戶的業務層和持久層的依賴關係解決。在開始 spring 的配置之前,我們要先準備
一下環境。由於我們是使用 spring 解決依賴關係,並不是真正的要做增刪改查操作,所以此時我們沒必要寫實體
類。並且我們在此處使用的是 java 工程,不是 java web 工程。

1. 案例的前期準備

1.1 準備 Spring 的開發包

官網http://spring.io/
下載地址
http://repo.springsource.org/libs-release-local/org/springframework/spring
解壓:(Spring 目錄結構:)

  • docs:API 和開發規範.
  • libs :jar 包和源碼.
  • schema :約束.
    在這裏插入圖片描述
    注意:如果使用的是 Spring 5,他是用jdk8 編寫的,要求我們的 jdk 版本是8及以上。同時 Tomcat 的版本要求8.5及以上。

1.2. 創建接口及業務實現類

/**
* 賬戶的業務層接口
* @author 黑馬程序員
* @Company http://www.ithiema.com
* @Version 1.0
*/
public interface IAccountService {
	/**
	* 保存賬戶(此處只是模擬,並不是真的要保存)
	*/
	void saveAccount();
}
/**
* 賬戶的業務層實現類
*/
public class AccountServiceImpl implements IAccountService {
	private IAccountDao accountDao = new AccountDaoImpl();//此處的依賴關係有待解決
	@Override
	public void saveAccount() {
	accountDao.saveAccount();
	}
}

1.3. 創建持久層接口和實現類

/**
* 賬戶的持久層接口
* @author 黑馬程序員
* @Company http://www.ithiema.com
* @Version 1.0
*/
public interface IAccountDao {
/**
* 保存賬戶
*/
	void saveAccount();
}
/**
* 賬戶的持久層實現類
* @author 黑馬程序員
* @Company http://www.ithiema.com
* @Version 1.0
*/
public class AccountDaoImpl implements IAccountDao {
	@Override
	public void saveAccount() {
	System.out.println("保存了賬戶");
	}
}

2. 基於XML的配置(入門案例)

2.1 第一步:創建Java項目,並加入jar包
在這裏插入圖片描述
2.2 第二步:在類的根路徑下創建一個任意名稱的 xml 文件(不能是中文)
在這裏插入圖片描述
給配置文件導入約束:
/spring-framework-5.0.2.RELEASE/docs/spring-framework-reference/html5/core.html

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>

2.3 第三步:讓Spring管理資源,在配置文件中配置service 和 dao

<!-- bean 標籤:用於配置讓spring創建對象,並且存入IOC容器之中
	id屬性:對象的唯一標識。
	class屬性:指定要創建對象的全限定類名。 -->
<!-- 配置 service -->
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
</bean>
<!-- 配置 dao -->
<bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl"></bean>

2.4 測試配置是否成功

/**
* 模擬一個表現層
*/
public class Client {
/**
 * 使用 main 方法獲取容器測試執行
*/
	public static void main(String[] args) {
		//1.使用 ApplicationContext 接口,就是在獲取 spring 容器
		ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
		//2.根據 bean 的 id 獲取對象
		IAccountService aService = (IAccountService) ac.getBean("accountService");
		System.out.println(aService);
		IAccountDao aDao = (IAccountDao) ac.getBean("accountDao");
		System.out.println(aDao);
	}
}

運行結果:
在這裏插入圖片描述

3. Spring基於XML的IOC細節

3.1 Spring 中工廠的類結構圖
在這裏插入圖片描述
在這裏插入圖片描述

3.1.1 BeanFactory 和ApplicationContext 的區別

BeanFactory 纔是Spring 容器中的頂層接口。
ApplicationContext是他的子接口。
兩者的區別:
創建對象的時間點不一樣。
Application Context:只要一讀取配置文件,默認情況下就會創建對象。(立即加載)
BeanFactory:什麼時候使用什麼什麼時候創建對象。(延遲加載)

3.1.2 Application Context 的實現類

  • ClassPathXmlApplicationContext:他是從類的根路徑下加載配置文件,推薦使用。
  • FileSystemXmlApplicationContext:它是從磁盤路徑上加載配置文件,配置文件可以在磁盤的任意位置。
  • AnnotationConfigApplicationContext:當我們使用註解配置容器對象時,需要使用此類來創建Spring 容器。它用來讀取註解。

3.2 IOC 中bean 標籤和管理對象細節

3.2.1 bean標籤

作用:

  • 用於配置對象讓Spring 來創建的。
  • 默認情況下,他調用的是類中的無參構造函數。如果沒有無參構造函數則不能創建成功。

屬性:

  • id:給對象在容器中提供一個唯一標識。用於獲取對象。
  • class:指定類的全限定類名。用於反射創建對象。默認情況下調用無參構造函數。
  • scope:指定對象的作用範圍。
    • singleton:默認值,單例的。
    • prototype:多例的。
    • request:web項目中,Sprring 創建一個Bean的對象 ,將對象存入到request 域中。
    • session:web項目中,Sprring 創建一個Bean的對象 ,將對象存入到session 域中。
    • global session:web項目中,應用在 Porlet環境。如果沒有 Portlet 環境,那麼 globalSession 相當於 session。
  • init-method:指定類中的初始化方法名稱。
  • destory-method:指定類中銷燬方法名稱。

3.2.2 bean 的作用範圍和生命週期

  • 單例對象:scope=“singleton”

    • 一個應用只有一個對象的實例。它的作用範圍就是整個應用。
    • 生命週期
      • 對象出生:當應用加載,創建容器時,對象就被創建了。
      • 對象活着:只要容器在,對象一直活着。
      • 對象死亡:當應用卸載,銷燬容器時,對象就被銷燬了。
  • 多例對象:scope=“singleton”

    • 每次訪問對象時,都會創建對象實例。
    • 生命週期
      • 對象出生:當使用對象時,創建新的對象實例。
      • 對象活着:只要對象一直在使用中,就一直活着。
      • 對象死亡:當對象長時間不用時,被Java的垃圾回收器回收。

3.2.3 實例化 Bean的三種方式

第一種方式:使用無參構造函數
<!-- 在默認情況下:
它會根據默認無參構造函數來創建類對象。如果Bean 中沒有默認無參構造函數,將會創建失敗。
-->
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"/>
第二種方式:Spring管理靜態工廠-使用靜態工廠的方法創建對象
/**
* 模擬一個靜態工廠,創建業務層實現類
*/
public class StaticFactory {
	public static IAccountService createAccountService(){
		return new AccountServiceImpl();
	}
}
<!--
此種方式是:
	使用StaticFactory類中的靜態方法createAccountService 創建對象,並存入Spring容器。
	id屬性:指定bean的id,用於從容器獲取
	class 屬性:指定靜態工廠的全限定類名
	factory-method 屬性:指定生產對象的靜態方法。
-->
<bean id="accountService"
 class="com.itheima.factory.StaticFactory"
 factory-method="createAccountService"></bean>
第三種方式:Spring管理實例工廠-使用實例工廠的方法創建對象
/**
* 模擬一個實例工廠,創建業務層實現類
* 此工廠創建對象,必須現有工廠實例對象,再調用方法。
*/
public class InstanceFactory {
	public IAccountService createAccountService(){
		return new AccountServiceImpl();
	}
}
<!--此種方式是:先把工廠的創建交給Spring來管理。
然後在使用工廠的bean來調用裏面的方法。
factory-bean 屬性:用於指定實例工廠bean的id
factory-method 屬性:用於指定實例工廠中創建對象的方法。
-->
<bean id="instanceFactory" class="com.itheima.factory.InstanceFactory"></bean>
<bean id="accountService" factory-bean="instanceFactory" factory-method="createAccountService"></bean>

3.3 Spring 的依賴注入

3.3.1 依賴注入的概念

  • 依賴注入:Dependency Injection。它是Spring 框架核心IOC的具體實現。
  • 我們的程序在編寫時,通過控制反轉,把對象的創建交給了Spring,但是代碼不可能出現沒有依賴的情況。
  • IOC 解耦只是降低他們的依賴關係,但不會消除。例如:我們的業務層會調用持久層的方法。
  • 那這種業務層和持久層的依賴關係,在使用Spring之後,就讓Spring 來維護了。
  • 簡單地說,就是坐等框架把持久層對象傳入業務層,而不用我們自己去獲取。

3.3.2 構造函數注入

顧名思義,就是使用類中的構造函數,給成員變量賦值。注意,賦值的操作不是我們自己做的,而是通過配置
的方式,讓 spring 框架來爲我們注入。具體代碼如下:
/**
*/
public class AccountServiceImpl implements IAccountService {
	private String name;
	private Integer age;
	private Date birthday;
	public AccountServiceImpl(String name, Integer age, Date birthday) {
		this.name = name;
		this.age = age;
		this.birthday = birthday;
	}
	@Override
	public void saveAccount() {
		System.out.println(name+","+age+","+birthday);
	}
}
<!-- 使用構造函數的方式,給service中的屬性傳值
要求:
	類中需要提供一個對應參數列表的構造函數
	涉及的標籤:
		construor-arg
			屬性:
				index:指定參數在構造函數參數列表的索引位置。
				type:指定參數在構造函數中的數據類型。
				name:指定參數在構造函數中的名稱 用這個找給誰賦值
				=======上面三個都是找給誰賦值,下面兩個指的是賦什麼值的==============
				value:它能賦的值是基本數據類型和 String 類型
				ref:它能賦的值是其他 bean 類型,也就是說,必須得是在配置文件中配置過的 bean
-->

<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
	<constructor-arg name="name" value="張三"></constructor-arg>
	<constructor-arg name="age" value="18"></constructor-arg>
	<constructor-arg name="birthday" ref="now"></constructor-arg>
</bean>
<bean id="now" class="java.util.Date"></bean>

3.3.4 使用 p 名稱空間注入數據(本質還是調用 set 方法)
此種方式是通過在 xml 中導入 p 名稱空間,使用 p:propertyName 來注入數據,它的本質仍然是調用類中的set 方法實現注入功能。
Java 類代碼:

/**
* 使用 p 名稱空間注入,本質還是調用類中的 set 方法
*/
public class AccountServiceImpl4 implements IAccountService {
	private String name;
	private Integer age;
	private Date birthday;
	public void setName(String name) {
		this.name = name;
	}
	public void setAge(Integer age) {
		this.age = age;
	}
	public void setBirthday(Date birthday) {
		this.birthday = birthday;
	}
	@Override
	public void saveAccount() {
		System.out.println(name+","+age+","+birthday);
	}
}

配置文件代碼:

<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:p="http://www.springframework.org/schema/p"
傳智播客——專注於 Java、.Net 和 Php、網頁平面設計工程師的培訓
北京市昌平區建材城西路金燕龍辦公樓一層 電話:400-618-9090
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation=" http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="accountService"
 class="com.itheima.service.impl.AccountServiceImpl4"
 p:name="test" p:age="21" p:birthday-ref="now"/>
</beans>

3.3.5 注入集合屬性

顧名思義,就是給類中的集合成員傳值,它用的也是set方法注入的方式,只不過變量的數據類型都是集合。
我們這裏介紹注入數組,List,Set,Map,Properties。具體代碼如下:
/***/
public class AccountServiceImpl implements IAccountService {
	private String[] myStrs;
	private List<String> myList;
	private Set<String> mySet;
	private Map<String,String> myMap;
	private Properties myProps;
	public void setMyStrs(String[] myStrs) {
		this.myStrs = myStrs;
	}
	public void setMyList(List<String> myList) {
		this.myList = myList;
	}
	public void setMySet(Set<String> mySet) {
		this.mySet = mySet;
	}
	public void setMyMap(Map<String, String> myMap) {
		this.myMap = myMap;
	}
	public void setMyProps(Properties myProps) {
		this.myProps = myProps;
	}
	@Override
	public void saveAccount() {
		System.out.println(Arrays.toString(myStrs));
		System.out.println(myList);
		System.out.println(mySet);
		System.out.println(myMap);
		System.out.println(myProps);
	}
}
<!-- 注入集合數據
List 結構的:array,list,set
Map 結構的:map,entry,props,prop
-->
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
<!-- 在注入集合數據時,只要結構相同,標籤可以互換 -->
<!-- 給數組注入數據 -->
<property name="myStrs">
	<set>
		<value>AAA</value>
		<value>BBB</value>
		<value>CCC</value>
	</set>
</property>
<!-- 注入 list 集合數據 -->
<property name="myList">
	<array>
		<value>AAA</value>
		<value>BBB</value>
		<value>CCC</value>
	</array>
</property>
<!-- 注入 set 集合數據 -->
<property name="mySet">
	<list>
		<value>AAA</value>
		<value>BBB</value>
		<value>CCC</value>
	</list>
</property>
<!-- 注入 Map 數據 -->
<property name="myMap">
	<props>
		<prop key="testA">aaa</prop>
		<prop key="testB">bbb</prop>
	</props>
</property>
<!-- 注入 properties 數據 -->
<property name="myProps">
	<map>
		<entry key="testA" value="aaa"></entry>
		<entry key="testB">
			<value>bbb</value>
		</entry>
	</map>
</property>
</bean>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章