Spring 是分層的 Java SE/EE 應用 full-stack 輕量級開源框架,以 IoC(Inverse Of Control: 反轉控制)和 AOP(Aspect Oriented Programming:面向切面編程)爲內核,提供了展示層 Spring MVC 和持久層java
Spring JDBC 以及業務層事務管理等衆多的企業級應用技術,還能整合開源世界衆多著名的第三方框架和類庫,逐漸成爲使用最多的 Java EE 企業應用開源框架。web
在軟件設計中,若是項目的耦合程度高,那麼維護項目所須要的崇本也高。設計一個軟件,其耦合度和內聚度一般做爲衡量模塊獨立的標準。而劃分模塊的一個準則就是高內聚低耦合。spring
耦合是不可能被徹底消除的,只能下降一部分的耦合能用數據耦合(模塊間經過參數來傳遞數據,最低的耦合關係)就不用控制耦合(傳遞控制信號來執行操做),能用公共耦合(多個模塊共擁有給全局數據項)就不用內容耦合(一個模塊直接操做另外一個模塊的數據,或不經過正常入口而轉入另外一個模塊,最高程度的耦合)。sql
因此使用配置文件配置key value的全限定類名字符串,就能夠動態配置不一樣的驅動。編程
而且jdbc的這種方式再也不依賴具體的驅動類。session
因此咱們可使用工廠模式,經過配置文件來配置建立對象的條件,來使用一個讀取配置文件,並能建立和獲取三層對象的工廠來解耦。框架
比起直接建立對象的主動方式,用工廠來建立對象的方式是被動的。而這種被動接收的方式來獲取對象,就是控制反轉IOC的思想(下降程序的耦合),他是spring框架的核心之一。dom
下面咱們用一個銀行轉帳的小demo用3種方式來進行配置(事務控制用了2種方式)
項目結構:
config是配置目錄:函數
由SpringConfiguration做爲總配置類
JdbcConfig類和TransactionConfig類是配置數據庫鏈接的類和事務控制的類
dao是持久層
domain存放的是實體類
service是服務層
resources
jdbcConfig.properties中存放鏈接數據庫的鏈接數據
test
AccountServiceTest測試類
由運行順序講解代碼
經過測試類
由於junit的原理,因此他是沒法識別咱們是否使用了Spring框架的,因此必需要用spring的@Runwith來替換Junit原有的運行器
而後使用@ContextConfiguration的classes屬性指定 spring 配置文件的位置
@ContextConfiguration 註解:
locations 屬性:用於指定配置文件的位置。若是是類路徑下,須要用 classpath:代表
classes 屬性:用於指定註解的類。當不使用 xml 配置時,須要用此屬性指定註解類的位置。
而後咱們經過註解進入配置文件類
掃描全部包就能夠識別出全部的註解配置
導入了2個配置類
開啓對AOP註解的支持
掃描包的話給出幾個配置的典型例子
持久層的實現類:
@Repository持久層註解,
與@Component功能一致
做用:
把資源讓 spring 來管理。至關於在 xml 中配置一個 bean。
屬性:
value:指定 bean 的 id。若是不指定 value 屬性,默認 bean 的 id 是當前類的類名。首字母小寫。
@Controller @Service @Repository
他們三個註解都是針對第一個的衍生註解,他們的屬性都是如出一轍的。
他們只不過是提供了更加明確的語義化。
@Controller:通常用於表現層的註解。
@Service:通常用於業務層的註解。
@Repository:通常用於持久層的註解。
細節:若是註解中有且只有一個屬性要賦值時,且名稱是 value,value 在賦值是能夠不寫。
@Autowired
做用:
自動按照類型注入。當使用註解注入屬性時,set 方法能夠省略。它只能注入其餘 bean 類型。當有多個
類型匹配時,使用要注入的對象變量名稱做爲 bean 的 id,在 spring 容器查找,找到了也能夠注入成功。找不到
就報錯。
服務層的實現類:
@Service:業務層的註解
@Autowired自動注入
@Transactional
該註解的屬性和 xml 中的屬性含義一致。該註解能夠出如今接口上,類上和方法上。
出現接口上,表示該接口的全部實現類都有事務支持。
出如今類上,表示類中全部方法有事務支持
出如今方法上,表示方法有事務支持。
以上三個位置的優先級:方法>類>接口
進入導入的第一個配置類
@Value注入基本類型和String類型的數據
@Bean標籤,
做用:
該註解只能寫在方法上,代表使用此方法建立一個對象,而且放入 spring 容器。
屬性:
name:給當前@Bean 註解方法建立的對象指定一個名稱(即 bean 的 id)。
而後看另外一個子配置類
仍然是提供了一個獲取txManager對象的方法
而後是讀取jdbc屬性的配置,參數爲類路徑下的配置文件位置
最後@EnableTransactionManagement的配置是開啓AOP的支持,配置了就開啓,不配置就關閉
而後就實現了徹底基於註解的配置
能夠和上面進行對照更便於理解、使用
* 他們的做用就和在xml配置文件中編寫一個<bean>標籤實現的功能是同樣的
* Component
* 做用:把當前對象存入spring容器中
* 屬性:
* value:用於指定bean的id,當咱們不寫時,默認值是當前類名且首字母變爲小寫
* Controller:通常用在表現層
* Service:通常用在業務層
* Repository:通常用在持久層
* 以上三個註解他們的做用和屬性與Component是如出一轍的
* 他們三個是spring框架爲咱們提供明確的三層使用的註解,使咱們的三層對象更加清晰
*
* 他們的做用就和在xml配置文件中的<bean>標籤中寫<property>標籤的做用是同樣的
* Autowired
* private IAccountDao accountDao1=null;
* 訪問修飾符 數據類型 變量名稱 數據類型
* 注入時主要考慮變量名稱與id的匹配
* 做用:
* 自動按照類型注入(接口的名稱,而非本身起的id)。只要容器中有惟一的一個bean對象類型和要注入的變量類型匹配就能夠注入成功。
* 若是ioc容器中沒有任何bean的類型和要注入的變量類型匹配就報錯。
* 出現位置:
* 變量、方法
* 細節:
* 在使用註解注入時,set方法不是必須的。
* Qualifier:
* 做用:
* 在按照類中注入的基礎上再按照名稱注入。他在給類成員注入時不能單獨使用(要與Autowired配合)。可是給方法參數注入時能夠
* Resource:
* 做用:
* 直接按照bean的id注入。他能夠獨立使用
* 屬性:
* name:用於指定bean的id。
* 以上3中注入都只能注入其餘bean類型的數據,而基本類型和String類型沒法使用上述註解實現
* 另外集合類型的注入只能經過xml來實現。
*
* value
* 做用:
* 用於注入基本類型和String類型的數據
* 屬性:
* value:用於指定數據的值。它可使用spring中SpEL(也就是spring的el表達式)
* SpEL的寫法:${表達式},看el表達式出現的位置
*
* 他們的做用和在bean標籤中使用scope屬性實現的功能是同樣的
* Scope
* 做用:
* 用於指定bean的做用範圍
* 屬性:
* value:指定範圍的取值。經常使用取值:singleton prototype,不寫默認是單例的
* 他們的做用和在bean標籤中使用init-method和destroy-method的做用是同樣的
* PostConstruct
* 做用:
* 用於指定初始化方法
* PreDestroy
* 做用:
* 用於指定銷燬方法(多例對象的銷燬,spring並不負責)
事務控制方面選Z額了spring編程式事務控制
dao是持久層
domain存放的是實體類
service是服務層
resources
bean.xml中存放了基於xml的spring配置信息
test
AccountServiceTest測試類
如下全部重複的部分不作解釋:
@ContextConfiguration 註解:使用locations 屬性來讀取配置文件
locations 屬性:用於指定配置文件的位置。若是是類路徑下,須要用 classpath:代表
classes 屬性:用於指定註解的類。當不使用 xml 配置時,須要用此屬性指定註解類的位置。
而後咱們進入配置文件
關於bean標籤和依賴注入的細節在下面:
依賴注入使用的是下面依賴注入方法中的set方法
用TransactionTemplate手動管理事務
<!--<bean id="accountService" class="com.lky.service.impl.AccountServiceImpl"></bean>-->
<!--<bean id="instanceFactory" class="com.lky.factory.InstanceFactory"></bean>-->
<!--<bean id="accountService" factory-bean="instanceFactory" factory-method="getAccountService"></bean>-->
<!--<bean id="accountService" class="com.lky.factory.StaticFactory" factory-method="getAccountService"></bean>—>
<!--<bean id="accountService" class="com.lky.service.impl.AccountServiceImpl" scope="prototype"></bean>—>
<!--
依賴注入:
Dependency Injection
IOC的做用:
下降程序間的耦合(依賴關係)
依賴關係的管理:
都交給spring來維護
當前類須要用到其餘類的對象,由spring爲咱們提供,咱們只須要在配置文件中說明
依賴關係的維護:
稱之爲依賴注入
依賴注入:
能注入的數據有3類:
基本類型和String
其餘bean類型(在配置文件中或註解配置過的bean)
複雜類型/集合類型
注入的方式:
1。使用構造函數提供
2。使用set方法
3。使用註解提供
—>
使用的標籤:constructor-arg
出現的位置:bean標籤內部
標籤中的屬性
type:用於指定要注入的數據的類型,該數據類型也是構造函數中某個或某些參數的類型(沒法區分多個,因此不能獨立實現)
index:用於指定要注入的數據給構造函數中指定索引位置的參數賦值,從0開始(能夠獨立實現,但必須清楚類型)
name:用於指定給構造函數中指定名稱的參數賦值(經常使用)
=============以上3個用於指定給構造函數中的參數賦值=================================
value:用於提供基本類型和String類型的數據
ref:用於指定其餘的bean類型的數據。指的是spring的IOC核心容器中出現過的bean對象
優點:
在獲取bean對象時,注入數據時必須的操做,不然對象沒法建立成功。
缺點:
改變了bean對象的實例化方式,使建立對象時,若是不使用這些數據,也必須提供。
-->
<bean id="accountService" class="com.lky.service.impl.AccountServiceImpl">
<constructor-arg name="name" value="柯"></constructor-arg>
<constructor-arg name="age" value="18"></constructor-arg>
<constructor-arg name="birthday" ref="time"></constructor-arg>
</bean>
<!--配置一個時間對象-->
<bean id="time" class="java.util.Date"></bean>
使用的標籤:property
出現的位置:bean標籤內部
標籤中的屬性:
name:用於指定給構造函數中指定名稱的參數賦值(經常使用),從set方法中尋找
=============以上3個用於指定給構造函數中的參數賦值=================================
value:用於提供基本類型和String類型的數據
ref:用於指定其餘的bean類型的數據。指的是spring的IOC核心容器中出現過的bean對象
優點:
建立對象時沒有明確的限制,能夠直接使用默認構造函數
缺點:
若是摸個成員必須有值,則獲取對象時,set方法沒法保證必定注入(可能不執行set),
-->
<bean id="accountService2" class="com.lky.service.impl.AccountServiceImpl2">
<property name="name" value="柯雨"></property>
<property name="age" value="21"></property>
<property name="birthday" ref="time"></property>
</bean>
用於給list集合注入的標籤有:
list array set
用於給Map集合注入的標籤有:
map props
結構相同,標籤能夠互換
-->
<bean id="accountService3" class="com.lky.service.impl.AccountServiceImpl3">
<property name="myStrs">
<array>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</array>
</property>
<property name="myList">
<list>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</list>
</property>
<property name="mySet">
<set>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</set>
</property>
<property name="myMap">
<map>
<entry key="a" value="AAA"></entry>
<entry key="b">
<value>BBB</value>
</entry>
</map>
</property>
<property name="myProps">
<props>
<prop key="c">CCC</prop>
<prop key="d">DDD</prop>
</props>
</property>
</bean>
由於採用xml文件配置,因此類中都是基本代碼。
重點帖一下服務層實現的部分,由於服務層進行着事務控制
spring編程式事務控制,經過使用transactionTemplate中的excute方法
excute在執行時會使用doInTransaction(status)的方法,而且在執行時,這個doInTransaction的部分實現了rollback,commit等事務操做。
由於經過代碼來實現事務很是麻煩,增長開發難度,而且會增長業務層重複的代碼。因此通常不多使用。
是由於銀行轉帳其實是進行事務控制,而事務控制是許多業務的必要部分。
持久層connection 對象的 setAutoCommit(true)方法會使單條sql語句的事務獲得成功的執行,但若是業務層同時執行多個持久層方法,而且在中間產生了異常,業務層的此次調用在不進行處理的狀況下是沒法進行總體自動回滾的,他會執行能成功的語句,回滾失敗的語句。此時咱們的銀行轉帳會出現嚴重的問題。咱們也能夠是用try catch finally的方式去處理,可是由於許多業務都涉及了事務控制,這樣去處理,重複的代碼量極大,是會增長開發成本的。
動態代理,咱們能夠經過動態代理,使須要進行事務控制的方法通過代理類,從而實現事務控制。
而動態代理的實現方式有兩種,基於接口和基於子類。
而在spring中,經過配置,框架會根據目標類是否實現了接口來決定採用哪一種動態代理的方式。
這這種解決上述問題的spring配置,稱之爲AOP
Joinpoint(鏈接點):
所謂鏈接點是指那些被攔截到的點。在 spring 中,這些點指的是方法,由於 spring 只支持方法類型的鏈接點。
Pointcut(切入點):
所謂切入點是指咱們要對哪些 Joinpoint 進行攔截的定義。
Advice(通知/加強):
所謂通知是指攔截到 Joinpoint 以後所要作的事情就是通知。
通知的類型:前置通知,後置通知,異常通知,最終通知,環繞通知。
Introduction(引介):
引介是一種特殊的通知在不修改類代碼的前提下, Introduction 能夠在運行期爲類動態地添加一些方法或 Field。
Target(目標對象):
代理的目標對象。
Weaving(織入):
是指把加強應用到目標對象來建立新的代理對象的過程。
spring 採用動態代理織入,而 AspectJ 採用編譯期織入和類裝載期織入。
Proxy(代理):
一個類被 AOP 織入加強後,就產生一個結果代理類。
Aspect(切面):
是切入點和通知(引介)的結合。
重複的部分再也不贅述
首先是執行的測試類
而後是讀取的bean.xml配置文件
這裏的spring關於aop的配置約束能夠在spring官網的Core包中找到
首先配置事務管理器
而後配置事務的通知(加強)去引用事務管理器,而這個通知,
就是公共重複的代碼部分,也是咱們以前想經過動態代理去加強的部分。
對轉帳方法設定了必定有事務的事務傳播行爲
對全部的find開頭方法進行了查詢方式的事務加強
切入點表明咱們對業務層實現下的全部方法(鏈接點)進行攔截
而後把切入點與上述的通知創建關係。
即經過使用AOP的方式實現了事務的控制。