Spring
簡介
Spring 是一種用來簡化企業級應用開發的開源框架,包括Spring Framework, Spring Data, Spring Security,Spring Boot,SpringMVC等。Spring 家族最核心的概念當屬 AOP 和 IoC,詳解見下節。其中 Spring 優點如下:
1. 降低了組件之間的耦合性 ,實現了軟件各層之間的解耦
2. 可以使用便捷的衆多服務,如事務管理,消息服務等
3. 容器提供了AOP技術,利用它很容易實現如權限攔截,運行期監控等功能
4. Spring對於主流的應用框架提供了集成支持,如Hibernate、JPA等
5. Spring屬於低侵入式設計,代碼的污染極低
6. Spring的高度開放性,開發者可以自由選擇Spring的部分或全部
AOP和IOC
AOP(Aspect Oriented Programming,面向切面編程)
AOP簡單說就是在目標方法執行前後自定義一些操作,一般都是基於代理模式來實現的,Spring支持兩種代理模式,JDK原生代理和CGLib代理。AOP給程序帶來良好的擴展性和封裝性,可以實現業務代碼與非業務代碼的隔離。比如可以在不改變目標代碼的前提下實現目標方法的增強:方法執行時間監控,打印日誌,權限控制等等。
1. JDK動態代理是利用反射機制生成一個實現代理接口的匿名類,在調用具體方法前調用InvokeHandler來處理。只能對實現了接口的類生成代理。
2. CGLib動態代理是利用ASM開源包,對代理對象類的Class文件加載進來,通過修改其字節碼生成子類來處理。
- 切面(Aspect):類是對物體特徵的抽象,切面就是對橫切關注點的抽象。
- 切點(Pointcut):對連接點進行攔截的定義。
- 連接點(Joinpoint):被攔截到的點,比如方法(Spring中一般是方法)、字段、構造器。
- 通知(Advice):指攔截到連接點後要執行的代碼,通知分爲前置、後置、異常、最終、環繞五類。
在運行時,動態地將代碼切入到類的指定方法、指定位置上的編程思想
IoC(Inversion of Control,控制反轉)
IOC (Inversion of Control,控制反轉):對象之間的依賴關係由容器來創建。本來對象之間的關係是由開發者自己創建和維護的,在使用Spring框架後,對象之間的關係由容器來創建和維護,將開發者做的事讓容器做,這就是控制反轉。BeanFactory接口是Spring Ioc容器的核心接口。
DI (Dependecy Injection,依賴注入):我們在使用Spring容器的時候,容器通過調用set方法或者是構造器來建立對象之間的依賴關係。注入方式有設值注入、構造注入、註解注入、接口注入(基本不用
),設值注入直觀,自然;構造注入可以在構造器中決定依賴關係的順序。
控制反轉是目標,依賴注入是我們實現控制反轉的一種手段。
SpringMVC和Struts
SpringMVC執行流程如下:
1. 客戶端向Spring容器發起一個Http請求。
2. 發起的請求被前端控制器
(DispatcherServlet)攔截。
3. 查詢處理器映射
(HandlerMapping)得到執行鏈,並請求相應的處理器適配器
(HandlerAdapter)。
4. 執行處理器
(Handler)並處理請求,以ModelAndView(屬性值和返回頁面)的形式返回。此處Handler即平時我們編寫的Controller。
5. 前端控制器
查詢視圖解析器
(ViewResolver),並返回View。
6. 成功渲染視圖則返回給客戶端,否則拋異常。
比較點 | SpringMVC | Struts |
---|---|---|
核心控制器 | DispatcherServlet | FilterDispatcher |
配置文件 | 量少(AOP) | 量大(Interceptor機制) |
RESTful URL | 易實現(方法級別) | 實現費勁(類級別) |
處理Ajax請求 | @ResponseBody返回響應文本 | 攔截器集成Ajax |
性能 | 稍快 | 稍慢 |
Spring事務
數據庫事務是指作爲單個邏輯工作單元執行的一系列操作,要麼完全地執行,要麼完全地不執行。事務滿足原子性、一致性、持久性、隔離性四大特性。
事務管理
Spring事務管理器的接口是PlatformTransactionManager,通過這個接口,Spring爲各個平臺如JDBC(DataSourceTransactionManager)、Hibernate(HibernateTransactionManager、JpaTransactionManager)等都提供了對應的事務管理器。
事務的傳播特性
當事務方法被另一個事務方法調用時,必須指定事務應該如何傳播,Spring定義了7種傳播行爲:
傳播行爲(簡寫) | 含義 |
---|---|
MANDATORY | 支持當前事務;若不存在當前事務,則拋出一個異常 |
NESTED | 若果存在當前事務,則在一個嵌套的事務中執行 |
NEVER | 不支持當前事務;若存在當前事務,則拋出一個異常 |
NOT_SUPPORTED | 不支持當前事務;而總是執行非事務性 |
REQUIRED | 支持當前事務;若不存在事務,則創建一個新的事務 |
REQUIRES_NEW | 創建一個新事務,若存在一個事務,則把當前事務掛起 |
SUPPORTS | 支持當前事務;若不存在,則執行非事務性 |
TIMEOUT_DEFAULT | 使用默認超時的底層事務系統,若不支持超時則沒有 |
事務隔離級別
在Spring中定義了5中不同的事務隔離級別,見下表:
1. 髒讀:發生在一個事務讀取了另一個事務改寫但尚未提交的數據時。如果改寫在稍後被回滾了,那麼第一個事務獲取的數據就是無效的。
2. 不可重複讀:發生在一個事務執行相同的查詢兩次或兩次以上,但是每次都得到不同的數據時。這通常是因爲另一個併發事務在兩次查詢期間進行了更新。
3. 幻讀:發生在一個事務T1讀取了幾行數據,接着另一個併發事務T2插入了一些數據時。在隨後的查詢中,事務T1就會發現多了一些原本不存在的記錄。
隔離級別(簡寫) | 含義 |
---|---|
DEFAULT | 使用後端數據庫默認的隔離級別 |
READ_UNCOMMITTED | 允許讀取尚未提交的更改。可能導致髒讀、幻讀或不可重複讀 |
READ_COMMITTED | 允許從已經提交的併發事務讀取。可防止髒讀,可能導致幻讀或不可重複讀 |
REPEATABLE_READ | 對相同字段的多次讀取的結果是一致的,除非數據被當前事務本身改變。可防止髒讀和不可重複讀,可能導致幻讀 |
SERIALIZABLE | 完全服從ACID的隔離級別。可防止髒讀、不可重複讀和幻讀 |
Spring Boot
Spring Boot來自於 Spring 大家族,是一套全新的框架,它默認幫我們進行了很多配置,集成了大量常用的第三方庫(例如 Jackson、JDBC、MongoDB、Redis、Mail 等),這些第三方庫幾乎都可以開箱即用。Spring Boot可以幫助我們快速搭建一個項目,從而讓開發者能夠更加專注於業務邏輯。
Spring擴展
- 實現BeanPostProcess接口在Bean生成前後進行操作。
- 實現BeanFactoryPostProcessor接口配置Bean元屬性。
- 實現FactoryBean接口定製個性化的Bean。
MyBatis
MyBatis是一款優秀的持久層框架,它幾乎避免了所有的JDBC代碼和手動設置參數以及獲取結果集,它可以使用XML或註解來將接口和POJO映射成數據庫中的記錄。
MyBatis和Hibernate
比較點 | MyBatis | Hibernate |
---|---|---|
特點 | 半自動(手寫SQL) | 全自動(根據映射生成SQL) |
SQL直接優化 | 方便 | 複雜 |
數據庫移植性 | 弱 | 強 |
緩存機制 | 欠缺 | 更優 |
日誌系統 | 欠缺 | 完整 |
Statement和PreparedStatement的區別
/***PreparedStatement extends Statement***/
//Statement用法
sql1 = "select * from tbl_user where username='" + u + "' and password='" + p + "'";
statement = conn.createStatement();
result1 = statement.executeQuery(sql1);
//PrepareStatement用法
sql2 = "select * from tbl_user where username=? and password=?";
prepareStatement = conn.prepareStatement(sql2);
pstmt.setString(1, u);
pstmt.setString(2, p);
result2 = prepareStatement.executeQuery();
比較點 | Statement | PreparedStatement |
---|---|---|
用途 | 執行靜態SQL語句並返回結果 | 執行已預編譯SQL語句並返回結果 |
可讀性 | 低(字符串拼接) | 高(Set方法設值) |
效率 | 低(字符串拼接) | 高(佔位符) |
安全性 | 低(SQL注入) | 高 |
消息隊列
- 消息隊列中間件是分佈式系統中重要的組件,主要主要解決應用耦合、異步消息、流量削鋒等問題,具有異步性、可靠性(存儲到本地硬盤)、鬆耦合、分佈式的特性。
- 主要特點是異步處理
- 主要目的是減少請求響應時間、解耦
- 主要使用場景是將比較耗時且不需同步返回結果的操作當做消息存入隊列
- 流量削峯的一種解決方案
- MQ推送模式改爲定時或者批量拉取模式
- 消息接收方實現批量處理等方式
- RabbitMQ
- RabbitMQ 是一個由 ErLang 開發的AMQP的開源實現。
- 交換機(Exchange)的功能主要是接收消息並且轉發到綁定的隊列,交換機不存儲消息,交換機本質是一張路由查詢表。
- Direct:綁定時設定一個路由鍵,消息的路由鍵匹配纔會被投送到隊列中。
- Topic:根據模糊匹配轉發消息(最靈活)。
- Headers:設置頭部參數類型的交換機。
- Fanout:轉發消息到所有綁定隊列,消息廣播的模式。
路由鍵(routing_key)在消息中,而綁定鍵(binding_key)作用於交換機和隊列之間。當消息中的路由鍵和綁定鍵對應上的時候,交換機就知道將該消息存入哪個隊列。
分佈式
- 分佈式:一個業務分拆多個子業務,部署在不同的服務器上(廚師和配菜師的關係)
- 集羣:同一個業務,部署在多個服務器上(兩個廚師的關係)
微服務
微服務架構風格是一種使用一套小服務來開發單個應用的方式,每個服務運行在自己的進程中,並使用輕量級機制通信(通常是HTTP API
),這些服務能夠通過自動化部署機制來獨立部署、可以使用不同的編程語言實現、可以使用不同的數據存儲技術,並保持最低限度的集中式管理。
時下熱門的微服務開發框架有:Spring Cloud、Dubbo
RESTful
URL定位資源,HTTP動詞描述操作
- GET:讀取資源
- POST:創建資源
- PUT:更新資源
- DELETE:刪除資源
使用PUT方式更新時,必須發送資源所有的屬性
Nginx
反向代理
正向代理:隱藏真實的請求客戶端,服務端不知道真實的客戶端是誰,正向代理服務器會代替客戶端向服務器發送請求。正向代理代理的對象是客戶端。
反向代理:隱藏真實的響應服務端,客戶端不知道真實的服務端是誰,反向代理服務器會把請求轉發到真實的服務器。反向代理代理的對象是服務端。
10086總機就是一種反向代理,客戶不知道真正提供服務人的是誰。
負載均衡
- 四層負載均衡:工作在OSI模型的傳輸層,它在接收到客戶端的流量以後通過修改數據包的地址信息將流量轉發到應用服務器,因此四層負載均衡的主要工作就是轉發。
- 七層負載均衡:工作在OSI模型的應用層,七層負載均衡在接到客戶端的流量以後,還需要一個完整的TCP/IP協議棧與客戶端建立一條完整的連接,並將應用層的請求流量解析出來,再按照調度算法選擇一個應用服務器,並與應用服務器建立另外一條連接將請求發送過去,因此七層負載均衡的主要工作就是代理。
設計模式
設計模式的六大原則
- 單一職責原則:一個類只負責一個功能領域中的相應職責。
- 開閉原則:一個軟件實體應當對擴展開放,對修改關閉。
- 里氏替換原則:所有引用父類的地方必須能透明地使用其子類的對象。
- 依賴倒置原則:抽象不應該依賴於細節,細節應當依賴於抽象。(要針對接口編程,而不是針對實現編程)。
- 接口隔離原則:使用多個專門的接口,而不使用單一的總接口。
- 迪米特法則:一個軟件實體應當儘可能少地與其他實體發生相互作用。
單例、工廠、觀察者、適配器、責任鏈
- 單例模式:一個類負責創建自己的對象,同時確保只有單個對象被創建,並提供一種訪問其唯一對象的方式。
- 工廠模式:在創建對象時不暴露創建邏輯,並通過使用一個共同的接口來指向新創建的對象。
- 觀察者模式:定義對象間的一種一對多的依賴關係,當一個對象的狀態發生改變時,所有依賴於它的對象都得到通知並被自動更新。
- 適配器模式:負責加入獨立的或不兼容的接口功能的類,使得原本由於接口不兼容而不能一起工作的那些類可以一起工作。
- 責任鏈模式:爲請求創建了一個接收者對象的鏈,沿着這條鏈傳遞請求,直到有對象處理它爲止,對請求的發送者和接收者進行解耦。
寫出生產者消費者模式
生產者生產數據到緩衝區中,消費者從緩衝區中取數據。如果緩衝區已經滿了,則生產者線程阻塞;如果緩衝區爲空,那麼消費者線程阻塞。
public class ProducerAndConsumer {
static BlockingQueue resourceQueue = new LinkedBlockingQueue<Resource>(10);
public static void main(String[] args) {
Producer p = new Producer(); //生產者
Consumer c1 = new Consumer(); //消費者1
Consumer c2 = new Consumer(); //消費者2
Consumer c3 = new Consumer(); //消費者3
p.start();
c1.start();
c2.start();
c3.start();
}
}
/**
* 資源
*/
class Resource {
int id;
public Resource(int id) {
this.id = id;
}
}
/**
* 生產者
*/
class Producer extends Thread {
int p = 1;
@Override
public void run() {
while (true) {
try {
Resource resource = new Resource(p++);
System.out.println("生產資源" + resource.id);
ProducerAndConsumer.resourceQueue.put(resource);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/**
* 消費者
*/
class Consumer extends Thread {
@Override
public void run() {
while (true) {
try {
System.out.println("消費資源" +
((Resource) ProducerAndConsumer.resourceQueue.take()).id);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
高內聚、低耦合
- 耦合性:也稱塊間聯繫。指軟件各模塊之間相互聯繫緊密程度的一種度量。模塊之間聯繫越緊密,則其耦合性就越強。模塊間耦合高低取決於模塊間接口的複雜性、調用的方式及傳遞的信息。
- 內聚性:也稱塊內聯繫。指軟件模塊內部各元素彼此結合緊密程度的一種度量。模塊內各元素(語名之間、程序段之間)聯繫越緊密,則其內聚性就越高。
高內聚、低耦合的系統具有更好的重用性,維護性,擴展性,可以更高效的完成系統的維護開發,持續的支持業務的發展,而不會成爲業務發展的障礙