Spring架構學習01
spring架構的概述以及spring中基于XML的IOC配置
1. 什麼是spring?
來自百度:
Spring是一個輕量級的控制反轉(IoC)和面向切面(AOP)的容器架構。
輕量——從大小與開銷兩方面而言Spring都是輕量的。完整的Spring架構可以在一個大小隻有1MB多的JAR檔案裡釋出。并且Spring所需的處理開銷也是微不足道的。此外,Spring是非侵入式的:典型地,Spring應用中的對象不依賴于Spring的特定類。
控制反轉——Spring通過一種稱作控制反轉(IoC)的技術促進了松耦合。當應用了IoC,一個對象依賴的其它對象會通過被動的方式傳遞進來,而不是這個對象自己建立或者查找依賴對象。你可以認為IoC與JNDI相反——不是對象從容器中查找依賴,而是容器在對象初始化時不等對象請求就主動将依賴傳遞給它。
面向切面——Spring提供了面向切面程式設計的豐富支援,允許通過分離應用的業務邏輯與系統級服務(例如審計(auditing)和事務(transaction)管理)進行内聚性的開發。應用對象隻實作它們應該做的——完成業務邏輯——僅此而已。它們并不負責(甚至是意識)其它的系統級關注點,例如日志或事務支援。
2.Spring的優勢
1.友善解耦,簡化開發
2.AOP程式設計支援
3.聲明式事務的支援
4.友善程式的測試
5.友善內建各種優秀架構
6.降低JavaEE API的使用難度
這裡一說可能大家都看不懂優點都是什麼意思,不過沒關系,這些優勢我們會在後來的部落格中一點一點總結深入,今天我們主要詳細了解的就是IOC,是以AOP我們暫時不談,先講一講為什麼需要引入IOC。
上圖講的就是Spring架構的結構體系,今天我們詳細講解的就是 Core Container部分,就是Spring的核心容器,IOC相關的内容,Spring任何的其他部分要運作,必須得有核心容器的支援。
我們先用一個案例來入門,了解一下Spring架構的其中一個核心内容IOC的作用
案例:
正常情況下我們連接配接資料庫進行查詢操作的代碼如下:
package com.jack.jdbc;
import java.sql.*;
/**
* @Author: Jack
* @date: 2019/10/21 19:25
*/
public class JdbcDemo01 {
public static void main(String[] args) throws SQLException {
//1.注冊驅動
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
//2.擷取連接配接
Connection connection = DriverManager.getConnection("jdbc:mysql:///day23","root","root");
//3.建立執行sql語句的statement對象
PreparedStatement preparedStatement = connection.prepareStatement("select * from account");
//4.執行sql語句,獲得結果集
ResultSet resultSet = preparedStatement.executeQuery();
//5.周遊結果集
while (resultSet.next()){
System.out.println(" id:"+resultSet.getString(1));
System.out.println(" uid:"+resultSet.getString(2));
System.out.println("Money:"+resultSet.getString(3));
}
//6.釋放資源
resultSet.close();
preparedStatement.close();
connection.close();
}
}
代碼寫完了我們可以從代碼中看出
沒有注冊驅動,就不能獲得連接配接,沒有獲得連接配接就不能建立PreparedStatement ,不能建立PreparedStatement 就不能執行sql語句。。。一環套一環,缺一不可。
是以一個功能的實作都是各種類和方法之間的依賴,這就叫做耦合性,分為類之間依賴和方法間依賴。
解耦:降低程式間的依賴關系
在實際開發中,真正的做到沒有依賴完全獨立不太可能,是以實際開放中的解耦就是要做到
編譯期間不依賴,運作時才依賴
解耦的思路:
第一步:使用反射來建立對象,而不是使用new關鍵字。
//1.注冊驅動
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
Class.forName("com.mysql.jdbc.Driver");
這樣做的好處就是,我們注冊驅動的時候不再是需要一個具體的類,而是隻要有一段正确的描述類名的字元串就行了。
但是也存在他的問題,我們注冊驅動的時候,使用反射就已經把要注冊的驅動類型寫死,以後在使用其他資料庫的時候,會相當的麻煩。
解決方案:使用讀取配置檔案來擷取 要建立的對象的全限定類名
在我們平時做服務端開發的時候,我們一般都使用的是三層架構,也就是界面層,業務邏輯層,資料通路層三層,一般處理的業務邏輯就是界面層調用業務邏輯層,業務邏輯層調用資料通路層這樣的邏輯,具有很強的耦合性,三層之間缺一不可,我們使用spring架構的核心内容IOC就是解決了這三層之間的耦合性的問題。
IOC控制反轉
inversion of control,把對象建立的權力叫給架構,是架構的重要特征,大幅度降低了耦合。注意不是完全的消除耦合。今天我們主要介紹的就是spring的ioc。
入門案例:
1.導入jar包:
因為我使用的是maven項目,這裡我就把坐标貼一下
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
2.建立一個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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>
3.把對象的建立交給spring來管理
<bean id="accountService" class="com.jack.service.imp.AccountServiceImpl"/>
<bean id="accountDao" class="com.jack.dao.impl.AccountDaoImpl"/>
4.代碼測試一下
//擷取核心容器對象
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//根據id擷取bean對象
AccountService accountService = (AccountService) ac.getBean("accountService");
AccountDao accountDao = ac.getBean("accountDao",AccountDao.class);
System.out.println(accountService);
System.out.println(accountDao);
根據運作結果我們可以發現,在操作過程中,我們并沒有使用new關鍵字執行個體化任何一個對象,但是在列印結果中我們可以看到,兩個對象都被執行個體化了,這就是springIOC的基本操作。
配置檔案 -> 解析配置檔案獲得全類名 -> 反射建立對象。
符合我們通過解析配置檔案,擷取要建立對象的全限定類名,通過反射建立對象的降低耦合度的思路
是以IOC的就是一個容器,我們把需要用到的類全部都放在ioc中,在spring中登記,在适當的時候,spring會把你需要的類給你,并且同時也在适當的時候,把你交給其他需要你的類,一個類的生命周期不再決定于引用他的對象,而是全部交spring架構管理,對于對象來講,以前是對象控制其他的對象,但是現在,是spring控制所有的對象,這就叫做控制反轉。
IOC的一些其他細節
—細節1
核心容器的兩個接口:
1.ApplicationContext(單例對象适用)
建構核心容器時,建立對象采取的政策是立即加載的方式,也就是說,一讀取完配置檔案,就立刻建立配置檔案中注冊的對象。
2.BeanFactory(多例對象适用)
在建構核心容器的時,建立對象采取的是延遲加載的政策,什麼時候根據id擷取對象的時候,才真正的建立對象。
但是我們思考一個問題,ApplicationContext在我們配置檔案讀取的時候就把對象建立了,但是如果我們要第二次使用某個對象,肯定要再次的調用建立對象的方法,這樣看來,我們還不如不着急的建立對象,幹脆等到什麼時候用,什麼時候建立對象。是以根據應用場景來看,ApplicationContext和BeanFactory都有使用的必要,不過在實際情況中,spring架構具有強大的功能,非常智能,可以根據我們配置的不同,去改變建立對象的政策,是以兩個接口建立對象的政策顯得不是那麼的重要,但是因為BeanFactory是一個頂級的接口,功能不夠完善,是以在日常的開發中,多數情況下還是選擇使用ApplicationContext接口來定義容器對象。
—細節2
建立Bean對象的方式(三種):
1.使用目标類中的預設構造函數來建立對象:
采用的是目标類中的構造方法來建立對象,如果目标類中沒有預設的構造方法(空參構造方法),則無法建立對象。
2.使用某個類中的方法來建立目标類對象
某個類(工廠類)
public class ServiceFactory {
public AccountService getAccountService(){
return new AccountServiceImpl();
}
}
spring配置檔案:
<bean id="factory" class="com.jack.Utils.ServiceFactory"/>
<bean id="accountService" factory-bean="factory" factory-method="getAccountService"/>
首先在配置檔案中注冊工廠類,也就是說執行個體化了工廠類,然後再注冊要執行個體化的目标類(AccountService ),factory-bean指定哪個類中的方法可以建立目标對象,factory-method指定工廠類中的哪個方法來建立對象。
3.使用工廠類中的靜态方法來建立目标對象
工廠類和靜态方法
public class StaticFactory {
public static AccountService getAccountService(){
return new AccountServiceImpl();
}
}
spring配置檔案
—細節3
bean的作用範圍
這個就非常簡單了,隻要設定一下scope标簽就好了,我們來挨個說。
1.singleton:顧名思義,就是設定spring建立對象為單例的。
spring配置檔案:
測試類:
//擷取核心容器對象
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
AccountService accountService1 = ac.getBean("accountService", AccountService.class);
AccountService accountService2 = ac.getBean("accountService", AccountService.class);
System.out.println(accountService1 == accountService2);
執行結果如下:
可以看到AccountService的構造函數僅執行了一次,并且兩個AccountService對象的比較列印結果為true
2.prototype:多例的
執行結果如下:
AccountService的構造函數執行了兩次,也就是說,AccountService建立了兩次。
3.request
作用域為 web應用的請求範圍
4.session
作用域為 web應用的會話範圍
5.global-session
作用域為叢集web應用的會話範圍
—細節4
bean對象的生命周期
分為單例對象和多例對象
單例對象的生命周期為:
出生:容器建立時,對象建立
生存:隻要容器存在,對象一直存在
死亡:容器銷毀時,對象死亡
單例對象和容器的生命周期相同,同生共死。
多例對象的生命周期:
出生:使用對象時,spring才為我們建立對象
生存:對象隻要是在使用過程中就一直生存。
死亡:當對象長時間不用,并且沒有别的對象引用的時候,java的垃圾回收機制把對象消亡。