初學spring框架和小練習
Spring第一天
- Spring概述
1.1程式的耦合和解耦
-
-
- 什麼是程式的耦合
-
我們在開發中,會寫很多的類,而有些類之間不可避免的產生依賴關係,這種依賴關係稱之為耦合。有些依賴關係是必須的,有些依賴關係可以通過優化程式碼來解除的。請看下面的示例程式碼:
/** * 客戶的業務層實現類依賴customerDao類,這兩個類就稱為耦合 */ public class CustomerServiceImpl implements ICustomerService { private ICustomerDao customerDao = new CustomerDaoImpl(); } |
上面的程式碼表示:業務層呼叫持久層,並且此時業務層在依賴持久層的介面和實現類。如果此時沒有持久層實現類,編譯將不能通過。這種依賴關係就是我們可以通過優化程式碼解決的。
再比如:
下面的程式碼中,我們的類依賴了 MySQL 的具體驅動類,如果這時候更換了資料庫品牌,我們需要改原始碼來修改資料庫驅動。這顯然不是我們想要的。
public class JdbcDemo1 { /** * JDBC 操作資料庫的基本入門中存在什麼問題? * 導致驅動註冊兩次是個問題,但不是嚴重的。 * 嚴重的問題:是當前類和 mysql 的驅動類有很強的依賴關係。 * 當我們沒有驅動類的時候,連編譯都不讓。 * 那這種依賴關係,就叫做程式的耦合 * * 我們在開發中,理想的狀態應該是: * 我們應該盡力達到的:編譯時不依賴,執行時才依賴。 */ 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.遍歷結果集 } |
-
-
- 解決程式耦合的思路
-
當是我們講解 jdbc 時,是通過反射來註冊驅動的,程式碼如下:
Class.forName("com.mysql.jdbc.Driver");
這時的好處是,我們的類中不再依賴具體的驅動類,此時就算刪除 mysql 的驅動 jar 包,依然可以編譯。但是因為沒有驅動類,所以不能執行。
不過,此處也有個問題,就是我們反射類物件的全限定類名字串是在 java 類中寫死的,一旦要改還是要修改原始碼。
解決這個問題也很簡單,使用配置檔案配置。
-
-
- 工廠模式解耦
-
在實際開發中我們可以把所有的 dao mapper 和 service 和 action web controller物件使用配置檔案配置起來,當啟動伺服器應用載入的時候,通過讀取配置檔案,把這些物件創建出來 並存起來。在接下來的使用的時候,直接拿過來用就好了。
1.1.4 控制反轉-Inversion Of Control
1 、存哪去?
分析:由於我們是很多物件,肯定要找個集合來存。這時候有 Map 和 List 供選擇。
到底選 Map 還是 List 就看我們有沒有查詢需求。有查詢需求,選 Map。
所以我們的答案就是
在應用載入時,建立一個 Map,用於存放 action,Service 和 dao 物件。
我們把這個 map 稱之為 容器。
2、還是沒解釋什麼是工廠?
工廠就是負責給我們從容器中獲取指定物件的類。這時候我們獲取物件的方式發生了改變。
原來:
我們在獲取物件時,都是採用 new 的方式。是主動的。
現在:
我們獲取物件時,同時跟工廠要,有工廠為我們查詢或者建立物件。是被動的。
這種被動接收的方式獲取物件的思想就是控制反轉,它是 spring 框架的核心之一。
它的作用只有一個:削減計算機程式的耦合。
-
- Spring介紹
- Spring 概述
- Spring介紹
Spring框架有兩大核心 分別是IOC(控制反轉)和AOP(面向切面程式設計)
1.2.1.1 Spring簡介
Spring 是分層的 Java SE/EE 應用 full-stack 輕量級開源框架,以 IoC(Inverse Of Control:
反轉控制)和 AOP(Aspect Oriented Programming:面向切面程式設計)為核心,提供了展現層 SpringMVC 和持久層 Spring JDBC 以及業務層事務管理等眾多的企業級應用技術,還能整合開源世界眾多著名的第三方框架和類庫,逐漸成為使用最多的 Java EE 企業應用開源框架。
1.2.1.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 雛形)
-
-
- Spring的優勢
-
方便解耦,簡化開發
通過 Spring 提供的 IoC 容器,可以將物件間的依賴關係交由 Spring 進行控制,避免硬編碼所造成的過度程式耦合。使用者也不必再為單例模式類、屬性檔案解析等這些很底層的需求編寫程式碼,可以更專注於上層的應用。
AOP 程式設計的支援
通過 Spring 的 AOP 功能,方便進行面向切面的程式設計,許多不容易用傳統 OOP 實現的功能可以通過 AOP 輕鬆應付。
宣告式事務的支援
可以將我們從單調煩悶的事務管理程式碼中解脫出來,通過宣告式方式靈活的進行事務的管理,提高開發效率和質量。
方便程式的測試
可以用非容器依賴的程式設計方式進行幾乎所有的測試工作,測試不再是昂貴的操作,而是隨手可做的事情。
方便整合各種優秀框架
Spring 可以降低各種框架的使用難度,提供了對各種優秀框架(Struts、Hibernate、Hessian、Quartz 任務排程框架 等)的直接支援。低 降低 JavaEE API 的使用難度Spring 對 JavaEE API(如 JDBC、JavaMail、遠端呼叫 CXF 等)進行了薄薄的封裝層,使這些 API 的使用難度大為降低。
Java 原始碼是經典學習範例
Spring 的原始碼設計精妙、結構清晰、匠心獨用,處處體現著大師對 Java 設計模式靈活運用以及對 Java 技術的高深造詣。它的原始碼無意是 Java 技術的最佳實踐的範例。
-
-
- Spring的體系結構
-
-
-
- Spring開發包
-
官網:http://spring.io/
下載地址:
http://repo.springsource.org/libs-release-local/org/springframework/spring
解壓:(Spring 目錄結構:)
* docs :API 和開發規範.
* libs :jar 包和原始碼.
* schema :約束.
我們上課使用的版本是Spring 4.2.4
- 使用Spring的IOC解決程式耦合
2.1案例的前期準備
本章我們使用的案例是,客戶的業務層和持久層的依賴關係解決。在開始 spring 的配置之前,我們要先準備一下環境。由於我們是使用 spring 解決依賴關係,並不是真正的要做增刪改查操作,所以此時我們沒必要寫實體類。並且我們在此處使用的是 java 工程,不是 java web 工程。
2.1.1 建立業務層介面和實現類
客戶介面: CustomerService
/** * 客戶的業務層介面 */ public interface ICustomerService { /** * 儲存客戶 * @param customer */ void saveCustomer(); } |
客戶的實現類 CustomerServiceImpl:
/** * 客戶的業務層實現類 */ public class CustomerServiceImpl implements ICustomerService { private ICustomerDao customerDao = new CustomerDaoImpl();// 此處有依賴關係 @Override public void saveCustomer() { customerDao.saveCustomer(); } } |
2.1.2 建立持久層介面和實現類
客戶的持久層介面:
public interface ICustomerDao { /** * 儲存客戶 */ void saveCustomer(); }
|
客戶的持久層實現類:
public class CustomerDaoImpl implements ICustomerDao { @Override public void saveCustomer() { System.out.println("儲存了客戶"); } }
|
2.2 基於XML的配置
2.2.1 環境搭建
2.2.1.1 第一步: 拷貝必備的jar包到工程的lib目錄
2.2.1.2 第二步:在類的根路徑下建立一個任意名稱的 xml 檔案 (不能是中文)
給配置檔案匯入約束:
<?xml version="1.0" encoding="UTF-8"?> <!-- 匯入 schema 約束的位置在: ..\spring-framework-4.2.4.RELEASE\docs\spring-framework-reference\html\xsd-configuration.html 檔案中。注意:要匯入 schema 約束 --> <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.2.1.3 第三步:把資源交給 spring 來管理置 ,在配置檔案中配置 service 和 dao
<!-- 把資源交給 spring 來管理 --> <bean id="customerDao" class="com.bdqn.dao.impl.CustomerDaoImpl"/> <bean id="customerService" class="com.bdqn.service.impl.CustomerServiceImpl"/> |
2.2.2 測試環境是否成功
2.2.2.1 獲取 spring 容器
/** * 模擬一個表現層 */ public class Client { /** * 獲取 spring 的容器,以及從容器中獲取 bean 物件 * * ApplicationContext * 它是一個介面,有兩個實現類。 * 分別是: * ClassPathXmlApplicationContext: * 它永遠都是從類的根路徑下載入配置檔案 推薦使用這種 * FileSystemXmlApplicationContext: * 它是從磁碟路徑上載入配置檔案,配置檔案可以在磁碟的任意位置。 */ public static void main(String[] args) { //1.使用 ApplicationContext 介面,就是在獲取 spring 容器 ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml"); //2.根據 bean 的 id 獲取物件 ICustomerService customerService = (ICustomerService) ac.getBean("customerService"); System.out.println(customerService); ICustomerDao customerDao = (ICustomerDao) ac.getBean("customerDao"); System.out.println(customerDao); } } 問題:由於物件都交給 spring 管理了,這時如果我們把 service 實現了的這行程式碼: private ICustomerDao customerDao = new CustomerDaoImpl();// 此處有依賴關係 改為 private ICustomerDao customerDao = null; 使用 service 物件呼叫 saveCustomer 方法會出現什麼問題呢? 執行結果: 空指標異常。 原因是 customerDao 物件沒有賦值。 如果在程式執行期間,spring 框架能幫我們給 customerDao 物件賦了值,那該多好。 帶著腦海裡的這個想法,繼續往下看。 |
2.2.2.2 Spring工廠類結構
BeanFactory 才是 Spring 容器中的頂層介面。ApplicationContext 是它的子介面。
BeanFactory 和 ApplicationContext 的區別:建立物件的時間點不一樣。
ApplicationContext:只要一讀取配置檔案,預設情況下就會建立物件。
BeanFactory:什麼時候物件使用什麼時候建立物件。
2.2.3 IOC 中 中 bean 標籤 和 管理物件細節
2.2.3.1 bean 標籤
作用:
用於配置物件讓 spring 來建立的。
預設情況下它呼叫的是類中的無參建構函式。如果沒有無參建構函式則不能建立成功。
屬性:
id :給物件在容器中提供一個唯一標識。用於獲取物件。
class :指定類的全限定類名。用於反射建立物件。預設情況下呼叫無參建構函式。
scope :指定物件的作用範圍。
* singleton :預設值,單例的.
* prototype :多例的.
* request :WEB 專案中,Spring 建立一個 Bean 的物件,將物件存入到 request 域中.
* session :WEB 專案中,Spring 建立一個 Bean 的物件,將物件存入到 session 域中.
init-method :指定類中的初始化方法名稱。
destroy-method :指定類中銷燬方法名稱。
2.2.3.2 bean 的作用範圍和生命週期
單例物件: scope="singleton"
一個應用只有一個物件的例項。它的作用範圍就是整個引用。
生命週期:
物件出生:當應用載入,建立容器時,物件就被建立了。
物件活著:只要容器在,物件一直活著。
物件死亡:當應用解除安裝,銷燬容器時,物件就被銷燬了。
多例物件: scope="prototype"
每次訪問物件時,都會重新建立物件例項。
生命週期:
物件出生:當使用物件時,建立新的物件例項。
物件活著:只要物件在使用中,就一直活著。
物件死亡:當物件長時間不用時,被 java 的垃圾回收器回收了。
2.2.4 spring 的依賴注入
2.2.4.1 依賴注入的概念
它是 spring 框架核心 ioc 的具體實現方式。簡單的說,就是坐等框架把物件傳入,而不用我們自己去獲取.
2.2.4.2 建構函式注入
顧名思義,就是使用類中的建構函式,給成員變數賦值。注意,賦值的操作不是我們自己做的,而是通過配置的方式,讓 spring 框架來為我們注入。具體程式碼如下:
public class CustomerServiceImpl implements ICustomerService { private String name; private Integer age; private Date birthday; public CustomerServiceImpl(String name, Integer age, Date birthday) { this.name = name; this.age = age; this.birthday = birthday; } @Override public void saveCustomer() { System.out.println(name+","+age+","+birthday); } } <!-- 使用建構函式的方式,給 service 中的屬性傳值 要求: 類中需要提供一個對應引數列表的建構函式。 涉及的標籤: constructor-arg 屬性: index:指定引數在建構函式引數列表的索引位置 type:指定引數在建構函式中的資料型別 name:指定引數在建構函式中的名稱 用這個找給誰賦值 =======上面三個都是找給誰賦值,下面兩個指的是賦什麼值的============== value:它能賦的值是基本資料型別和 String 型別 ref:它能賦的值是其他 bean 型別,也就是說,必須得是在配置檔案中配置過的 bean --> <bean id="customerService" class="com.bdqn.service.impl.CustomerServiceImpl"> <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> |
2.2.4.3 Set方法注入(重點)
顧名思義,就是在類中提供需要注入成員的 set 方法。具體程式碼如下:
public class CustomerServiceImpl implements ICustomerService { 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 saveCustomer() { System.out.println(name+","+age+","+birthday); } } <!-- 通過配置檔案給 bean 中的屬性傳值:使用 set 方法的方式 涉及的標籤: property 屬性: name:找的是類中 set 方法後面的部分 ref:給屬性賦值是其他 bean 型別的 value:給屬性賦值是基本資料型別和 string 型別的 實際開發中,此種方式用的較多。 --> <bean id="customerService" class="com.bdqn.service.impl.CustomerServiceImpl"> <property name="name" value="test"></property> <property name="age" value="21"></property> <property name="birthday" ref="now"></property> </bean> <bean id="now" class="java.util.Date"></bean>
Spring 第 二 天第1 章 基於註解的 IOC 配置1.1 寫在最前學習基於註解的 IoC 配置,大家腦海裡首先得有一個認知,即註解配置和 xml 配置要實現的功能都是一樣的,都是要降低程式間的耦合。只是配置的形式不一樣。關於實際的開發中到底使用 xml 還是註解,每家公司有著不同的使用習慣。所以這兩種配置方式我們都需要掌握。 1.2 環境搭建1.2.1 第一步:拷貝必備 jar 包到工程的 lib 目錄。注意:在基於註解的配置中,我們還要多拷貝一個 aop 的 jar 包。如下圖: 1.2.2 第二步:在類的根路徑下建立一個任意名稱的 xml 檔案 (不能是中文)基於註解整合時,匯入約束時需要多匯入一個 context 名稱空間下的約束.
給配置檔案匯入 context 約束:
1.2.3 使用@Component 註解配置管理的資源
1.2.4 第四步在 spring 的配置檔案中開啟 spring 對註解 ioc 的支援
1.3 常用註解1.3.1 用於建立物件的相當於:<bean id="" class=""> 1.3.1.1 @Component 作用: 把資源讓 spring 來管理。相當於在 xml 中配置一個 bean。 屬性: value:指定 bean 的 id。如果不指定 value 屬性,預設 bean 的 id 是當前類的類名。首 字母小寫。 1.3.1.2 @Controller @Service @Repository 他們三個註解都是針對一個的衍生註解,他們的作用及屬性都是一模一樣的。 他們只不過是提供了更加明確的語義化。 @Controller :一般用於表現層的註解。 @Service :一般用於業務層的註解。 @Repository :一般用於持久層的註解。 細節:如果註解中有且只有一個屬性 要賦值時是 ,且名稱是 value ,value可以省略 。 1.3.2 用於注入資料的相當於:<property name="" ref=""> <property name="" value=""> 1.3.2.1 @Autowired 作用: 自動按照型別注入。當使用註解注入屬性時,set 方法可以省略。它只能注入其他 bean 型別。當有多個型別匹配時,使用要注入的物件變數名稱作為 bean 的 id,在 spring 容器查詢,找到了也可以注入成功。找不到就報錯。 1.3.2.2 @Qualifier(不經常使用) 作用: 在自動按照型別注入的基礎之上,再按照 Bean 的 id 注入。它在給欄位注入時不能獨 立使用,必須和@Autowire 一起使用;但是給方法引數注入時,可以獨立使用。 屬性: value:指定 bean 的 id。 1.3.2.3 @Resource 作用: 直接按照 Bean 的 id 注入。它也只能注入其他 bean 型別。 屬性: name:指定 bean 的 id。 1.3.2.4 @Value 作用: 注入基本資料型別和 String 型別資料的 屬性: value:用於指定值 1.3.3 用於改變作用範圍的:相當於:<bean id="" class="" scope=""> 1.3.3.1 @Scope 作用: 指定 bean 的作用範圍。 屬性:value:指定範圍的值。 取值:singleton prototype request session 1.3.4 和生命週期相關的:( 瞭解)相當於:<bean id="" class="" init-method="" destroy-method="" /> 1.3.4.1 @PostConstruct 作用: 用於指定初始化方法。 1.3.4.2 @PreDestroy 作用: 用於指定銷燬方法。 1.3.5 關於註解和 XML 的選擇問題註解的優勢: 配置簡單,維護方便(我們找到類,就相當於找到了對應的配置)。 XML 的優勢: 修改時,不用改原始碼。不涉及重新編譯和部署 1.4 spring 管理物件細節基於註解的 spring IoC 配置中,bean 物件的特點和基於 XML 配置是一模一樣的。 寫到此處,基於註解的 IoC 配置已經完成,但是大家都發現了一個問題:我們依然離不開 spring的 xml 配置檔案,那麼能不能不寫這個 bean.xml,所有配置都用註解來實現呢? 答案是肯定的,請看下一章節。 1.5 spring 的純註解配置建立物件的註解: 1.5.1 待改造的問題我們發現,之所以我們現在離不開 xml 配置檔案,是因為我們有一句很關鍵的配置: <!-- 告知spring框架在,讀取配置檔案,建立容器時,掃描註解,依據註解建立物件,並存入容器中 --> <context:component-scan base-package="cn.bdqn"></context:component-scan> 如果他要也能用註解配置,那麼我們就可以脫離 xml 檔案了。 1.5.2 使用註解配置要掃描的包專案結構圖: 在此圖中,我們已經一點也看不到 xml 的身影了。那麼,那句關鍵的配置跑哪去了呢? 在一個新的類上:用這個類代替配置檔案. 配置類程式碼:
測試類程式碼:
1.5.3 新註解說明1.5.3.1 @Configuration 作用: 用於指定當前類是一個配置類,會從該類上載入註解。讀取該類上@ ComponentScan 註解 初始化 spring 容器。 1.5.3.2 @ComponentScan 作用: 用於指定 spring 在初始化容器時要掃描的包。 屬性: basePackages :用於指定要掃描的包。和該註解中的 value 屬性作用一樣。 1.5.3.3 @Bean 作用:把方法的返回值放入到Spring容器中 該註解只能寫在方法上,表明使用此方法建立一個物件,並且交給 spring 管理。 屬性:name :給當前@Bean 註解的方法建立的物件指定一個名稱(即 bean 的 id)。 配置類程式碼:
測試程式碼:
1.5.3.4 @Import 作用: 用於匯入其他配置類 屬性: value[] :用於指定其他配置類的位元組碼。 示例程式碼:這樣就不需要再向容器中放入JdbcConfig類,@Component註解省略 直接使用@Important註解載入配置類
1.5.3.5 @PropertySource 作用:用於載入.properties 檔案中的配置 屬性: value[] :用於指定 properties 檔案位置。如果是在類路徑下,需要寫上 classpath:
注意4.3以前版本需要提供佔位符類:
1.5.3.6 @Qualifier的第二種使用方法 1.第一種方式 和@Autowire一起配合起來使用,用於指定固定一個物件的配置. 2.用於向方法引數注入Spring容器中物件. 示例程式碼:
測試程式碼:
第2章Spring整合Junit2.1 準備測試環境2.1.1 建立業務層介面和實現類業務層介面:
業務層實現類:
|