IOC
Bean的裝配
自動化裝配
自動裝配就是讓Spring自動滿足bean依賴的一種方法爽柒,在滿足依賴的過程中,會在Spring應用上下文中尋找匹配某個bean需求的其他bean祠肥。為了聲明要進行自動裝配,我們可以借助Spring的@Autowired注解歧寺。
Spring從兩個角度來實現(xiàn)自動化裝配:
組件掃描(component scanning):Spring會自動發(fā)現(xiàn)應用上下文中所創(chuàng)建的bean纵散。
自動裝配(autowiring):Spring自動滿足bean之間的依賴。
通過Java代碼裝配bean
想要將第三方庫中的組件裝配到你的應用中人芽,在這種情況下,是沒有辦法在它的類上添加@Component
和@Autowired
注解的绩脆,因此就不能使用自動化裝配的方案了萤厅。
通過@Configuration
和@Bean
通過XML裝配bean
該選擇構造器注入還是屬性注入呢?作為一個通用的規(guī)則靴迫,我傾向于對強依賴使用構造器注入惕味,而對可選性的依賴使用屬性注入。
<!-- 1.通過其它bean -->
<constructor-arg ref="compactDisc">
<!-- 2.通過參數(shù) -->
<constructor-arg value="sgt"/>
<constructor-arg value="beatles"/>
<!-- 3.通過list/set -->
<constructor-arg>
<list>
<value> value1</value>
<value> value2</value>
<value> value3</value>
<ref bean="bean1"></ref>
<ref bean="bean2"></ref>
<ref bean="bean3"></ref>
</list>
<set>
<value> value4</value>
<ref bean="bean4"></ref>
</set>
</constructor-arg>
</bean>
當Spring遇到這個<bean>元素時玉锌,它會創(chuàng)建一個CDPlayer實例名挥。<constructor-arg>元素會告知Spring要將一個ID為compactDisc的bean引用傳遞到CDPlayer的構造器中。
使用<constructor-arg>元素進行構造器參數(shù)的注入主守。除了使用“ref”屬性來引用其他的bean禀倔,還可以使用value
屬性榄融,通過該屬性表明給定的值要以字面量的形式注入到構造器之中。
<list>元素是<constructor-arg>的子元素救湖,這表明一個包含值的列表將會傳遞到構造器中愧杯。其中,<value>元素用來指定列表中的每個元素鞋既。也可以使用<ref>元素替代<value>力九,實現(xiàn)bean引用列表的裝配。
<bean id="cdPlayer" class="soundSystem.CDPlayer">
<property name="compactDisc" ref="compactDisc" />
<property name="title" value="july"/>
<property name="artist" value="jay"/>
<property name="tracks">
<list>
<value> value1</value>
<value> value2</value>
<value> value3</value>
</list>
</property>
</bean>
<property>元素為屬性的Setter方法所提供的功能與<constructor-arg>元素為構造器所提供的功能是一樣的邑闺。在本例中跌前,它引用了ID為compactDisc的bean(通過ref屬性),并將其注入到compactDisc屬性中(通過setCompactDisc()
方法陡舅。
bean的作用域
在默認情況下抵乓,Spring應用上下文中所有bean都是作為以單例(singleton)的形式創(chuàng)建的。也就是說蹭沛,不管給定的一個bean被注入到其他bean多少次臂寝,每次所注入的都是同一個實例。
在大多數(shù)情況下摊灭,單例bean是很理想的方案咆贬。初始化和垃圾回收對象實例所帶來的成本只留給一些小規(guī)模任務,在這些任務中帚呼,讓對象保持無狀態(tài)并且在應用中反復重用這些對象可能并不合理掏缎。
有時候,可能會發(fā)現(xiàn)煤杀,你所使用的類是易變的(mutable)眷蜈,它們會保持一些狀態(tài),因此重用是不安全的沈自。在這種情況下酌儒,將class聲明為單例的bean就不是什么好主意了,因為對象會被污染枯途,稍后重用的時候會出現(xiàn)意想不到的問題忌怎。
Spring定義了多種作用域,可以基于這些作用域創(chuàng)建bean酪夷,包括:
單例(Singleton):在整個應用中榴啸,只創(chuàng)建bean的一個實例。
原型(Prototype):每次注入或者通過Spring應用上下文獲取的時候晚岭,都會創(chuàng)建一個新的bean實例鸥印。
會話(Session):在Web應用中,為每個會話創(chuàng)建一個bean實例。
請求(Rquest):在Web應用中库说,為每個請求創(chuàng)建一個bean實例狂鞋。
在Web應用中,如果能夠?qū)嵗跁捄驼埱蠓秶鷥?nèi)共享的bean璃弄,那將是非常有價值的事情要销。例如,在典型的電子商務應用中夏块,可能會有一個bean代表用戶的購物車疏咐。如果購物車是單例的話,那么將會導致所有的用戶都會向同一個購物車中添加商品脐供。另一方面浑塞,如果購物車是原型作用域的,那么在應用中某一個地方往購物車中添加商品政己,在應用的另外一個地方可能就不可用了酌壕,因為在這里注入的是另外一個原型作用域的購物車。就購物車bean來說歇由,會話作用域是最為合適的卵牍,因為它與給定的用戶關聯(lián)性最大。
運行時值注入
將一個值注入到bean的屬性或者構造器參數(shù)中沦泌。
在Spring中娶牌,處理外部值的最簡單方式就是聲明屬性源并通過Spring的Environment來檢索屬性朝捆。
Spring一直支持將屬性定義到外部的屬性的文件中宿崭,并使用占位符值將其插入到Spring bean中愿阐。在Spring裝配中,占位符的形式為使用“${... }
”包裝的屬性名稱回挽。
AOP
Spring AOP構建在動態(tài)代理基礎之上没咙,因此,Spring對AOP的支持局限于方法攔截千劈。如果你的AOP需求超過了簡單的方法調(diào)用(如構造器或?qū)傩詳r截)祭刚,那么你需要考慮使用AspectJ來實現(xiàn)切面。
AOP術語
通知(Advice)
切面的工作被稱為通知,通知定義了切面是什么以及何時使用墙牌。
Spring切面可以應用5種類型的通知:
前置通知(Before):在目標方法被調(diào)用之前調(diào)用通知功能袁梗;
后置通知(After):在目標方法完成之后調(diào)用通知,此時不會關心方法的輸出是什么憔古;
返回通知(After-returning):在目標方法成功執(zhí)行之后調(diào)用通知;
異常通知(After-throwing):在目標方法拋出異常后調(diào)用通知淋袖;
環(huán)繞通知(Around):通知包裹了被通知的方法鸿市,在被通知的方法調(diào)用之前和調(diào)用之后執(zhí)行自定義的行為。
連接點(Join point)
連接點是在應用執(zhí)行過程中能夠插入切面的一個點。這個點可以是調(diào)用方法時焰情、拋出異常時陌凳、甚至修改一個字段時。切面代碼可以利用這些點插入到應用的正常流程之中内舟,并添加新的行為合敦。
切點(Poincut)
如果說通知定義了切面的“什么”和“何時”的話,那么切點就定義了“何處”验游。切點的定義會匹配通知所要織入的一個或多個連接點充岛。一個切面并不需要通知應用的所有連接點。切點有助于縮小切面所通知的連接點的范圍耕蝉。
我們通常使用明確的類和方法名稱崔梗,或是利用正則表達式定義所匹配的類和方法名稱來指定這些切點。
切面(Aspect)
切面是通知和切點的結合垒在。通知和切點共同定義了切面的全部內(nèi)容——它是什么蒜魄,在何時和何處完成其功能。
引入(Introduction)
引入允許我們向現(xiàn)有的類添加新方法或?qū)傩猿∏@缣肝覀兛梢詣?chuàng)建一個Auditable通知類,該類記錄了對象最后一次修改時的狀態(tài)踢关。這很簡單伞鲫,只需一個方法,setLastModified(Date)耘成,和一個實例變量來保存這個狀態(tài)榔昔。然后,這個新方法和實例變量就可以被引入到現(xiàn)有的類中瘪菌,從而可以在無需修改這些現(xiàn)有的類的情況下撒会,讓它們具有新的行為和狀態(tài)。
織入(Weaving)
織入是把切面應用到目標對象并創(chuàng)建新的代理對象的過程师妙。切面在指定的連接點被織入到目標對象中诵肛。
在目標對象的生命周期里有多個點可以進行織入:
編譯期:切面在目標類編譯時被織入。這種方式需要特殊的編譯器默穴。AspectJ的織入編譯器就是以這種方式織入切面的怔檩。
類加載期:切面在目標類加載到JVM時被織入。這種方式需要特殊的類加載器(ClassLoader)蓄诽,它可以在目標類被引入應用之前增強該目標類的字節(jié)碼薛训。AspectJ 5的加載時織入(load-timeweaving,LTW)就支持以這種方式織入切面仑氛。
運行期:切面在應用運行的某個時刻被織入乙埃。一般情況下闸英,在織入切面時,AOP容器會為目標對象動態(tài)地創(chuàng)建一個代理對象介袜。Spring AOP就是以這種方式織入切面的甫何。
Spring AOP
代理類封裝了目標類,并攔截被通知方法的調(diào)用遇伞,再把調(diào)用轉發(fā)給真正的目標bean辙喂。當代理攔截到方法調(diào)用時,在調(diào)用目標bean方法之前鸠珠,會執(zhí)行切面邏輯巍耗。
因為Spring基于動態(tài)代理,所以Spring只支持方法連接點跳芳。這與一些其他的AOP框架是不同的芍锦,例如AspectJ和JBoss,除了方法切點飞盆,它們還提供了字段和構造器接入點娄琉。Spring缺少對字段連接點的支持,無法讓我們創(chuàng)建細粒度的通知吓歇,例如攔截對象字段的修改孽水。而且它不支持構造器連接點,我們就無法在bean創(chuàng)建時應用通知城看。
關于環(huán)繞通知
環(huán)繞通知是最為強大的通知類型女气。它能夠讓你所編寫的邏輯將被通知的目標方法完全包裝起來。實際上就像在一個通知方法中同時編寫前置通知和后置通知测柠。
關于這個新的通知方法炼鞠,它接受ProceedingJoinPoint
作為參數(shù)。這個對象是必須要有的轰胁,因為你要在通知中通過它來調(diào)用被通知的方法谒主。通知方法中可以做任何的事情,當要將控制權交給被通知的方法時赃阀,它需要調(diào)用ProceedingJoinPoint
的proceed()
方法霎肯。需要注意的是,別忘記調(diào)用proceed()
方法榛斯。如果不調(diào)這個方法的話观游,那么你的通知實際上會阻塞對被通知方法的調(diào)用。有可能這就是你想要的效果驮俗,但更多的情況是你希望在某個點上執(zhí)行被通知的方法懂缕。
有意思的是,你可以不調(diào)用proceed()
方法王凑,從而阻塞對被通知方法的訪問提佣,與之類似吮蛹,你也可以在通知中對它進行多次調(diào)用。要這樣做的一個場景就是實現(xiàn)重試邏輯拌屏,也就是在被通知方法失敗后,進行重復嘗試术荤。
SpringMVC
[^] springMVC調(diào)用流程
DispatcherServlet:`Spring MVC所有的請求都會通過一個前端控制器(front controller)Servlet倚喂。前端控制器是常用的Web應用程序模式,在這里一個單實例的Servlet將請求委托給應用程序的其他組件來執(zhí)行實際的處理瓣戚。任務是將請求發(fā)送給Spring MVC控制器(controller)端圈。
controller
控制器是一個用于處理請求的Spring組件。在典型的應用程序中可能會有多個控制器子库,DispatcherServlet需要知道應該將請求發(fā)送給哪個控制器舱权。所以DispatcherServlet以會查詢一個或多個處理器映射(handler mapping) 來確定請求的下一站在哪里。處理器映射會根據(jù)請求所攜帶的URL信息來進行決策仑嗅。一旦選擇了合適的控制器宴倍,DispatcherServlet會將請求發(fā)送給選中的控制器〔旨迹控制器將業(yè)務邏輯委托給一個或多個服務對象進行處理鸵贬。
在完成邏輯處理后,通常會產(chǎn)生一些信息脖捻,這些信息需要返回給用戶并在瀏覽器上顯示阔逼。這些信息被稱為模型(model)。不過僅僅給用戶返回原始的信息是不夠的——這些信息需要以用戶友好的方式進行格式化地沮,一般會是HTML嗜浮。所以,信息需要發(fā)送給一個視圖(view)摩疑,通常會是JSP危融。控制器所做的最后一件事就是將模型數(shù)據(jù)打包未荒,并且標示出用于渲染輸出的視圖名专挪。它接下來會將請求連同模型和視圖名發(fā)送回DispatcherServlet
DispatcherServlet將會使用視圖解析器(view resolver)來將邏輯視圖名匹配為一個特定的視圖實現(xiàn)。
既然DispatcherServlet已經(jīng)知道由哪個視圖渲染結果片排,那請求的任務基本上也就完成了寨腔。它的最后一站是視圖的實現(xiàn)(可能是JSP) ,在這里它交付模型數(shù)據(jù)率寡。請求的任務就完成了迫卢。視圖將使用模型數(shù)據(jù)渲染輸出,這個輸出會通過響應對象傳遞給客戶端冶共。
Spring事務
所謂的事物管理指的是“按照指定事物規(guī)則執(zhí)行提交或者回滾操作”乾蛤,而spring的事物管理每界,指的是spring對事物管理規(guī)則的抽象。具體來說就是spring不直接管理事物家卖,而是為了不同的平臺(如jdbc hibernate jpa 等)提供了不同的事物管理器 眨层,將具體的事物管理委托給相應的平臺實現(xiàn),spring并不關心具體事物管理的實現(xiàn)上荡,從而為事物管理提供了通用編程模型趴樱。
spring事務管理器
jdbc事物管理器
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
hibernate事物管理器
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
其它事務管理器
org.springframework.orm.jpa
,org.springframework.transaction.jta.JtaTransactionManager
酪捡,org.springframework.jms.connection.JmsTransactionManager
spring 事物的基本行為
事物的相關屬性就定義在java.sql.Connection.TransactionDefinition類中
/**
* 獲取傳播行為
*/
int getPropagationBehavior();
/**
* 獲取隔離級別
*/
int getIsolationLevel();
/**
* 獲取超時時間
*/
int getTimeout();
/**
* 獲取只讀屬性
*/
boolean isReadOnly();
/**
* 獲取事物的名稱 默認為classname+"."+methodName
*/
String getName();
事物的傳播行為
所謂事物的傳播行為叁征,指的是被事物標記的方法(注解或者xml聲明),之間進行嵌套調(diào)用時事物的傳播規(guī)則逛薇。拿jdbc事物管理器來說捺疼,就是共用同一個jdbc connection,還是新建connection永罚,還是拋異常啤呼。這個規(guī)則就叫做事物的傳播行為。
傳播行為 | 說明 | |
---|---|---|
PROPAGATION_REQUIRED | 如果存在事務尤蛮,則加入當前事務媳友。如果不存在事務,則新建产捞。 | 默認傳播行為 |
PROPAGATION_SUPPORTS | 支持事務醇锚,如果不存在事務則以非事務的狀態(tài)運行。 | |
PROPAGATION_MANDATORY | 必須存在事務坯临,不存在拋異常焊唬。 | |
PROPAGATION_NEVER | 不支持事務,如當前存在事務則拋異常看靠。 | |
PROPAGATION_REQUIRES_NEW | 無論當前是否存在事務赶促,都新建事務。 | 僅支持JtaTransactionManager作為事務管理器挟炬。 |
PROPAGATION_NOT_SUPPORTED | 不支持事務鸥滨,如當前存在事務則將當前事物掛起,以非事務的方式運行 | 僅支持JtaTransactionManager作為事務管理器谤祖。 |
PROPAGATION_NESTED | 嵌套事務婿滓。如果當前不存在事務以PROPAGATION_REQUIRED的方式運行。如果存在事務則以嵌套事務的方式運行粥喜。 | 僅支持DataSourceTransactionManager作為事務管理器和部分JTA事務管理器 |
關于PROPAGATION_NESTED
@Transactional(propagation = Propagation.REQUIRED)
void methodA(){
//doSomethingA()
((ServiceName)AopContext.currentProxy()).methodB();
}
@Transactional(propagation = Propagation.NESTED)
void methodB(){
//doSomethingB()
//successOrNot
}
單獨調(diào)用methodB 時凸主,相當于 PROPAGATION_REQUIRED。當調(diào)用methodA時额湘,則以嵌套事物的方式運行卿吐,methodB作為methodA的子事物旁舰,提交和回滾都會受methodA的事物的影響。
一般我們用PROPAGATION_NESTED來執(zhí)行分支邏輯嗡官。當methodB執(zhí)行失敗的時候箭窜,則methodA 回滾到之前的保存點,然后執(zhí)行catch塊中的其它業(yè)務邏輯衍腥,就像methodB從未執(zhí)行過一樣绽快。這也是PROPAGATION_NESTED最常用的用法。
事物的隔離級別
事物的隔離級別紧阔,指的是并發(fā)情況下,事物之間的隔離度续担。不同的隔離級別擅耽,可以解決不同的隔離問題,也對應著不同的并發(fā)度物遇,使用的時候乖仇,要結合實際情況,按需選擇询兴。
問題 | 描述 |
---|---|
臟讀 | 事物A讀到了事物B未提交的數(shù)據(jù)乃沙,如果事物B的操作被回滾了,那么事物A讀到的就是無效數(shù)據(jù)诗舰。 |
不可重復讀 | 事物A執(zhí)行select 查詢到結果集R1警儒,此時事物B執(zhí)行update 并提交,事物A再執(zhí)行同樣的select 查詢到結果集R2眶根。兩次查詢到的結果集不一致蜀铲。 |
幻讀 | 事物A執(zhí)行select 查詢到結果集R1,此事事物B執(zhí)行了 insert 或者 delete 操作 并提交属百,事物A再執(zhí)行同樣的select 查詢到結果集R2记劝。此時發(fā)現(xiàn)R2相比R1多了或者少了一些記錄 |
spring事務隔離級別
級別 | 描述 | 臟讀 | 不可重復讀 | 幻讀 |
---|---|---|---|---|
ISOLATION_DEFAULT | 使用數(shù)據(jù)庫隔離級別。 | |||
ISOLATION_READ_UNCOMMITTED | 讀未提交 | X | X | X |
ISOLATION_READ_COMMITTED | 讀已提交oracle 默認隔離級別族扰,利用快照讀解決臟讀
|
Y | X | X |
ISOLATION_REPEATABLE_READ | 可重復讀mysql 默認隔離級別厌丑,除了讀快照之外,事物啟動后不允許執(zhí)行update操作
|
Y | Y | X |
ISOLATION_SERIALIZABLE | 串行化 | Y | Y | Y |
事物的只讀屬性-readOnly
spring 事物的只讀屬性是通過設置渔呵,java.sql.Connection 的 readOnly 屬性來實現(xiàn)的怒竿。當設置了只讀屬性后,數(shù)據(jù)庫會對只讀事物進行一些優(yōu)化厘肮,如不啟用回滾段愧口,不啟用回滾日志等。
事物的超時時間-timeout
事物的超時指的是設置一個時間类茂,當執(zhí)行時間超過這個時間后耍属,拋異常并回滾托嚣。是通過設置一個截至時間來實現(xiàn)的,值得注意的是厚骗,這個超時時間只有特定情況下才會生效示启,如dao層使用jdbcTemplete 執(zhí)行sql
回滾規(guī)則-rollbackFor
回滾規(guī)則只得是spring 事物遇到哪種異常會回滾。默認只回滾RuntimeException和Error