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

耦合性(Coupling),也叫耦合度,是對子產品間關聯程度的度量。耦合的強弱取決于子產品間接口的複雜性、調 用子產品的方式以及通過界面傳送資料的多少。子產品間的耦合度是指子產品之間的依賴關系,包括控制關系、調用關 系、資料傳遞關系。子產品間聯系越多,其耦合性越強,同時表明其獨立性越差( 降低耦合性,可以提高其獨立 性)。耦合性存在于各個領域,而非軟體設計中獨有的,但是我們隻讨論軟體工程中的耦合。 在軟體工程中,耦合指的就是就是對象之間的依賴性。對象之間的耦合越高,維護成本越高。是以對象的設計 應使類和構件之間的耦合最小。軟體設計中通常用耦合度和内聚度作為衡量子產品獨立程度的标準。劃分子產品的一個 準則就是高内聚低耦合。
這裡用資料庫連接配接作為例子講解。
從上面的例子可以看出,如果采用DriverManager.registerDriver(new com.mysql.cj.jdbc.Driver())方式注冊驅動,在沒有導入資料庫相應的jar包之前,IDEA第一時間會報錯,也就是編譯時錯誤,找不到對應的類,這樣就造成了編譯時依賴。而當我們使用java反射的方式,如Class.forName("com.mysql.cj.jdbc.Driver")注冊驅動時,如果沒有對應的jar包,是不會在編譯時刻出現錯誤,隻會在運作時抛出異常(找不到對應的驅動)。
這樣又會産生一個問題,這裡咱們采用的是mysql的驅動,如果需要換成oracle的資料庫驅動,我們需要在源碼中進行修改,這也是很麻煩的。
在實際開發中我們可以把三層的對象都使用配置檔案配置起來,當啟動伺服器應用加載的時候,讓一個類中的 方法通過讀取配置檔案,把這些對象建立出來并存起來。在接下來的使用的時候,直接拿過來用就好了。 那麼,這個讀取配置檔案,建立和擷取三層對象的類就是工廠。
首先是配置檔案,這裡選擇.properties字尾的檔案
然後是bean工廠代碼的編寫,其中javabean的意思就是用java語言編寫的可重用元件
工廠的本質就是一個容器,是以我選擇使用hashmap作為對象的容器。建立bean對象時可以采用餓漢式或者懶漢式進行對象的建立,看自己的需求而定。可以看到,BeanFactory中建立對象均是采用反射建立對象,避免new關鍵字造成程式的耦合。
後續建立對象:
通過以上方式,就可以降低程式之間的耦合,而spring正是為我們提供了一個簡便的方式降低耦合并且增加了許多功能。
首先了解IOC的概念:
控制反轉(Inversion of Control,縮寫為IoC),是面向對象程式設計中的一種設計原則,可以用來減低計算機代碼之間的耦合度。其中最常見的方式叫做依賴注入(Dependency Injection,簡稱DI),還有一種方式叫“依賴查找”(Dependency Lookup)。通過控制反轉,對象在被建立的時候,由一個調控系統内所有對象的外界實體(這誰寫的,這是什麼實體?應該是Core的程序、線程、對象、元件?另外“實體”他的修飾詞,内部和外界的劃分不應該以建立和被建立為參照物來定義,而是應該以core為界線來定義,這樣修飾詞就可以稱為“内部”),将其所依賴的對象的引用傳遞(注入)給它。
如果還不怎麼了解,這裡有一張圖幫助了解。
不難看出,上個例子中的BeanFactory就是提供了管理javabean的工廠,建立對象的控制權給到了BeanFactory,而不是使用者。
現在學習spring的IOC,第一步導入spring的依賴。
第二步在Resources目錄建立字尾為.xml的檔案。這裡就是spring的配置檔案,必須導入配置檔案的限制,如下。
第三步讓spring管理資源,是以需要在配置檔案中配置咱們的service層和dao層的實作類。
第四步測試配置是否成功。這裡使用帶有main方法的類進行測試,模拟
getbean()的第二個參數為xxx.class,作用是避免強制轉換(我懶)。
1、作用:
用于配置對象讓 spring 來建立的。 預設情況下它調用的是類中的無參構造函數。如果沒有無參構造函數則不能建立成功。
2、屬性:
3.作用範圍以及生命周期
4、執行個體化Bean的三種方式
第一種:使用預設無參構造函數
其中bean.xml加入以下代碼。
第二種:spring 管理執行個體工廠——使用執行個體工廠的方法建立對象
instanceFactory.java源代碼:
第三種:spring 管理靜态工廠——使用靜态工廠的方法建立對象
StaticFactory源代碼:
1、依賴注入的概念
2、構造函數注入
首先建立一個名為UserTest1的實體類,其中隻包含有參構造函數。
bean.xml加入如下代碼:
constructor-arg的屬性及其用法
3、setter方法注入
首先建立名為UserTest2的實體類,并加入setter方法
bean.xml加入以下代碼:
property标簽的屬性及其用法:
4、注入集合類型
建立CollectionDI實體類
bean.xml加入以下代碼
5、實際開發中使用
使用場景:業務層需要調用資料層的方法。
如果我們需要将AccountDao的實作類注入到AccountService的實作類中,可以選擇構造方法注入或者setter方法注入,我這裡選擇setter方法進行注入。
bean.xml檔案的編寫
這樣就能實作在業務層中注入資料層的實作類。
1、在配置檔案中開啟對注解的支援
2、常用注解
1、導入依賴
這裡我采用資料庫連接配接進行一個測試,順帶使用JdbcTemplate操作資料庫
2、修改測試類
通過 @RunWith 注解,指定 Spring 的運作器,這裡 Spring的運作器是SpringJunit4ClassRunner
通過 @ContextConfiguration 注解,指定 Spring 運作器需要的配置檔案路徑或者配置類,這裡由于我們使用的全注解的方式,是以指定的是配置類。
Test.java源代碼:
改造以後就可以在測試類中使用@Autowired等注解注入資料!
3、編寫bean.xml配置檔案
4、測試
輸出結果:
整合成功!
轉賬的案例:當一個賬戶向另一個賬戶進行轉賬操作時,在轉出賬戶更新之後,抛出異常,則轉入賬戶則不會更新,這違背了事務的一緻性(部分代碼如下)
分析問題産生的原因:
因為轉賬方法中,需要擷取四個連接配接,并且每個連接配接不是相同的,第一步操作就是使用ThreadLocal将線程和連接配接綁定,保證一個線程中隻有一個能控制事務的連接配接。
第一步,建立ConnectionUtils工具類,實作對線程和連接配接的綁定,保證隻有一個連接配接。
擴充:
第二步,實作對事務的控制。
第三步、在業務層實作事務控制。
第四步,所有的持久層方法的connection都應該從ConnectionUtils中擷取,保證控制事務的連接配接隻有一個
第五步,建立bean.xml實作對注解的支援以及DataSource的配置 ,(簡簡單單,就不複制過來了)
以上對業務層的改造,能實作對轉賬方法的事務控制,但是存在一些問題,那就是代碼備援。如果我們需要對業務層的每個方法進行事務控制,就需要寫大量的重複代碼。況且如果以後修改ConnectionUtils或者TxManager的代碼,需要修改很多地方,對日常開發非常的不友善。代理模式能很好的解決此類問題,減少備援的代碼。
代理模式是一種設計模式,提供了對目标對象額外的通路方式,即通過代理對象通路目标對象,這樣可以在不修改原目标對象的前提下,提供額外的功能操作,擴充目标對象的功能。
簡言之,代理模式就是設定一個中間代理來控制通路原目标對象,以達到增強原對象的功能和簡化通路方式。
這種代理方式需要代理對象和目标對象實作一樣的接口。
優點:可以在不修改目标對象的前提下擴充目标對象的功能。
缺點:
備援。由于代理對象要實作與目标對象一緻的接口,會産生過多的代理類。
不易維護。一旦接口增加方法,目标對象與代理對象都要進行修改。
舉例:
接口類AccountService
接口實作類AccountServiceImpl
靜态代理類AccountServiceProxy
注:使用時就要使用靜态代理類AccountServiceProxy,而不是接口實作類AccountServiceImpl
動态代理利用了JDK API,動态地在記憶體中建構代理對象,進而實作對目标對象的代理功能。動态代理又被稱為JDK代理或接口代理。
靜态代理與動态代理的差別主要在:
靜态代理在編譯時就已經實作,編譯完成後代理類是一個實際的class檔案
動态代理是在運作時動态生成的,即編譯完成後沒有實際的class檔案,而是在運作時動态生成類位元組碼,并加載到JVM中
特點:
動态代理對象不需要實作接口,但是要求目标對象必須實作接口,否則不能使用動态代理。
java.lang.reflect Proxy,主要方法為
java.lang.reflect InvocationHandler,主要方法為
動态代理對象:ProducerProxy
cglib (Code Generation Library )是一個第三方代碼生成類庫,運作時在記憶體中動态生成一個子類對象進而實作對目标對象功能的擴充。
cglib特點
JDK的動态代理有一個限制,就是使用動态代理的對象必須實作一個或多個接口。
如果想代理沒有實作接口的類,就可以使用CGLIB實作。
CGLIB是一個強大的高性能的代碼生成包,它可以在運作期擴充Java類與實作Java接口。
它廣泛的被許多AOP的架構使用,例如Spring AOP和dynaop,為他們提供方法的interception(攔截)。
CGLIB包的底層是通過使用一個小而快的位元組碼處理架構ASM,來轉換位元組碼并生成新的類。
不鼓勵直接使用ASM,因為它需要你對JVM内部結構包括class檔案的格式和指令集都很熟悉。
cglib與動态代理最大的差別就是
使用動态代理的對象必須實作一個或多個接口
使用cglib代理的對象則無需實作接口,達到代理類無侵入。
使用cglib需要引入cglib的jar包,如果你已經有spring-core的jar包,則無需引入,因為spring中包含了cglib。
cglib的Maven坐标
cglib代理工廠CglibFactory
用于建立AccountServiceImpl的代理對象的工廠
使用xml配置IOC
使用Junit單元測試
<code>Aspect</code>(切面): Aspect 聲明類似于 Java 中的類聲明,在 Aspect 中會包含着一些 Pointcut 以及相應的 Advice。
<code>Joint point</code>(連接配接點):表示在程式中明确定義的點,典型的包括方法調用,對類成員的通路以及異常處理程式塊的執行等等,它自身還可以嵌套其它 joint point。
<code>Pointcut</code>(切點):表示一組 joint point,這些 joint point 或是通過邏輯關系組合起來,或是通過通配、正規表達式等方式集中起來,它定義了相應的 Advice 将要發生的地方。
<code>Advice</code>(增強):Advice 定義了在 <code>Pointcut</code> 裡面定義的程式點具體要做的操作,它通過 before、after 和 around 來差別是在每個 joint point 之前、之後還是代替執行的代碼。
<code>Target</code>(目标對象):織入 <code>Advice</code> 的目标對象。
<code>Weaving</code>(織入):将 <code>Aspect</code> 和其他對象連接配接起來, 并建立 <code>Adviced</code> object 的過程
其實這些專業術語在學習動态之後就很好了解
導入AOP需要的依賴,aspectjweaver用于解析AOP切入點表達式。
導入xml限制
目标:使用AOP完成動态代理實作的功能,實作對功能的增強
準備一個通知類,用于增強代碼。
準備待增強的目标對象,即AOP術語中的<code>target</code>。
基于xml的AOP配置,包括前置通知,後置通知,異常通知,最終通知。
使用Junit進行測試
輸出結果
它是spring架構為我們提供的一種可以在代碼中<code>手動控制</code>增強方法何時執行的方式。
在Logger類中添加環繞通知的代碼
其實配置AOP的環繞通知與自己寫的動态代理的代碼非常相似。
xml中添加代碼
測試結果
在xml中添加限制,開啟對注解的支援
使用注解改造Logger類
使用注解完成轉賬案例時應該在TxManaager中配置。
Spring所有事務代理類都是基于<code>PlatformTransactionManager</code>接口的實作。此接口是spring的事務管理器,它裡面提供了我們常用的操作事務的方法,如下代碼片段。
<code>PlatformTransactionManager</code>包括以下三個操作:
我們在開發中都是使用它的實作類,如下:
導入依賴
編寫spring配置檔案,完成對事務的控制
使用注解時xml的配置