Java知識體系html
目的:爲了更好的認識java體系java
新建java_home變量(安裝目錄),值爲:C:\Program Files\Java\jdk1.6.0_14;此值爲JDK的安裝位置。程序員
新建classpath變量(類加載路徑),值爲:.;%java_home%\lib;%java_home%\lib\tools.jarweb
修改path變量(使得系統能夠在任何路徑下識別java命令),值爲:%java_home%\bin;%java_home%\jre\bin面試
Public:任何地方能夠訪問正則表達式
Private:只有自身能夠訪問算法
Protected:同一個包和子類能夠訪問spring
默認:只有同一個包內能夠訪問sql
垃圾回收是Java語言的一大特性,方便了編程,是以消耗性能爲代價的。而垃圾在這裏只無用的對象。而C++是須要程序員本身寫析構函數來釋放內存的,麻煩,也有可能忘記而致使內存泄露。數據庫
Java語言對內存的分配管理是經過JVM內部機制決定的。程序員能夠不關心其處理。
Java虛擬機中有個稱之爲垃圾回收器的東西,實際上這個東西也許真正不存在,或者是已經集成到JVM中了,但這可有可無,咱們仍然能夠稱爲爲垃圾回收器。
垃圾回收器的做用是查找和回收(清理)無用的對象。以便讓JVM更有效的使用內存。
垃圾回收器的運行時間是不肯定的,由JVM決定,在運行時是間歇執行的。雖然能夠經過System.gc()來強制回收垃圾,可是這個命令下達後沒法保證JVM會當即響應執行,但經驗代表,下達命令後,會在短時間內執行你的請求。JVM一般會感到內存緊缺時候去執行垃圾回收操做。
垃圾回收過於頻繁會致使性能降低,過於稀疏會致使內存緊缺。這個JVM會將其控制到最好,不用程序員擔憂。但有些程序在短時間會吃掉大量內存,而這些恐怖的對象很快使用結束了,這時候也許有必要強制下達一條垃圾回命令,這是頗有必要的,以便有更多可用的物理內存。
從上面瞭解到,沒有用的對象就是垃圾。準確的說,當沒有任何線程訪問一個對象時,該對象就符合垃圾回收的條件。
對於String,存在一個字符串池,這個不屬於本文討論的範圍,字符串池中的垃圾回收,算法和這裏所討論的垃圾回收徹底是兩碼事。可是不得不說的是,字符串的胡亂拼接,每每致使性能急劇降低,尤爲是在龐大的循環語句中,拼接字符串就是在讓程序慢性自殺。這也是不少Java程序員容易犯的毛病。
字符串既然是池,就是爲了緩衝,爲了有更高的命中率,所以垃圾回收的頻率也許會比JVM對象垃圾回收器要低不少。
垃圾回收器僅僅能作的是儘量保證可用內存的使用效率,讓可用內存獲得高效的管理。程序員能夠影響垃圾回收的執行,但不能控制。
雖然程序員沒法控制JVM的垃圾回收機制。可是能夠經過編程的手段來影響,影響的方法是,讓對象符合垃圾回收條件。
分別說來有一下幾種:
一、將無用對象賦值爲null.
二、從新爲引用變量賦值。好比:
Person p = new Person("aaa");
p = new Person("bbb");
這樣,new Person("aaa")這個對象就是垃圾了——符合垃圾回收條件了。
三、讓相互聯繫的對象稱爲「島」對象
Person p1 = new Person("aaa");
Person p2 = new Person("bbb");
Person p3 = new Person("ccc");
p1=p2; p2=p3; p3=p1;
p1=null; p2=null; p3=null;
在沒有對p一、p二、p3置null以前,它們之間是一種三角戀關係。分別置null,三角戀關係依然存在,可是三個變量不在使用它們了。三個Person對象就組成了一個孤島,最後死在堆上——被垃圾回收掉。
實際上這裏的強制,是程序員的意願、建議,何時執行是JVM的垃圾回收器說了算。
調用垃圾回收也不必定能保證未使用的對象必定能從內存中刪除。
惟一能保證的是,當你內存在極少的狀況,垃圾回收器在程序拋出OutofMemaryException以前運行一次。
TCP是面向鏈接的通訊協議,TCP提供兩臺計算機之間的可靠的無差異錯的數據傳輸。
UDP是無鏈接通訊協議,UDP不保證可靠的數據的傳輸,但可以向若干個目標發送數據,接受發自若干個源的數據。
實現多線程:繼承自Thread類,能夠重寫run()方法去覆蓋去Thread中的run()方法;實現Runnable接口並實現它的run()方法。
線程的優先級表明該線程的重要程度(不是絕對的),當有多個線程同時處於可執行狀態並等待得到 CPU 時間時,線程調度系統根據各個線程的優先級來決定給誰分配 CPU 時間,優先級高的線程有更大的機會得到 CPU 時間,優先級低的線程也不是沒有機會,只是機會要小一些罷了。
1.start()是啓動一個線程。
2.join()是直到執行完(或強制執行一段時間)當前的線程後才往下執行主線程或其餘的線程
3.stop()是中止當前的線程。
阻塞線程比較多
sleep() 方法:sleep() 容許 指定以毫秒爲單位的一段時間做爲參數,它使得線程在指定的時間內進入阻塞狀態,不能獲得CPU 時間,指定的時間一過,線程從新進入可執行狀態。可能給其餘線程執行的機會(自私,睡着了,不釋放鎖,時間到了才放鎖)
suspend() 和 resume() 方法:兩個方法配套使用,suspend()使得線程進入阻塞狀態,而且不會自動恢復,必須其對應的resume() 被調用,才能使得線程從新進入可執行狀態。
yield() 方法:yield() 使得線程放棄當前分得的 CPU 時間,可是不使線程阻塞,即線程仍處於可執行狀態,隨時可能再次分得 CPU 時間。調用 yield() 的效果等價於調度程序認爲該線程已執行了足夠的時間從而轉到另外一個線程。不過yield()只能使同等級別的線程獲取執行的機會(公平競爭,釋放你們再次選舉)。而sleep(1000)使同級別或不一樣級別的都有可能。
wait() 和 notify() 和notifyAll()方法是Object中定義的方法:
必須在synchronized代碼塊中使用,在synchronized代碼被執行期間,線程能夠調用對象的wait()方法,釋放對象的鎖標誌,進入等待的狀態,而且能夠調用notify()或者notifyAll()方法通知正在等待的其餘線程。notify()通知的是等待隊列中的第一個線程,notifyAll()通知的是等待隊列中的全部數量。
幾個方法配套使用,wait() 使得線程進入阻塞狀態,它有兩種形式,一種容許 指定以毫秒爲單位的一段時間做爲參數,另外一種沒有參數,前者當對應的 notify() 被調用或者超出指定時間時線程從新進入可執行狀態,後者則必須對應的 notify() 被調用。
wait()在object類裏定義;sleep()在Thread類裏定義。
wait()方法只能放在同步方法或同步塊中,表示資源同步時,線程須要等待。
sleep()方法可放在任何位置,表示當前線程睡眠。
wait()方法會釋放對象鎖;sleep()不會釋放對象鎖。
wait()方法要等待喚醒以後,線程纔會繼續執行。
sleep()則是休眠一段時間,線程自動恢復執行.
sleep()必須捕獲異常,而wait(),notify()和notifyAll()不須要捕獲異常
notify() 方法致使解除阻塞的線程是從因調用該對象的 wait() 方法而阻塞的線程中隨機選取的,咱們沒法預料哪個線程將會被選擇,因此編程時要特別當心,避免因這種不肯定性而產生問題。
notifyAll() 方法將把因調用該對象的 wait() 方法而阻塞的全部線程一次性所有解除阻塞。固然,只有得到鎖的那一個線程才能進入可執行狀態。
run()方法:在本線程內調用該Runnable對象的run()方法,能夠重複屢次調用;
start()方法:啓動一個線程,調用該Runnable對象的run()方法,不能屢次啓動一個線程;
start()方法是啓動(即開闢)一個線程的方法,所以線程的啓動必須經過此方法,
而run()方法,只是Thread類的一個方法,它自己並不能開闢線程。
運行時異常和Error是catch不了的。
throw和throws的區別
throw是拋出一個具體的異常類,產生一個異常。
throws則是在方法名後標出該方法會產生何種異常須要方法的使用者捕獲並處理。
自定義異常必須繼承Exception類。
略
略
* 0次或屢次
+ 至少1次
? 0或1次
{n} 指定n次
{n,} 至少n次
{n,m} 匹配n-m次
屬性是私有,方法的公開的。
本身的數據和方法只讓可信的類或者對象操做,對不可信的進行信息隱藏。
它能夠使用現有類的全部功能,並在無需從新編寫原來的類的狀況下對這些功能進行擴展。
實現多態有2種方式:靜態多態(編譯時的多態,在同一個類中)和動態多態(一個對象能夠有多種類型)。
是容許你將父對象設置成爲和一個或更多的他的子對象相等的技術,賦值以後,父對象就能夠根據當前賦值給它的子對象的特性以不一樣的方式運做。
一句話:容許將子類對象賦值給父類的引用或父類指向子類的對象。
封裝能夠隱藏實現細節,使得代碼模塊化。
繼承能夠擴展已存在的代碼模塊(類)。
封裝和繼承屬於代碼重用。
多態是爲了接口重用。
繼承很好理解,下圖表示(實線):
實現也很好理解,下圖表示(虛線):
依賴(代碼中一般表示的是局部變量、方法參數、返回值),下圖表示:
關聯(代碼中一般表示的類的屬性),下圖表示:
聚合(關聯的一種,此時總體與部分之間是可分離的,他們能夠具備各自的生命週期,部分能夠屬於多個總體對象,也能夠爲多個總體對象共享;has-a,空心菱形),下圖表示
組合(關聯的一種,表示更強的關係,總體與部分是不可分的,總體的生命週期結束也就意味着部分的生命週期結束,contains-a,實心菱形),下圖表示
抽象類裏面能夠有非抽象方法
但接口裏只能有抽象方法
聲明方法的存在而不去實現它的類被叫作抽像類(abstract class),它用於要建立一個體現某些基本行爲的類,併爲該類聲明方法,但不能在該類中實現該類的狀況。不能建立abstract 類的實例。然而能夠建立一個變量,其類型是一個抽像類,並讓它指向具體子類的一個實例。不能有抽像構造函數或抽像靜態方法。Abstract 類的子類爲它們父類中的全部抽像方法提供實現,不然它們也是抽像類爲。取而代之,在子類中實現該方法。知道其行爲的其它類能夠在類中實現這些方法。
接口(interface)是抽像類的變體。在接口中,全部方法都是抽像的。多繼承性可經過實現這樣的接口而得到。接口中的全部方法都是抽像的,沒有一個有程序體。接口只能夠定義static final成員變量。接口的實現與子類類似,除了該實現類不能從接口定義中繼承行爲。當類實現特殊接口時,它定義(即將程序體給予)全部這種接口的方法。而後,它能夠在實現了該接口的類的任何對像上調用接口的方法。因爲有抽像類,它容許使用接口名做爲引用變量的類型。一般的動態聯編將生效。引用能夠轉換到接口類型或從接口類型轉換,instanceof 運算符能夠用來決定某對象的類是否實現了接口。
在一個類中定義另一個類,這個類就叫作內部類或內置類 (inner class) 。
內部類分爲成員內部類、靜態嵌套類、方法內部類、匿名內部類。
內部類仍然是一個獨立的類,在編譯以後會內部類會被編譯成獨立的.class文件,可是前面冠之外部類的類命和$符號。內部類不能用普通的方式訪問。內部類是外部類的一個成員,所以內部類能夠自由地訪問外部類的成員變量,不管是不是private的。
咱們爲何使用內部類?
A、在內部類(inner class)中,能夠隨意的訪問外部類的成員,這可讓咱們更好地組織管理咱們的代碼,加強代碼的可讀性。
B、內部類能夠用於建立適配器類,適配器類是用於實現接口的類。使用內部類來實現接口,能夠更好地定位與接口關聯的方法在代碼中的位置。
C、內部類的更多用法(匿名inner class實現一個接口,不過切記最後那個分號;實現繼承和接口實現時出現同名方法的問題;實現多重繼承的功能)
這種動態的獲取信息及動態調用方法的機制在Java中稱爲「反射」(reflection)。
Reflection 是Java被視爲動態(或準動態)語言的一個關鍵性質。
在JDK中,主要由如下類來實現Java反射機制,這些類都位於java.lang.reflect包中:
Class類:表明一個類;
Field 類:表明類的成員變量(成員變量也稱爲類的屬性);
Method類:表明類的方法;
Constructor 類:表明類的構造方法;
Array類:提供了動態建立數組,以及訪問數組的元素的靜態方法;
在java.lang.Object 類中定義了getClass()方法,所以對於任意一個Java對象,均可以經過此方法得到對象的類型。Class類是Reflection API 中的核心類,它有如下方法:
getName():得到類的完整名字;
getFields():得到類的public類型的屬性(包括繼承的類的public屬性);
getDeclaredFields():得到類的全部屬性;
getMethods():得到類的public類型的方法; (包括繼承的類的public方法);
getDeclaredMethods():得到類的全部方法;
getMethod(String name, Class[] parameterTypes):得到類的特定方法,name參數指定方法的名字,parameterTypes 參數指定方法的參數類型;
getConstructors():得到類的public類型的構造方法;
getConstructor(Class[] parameterTypes):得到類的特定構造方法,parameterTypes 參數指定構造方法的參數類型;
newInstance():經過類的不帶參數的構造方法建立這個類的一個對象;
經過默認構造方法建立一個新對象:
Object objectCopy=classType.getConstructor(new Class[]{}).newInstance(new Object[]{});
以上代碼先調用Class類的getConstructor()方法得到一個Constructor 對象,它表明默認的構造方法,而後調用Constructor對象的newInstance()方法構造一個實例。
得到對象的全部屬性:
Field fields[]=classType.getDeclaredFields();
Class 類的getDeclaredFields()方法返回類的全部屬性,包括public、protected、默認和private訪問級別的屬性。
Java容許咱們從多種途徑爲一個class生成對應的Class object
(一)運用getClass()方法
(二)運用Class.getSuperclass()方法
(三)運用靜態方法Class.forName(),這個最經常使用
(四)運用.class語法
(五)運用原始包裝類的TYPE方法
淺負責也叫淺克隆,深複製也叫深克隆。
克隆通常經過實現Cloneable中的clone方法來實現淺克隆和深克隆,可是深克隆有一個問題,若是引用對象有不少,或者說引用套引用不少重,那麼太麻煩了。
業界經常使用的方法是使用串行化而後反串行化的方法來實現深克隆。因爲串行化後,對象寫到流中,全部引用的對象都包含進來了,因此反串行化後,等於生成了一個徹底克隆的對象。絕! 這個方法的要求是對象(包括被引用對象)必須事先了Serializable接口,不然就要用transient關鍵字將其排除在複製過程當中。
需求捕獲、系統分析與設計、系統實現、測試、維護
視圖(View)是一個或多個圖組成的對系統某個角度的抽象
圖(Diagram)是模型元素集的圖形表示
模型元素(Model Element)表明面向對象中的類、對象、接口、消息和關係的概念
通用機制(General Mechanism)用於表示其餘信息,如註釋、模型元素的語義等
視圖由圖組成,UML提供9種不一樣的視圖,其中視圖的包括
用例視圖(強調系統功能)也稱用戶模型視圖
用例圖
邏輯視圖(展示系統的靜態或結構組成特徵)也稱爲結構模型視圖或靜態視圖
類圖、對象圖
併發視圖(體現系統的動態或行爲特徵)也稱爲行爲模型視圖或動態視圖
時序圖、協做圖、狀態圖、活動圖
組件視圖(體現系統實現的結構和行爲特徵)也成爲實現模型視圖
組件圖
配置視圖(體現系統實現的環境的結構和行爲特徵)也稱環境模型視圖或物理視圖
配置圖
UML內容概括爲2大類:靜態建模機制和動態建模機制
靜態建模機制包括用例圖、類圖、對象圖、組件圖、配置圖、包等
動態建模機制包括時序圖、協做圖、狀態圖、活動圖、消息等
主要說說事物和關係
事物包括以下
結構事物:用例(橢圓)、類(矩形)、接口(圓心)、協做(虛橢圓)、活動類(粗線矩形)、組件、節點(資源:電腦)
行爲事物:交互(一般畫成帶箭頭的信息)、狀態機(對象的一個或多個狀態的集合)
組織事物:包
輔助事物:註釋
關係:關聯、依賴、泛化、實現
類(矩形:名稱、屬性、操做、職責)
其中表示+、-、#是public、private、protected
關係
依賴(有方向的虛線):使用依賴、抽象依賴、受權依賴、綁定依賴
泛化(父類和子類的關係):描述了類之間的「is a kind of 」的關係,用子類指向父類的空心三角形箭頭(實線)表示該關係。泛
化使得多態成爲可能。
關聯:描述一組具備相同結構特徵、行爲特徵、關係和語義的連接。
關聯的修飾有:名稱(描述關係的性質)、角色(職責)、多重性(0..n)、聚合(總體和部分,即has a的關係)、
組合(是更強的關係,是另一種聚合,總體有管理部分特有的職責而且有一致的生命週期)等。
實現:實現規格說明和其實現間的關係。它表示不繼承結構而只繼承行爲。大多數狀況下,實現關係用來規定接口和實現接口的類或
組件之間的關係。(帶空心的箭頭表示,線是虛線)
描述類、接口、協做以及它們之間關係的圖。
類圖包括7個元素:類、接口、協做、依賴關係、泛化關係、實現關係以及關聯關係
類圖的做用:對系統的詞彙建模、對簡單的協做建模、對邏輯數據庫模式建模
表示某一刻一組對象以及它們之間關係的圖
用例(Use Case):對一個系統或一個應用的一種單一的使用方式所做的描述。
參與者(Actor):系統外部的一個實體(能夠說任何事物或人)
參與者之間的關係(泛化關係、包含關係、擴展關係)
描述對象之間傳遞消息的時間順序,他用來表示用例中的行爲順序,是強調消息時間的交互圖
包括4個元素:對象、生命線、激活、消息
強調參與交互的各對象結構的信息。協做圖是一種類圖。
協做圖中包含3個元素:對象、鏈、消息。
如圖:
經過類對象的生命週期創建模型來描述對象隨時間變化的動態行爲。
它包括:狀態和轉換
注意:初始狀態:實圓心、終止狀態:半實圓心。
狀態機、狀態、轉化
其中狀態包括:名字、入口/出口動做、內部轉換、延遲事件、子狀態。
轉換包括:源狀態、目標狀態、出發事件、監護條件、動做
描述一個過程或操做的步驟。描述狀態外,更突出活動
動做狀態、活動狀態、動做流、分支、分叉和匯合、泳道、對象流。
描述軟件組件及組件之間的關係。
包括:組件、接口、關係(依賴、泛化、關聯、實現)。
顯示軟件系統運行的物理硬件。
包括節點(單個物理對象)、組件和關係(依賴和關聯)
(略)
MVC模式的目的就是實現Web系統的職能分工。 Model層實現系統中的業務邏輯,一般能夠用JavaBean或EJB來實現。 View層用於與用戶的交互,一般用JSP來實現。 Controller層是Model與View之間溝通的橋樑,它能夠分派用戶的請求並選擇恰當的視圖以用於顯示,同時它也能夠解釋用戶的輸入並將它們映射爲模型層可執行的操做。
優勢:低耦合、高可複用性和可適用性、較低的生命週期成本、快速部署、可維護性、有利於軟件工程化管理。
缺點:原理較複雜花費時間思考、程序分紅三個部分須要管理更多的文件。
Struts: Apache的,最流行的MVC組件
Struts2 :Apache用Struts 和 WebWork的組合出來的新產品,目前上升勢頭強勁
WebWork: 這個但是老牌的MVC組件,後來組合成了Struts2, 不過自身仍在發展
Spring MVC:SpringFramework本身整合本身Spring的優點推出的MVC組件,用戶也很多。
JSF: 這個是一個規範,Sun的和 Apache的都有各自的實現。用戶量很大,被衆多IDE支持。
Model1是將業務、控制、視圖都放在jsp頁面、Model2是由業務(javaBean)、控制(ActionServlet)、視圖(Jsp)組成。
Struts 2是Struts的下一代產品,是在 struts 和WebWork的技術基礎上進行了合併的全新的Struts 2框架。其全新的Struts 2的體系結構與Struts 1的體系結構的差異巨大。Struts 2以WebWork爲核心,採用攔截器的機制來處理用戶的請求,這樣的設計也使得業務邏輯控制器可以與Servlet API徹底脫離開,因此Struts 2能夠理解爲WebWork的更新產品。雖然從Struts 1到Struts 2有着太大的變化,可是相對於WebWork,Struts 2只有很小的變化。
目的:是爲了幫助咱們減小在運用MVC設計模型來開發Web應用的時間。
Struts2的體系與Struts1體系的差異很是大,由於Struts2使用了WebWork的設計核心,而不是Struts1的設計核心。Struts2中大量使用攔截器來處理用戶的請求,從而容許用戶的業務邏輯控制器與Servlet API分離。
Struts2框架的大概處理流程以下:
1、加載類(FilterDispatcher)
2、讀取配置(struts配置文件中的Action)
3、派發請求(客戶端發送請求)
4、調用Action(FilterDispatcher從struts配置文件中讀取與之相對應的Action )
5、啓用攔截器(WebWork攔截器鏈自動對請求應用通用功能,如驗證)
6、處理業務(回調Action的execute()方法)
7、返回響應(經過execute方法將信息返回到FilterDispatcher)
8、查找響應(FilterDispatcher根據配置查找響應的是什麼信息如:SUCCESS、ERROER,將跳轉到哪一個jsp頁面)
9、響應用戶(jsp--->客戶瀏覽器端顯示)
10、struts2標籤庫(相比struts1的標籤庫,struts2是大大增強了,對數據的操做功能很強大)。
在Action的實現方面:Struts1要求必須統一擴展自Action類,而Struts2中能夠是一個普通的POJO。
線程模型方面:Struts1的Action工做在單例模式,一個Action的實例處理全部的請求。Struts2的Action是一個請求對應一個實例。沒有線程安全方面的問題。
Servlet依賴方面:Struts1的Action依賴於Servlet API,好比Action的execute方法的參數就包括request和response對象。這使程序難於測試。Struts2中的Action再也不依賴於Servlet API,有利於測試,而且實現TDD。
封裝請求參數:Struts1中強制使用ActionForm對象封裝請求的參數。Struts2能夠選擇使用POJO類來封裝請求的參數,或者直接使用Action的屬性。
表達式語言方面:Struts1中整合了EL,可是EL對集合和索引的支持不強,Struts2整合了OGNL(Object Graph NavigationLanguage)。
綁定值到視圖技術:Struts1使用標準的JSP,Struts2使用「ValueStack」技術。
參見文檔:http://www.blogjava.net/freeman1984/archive/2011/02/16/344447.html
類型轉換:Struts1中的ActionForm基本使用String類型的屬性。Struts2中使用OGNL進行轉換,能夠更方便的使用。
數據校驗:Struts1中支持覆蓋validate方法或者使用Validator框架。Struts2支持重寫validate方法或者使用XWork的驗證框架。
Action執行控制的對比:Struts1支持每個模塊對應一個請求處理,可是模塊中的全部Action必須共享相同的生命週期。Struts2支持經過攔截器堆棧爲每個Action建立不一樣的生命週期。
<filter>
<filter-name>action2</filter-name>
<filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
</filter>
<filter-mapping>
<filter-name>action2</filter-name>
<url-pattern>*.action</url-pattern>
</filter-mapping>
Hibernate是一個對象關係映射框架,它對JDBC進行了很是輕量的對象封裝,使得java程序員能夠爲所欲爲地使用對象編程思惟來操縱數據庫。
Assigned(依賴用戶)
Assigned方式由用戶生成主鍵值,而且要在save()以前指定不然會拋出異常
特色:主鍵的生成值徹底由用戶決定,與底層數據庫無關。用戶須要維護主鍵值,在調用session.save()以前要指定主鍵值。
Hilo(依賴數據表)
Hilo使用高低位算法生成主鍵,高低位算法使用一個高位值和一個低位值,而後把算法獲得的兩個值拼接起來做爲數據庫中的惟一主鍵。Hilo方式須要額外的數據庫表和字段提供高位值來源。默認狀況下使用的表是 hibernate_unique_key,默認字段叫做next_hi。next_hi必須有一條記錄不然會出現錯誤。
特色:須要額外的數據庫表的支持,能保證同一個數據庫中主鍵的惟一性 ,但不能保證多個數據庫之間主鍵的惟一性。Hilo主鍵生成方式由Hibernate 維護,因此Hilo方式與底層數據庫無關,但不該該手動修改hi/lo算法使用的表的值,不然會引發主鍵重複的異常。
Increment(依賴數據Sequence)
Increment方式對主鍵值採起自動增加的方式生成新的主鍵值,但要求底層數據庫的支持Sequence。如Oracle,DB2等。須要在映射文件xxx.hbm.xml中加入Increment標誌符的設置。
這個是由Hibernate在內存中生成主鍵,每次增量爲1,不依賴於底層的數據庫,所以全部的數據庫均可以使用,但問題也隨之而來,因爲是Hibernate生成的,因此只能有一個Hibernate應用進程訪問數據庫,不然就會產生主鍵衝突,不能在集羣狀況下使用插入數據的時候hibernate會給主鍵添加一個自增的主鍵,可是一個hibernate實例就維護一個計數器,因此在多個實例運行的時候不能使用這個方法。
特色:由Hibernate自己維護,適用於全部的數據庫,不適合多進程併發更新數據庫,適合單一進程訪問數據庫。不能用於羣集環境。
Identity(自增)
Identity當時根據底層數據庫,來支持自動增加,不一樣的數據庫用不一樣的主鍵增加方式。
特色:與底層數據庫有關,要求數據庫支持Identity,如MySQl中是auto_increment, SQL Server 中是Identity,支持的數據庫有MySql、SQL Server、DB二、Sybase和HypersonicSQL。 Identity無需Hibernate和用戶的干涉,使用較爲方便,但不便於在不一樣的數據庫之間移植程序。
Sequence(依賴數據Sequence)
Sequence須要底層數據庫支持Sequence方式,例如Oracle數據庫等
特色:須要底層數據庫的支持序列,支持序列的數據庫有DB二、PostgreSql、Oracle、SAPDb等在不一樣數據庫之間移植程序,特別從支持序列的數據庫移植到不支持序列的數據庫須要修改配置文件
Native(依賴具體的數據庫)
Native主鍵生成方式會根據不一樣的底層數據庫自動選擇Identity、Sequence、Hilo主鍵生成方式
特色:根據不一樣的底層數據庫採用不一樣的主鍵生成方式。因爲Hibernate會根據底層數據庫採用不一樣的映射方式,所以便於程序移植,項目中若是用到多個數據庫時,能夠使用這種方式。
UUID(依賴算法)
UUID使用128位UUID算法生成主鍵,可以保證網絡環境下的主鍵惟一性,也就可以保證在不一樣數據庫及不一樣服務器下主鍵的惟一性。
特色;可以保證數據庫中的主鍵惟一性,生成的主鍵佔用比較多的存貯空間
Foreign GUID(外鍵)
Foreign用於一對一關係中,使用外部表的字段做爲主鍵。
Hibernate 中提供了兩級Cache,第一級別的緩存是Session級別的緩存,它是屬於事務範圍的緩存。這一級別的緩存由hibernate管理的,通常狀況下無需進行干預;第二級別的緩存是SessionFactory級別的緩存,它是屬於進程範圍或羣集範圍的緩存。這一級別的緩存能夠進行配置和更改,而且能夠動態加載和卸載。 Hibernate還爲查詢結果提供了一個查詢緩存,它依賴於第二級緩存。
一級緩存的管理
當應用程序調用Session的save()、update()、saveOrUpdate()、get()或load(),以及調用查詢接口的 list()、iterate()或filter()方法時,若是在Session緩存中還不存在相應的對象,Hibernate就會把該對象加入到第一級緩存中。當清理緩存時,Hibernate會根據緩存中對象的狀態變化來同步更新數據庫。 Session爲應用程序提供了兩個管理緩存的方法: evict(Object obj):從緩存中清除參數指定的持久化對象。 clear():清空緩存中全部持久化對象。
二級緩存的管理
Hibernate的二級緩存策略的通常過程以下:
1) 條件查詢的時候,老是發出一條select * from table_name where …. (選擇全部字段)這樣的SQL語句查詢數據庫,一次得到全部的數據對象。
2) 把得到的全部數據對象根據ID放入到第二級緩存中。
3) 當Hibernate根據ID訪問數據對象的時候,首先從Session一級緩存中查;查不到,若是配置了二級緩存,那麼從二級緩存中查;查不到,再查詢數據庫,把結果按照ID放入到緩存。
4) 刪除、更新、增長數據的時候,同時更新緩存。
Hibernate的二級緩存策略,是針對於ID查詢的緩存策略,對於條件查詢則毫無做用。爲此,Hibernate提供了針對條件查詢的Query Cache。
什麼樣的數據適合存放到第二級緩存中?
1 不多被修改的數據 2 不是很重要的數據,容許出現偶爾併發的數據 3 不會被併發訪問的數據 4 參考數據,指的是供應用參考的常量數據,它的實例數目有限,它的實例會被許多其餘類的實例引用,實例極少或者歷來不會被修改。
不適合存放到第二級緩存的數據?
1 常常被修改的數據 2 財務數據,絕對不容許出現併發 3 與其餘應用共享的數據。
經常使用的緩存插件 Hibernater 的二級緩存是一個插件
下面是幾種經常使用的緩存插件:
EhCache:可做爲進程範圍的緩存,存放數據的物理介質能夠是內存或硬盤,對Hibernate的查詢緩存提供了支持。
OSCache:可做爲進程範圍的緩存,存放數據的物理介質能夠是內存或硬盤,提供了豐富的緩存數據過時策略,對Hibernate的查詢緩存提供了支持。
SwarmCache:可做爲羣集範圍內的緩存,但不支持Hibernate的查詢緩存。
JBossCache:可做爲羣集範圍內的緩存,支持事務型併發訪問策略,對Hibernate的查詢緩存提供了支持。
上述4種緩存插件的對比狀況列於表9-3中。
表9-3 4種緩存插件的對比狀況
緩 存 插 件 |
支 持 只 讀 |
支持非嚴格讀寫 |
支 持 讀 寫 |
支 持 事 務 |
EhCache |
是 |
是 |
是 |
|
OSCache |
是 |
是 |
是 |
|
SwarmCache |
是 |
是 |
|
|
JBossCache |
是 |
|
|
是 |
它們的提供器列於表9-4中。
表9-4 緩存策略的提供器
緩 存 插 件 |
提供器(Cache Providers) |
Hashtable(只能測試時使用) |
org.hibernate.cache.HashtableCacheProvider |
EhCache |
org.hibernate.cache.EhCacheProvider |
OSCache |
org.hibernate.cache.OSCacheProvider |
SwarmCache |
org.hibernate.cache.SwarmCacheProvider |
JBossCache |
org.hibernate.cache.TreeCacheProvider |
在默認狀況下,Hibernate使用EhCache進行JVM級別的緩存。用戶能夠經過設置Hibernate配置文件中的hibernate.cache.provider_class的屬性,指定其餘的緩存策略,該緩存策略必須實現org.hibernate.cache.CacheProvider接口。
配置二級緩存的主要步驟:
1) 選擇須要使用二級緩存的持久化類,設置它的命名緩存的併發訪問策略。這是最值得認真考慮的步驟。
2) 選擇合適的緩存插件,而後編輯該插件的配置文件。
緩存做用範圍
事務範圍(一級緩存——Session級別)
事務存在與內存中,事務範圍內的對象都有一個對象ID(OID),它對應數據庫表中的代理主鍵(沒有實際意義僅用來標示記錄號,相對於有實際意義的天然主鍵)
進程範圍(二級緩存——SessionFactory級別)
緩存內的數據被進程中的事務共享操做數據時需運用所機制
集羣範圍(二級緩存——SessionFactory級別)
一級緩存Session
減小訪問數據庫的頻率
Transaction tx = session . beginTransaction();
//第一次
Bird b1 = (Bird) session . get(Bird . class , new long(1));
//第二次
Bird b2 = (Bird) session . get(Bird . class , new long(1));
tx . commit();
session第一次加載對象時先從緩存中查找,如沒找到在從數據庫中查找並加載到緩存中;第二次加載時直接從緩存中加載而不需再次訪問數據庫。
保證數據庫中的相關記錄與緩存中相應對象保持同步
Session緩存中的數據在提交數據庫以前進行了幾回修改,當數據提交時只執行一次最後一次修改的update操做。
配置二級緩存步驟
選擇須要使用二級緩存的持久化類,設置它的二級緩存併發訪問策略(只讀,只寫,可讀可寫)。
選擇合適的緩存插件,每一種緩存插件都有自帶的配置文件,須要手動配置。
例如配置ehcache緩存
一、在Hibernate配置文件中,指定ehcache適配器
Hibernate . cfg . xml添加元素
//二級緩存存在的類
<property name=」hibernate . cache . provider_class」>org . hibernate . cache . EhCacheProvide</Property>
/查詢時是否使用二級緩存
<property name=」hibernate . cache . use_query_cache」>true</Property>
二、在映射文件中配置<cache>元素
XXX . hbm . xml
//指定併發訪問策略
<cache usage=」read-write」/>
三、編寫ehcache . xml,並將此文件放在src文件夾下
eacache . xml
<?xml version=」1.0」 encoding=」UTF-8」?>
<ehcache>
<diskStore path=」java . io . tempdir」/>
<!--
maxElementsInMemory:容許緩存可存儲的總記錄數
eternal:當前緩存是否永遠不過時
overflowToDisk:當前緩存中數據達到最大值時,是否把緩存數據寫入硬盤
timeToIdleSeconds:當前緩存最大閒置時間,超過該時間則銷燬緩存
timeToLiveSeconds:設置超時時間,當緩存建立後達到該時間就自動銷燬
-->
<defaultCache
maxElementsInMemory=」5」
eternal=」false」
overflowToDisk=」true」
timeToIdleSeconds=」15」
timeToLiveSeconds=」120」
/>
</ehcache>
Hibernate映射分爲:內置映射和自定義映射。
使用<property>定義屬性
使用<many-to-one>配置多對一映射
使用<one-to-one>配置一對一映射(主鍵關聯和惟一外鍵關聯)
Hibernate的核心接口一共有5個,分別爲:Session、SessionFactory、Transaction、Query和Configuration。這5個核心接口在任何開發中都會用到。經過這些接口,不只能夠對持久化對象進行存取,還可以進行事務控制。
Session接口
Session接口負責執行被持久化對象的CRUD操做(CRUD的任務是完成與數據庫的交流,包含了不少常見的SQL語句。)。但須要注意的是Session對象是非線程安全的。同時,Hibernate的session不一樣於JSP應用中的HttpSession。這裏當使用session這個術語時,其實指的是Hibernate中的session,而之後會將HttpSession對象稱爲用戶session。
SessionFactory接口
SessionFactory接口負責初始化Hibernate。它充當數據存儲源的代理,並負責建立Session對象。這裏用到了工廠模式。須要注意的是SessionFactory並非輕量級的,由於通常狀況下,一個項目一般只須要一個SessionFactory就夠,當須要操做多個數據庫時,能夠爲每一個數據庫指定一個SessionFactory。
Configuration接口
Configuration接口負責配置並啓動Hibernate,建立SessionFactory對象。在Hibernate的啓動的過程當中,Configuration類的實例首先定位映射文檔位置、讀取配置,而後建立SessionFactory對象。
Transaction接口
Transaction接口負責事務相關的操做。它是可選的,開發人員也能夠設計編寫本身的底層事務處理代碼。
Query和Criteria接口
Query和Criteria接口負責執行各類數據庫查詢。它能夠使用HQL語言或SQL語句兩種表達方式。
注意:Configuration實例時一個啓動期間的對象,一旦SessionFactory建立完成它就被丟棄。
自由態(未曾進行持久化,未與任何Session相關聯)
持久態(僅與一個Session相關聯)
遊離態(已經進行了持久化,但未與當前任何Session相關聯)
Transient(自由態)狀態最大的特徵是:
* 在數據庫中沒有與之匹配的數據
* 沒有歸入session的管理
Persistent(持久態)狀態最大的特徵是:
* 在數據庫中有與之匹配的數據
* 歸入了session的管理
* 在清理緩存(髒數據檢查)的時候,會和數據庫同步
Detached(遊離態)狀態最大的特徵是:
* 在數據庫中有與之匹配的數據
* 沒有歸入session的管理
狀態轉換:
save()方法保存對象 引起INSERT
load()方法裝載對象 若是沒有匹配的數據庫記錄,load()方法可能會拋出沒法恢復的異常。
get()方法裝載對象 會當即訪問數據庫,若是沒有對象的記錄,則返回null。
flush()方法強制提交刷新。
update()方法提交遊離狀態的對象。
delete()方法移除持久化對象。
refresh ()方法強制裝載對象。
不帶參數查詢
Query query = session.createQuery(「from User」);
帶參數查詢
Query query = session.createQuery(「from User where username=:username」);
Query.setLockMode(「user」,LockMode.upgrade); 加鎖
Query.setString(「username」,」admin」);
也可以使用setParameterList()設置該參數
List names = new ArrayList();
names.add(「admin」);
names.add(「test」);
Query query = session.createQuery(「from User where username in(:unameList)」);
Query.setString(「unameList」,names);
還能夠帶問號
Query query = session.createQuery(「from User where username=?」);
Query.setString(0,」admin」);
setProperties()方法:
在Hibernate中能夠使用setProperties()方法,將命名參數與一個對象的屬性值綁定在一塊兒,以下程序代碼:
Customer customer=new Customer();
customer.setName(「pansl」);
customer.setAge(80);
Query query=session.createQuery(「from Customer c where c.name=:name and c.age=:age 」);
query.setProperties(customer);
取得List結果集
List list =query.list();
取得迭代結果集
Iterator iter=query.iterate();
或
Iterator iter=query.list().iterator();
list()和iterator()區別:查詢機制不一樣
list方法僅查詢一次,查詢所有數據
iterator可能會查詢屢次,首先從數據庫中檢索出全部符合條件的記錄的id字段(僅有id),而後掃描緩存,若是緩存中包含所有數據則無需再查詢數據庫,直接引用。若是緩存中不包含任何數據須要再次查詢數據庫。
大多數狀況下使用list進行查詢,當對象包含大量屬性,或者要加載的大部分數據已經在緩存中,可以使用iterator;
取得一個對象
Query query = session.createQuery(「from User where username=:username」);
Query.setString(「username」,」admin」);
User user = (User)query.uniqueResult();
標量查詢(查某個具體的字段或是統計函數等)
略
分頁查詢
Query query = session.createQuery(「from User」);
query.setFirstResult(10);//設置起始範圍,從第11數據開始取
query.setMaxResults(20);//設置結束範圍,最多能取20條數據(若是有的話)
List list =query.list();
建立SQL查詢
Query query = session.createSQLQuery(「select {user.*} from User {user} where username=?」);
其餘同樣(別名須要大括號括起來)
建立Criteria實例
Criteria criteria = session.createCriteria(User.class);
criteria.setMaxResults(50);
List users= criteria.list();
添加查詢條件
criteria.add(Restrictions.like(「username」,」%admin%」));
criteria.add(Restrictions.between(「ID」,1,10));
添加排序
criteria.add(Order.asc(「username」));
示例查詢
User user=new User();
user.setUsernem(「admin」);
criteria.add(Example.create(user));
一、讀取並解析配置文件
Configuration config=new Configuration() . configure();
二、讀取並解析映射信息,並建立SessionFactory
SessionFactory sessionfactory=config . buildSessionFactory();
三、打開session
this . session=sessionfactory . openSession();
四、打開一個事務(增刪改必選,查詢可選)
tran=this.session . beginTransaction();
五、持久化操做
this . session . save(login);(增長操做)
this . session . update(login);(修改操做)
this . session . dalete(login)(刪除操做)
六、提交事務
tran . commit();
七、關閉session
This . session.close();
基於JTA的事物管理
JTA提供了跨Session的事物管理能力
JTA事物管理則由JTA容器實現,JTA容器對當前加入事務的衆多Connection 進行調度
JTA的事務週期可橫跨多個JDBC Connection生命週期
JTA事務是由JTA Container維護,而參與事務的Connection無需對事務管理進行干涉
LockMode.NONE : 無鎖機制
LockMode.WRITE : Hibernate在Insert和Update記錄時會自動獲取
LockMode.READ :Hibernate在讀取記錄時會自動獲取
LockMode.UPGRADE : 利用數據庫的for update子句加鎖
LockMode.UPGRADE_NOWAIT : Oracle的特定實現,利用Oracle的for update nowait子句實現加鎖
Criteria.setLockMode
Query.setLockMode
Session.lock
Session不是線程安全的,因此讓多個線程共享一個Session發生數據衝突,因此Session須要管理
ThreadLocal模式: ThreadLocal並非一個線程的本地實現,即他不是一個線程(Thread),而是一個線程局部變量(Thread local variable)。它的做用就是爲每個使用這個變量的線程都提供一個變量值的副本,而且每個線程均可以獨立的改變本身的副本,而不會和其餘線程的副本產生衝突。
openSession()和getCurrentSession()
建立session的方法除了有openSession()以外,getCurrentSession()也能夠建立Session;
getCurrentSession()建立的Session會綁定到當前線程,而openSession()不會
getCurrentSession()建立的Session會在事務提交或回滾後自動關閉,而openSession()則須要手動關閉(調用Session的close()方法)
使用getCurrentSession()的方法
當使用本地事務時(JDBC事務)
須要在hibernate.cfg.xml文件的<session-factory>節點中添加
<property name=」hibernate.current_session_context_class」>thread</property>
當使用全局事務時(JTA事務)
須要在hibernate.cfg.xml文件的<session-factory>節點中添加
<property name=」hibernate.current_session_context_class」>jta</property>
持久化對象不經過外鍵創建對象之間的關聯關係,而是經過屬性
持久化對象之間的關聯關係的種類:一對一,一對多(多對一),多對多
持久化類之間關聯關係的方向:單向關聯,雙向關聯
一對多(在年級的xml中)
<set name=」students」>
<key column=」gid」/> //年級 <!—此處gid爲Student表中的gid外鍵字段-->
<one-to-many class=」com . pb . hibernate . pb . Students」/>
</set>
多對一(在學生中)
<many-to-one name=」grade」 class=」hib3.po.Grade」 cascade=」save-update」>
<column name=」gid」/>
</many-to-one>
配置反轉屬性:inverse,將維護關聯關係的任務反轉,交給對方完成
inverse只能用於集合屬性中,即關聯關係中「一」的一方;反轉關聯關係給「多」的一方。
即:
<set name=」students」 inverse=「true」>
<key column=」gid」/>
<one-to-many class=」com . pb . hibernate . pb . Students」/>
</set>
一對一關聯
在People類中添加License類的屬性,在License類中添加People的屬性
People . hbm . xml中經過<one-to-one>標籤實現映射
License . hbm . xml中經過<many-to-one>標籤實現映射
People類 License類
pid :int lid :int
pname :String 1:1 ldesc :String
pgender :String people :People
license :License
People類People . hbm . xml(主動方)
<one-to-one name=」license」 class=」hib3.po.License」 cascade=」all」 lazy=」false」/>
注:在<one-to-one>標籤中通常吧cascade屬性值設爲all,lazy屬性設爲false
License類License . hbm . xml(被動方)
<many-to-one name=」people」 class=」hib3.po.People」 unique=」true」 lazy=」false」>
<!--外鍵設置在被動方表中-->
<column name=」pid」/>
</many-to-one>
注:unique意爲惟一,hibernate經過unique屬性將多對一關係限制爲一對一;
多對多關聯關係
一、不建立中間表的持久化類,只建立兩端表的持久化類,在映射文件中使用<many-to-many>標籤設置映射
Student.hbm.xml
<set name=」courses」 table=」sc」 cascade=」save-update」>
<!-- Student與中間表發生關聯的字段是「sid」-->
<key column=」sid」>
<!—中間表與Course類發生關聯的字段是「cid」-->
<many-to-many class=」hib3.po.Course」 column=」cid」>
</set>
Course.hbm.xml
<set name=」students」 table=」sc」 cascade=」save-update」 inverse=「true」>
<!-- Course與中間表發生關聯的字段是「cid」-->
<key column=」cid」>
<!—中間表與Student類發生關聯的字段是「sid」-->
<many-to-many class=」hib3.po.Student」 column=」sid」>
</set>
注:在其中一方添加inverse屬性,只有一方維護關聯關係
二、建立中間表,兩端表的持久化類,針對中間表的持久化類分別和兩端表的持久化類建立一對多的關聯關係
分別是HQL查詢 ,對象化查詢Criteria方法,動態查詢DetachedCriteria,例子查詢,sql查詢,命名查詢。
HQL查詢
經過Query接口使用HQL語言進行查詢,HQL是hibernate本身的一套查詢語言,於SQL語法不一樣,具備跨數據庫的優勢。
對象化查詢方式
經過Criteria等接口和類進行查詢。
動態分離查詢DetachedCriteria
面向對象操做,分離業務與底層,不須要字段屬性攝入到Dao實現層
SQL查詢方式
使用原聲SQL語言進行查詢。
命名查詢方式
適用狀況:萬能方法,有點像ibatis輕量級框架的操做,方便維護。 缺點:不面向對象。基於hql和sql,有必定缺陷。
屬性查詢有直接查詢和經過構造方法查詢。
fetch關鍵字
用來表示家在對象時查詢語句的形式和加載時機。有3個取值:
select:加載關聯對象時經過select語句實現。默認
subselect:經過帶子查詢語句實現
join:執行當即檢索,採用迫切左外鏈接檢索全部關聯對象,lazy屬性將被忽略
注:select和subselect適用於當即檢索和延遲檢索,jion僅適用於當即檢索
select和subselect決定加載關聯對象時查詢語句的形式,join決定加載時機
HQL語句混雜在代碼中會破壞代碼可讀性
Hibernate容許在影射配置文件中定義字符串形式的查詢語句,便是命名查詢。
命名查詢——映射配置文件
<hibernate-mapping>
<class name=」hib3.po.Login」 table=」LOGIN」>
<!--。。。。。省略其餘配置-->
</class>
<query name=」loginUser」>
<![CDATA[
from Login login where login.username=:username and login.password=:password]]>
</query>
</hibernate-mapping>
以<![CDATA[HQL]]>方式保存HQL語句
在程序中使用Session的getNamedQuery(「hql語句名」)方法獲取配置文件中的hql語句。
<!--設置JDBC單次批量處理數目(通常10-50)-->
<property name=」hibernate . jdbc . batch_size」>20</property>
(Session緩存爲Hibernate一級緩存,是事務範圍內的緩存;Session外置緩存是Hibernate二級緩存,是應用範圍內的緩存,即全部事物共享二級緩存。二級緩存默認關閉)
<property name=」hibernate . cache . use_second_level_cache」>false</property>
批量更新處理:使用可滾動結果集(ScrollableResults)
//ScrollableResults對象中存儲的是遊標,但須要此對象時纔會到數據庫中加載
ScrollableResults logins = session . createQuery(「from Login」) . scroll(ScrollMode . forward_only);
使用CallableStatement調用存儲過程
CallableStatement cst = session . connection() . prepareCall(「{call login_insert(?,?,?)}」)
cst . setString(1,「aaa」);
cst . setString(2,「22222」);
cst . setLong (3,「21」);
cst . executeUpadte();
調用帶有返回結果集的存儲過程
Session session = new Configuration() . configure() . buildSessionFactory() . openSession();
Transaction tran = session . beginTransaction();
CallableStatement cst = session . connection() . prepareCall(「{call login_getlist(?)}」)
cst . registerOutParameter(1 , oracle . jdbc . OracleType . cursor);
cst . execute();
ResultSet rs=(ResultSet)cst . getObject(1);
while(rs.next()){
……
}
使用命名SQL調用存儲過程
使用命名SQL的插入方法(更新功能相似)
使用註解方式來註釋類,屬性
能夠省略類——表映射文件(hbm . xml文件)
Hibernate註解的優點
簡化對象關係映射的開發
經過註解方式能夠自動生成數據庫表
使用註解進行對象映射步驟
一、爲每個實體Bean使用@Entity註解
二、使用@ID指定實體Bean的標識屬性
三、使用@Table指明當前實體Bean對應數據庫那張表
四、使用@GeneratedValue指定主鍵生成類型
五、修改Hibernate配置文件
Spring是一個開源框架,它由Rod Johnson建立。它是爲了解決企業應用開發的複雜性而建立的。Spring使用基本的JavaBean來完成之前只可能由EJB完成的事情。然而,Spring的用途不只限於服務器端的開發。從簡單性、可測試性和鬆耦合的角度而言,任何Java應用均可以從Spring中受益。
簡單來講,Spring是一個輕量級的控制反轉(IoC)和麪向切面(AOP)的容器框架。
◆輕量——從大小與開銷兩方面而言Spring都是輕量的。完整的Spring框架能夠在一個大小隻有1MB多的JAR文件裏發佈。而且Spring所需的處理開銷也是微不足道的。此外,Spring是非侵入式的:典型地,Spring應用中的對象不依賴於Spring的特定類。
◆控制反轉——Spring經過一種稱做控制反轉(IoC)的技術促進了鬆耦合。當應用了IoC,一個對象依賴的其它對象會經過被動的方式傳遞進來,而不是這個對象本身建立或者查找依賴對象。你能夠認爲IoC與JNDI相反——不是對象從容器中查找依賴,而是容器在對象初始化時不等對象請求就主動將依賴傳遞給它。
◆面向切面——Spring提供了面向切面編程的豐富支持,容許經過分離應用的業務邏輯與系統級服務(例如審計(auditing)和事務(transaction)管理)進行內聚性的開發。應用對象只實現它們應該作的——完成業務邏輯——僅此而已。它們並不負責(甚至是意識)其它的系統級關注點,例如日誌或事務支持。
◆容器——Spring包含並管理應用對象的配置和生命週期,在這個意義上它是一種容器,你能夠配置你的每一個bean如何被建立——基於一個可配置原型(prototype),你的bean能夠建立一個單獨的實例或者每次須要時都生成一個新的實例——以及它們是如何相互關聯的。然而,Spring不該該被混同於傳統的重量級的EJB容器,它們常常是龐大與笨重的,難以使用。
◆框架——Spring能夠將簡單的組件配置、組合成爲複雜的應用。在Spring中,應用對象被聲明式地組合,典型地是在一個XML文件裏。Spring也提供了不少基礎功能(事務管理、持久化框架集成等等),將應用邏輯的開發留給了你。
全部Spring的這些特徵使你可以編寫更乾淨、更可管理、而且更易於測試的代碼。它們也爲Spring中的各類模塊提供了基礎支持。
◆方便解耦,簡化開發
經過Spring提供的IoC容器,咱們能夠將對象之間的依賴關係交由Spring進行控制,避免硬編碼所形成的過分程序耦合。有了Spring,用戶沒必要再爲單實例模式類、屬性文件解析等這些很底層的需求編寫代碼,能夠更專一於上層的應用。
◆AOP編程的支持
經過Spring提供的AOP功能,方便進行面向切面的編程,許多不容易用傳統OOP實現的功能能夠經過AOP輕鬆應付。
◆聲明式事務的支持
在Spring中,咱們能夠從單調煩悶的事務管理代碼中解脫出來,經過聲明式方式靈活地進行事務的管理,提升開發效率和質量。
◆方便程序的測試
能夠用非容器依賴的編程方式進行幾乎全部的測試工做,在Spring裏,測試再也不是昂貴的操做,而是隨手可作的事情。
◆方便集成各類優秀框架
Spring不排斥各類優秀的開源框架,相反,Spring能夠下降各類框架的使用難度,Spring提供了對各類優秀框架(如Struts,Hibernate、Hessian、Quartz)等的直接支持。
◆下降Java EE API的使用難度
Spring對不少難用的Java EE API(如JDBC,JavaMail,遠程調用等)提供了一個薄薄的封裝層,經過Spring的簡易封裝,這些Java EE API的使用難度大爲下降。
Spring核心容器(提供框架的基本功能,核心容器的組件是BeanFactory,是工廠模式的體現,使用IOC(控制反轉)模式將應用程序的配置和依賴性規範與實際的應用代碼分開)。
Spring上下文 是一個配置文件(applicationContext.xml),向框架提供上下文信息。包括的服務如JNDI,調度等。
Spring AOP 面向方面的編程,主要用於支持事務管理。
Spring Dao 提供異常層次結構,可管理異常處理和不一樣數據庫供應商拋出的錯誤信息。
Spring ORM 集成了多個ORM框架,提供ORM的對象關係工具。
Spring Web 集成了MVC的框架,如Struts等
Spring MVC 自身提供的MVC框架,高度的可配置性。
定義:由容器來控制業務和業務之間的依賴關係,而非傳統的經過代碼來直接操控,此處控制權的轉移就是所謂的反轉。
實現策略:依賴查找(相似與JDNI的實現)、依賴注入(DI)
其中依賴注入是一種更加合適的方法,讓容器去全權負責依賴查詢,受管組件只須要暴露JavaBean的setter方法或者帶參數的構造子或者接口,使容器能夠在初始化時組裝對象的依賴關係。
好處是:查詢依賴操做和應用代碼分離;受控對象不會使用到容器特定的API,這樣咱們的受控對象能夠搬出容器單獨使用。
依賴注入有三種:又有接口注入(Interface Injection),設值注入(Setter Injection)和構造子注入(Constructor Injection)三種方式。
4種Bean封裝機制:
BeanWrapper機制
BeanFactory機制
ApplicationContext機制:由BeanFactory擴展而來。
WebContext機制
配置XML元數據:服務層對象、DAO數據訪問層對象、Action表示層對象、Hibernate SessionFactory對象、JMS Queue對象等。
舉例:
Object obj=Class.forName("domain.Test").newInstance();
BeanWrapperwrapper=newBeanWrapperImpl(obj);//BeanWrapper實現了Wrapper接口
wrapper.setPropertyValue("testN","sasa");
Testte=(Test)obj;
System.out.println(te.getTestN());
實例化容器
Resource r = new FileSystemResource(「beans.xml」);
BeanFactory factory = new XmlBeanFactory(r);
或者
ClassPathResource r = new ClassPathResource(「beans.xml」);
BeanFactory factory = new XmlBeanFactory(r);
或者
ApplicationContext ctx = null;
ctx = new ClassPathXmlApplicationContext(new String[]{「beans1.xml」,」beans2.xml」});
BeanFactory factory = (BeanFactory)ctx;
注意:以上都是相對路徑。
BeanFactory提供的方法
Boolean containsBean(String);
Object getBean(String)
Object getBean(String,Class)
Class getType(String name)
Boolean isSingleton(String)
String[] getAliases(String) 經過bean名稱的全部別名
配置<bean>的屬性
id、class、factory-method(必須靜態的方法)、factory-bean(使用是class屬性必須爲空)、scope(singleton、prototype、request、session、global session)、depends-on (指定依賴的bean))、lazy-init(true、false)、init-method(初始化回調)、destory-method(bean析構回調)、parent(繼承bean)
配置<bean>的子元素
<property>(主要是針對屬性注入)
簡單:name value
集合:
Properties 使用<props><prop key>value</prop></props>
List 使用<list><value>value</value></list>
Map 使用
<map>
<entry>
<key>
<value>value</value>
</key>
<value>value</value>
</entry>
</map>
Set 使用<set><value>value</value></set>
注意:map的key或value值,或set的value值不能是如下元素:bean、ref、idref、list、set、map、props、value、null。
配置<constructor-arg>元素
一、用type指定輸入參數的java類型,value指定輸入參數的值
二、用ref屬性指定其餘的bean
三、使用<ref>子元素指向另外一個bean
上下文
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext*.xml</param-value>
</context-param>
監聽
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
字符集的設置
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
1、簡介
AOP也就是面向切面編程,做爲面向對象編程的補充。面向切面編程(AOP)和麪向對象編程(OOP)互爲補充,面向對象編程將程序分解成各個層次的對象,而面向切面編程將程序運行過程分解成各個切面,能夠這樣理解,面向對象編程從靜態角度考慮程序結構,面向切面編程是從動態角度考慮運行過程。AOP專門用來處理具備橫切性質的系統服務,如事務管理、安全檢查、緩存、對象池管理等。
2、概念
切面(Aspect):業務流程運行的某個特定步驟,也就是應用運行過程當中的關注點,關注點能夠橫切多個對象,因此經常也稱爲橫切關注點。
鏈接點(Joinpoint):程序運行過程當中明確的點,如方法的調用,或者異常的拋出。Spring AOP中,鏈接點老是方法的調用,指的就是Method。
加強處理(Advice):AOP框架在特定的切入點執行的加強處理。處理有」around」、」before」、」after」等類型。
切入點(Pointcut):能夠插入加強處理的鏈接點。簡而言之,當某個鏈接點知足指定要求時,該鏈接點將被添加加強處理,該鏈接點也就變成了切入點。
一組Joinpoint,就是說一個Advice可能在多個地方織入。
引入:將方法或者字段添加到被處理的類中。Spring容許引入新的接口到任何被處理的對象。
目標對象(Target):被AOP框架進行加強處理的對象,也被稱爲加強的對象。若是AOP框架是經過運行時代理來實現的,那麼這個對象是一個被代理的對象。
AOP代理:AOP框架建立的對象,簡單地說代理就是對目標對象的增強。Spring中的AOP代理能夠是JDK動態代理,也能夠是DBLIB代理。前者爲實現接口的目標對象的代理,後者爲不實現接口的目標對象的代理。
織入(Weaving):將加強處理添加到目標對象中,並建立一個被加強的對象(AOP代理)的過程就是織入。織入有兩種方式:編譯時加強(AspectJ)和運行時加強(DGLIB)。
3、AOP 種類
一、靜態織入:指在編譯時期就織入Aspect代碼,AspectJ好像是這樣作的。
二、動態織入:在運行時期織入,Spring AOP屬於動態織入,動態織入又分靜動兩種,靜則指織入過程只在第一次調用時執行;動則指根據代碼動態運行的中間狀態來決定如何操做,每次調用Target的時候都執行(性能較差)。
四、Spring AOP 代理原理
Spring AOP 是使用代理來完成的,Spring 會使用下面兩種方式的其中一種來建立代理:
一、JDK動態代理,特色只能代理接口,性能相對較差,須要設定一組代理接口。
二、CGLIB 代理,可代理接口和類(final method除外),性能較高(生成字節碼)。
5、經常使用加強處理
@Before("execution(* kuozhan.before.*.*(..))")
@AfterReturning(pointcut="execttion(* kuozhan.after.*.*(..))",returning="rvt")
@AfterThrowing(pointcut="execution(* lee.*.*(..))", throwing="ex")
@After("execution(* lee.*.*(..))")
@Around("execution(* lee.*.*(..))")
設計模式(Design pattern)是一套被反覆使用、多數人知曉的、通過分類編目的、代碼設計經驗的總結。使用設計模式是爲了可重用代碼、讓代碼更容易被他人理解、保證代碼可靠性、可維護性。 毫無疑問,設計模式於己於他人於系統都是多贏的,設計模式使代碼編制真正工程化,設計模式是軟件工程的基石,如同大廈的一塊塊磚石同樣。
開閉原則(對擴展開放,對修改關閉)
如簡單工廠模式,只要再建立一個子類就能夠了,而後在工廠的方法中返回一個新的子類對象就能夠了。
里氏替換原則(任何基類能夠出現的地方,子類必定能夠出現)
若是調用的是父類的話,那麼換成子類也徹底能夠運行。Java編譯程序會檢查程序是否符合里氏代換原則。還記得java繼承的一個原則嗎?子類override方法的訪問權限不能小於父類對應方法的訪問權限。能夠說:里氏代換原則是繼承複用的一個基礎。
合成複用原則(少用繼承,多用合成/聚合)
在面向對象的設計裏,有兩種基本的辦法能夠在不一樣的環境中複用已有的設計和實現,即經過合成/聚合或經過繼承。
合成/聚合複用:
優勢:支持包裝、黑箱複用看不見內部的細節。依賴較少。這種複用能夠在運行時間內動態進行,新對象能夠動態地引用與成分對象類型相同的對象。
缺點:是經過使用這種複用建造的系統會有較多的對象須要管理
繼承複用:
優勢:新的實現較爲容易,由於超類的大部分功能均可以經過繼承關係自動進入子類,修改或擴展繼承而來的實現較爲容易。
缺點:繼承複用破壞包裝,由於繼承將超類的實現細節暴露給子類。由於超類的內部細節經常是對子類透明的,所以這種複用是透明的複用,又稱「白箱」複用。若是超類的實現發生改變,那麼子類的實現也不得不發生改變。從超類忌辰而來的實現是靜態的,不可能在運行時間內發生改變,所以沒有足夠的靈活性。
通常來講,對違反里氏代換原則的設計進行重構時,能夠採起兩種辦法:一是加入一個抽象超類;二是將繼承關係改寫爲合成/聚合關係。
「IS-A」是嚴格的分類學意義上的定義,意思是一個類是另外一個類的「一種」。而「HAS-A」則不一樣,它表示某一個角色具備某一項責任。
依賴倒置原則(抽象不該該依賴於細節,細節應當依賴於抽象)
要針對接口編程,而不是針對實現編程。
傳遞參數,或者在組合聚合關係中,儘可能引用層次高的類。
主要是在構造對象時能夠動態的建立各類具體對象,固然若是一些具體類比較穩定,就沒必要在弄一個抽象類作它的父類,這樣有多此一舉的感受。
接口隔離原則(使用多個專門的接口比使用單一的總接口要好)
一個類對另一個類的依賴性應當是創建在最小的接口上的。
一個接口表明一個角色,不該當將不一樣的角色都交給一個接口。沒有關係的接口合併在一塊兒,造成一個臃腫的大接口,這是對角色和接口的污染。
「不該該強迫客戶依賴於它們不用的方法。接口屬於客戶,不屬於它所在的類層次結構。」這個說得很明白了,再通俗點說,不要強迫客戶使用它們不用的方法,若是強迫用戶使用它們不使用的方法,那麼這些客戶就會面臨因爲這些不使用的方法的改變所帶來的改變。
迪米特原則(最少知識原則。不要和陌生人說話)
迪米特法則的各類表述
① 只與你直接的朋友們通訊;
② 不要跟「陌生人」說話;
③ 每個軟件單位對其餘的單位都只有最少的知識,並且侷限於那些與本單位密切相關的軟件單位。
狹義的迪米特法則
☆ 若是兩個類沒必要彼此直接通訊,那麼這兩個類就不該當發生直接的相互做用。若是其中的一個類須要調用另一個類的某一個方法,能夠經過第三者轉發這個調用。
在系統裏造出大量的小方法,這些方法僅僅是傳遞間接的調用,與系統的商務邏輯無關。
遵循類之間的迪米特法則會是一個系統的局部設計簡化,由於每個局部都不會和遠距離的對象有直接的關聯。可是,這也會形成系統的不一樣模塊之間的通訊效率下降,也會使系統的不一樣模塊之間不容易協調。
門面模式和調停者模式實際上就是迪米特法則的應用。
廣義的迪米特法則在類的設計上的體現:
優先考慮將一個類設置成不變類。
儘可能下降一個類的訪問權限。
謹慎使用Serializable。
儘可能下降成員的訪問權限。
注意:在進行設計的時候,咱們儘可能從抽象類繼承,而不是從具體類繼承。若是從繼承等級樹來看,全部葉子節點應當是具體類,而全部的樹枝節點應當是抽象類或者接口。固然這個只是一個通常性的指導原則,使用的時候還要具體狀況具體分析。
模式名稱:一個助記名,它用一兩個詞來描述模式的問題、解決方案和效果。
問題:描述了應該在什麼時候使用模式。
解決方案:描述了設計的組成成分,它們之間的相互關係及各自的職責和協做方式。
效果:描述了模式應用的效果及使用模式應權衡的問題。
提供一個建立一系列相關或相互依賴對象的接口,而無須指定它們的具體類。客戶類和工廠類分開。消費者任什麼時候候須要某種產品,只需向工廠請求便可。消費者無須修改就能夠接納新產品。缺點是當產品修改時,工廠類也要作相應的修改。
生成器模式也稱爲建造者模式。將一個複雜對象的構建與他的表示相分離,使得一樣的構建過程能夠建立不一樣的表示。將產品的內部表象和產品的生成過程分割開來,從而使一個建造過程生成具備不一樣的內部表象的產品對象。建造模式使得產品內部表象能夠獨立的變化,客戶沒必要知道產品內部組成的細節。建造模式能夠強制實行一種分步驟進行的建造過程。
核心工廠類再也不負責全部產品的建立,而是將具體建立的工做交給子類去作,成爲一個抽象工廠角色,僅負責給出具體工廠類必須實現的接口,而不接觸哪個產品類應當被實例化這種細節。
簡單工廠模式
經過給出一個原型對象來指明所要建立的對象的類型,而後用複製這個原型對象的方法建立出更多同類型的對象。原始模型模式容許動態的增長或減小產品類,產品類不須要非得有任何事先肯定的等級結構,原始模型模式適用於任何的等級結構。缺點是每個類都必須配備一個克隆方法。
在Java中Prototype模式變成clone()方法的使用,因爲Java的純潔的面向對象特性,使得在Java中使用設計模式變得很天然,二者已經幾乎是渾然一體了。這反映在不少模式上,如Iterator遍歷模式。
保證一個類只有一個實例,並提供一個訪問它的全局訪問點
提供一個方法順序訪問一個聚合對象的各個元素,而又不須要暴露該對象的內部表示。
定義對象間一對多的依賴關係,當一個對象的狀態發生改變時,全部依賴於它的對象都獲得通知自動更新(以前寫的天氣預報)。
定義一個操做中的算法的骨架,而將一些步驟延遲到子類中,TemplateMethod使得子類能夠不改變一個算法的結構便可以重定義該算法得某些特定步驟。
將一個請求封裝爲一個對象,從而使你能夠用不一樣的請求對客戶進行參數化,對請求排隊和記錄請求日誌,以及支持可撤銷的操做。Command模式是很是簡單而又優雅的一種設計模式,它的根本目的在於將「行爲請求者」與「行爲實現者」解耦。
容許對象在其內部狀態改變時改變他的行爲。對象看起來彷佛改變了他的類。主要解決的是當控制一個對象狀態的條件表達式過於複雜時的狀況。把狀態的判斷邏輯轉移到表示不一樣狀態的一系列類中,能夠把複雜的判斷邏輯簡化。
意圖:容許一個對象在其內部狀態改變時改變它的行爲
適用場景:
1.一個對象的行爲取決於它的狀態,而且它必須在運行時刻根據狀態改變它的行爲。
2.一個操做中含有龐大的多分支結構,而且這些分支決定於對象的狀態。
策略模式和狀態模式是雙胞胎,它們有相同的類圖,可是它們的意圖不一樣。策略模式是圍繞能夠互換的算法來成功建立業務的,然而狀態模式是經過改變對象內部的狀態來幫助對象控制本身的行爲。
定義一系列的算法,把他們一個個封裝起來,並使他們能夠互相替換,本模式使得算法能夠獨立於使用它們的客戶。
優勢:
一、 提供了一種替代繼承的方法,並且既保持了繼承的優勢(代碼重用)還比繼承更靈活(算法獨立,能夠任意擴展)。
二、 避免程序中使用多重條件轉移語句,使系統更靈活,並易於擴展。
三、 遵照大部分GRASP原則和經常使用設計原則,高內聚、低偶合。
缺點:
一、 由於每一個具體策略類都會產生一個新類,因此會增長系統須要維護的類的數量。
見例子:
1. 抽象策略
package com.eekq.strategy;
public interface IStrategy {
/**策略方法*/
public abstract double add();
}
2. 具體策略,這裏我以兩個具體策略爲例
package com.eekq.strategy;
public class ConcreteStrategy1 implements IStrategy {
/**示意性算法*/
public double add() {
// TODO 自動生成方法存根
System.out.println(this.getClass().getName() + "的加法運算");
return 0;
}
}
package com.eekq.strategy;
public class ConcreteStrategy2 implements IStrategy {
public double add() {
// TODO 自動生成方法存根
System.out.println(this.getClass().getName() + "的加法運算");
return 0;
}
}
3.環境角色
package com.eekq.strategy;
public class Context {
/**環境角色類*/
private IStrategy strategy;
public Context(IStrategy strategy) {
this.strategy = strategy;
}
/**策略方法*/
public double add() {
this.strategy.add();
return 0;
}
}
4.客戶端調用
package com.eekq.strategy;
public class Main {
/**
*@paramargs
*/
public static void main(String[] args) {
// TODO 自動生成方法存根
Context context = new Context(new ConcreteStrategy1());
context.add();//執行算法1
context = new Context(new ConcreteStrategy2());
context.add();//執行算法2
}
}
5.執行結果:
com.eekq.strategy.ConcreteStrategy1的加法運算
com.eekq.strategy.ConcreteStrategy2的加法運算
6.總結
優勢:動態改變算法或行爲
缺點:客戶端必須知道全部的策略類,並自行決定使用哪個策略類,必須對每個算法瞭解
使多個對象都有機會處理請求,從而避免請求的送發者和接收者之間的耦合關係 。一個鏈能夠是一條線,一個樹,也能夠是一個環。以下圖所示,責任鏈是一個樹結構的一部分。在責任鏈模式裏,不少對象由每個對象對其下家的引用而鏈接起來造成一條鏈。請求在這個鏈上傳遞,直到鏈上的某一個對象決定處理此請求。發出這個請求的客戶端並不知道鏈上的哪個對象最終處理這個請求,這使得系統能夠在不影響客戶端的狀況下動態地從新組織鏈和分配責任。
// "Handler"
abstract class Handler
{
// Fields
protected Handler successor;
// Methods
public void SetSuccessor( Handler successor )
{
this.successor = successor;
}
abstract public void HandleRequest( int request );
}
// "ConcreteHandler1"
class ConcreteHandler1 : Handler
{
// Methods
override public void HandleRequest( int request )
{
if( request >= 0 && request < 10 )
Console.WriteLine("{0} handled request {1}",
this, request );
else
if( successor != null )
successor.HandleRequest( request );
}
}
中介者又叫調停者,用一箇中介對象封裝一些列的對象交互。
表示一個做用於某對象結構中的各元素的操做,它使你能夠在不改變各元素類的前提下定義做用於這個元素的新操做。訪問者模式用於對不一樣節點進行操做的狀況(如List的中的多個元素並不相同),若是將操做放在節點中,會增長節點的複雜性,並不易維護,若是將操做放在調用函數中,則會出現多個判斷語句,對不一樣的節點作不一樣的操做,增長了節點和調用函數之間的耦合。爲了解決這樣的問題,在調用函數(對節點進行操做的函數)和節點之間增長了vistor的類,在vistor中實現對各個節點的操做。
給定一個語言,定義他的文法的一個表示,並定義一個解釋器,這個解釋器使用該表示來解釋語言中的句子。
在不破壞對象的前提下,捕獲一個對象的內部狀態,並在該對象以外保存這個狀態。
將對象組合成樹形結構以表示部分總體的關係,Composite使得用戶對單個對象和組合對象的使用具備一致性。
爲子系統中的一組接口提供一致的界面,fa?ade提供了一高層接口,這個接口使得子系統更容易使用。
爲其餘對象提供一種代理以控制對這個對象的訪問
將一類的接口轉換成客戶但願的另一個接口,Adapter模式使得本來因爲接口不兼容而不能一塊兒工做那些類能夠一塊兒工做。
動態地給一個對象增長一些額外的職責,就增長的功能來講,Decorator模式相比生成子類更加靈活。
將抽象部分與它的實現部分相分離,使他們能夠獨立的變化。
用來儘量減小內存使用量以及分享資訊給儘量多的類似物件;它適合用於當大量物件只是重複於是致使沒法使人接受的使用大量內存。一般物件中的部分狀態是能夠分享。常見作法是把它們放在外部數據結構,當須要使用時再將它們傳遞給享元。
JDBC(Java Database Connectivity): JDBC API爲訪問不一樣的數據庫提供了一種統一的途徑,象ODBC同樣,JDBC對開發者屏蔽了一些細節問題,另外,JDCB對數據庫的訪問也具備平臺無關性。
一、JNDI(Java Name and Directory Interface): JNDI API被用於執行名字和目錄服務。它提供了一致的模型來存取和操做企業級的資源如DNS和LDAP,本地文件系統,或應用服務器中的對象。
二、JNDI(Java Naming and Directory Interface)是一個應用程序設計的API,爲開發人員提供了查找和訪問各類命名和目錄服務的通用、統一的接口,相似JDBC都是構建在抽象層上。
三、JNDI可訪問的現有的目錄及服務有:
DNS、XNam 、Novell目錄服務、LDAP(Lightweight Directory Access Protocol 輕型目錄訪問協議)、 CORBA對象服務、文件系統、Windows XP/2000/NT/Me/9x的註冊表、RMI、DSML v1&v二、NIS。
四、優勢:
包含了大量的命名和目錄服務,使用通用接口來訪問不一樣種類的服務;
能夠同時鏈接到多個命名或目錄服務上;
創建起邏輯關聯,容許把名稱同Java對象或資源關聯起來,而沒必要知道對象或資源的物理ID。
五、JNDI與JDBC
JNDI提供了一種統一的方式,能夠用在網絡上查找和訪問服務。經過指定一個資源名稱,該名稱對應於數據庫或命名服務中的一個記錄,同時返回數據庫鏈接創建所必須的信息。
JNDI主要有兩部分組成:應用程序編程接口和服務供應商接口。應用程序編程接口提供了Java應用程序訪問各類命名和目錄服務的功能,服務供應商接口提供了任意一種服務的供應商使用的功能。
代碼示例:
try{
Context cntxt = new InitialContext();
DataSource ds = (DataSource) cntxt.lookup("jdbc/dpt");
}
catch(NamingException ne){
...
}
六、JNDI與JMS
消息通訊是軟件組件或應用程序用來通訊的一種方法。JMS就是一種容許應用程序建立、發送、接收、和讀取消息的JAVA技術。
代碼示例:
try{
Properties env = new Properties();
InitialContext inictxt = new InitialContext(env);
TopicConnectionFactory connFactory = (TopicConnectionFactory) inictxt.lookup("TTopicConnectionFactory");
...
}
catch(NamingException ne){
...
}
訪問特定目錄:舉個例子,人是個對象,他有好幾個屬性,諸如這我的的姓名、電話號碼、電子郵件地址、郵政編碼等屬性。
經過getAttributes()方法
Attribute attr = directory.getAttributes(personName).get("email");
String email = (String)attr.get();
經過使用JNDI讓客戶使用對象的名稱或屬性來查找對象:
foxes = directory.search("o=Wiz,c=US", "sn=Fox", controls);
經過使用JNDI來查找諸如打印機、數據庫這樣的對象,查找打印機的例子: Printer printer = (Printer)namespace.lookup(printerName); printer.print(document);
瀏覽命名空間:
NamingEnumeration list = namespace.list("o=Widget, c=US"); while (list.hasMore()) {
NameClassPair entry = (NameClassPair)list.next(); display(entry.getName(), entry.getClassName());
}
EJB(Enterprise JavaBean): J2EE技術之因此贏得某體普遍重視的緣由之一就是EJB。它們提供了一個框架來開發和實施分佈式商務邏輯,由此很顯著地簡化了具備可伸縮性和高度複雜的企業級應用的開發。EJB規範定義了EJB組件在什麼時候如何與它們的容器進行交互做用。容器負責提供公用的服務,例如目錄服務、事務管理、安全性、資源緩衝池以及容錯性。但這裏值得注意的是,EJB並非實現J2EE的惟一途徑。正是因爲J2EE的開放性,使得有的廠商可以以一種和EJB平行的方式來達到一樣的目的。
分類:分別是會話Bean(Session Bean),實體Bean(Entity Bean)和消息驅動Bean(MessageDriven Bean)。
1.Session Bean用於實現業務邏輯,它能夠是有狀態的,也能夠是無狀態的。每當客戶端請求時,容器就會選擇一個Session Bean來爲客戶端服務。Session Bean能夠直接訪問數據庫,但更多時候,它會經過Entity Bean實現數據訪問。
2.Entity Bean是域模型對象,用於實現O/R映射,負責將數據庫中的表記錄映射爲內存中的Entity對象,事實上,建立一個Entity Bean對象至關於新建一條記錄,刪除一個Entity Bean會同時從數據庫中刪除對應記錄,修改一個Entity Bean時,容器會自動將Entity Bean的狀態和數據庫同步。
3.MessageDriven Bean是EJB2.0中引入的新的企業Bean,它基於JMS消息,只能接收客戶端發送的JMS消息而後處理。MDB其實是一個異步的無狀態Session Bean,客戶端調用MDB後無需等待,馬上返回,MDB將異步處理客戶請求。這適合於須要異步處理請求的場合,好比訂單處理,這樣就能避免客戶端長時間的等待一個方法調用直到返回結果。
RMI(Remote Method Invoke): 正如其名字所表示的那樣,RMI協議調用遠程對象上方法。它使用了序列化方式在客戶端和服務器端傳遞數據。RMI是一種被EJB使用的更底層的協議。
舉例:
//接口RmiSample
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface RmiSample extends Remote {
public int sum(int a, int b) throws RemoteException;
}
//實現類RmiSampleImpl
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class RmiSampleImpl extends UnicastRemoteObject implements RmiSample {
protected RmiSampleImpl() throws RemoteException {
super();
// TODO Auto-generated constructor stub
}
@Override
public int sum(int a, int b) throws RemoteException {
// TODO Auto-generated method stub
return a+b;
}
}
//服務器RmiSampleServer
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
public class RmiSampleServer {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
try {LocateRegistry.createRegistry(8083) ;
RmiSampleImpl Server = new RmiSampleImpl();
// 將該對象實例與名稱「SAMPLE-SERVER」捆綁
Naming.rebind("rmi://localhost:8083/SAMPLE-SERVER" , Server);
} catch (java.net.MalformedURLException me) {
System.out.println("Malformed URL: " + me.toString());
} catch (RemoteException re) {
System.out.println("Remote exception: " + re.toString());
}
}
}
//客戶端
import java.rmi.Naming;
import java.rmi.RemoteException;
public class RmiSampleClient {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
try {
String url = "rmi://localhost:8083/SAMPLE-SERVER";
RmiSample RmiObject = (RmiSample) Naming.lookup(url);
System.out.println(" 1 + 2 = " + RmiObject.sum(1, 2));
} catch (RemoteException exc) {
System.out.println("Error in lookup: " + exc.toString());
} catch (java.net.MalformedURLException exc) {
System.out.println("Malformed URL: " + exc.toString());
} catch (java.rmi.NotBoundException exc) {
System.out.println("NotBound: " + exc.toString());
}
}
}
Java IDL/CORBA: 在Java IDL的支持下,開發人員能夠將Java和CORBA集成在一塊兒。他們能夠建立Java對象並使之可在CORBA ORB中展開, 或者他們還能夠建立Java類並做爲和其它ORB一塊兒展開的CORBA對象的客戶。後一種方法提供了另一種途徑,經過它Java能夠被用於將你的新的應用和舊的系統相集成。
CORBA的核心是開放軟件總線——ORB,它提供了網絡環境無關性、操做系統無關性和開發語言無關性的公共平臺。在面向對象的應用環境中,CORBA對象(本文所說的「對象」是面向對象系統中的術語,是指一個實體)的請求者沒必要知道它所請求的對象是在哪裏,是如何實現的,而是由ORB來負責跨平臺的運做管理,無須應用系統的開發者干預。CORBA所具備的跨平臺、分佈式、面向對象的這些優勢使它具備廣闊的應用前景,日益受到軟件行業的重視。
CORBA是一箇中間件規範並非一個實體軟件。軟件開發者經過使用第三方的ORB工具或IDL語言來定義CORBA對象,實現ORB功能。
JSP(Java Server Pages): JSP頁面由HTML代碼和嵌入其中的Java代碼所組成。服務器在頁面被客戶端所請求之後對這些Java代碼進行處理,而後將生成的HTML頁面返回給客戶端的瀏覽器。
Servlet是一種小型的Java程序,它擴展了Web服務器的功能。做爲一種服務器端的應用,當被請求時開始執行,這和CGI Perl腳本很類似。Servlet提供的功能大多與JSP相似,不過實現的方式不一樣。JSP一般是大多數HTML代碼中嵌入少許的Java代碼,而servlets所有由Java寫成而且生成HTML。
XML是一種能夠用來定義其它標記語言的語言。它被用來在不一樣的商務過程當中共享數據。XML的發展和Java是相互獨立的,可是,它和Java具備的相同目標正是平臺獨立性。經過將Java和XML的組合,您能夠獲得一個完美的具備平臺獨立性的解決方案。
JMS是用於和麪向消息的中間件相互通訊的應用程序接口(API)。它既支持點對點的域,有支持發佈/訂閱(publish/subscribe)類型的域,而且提供對下列類型的支持:經承認的消息傳遞,事務型消息的傳遞,一致性消息和具備持久性的訂閱者支持。JMS還提供了另外一種方式來對您的應用與舊的後臺系統相集成。
JMS即Java消息服務(Java Message Service)應用程序接口是一個Java平臺中關於面向消息中間件(MOM)的API,用於在兩個應用程序之間,或分佈式系統中發送消息,進行異步通訊。
JMS 使您可以經過消息收發服務(有時稱爲消息中介程序或路由器)從一個 JMS 客戶機向另外一個 JMS客戶機發送消息。消息是 JMS 中的一種類型對象,由兩部分組成:報頭和消息主體。報頭由路由信息以及有關該消息的元數據組成。消息主體則攜帶着應用程序的數據或有效負載。根據有效負載的類型來劃分,能夠將消息分爲幾種類型,它們分別攜帶:簡單文本 (TextMessage)、可序列化的對象 (ObjectMessage)、屬性集合 (MapMessage)、字節流 (BytesMessage)、原始值流 (StreamMessage),還有無有效負載的消息 (Message)。
JMS提供者
鏈接面向消息中間件的,JMS接口的一個實現。提供者能夠是Java平臺的JMS實現,也能夠是非Java平臺的面向消息中間件的適配器。
JMS客戶
生產或消費基於消息的Java的應用程序或對象。
JMS生產者
建立併發送消息的JMS客戶。
JMS消費者
接收消息的JMS客戶。
JMS消息
包括能夠在JMS客戶之間傳遞的數據的對象
JMS隊列
一個容納那些被髮送的等待閱讀的消息的區域。隊列暗示,這些消息將按照順序發送。一旦一個消息被閱讀,該消息將被從隊列中移走。
JMS主題
一種支持發送消息給多個訂閱者的機制。
ConnectionFactory 接口(鏈接工廠)
用戶用來建立到JMS提供者的鏈接的被管對象。JMS客戶經過可移植的接口訪問鏈接,這樣當下層的實現改變時,代碼不須要進行修改。 管理員在JNDI名字空間中配置鏈接工廠,這樣,JMS客戶纔可以查找到它們。根據消息類型的不一樣,用戶將使用隊列鏈接工廠,或者主題鏈接工廠。
Connection 接口(鏈接)
鏈接表明了應用程序和消息服務器之間的通訊鏈路。在得到了鏈接工廠後,就能夠建立一個與JMS提供者的鏈接。根據不一樣的鏈接類型,鏈接容許用戶建立會話,以發送和接收隊列和主題到目標。
Destination 接口(目標)
目標是一個包裝了消息目標標識符的被管對象,消息目標是指消息發佈和接收的地點,或者是隊列,或者是主題。JMS管理員建立這些對象,而後用戶經過JNDI發現它們。和鏈接工廠同樣,管理員能夠建立兩種類型的目標,點對點模型的隊列,以及發佈者/訂閱者模型的主題。
MessageConsumer 接口(消息消費者)
由會話建立的對象,用於接收發送到目標的消息。消費者能夠同步地(阻塞模式),或異步(非阻塞)接收隊列和主題類型的消息。
MessageProducer 接口(消息生產者)
由會話建立的對象,用於發送消息到目標。用戶能夠建立某個目標的發送者,也能夠建立一個通用的發送者,在發送消息時指定目標。
Message 接口(消息)
是在消費者和生產者之間傳送的對象,也就是說從一個應用程序傳送到另外一個應用程序。一個消息有三個主要部分:
消息頭(必須):包含用於識別和爲消息尋找路由的操做設置。
一組消息屬性(可選):包含額外的屬性,支持其餘提供者和用戶的兼容。能夠建立定製的字段和過濾器(消息選擇器)。
一個消息體(可選):容許用戶建立五種類型的消息(文本消息,映射消息,字節消息,流消息和對象消息)。
消息接口很是靈活,並提供了許多方式來定製消息的內容。
Session 接口(會話)
表示一個單線程的上下文,用於發送和接收消息。因爲會話是單線程的,因此消息是連續的,就是說消息是按照發送的順序一個一個接收的。會話的好處是它支持事務。若是用戶選擇了事務支持,會話上下文將保存一組消息,直到事務被提交才發送這些消息。在提交事務以前,用戶能夠使用回滾操做取消這些消息。一個會話容許用戶建立消息生產者來發送消息,建立消息消費者來接收消息。
1. 消息通訊模型
JMS 支持兩種消息通訊模型:點到點(point-to-point)(PTP)模型和發佈/訂閱(Pub/Sub)模型。除了下列不一樣以外,這兩種消息通訊模型很是地類似:
PTP 模型規定了一個消息只能有一個接收者;Pub/Sub 模型容許一個消息能夠有多個接收者。
2. 消息組成
消息傳遞系統的中心就是消息。
一條 Message 分爲三個組成部分:
頭(header)是個標準字段集,客戶機和供應商都用它來標識和路由消息。
屬性(property)支持把可選頭字段添加到消息。若是您的應用程序須要不使用標準頭字段對消息編目和分類,您就能夠添加一個屬性到消息以實現這個編目和分類。提供 set<Type>Property(...) 和 get<Type>Property(...) 方法以設置和獲取各類 Java 類型的屬性,包括 Object。JMS 定義了一個供應商選擇提供的標準屬性集。
消息的主體(body)包含要發送給接收應用程序的內容。每一個消息接口特定於它所支持的內容類型。
JMS 爲不一樣類型的內容提供了它們各自的消息類型,可是全部消息都派生自 Message 接口。
StreamMessage:包含 Java 基本數值流,用標準流操做來順序的填充和讀取。
MapMessage:包含一組名/值對;名稱爲 string 類型,而值爲 Java 的基本類型。
TextMessage:包含一個 String。
ObjectMessage:包含一個 Serializable Java 對象;能使用 JDK 的集合類。
BytesMessage:包含未解釋字節流: 編碼主體以匹配現存的消息格式。
XMLMessage: 包含XML內容。擴展TextMessage,XMLMessage 類型的使用,使得消息過濾很是便利。
3. 消息確認模式
非事務性會話中,應用程序建立的會話有5 種確認模式,而在事務性會話中,確認模式被忽略。
五種確認模式說明:
AUTO_ACKNOWLEDGE:自動確認模式。一旦接收方應用程序的方法調用從處理消息處返回,會話對象就會確認消息的接收。
CLIENT_ACKNOWLEDGE:客戶端確認模式。會話對象依賴於應用程序對被接收的消息調用一個acknowledge()方法。一旦這個方法被調用,會話會確認最後一次確認以後全部接收到的消息。這種模式容許應用程序以一個調用來接收,處理並確認一批消息。注意:在管理控制檯中,若是鏈接工廠的Acknowledge Policy(確認方針)屬性被設置爲"Previous"(提早),可是你但願爲一個給定的會話確認全部接收到的消息,那麼就用最後一條消息來調用acknowledge()方法。
DUPS_OK_ACKNOWLEDGE:容許副本的確認模式。一旦接收方應用程序的方法調用從處理消息處返回,會話對象就會確認消息的接收;並且容許重複確認。在須要考慮資源使用時,這種模式很是有效。注意:若是你的應用程序沒法處理重複的消息的話,你應該避免使用這種模式。若是發送消息的初始化嘗試失敗,那麼重複的消息能夠被從新發送。
NO_ACKNOWLEDGE:不確認模式。不確認收到的消息是須要的。消息發送給一個NO_ACKNOWLEDGE 會話後,它們會被WebLogic 服務器當即刪除。在這種模式下,將沒法從新得到已接收的消息,並且可能致使下面的結果:1. 消息可能丟失;和(或者)另外一種狀況:2. 若是發送消息的初始化嘗試失敗,會出現重複消息被髮送的狀況。
MULTICAST_NO_ACKNOWLEDGE:IP組播下的不確認模式,一樣無需確認。發送給一個MULTICAST_NO_ACKNOWLEDGE會話的消息, 會共享以前所述的NO_ACKNOWLEDGE 確認模式同樣的特徵。這種模式支持但願經過IP 組播方式進行消息通訊的應用程序,並且無需依賴會話確認提供的服務質量。注意:若是你的應用程序沒法處理消息的丟失或者重複,那麼你應該避免使用這種模式。若是發送消息的初始化嘗試失敗的話,重複的消息可能會被再次發送。
注:在上表的5 種確認模式中,AUTO_ACKNOWLEDGE ,DUPS_OK_ACKNOWLEDGE 和CLIENT_ACKNOWLEDGE 是JMS 規範定義的,NO_ACKNOWLEDGE 和MULTICAST_NO_ACKNOWLEDGE是WebLogic JMS 提供的。
//獲取初始化容器
private static InitialContext getInitialContext() throws NamingException {
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);
env.put(Context.PROVIDER_URL, PROVIDER_URL);
return new InitialContext(env);
}
具體列子,在weblogic中配置JMS服務器
一、新建JMS Servers
二、新建JMS Model
三、新建工廠
四、新建隊列(點對點)(或新建發佈/訂閱)
代碼:
點對點:寫一個服務類(發送類),以及一個客戶端類(接受類)
//代碼略
訂閱/發佈模式
//發佈者部分代碼
ctx = new InitialContext();
// 1.經過JNDI查詢ConnectionFactory。JMS中鏈接工廠分QueueConnectionFactory和
// TopicConnectionFactory兩種,Weblogic不區分這兩種類型。
TopicConnectionFactory factory = (TopicConnectionFactory) ctx
.lookup("ConnectionFactoryTest");
// 2.經過工廠創建鏈接connection
connection = factory.createTopicConnection();
// 3.建立session
session = connection.createTopicSession(false,
TopicSession.AUTO_ACKNOWLEDGE);
// 4.建立Topic,必須新建一個Topic JNDI。
Topic topic = (Topic) ctx.lookup("TopicTest");
// 5.建立TopicPublisher
TopicPublisher tp = session.createPublisher(topic);
// 構造消息體
TextMessage tm = session.createTextMessage();
tm.setText("csdn tczxg");
tm.setStringProperty("user", "嘿嘿,JMS挺簡單的123。。。");
tp.publish(tm);
// 必須關閉鏈接
connection.close();
//接收者(訂閱者)部分代碼
ctx = new InitialContext();
// 1.經過JNDI查詢ConnectionFactory
TopicConnectionFactory factory = (TopicConnectionFactory) ctx
.lookup("ConnectionFactoryTest");
// 2.經過工廠創建鏈接connection
connection = factory.createTopicConnection();
// 3.建立session
session = connection.createTopicSession(false,
TopicSession.AUTO_ACKNOWLEDGE);
// 4.建立Topic
Topic topic = (Topic) ctx.lookup("TopicTest");
// 5.建立訂閱者
TopicSubscriber ts = session.createSubscriber(topic);
connection.start();
// 構造消息體
Message message = ts.receive();
if (message instanceof TextMessage) {
TextMessage tm = (TextMessage) message;
String user = tm.getStringProperty("user");
System.out.println(tm.getText() + ", user : " + user);
}
connection.close();
JTA定義了一種標準的API,應用系統由此能夠訪問各類事務監控。
JTS是CORBA OTS事務監控的基本的實現。JTS規定了事務管理器的實現方式。該事務管理器是在高層支持Java Transaction API (JTA)規範,而且在較底層實現OMG OTS specification的Java映像。JTS事務管理器爲應用服務器、資源管理器、獨立的應用以及通訊資源管理器提供了事務服務。
JavaMail是用於存取郵件服務器的API,它提供了一套郵件服務器的抽象類。不只支持SMTP服務器,也支持IMAP服務器。
JAF是一個專用的數據處理框架,它用於封裝數據,併爲應用程序提供訪問和操做數據的接口。JAF的主要做用在於讓java應用程序知道如何對一個數據源進行查看、編輯和打印等操做。
Web Services是由企業發佈的完成其特定商務需求的在線應用服務,其餘公司或應用軟件可以經過Internet來訪問並使用這項在線服務。
它是一種構建應用程序的廣泛模型,能夠在任何支持網絡通訊的操做系統中實施運行;它是一種新的web 應用程序分支,是自包含、自描述、模塊化的應用,能夠發佈、定位、經過web調用。Web Service是一個應用組件,它邏輯性的爲其餘應用程序提供數據與服務.各應用程序經過網絡協議和規定的一些標準數據格式(Http,XML,Soap)來訪問Web Service,經過Web Service內部執行獲得所需結果.Web Service能夠執行從簡單的請求到複雜商務處理的任何功能。一旦部署之後,其餘Web Service應用程序能夠發現並調用它部署的服務。
XFire是新一代的Java Web服務引擎,XFire使得在JavaEE應用中發佈Web服務變得垂手可得。和其餘Web服務引擎相比,XFire的配置很是簡單,能夠很是容易地和Spring集成,它使得Java開發人員終於能夠得到和.Net開發人員同樣的開發效率。
舉例:首先應用相關的xfile的jar文件
一、Web.xml中配置
<servlet>
<servlet-name>XFireServlet</servlet-name>
<servlet-class>org.codehaus.xfire.transport.http.XFireConfigurableServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>XFireServlet</servlet-name>
<url-pattern>/servlet/XFireServlet/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>XFireServlet</servlet-name>
<url-pattern>/services/*</url-pattern>
</servlet-mapping>
二、service.xml中配置
<beans>
<service xmlns="http://xfire.codehaus.org/config/1.0">
<name>SayHiService</name>
<namespace>http://cn.com.pansky/SayHiService</namespace>
<serviceClass>com.xfire.SayHiService</serviceClass>
<implementationClass>com.xfire.SayHiServiceImpl</implementationClass>
</service>
</beans>
三、接口
public interface SayHiService {
public String sayHi(String name);
}
四、實現類
public class SayHiServiceImpl implements SayHiService {
@Override
public String sayHi(String name) {
if (name == null) {
return "連名字也不願告訴我嗎?";
}
return name + ", 你吃了嗎?沒吃回家吃去吧。";
}
public String 不告訴你() {
return "個人名字不告訴你!";
}
}
五、客戶端調用類
public class tt {
/**
* @param args
*/
public static void main(String[] args) {
String serviceURL = "http://localhost:8082/TestWebservice/services/SayHiService";
Service serviceModel = new ObjectServiceFactory().create(
SayHiService.class, null, "http://cn.com.pansky/SayHiService",
null);
XFireProxyFactory serviceFactory = new XFireProxyFactory();
try {
SayHiService service = (SayHiService) serviceFactory.create(
serviceModel, serviceURL);
Client client = Client.getInstance(service);
// client.addOutHandler(new OutHeaderHandler());
// disable timeout
client.setProperty(CommonsHttpMessageSender.HTTP_TIMEOUT, "1");
String hello = service.sayHi("張山瘋");
System.out.println("服務器對[張山瘋] 的回答是:" + hello);
hello = service.sayHi(null);
System.out.println("服務器胡言亂語說:" + hello);
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
}
髒讀又稱無效數據的讀出,是指在數據庫訪問中,事務T1將某一值修改,而後事務T2讀取該值,此後T1由於某種緣由撤銷對該值的修改,這就致使了T2所讀取到的數據是無效的。髒讀就是指當一個事務正在訪問數據,而且對數據進行了修改,而這種修改尚未提交到數據庫中,這時,另一個事務也訪問這個數據,而後使用了這個數據。由於這個數據是尚未提交的數據,那麼另一個事務讀到的這個數據是髒數據,依據髒數據所作的操做多是不正確的。
第一類更新丟失(回滾丟失)
第二類更新丟失(覆蓋丟失)
兩個事務都同時更新一行數據,可是後來提交的事務所作的修改會覆蓋前面提交的事務的修改或者後面的事務回滾了前面的事務提交的數據,致使結果不是用戶想要的結果。這是由於系統沒有執行任何的鎖操做,所以併發事務並無被隔離開來。
1、解決思路
基本兩種思路,一種是悲觀鎖,另一種是樂觀鎖; 簡單的說就是一種(悲觀鎖)假定這樣的問題是高几率的,最好一開始就鎖住,省得更新總是失敗;另一種(樂觀鎖)假定這樣的問題是小几率的,最後一步作更新的時候再鎖住,省得鎖住時間太長影響其餘人作有關操做。
2、解決方案
一、悲觀鎖
a.傳統的悲觀鎖法(不推薦):
在彈出修改頁面初始化時(這種狀況下通常會去從數據庫查詢出來),在這個初始化查詢中使用select ...for update nowait, 經過添加for update nowait語句,將這條記錄鎖住,避免其餘用戶更新,從而保證後續的更新是在正確的狀態下更新的。而後在保持這個連接的狀態下,在作更新提交。固然這個有個前提就是要保持連接,就是要對連接要佔用較長時間,這個在如今web系統高併發高頻率下顯然是不現實的。
b.如今的悲觀鎖法(推薦優先使用):
在修改這個頁面作提交時先查詢下,固然這個查詢必須也要加鎖(select ...for update nowait),有人會說,在這裏作個查詢確認記錄是否有改變不就好了嗎,是的,是要作個確認,只是你不加for update就不能保證你在查詢到更新提交這段時間裏這條記錄沒有被其餘會話更新過,因此這種方式也須要在查詢時鎖定記錄,保證在這條記錄沒有變化的基礎上再作更新,如有變化則提示告知用戶。
二、樂觀鎖
a.舊值條件(前鏡像)法:
就是在sql更新時使用舊的狀態值作條件,SQL大體以下 Update table set col1 = newcol1value, col2 = newcol2value…. where col1 = oldcol1value and col2 = oldcol2value….,在上面的例子中咱們就能夠把當前工資做爲條件進行更新,若是這條記錄已經被其餘會話更新過,則本次更新了0行,這裏咱們應用系統通常會作個提示告知用戶從新查詢更新。這個取哪些舊值做爲條件更新視具體系統實際狀況而定。(這種方式有可能發生阻塞,若是應用其餘地方使用悲觀鎖法長時間鎖定了這條記錄,則本次會話就須要等待,因此使用這種方式時最好統一使用樂觀鎖法。)
b.使用版本列法(推薦優先使用):
其實這種方式是一個特殊化的前鏡像法,就是不須要使用多箇舊值作條件,只須要在表上加一個版本列,這一列能夠是NUMBER或 DATE/TIMESTAMP列,加這列的做用就是用來記錄這條數據的版本(在表設計時通常咱們都會給每一個表增長一些NUMBER型和DATE型的冗餘字段,以便擴展使用,這些冗餘字段徹底能夠做爲版本列用),在應用程序中咱們每次操做對版本列作維護便可。在更新時咱們把上次版本做爲條件進行更新。
綜上所述,咱們對丟失更新問題建議採起上面的悲觀鎖b方法或樂觀鎖b方法(藍色字體已標註),其實這兩種方式的本質都同樣,都是在更新提交時作一次查詢確認在更新提交,我我的以爲都是樂觀的作法,區別在於悲觀鎖b方法是經過select..for update方式,這個可能會致使其餘會話的阻塞,而樂觀鎖b方法須要多一個版本列的維護。
我的建議:在用戶併發數比較少且衝突比較嚴重的應用系統中選擇悲觀鎖b方法,其餘狀況首先樂觀鎖版本列法。
一個事務對同一行數據重複讀取兩次,可是卻獲得了不一樣的結果。它包括如下狀況:
(1) 事務T1讀取某一數據後,事務T2對其作了修改,當事務T1再次讀該數據時獲得與前一次不一樣的值。
(2) 幻讀(Phantom Reads):事務在操做過程當中進行兩次查詢,第二次查詢的結果包含了第一次查詢中未出現的數據或者缺乏了第一次查詢中出現的數據(這裏並不要求兩次查詢的SQL語句相同)。這是由於在兩次查詢過程當中有另一個事務插入數據或刪除數據形成的。
爲了不上面出現的幾種狀況,在標準SQL規範中,定義了4個事務隔離級別,不一樣的隔離級別對事務的處理不一樣。
也稱爲讀未提交(Read Uncommitted):容許髒讀取,但不容許更新丟失。若是一個事務已經開始寫數據,則另一個數據則不容許同時進行寫操做,但容許其餘事務讀此行數據。該隔離級別能夠經過「排他寫鎖」實現。
也稱爲讀提交(Read Committed):容許不可重複讀取,但不容許髒讀取。這能夠經過「瞬間共享讀鎖」和「排他寫鎖」實現。讀取數據的事務容許其餘事務繼續訪問該行數據,可是未提交的寫事務將會禁止其餘事務訪問該行。
可重複讀取(Repeatable Read):禁止不可重複讀取和髒讀取,可是有時可能出現幻影數據。這能夠經過「共享讀鎖」和「排他寫鎖」實現。讀取數據的事務將會禁止寫事務(但容許讀事務),寫事務則禁止任何其餘事務。
序列化(Serializable):提供嚴格的事務隔離。它要求事務序列化執行,事務只能一個接着一個地執行,但不能併發執行。若是僅僅經過「行級鎖」是沒法實現事務序列化的,必須經過其餘機制保證新插入的數據不會被剛執行查詢操做的事務訪問到。
隔離級別越高,越能保證數據的完整性和一致性,可是對併發性能的影響也越大。對於多數應用程序,能夠優先考慮把數據庫系統的隔離級別設爲Read Committed。它可以避免髒讀取,並且具備較好的併發性能。儘管它會致使不可重複讀、虛讀和第二類丟失更新這些併發問題,在可能出現這類問題的個別場合,能夠由應用程序採用悲觀鎖或樂觀鎖來控制。
隔離級別 |
更新丟失
|
髒讀取 |
重複讀取 |
幻讀 |
未受權讀取(排他寫鎖) |
N |
Y |
Y |
Y |
受權讀取(瞬間共享讀鎖、排他寫鎖) |
N |
N |
Y |
Y |
可重複讀取(共享讀鎖、排他寫鎖) |
N |
N |
N |
Y |
串行(行級鎖) |
N |
N |
N |
N |
原子性(atomicity)、一致性(consistency)、隔離性(isolation)和持久性(durability)。
在數據庫中有兩種基本的鎖類型:排它鎖(Exclusive Locks,即X鎖)和共享鎖(Share Locks,即S鎖)。當數據對象被加上排它鎖時,其餘的事務不能對它讀取和修改。加了共享鎖的數據對象能夠被其餘事務讀取,但不能修改。數據庫利用這兩種基本的鎖類型來對數據庫的事務進行併發控制。
根據保護的對象不一樣,Oracle數據庫鎖能夠分爲如下幾大類:DML鎖(data locks,數據鎖),用於保護數據的完整性;DDL鎖(dictionary locks,字典鎖),用於保護數據庫對象的結構,如表、索引等的結構定義;內部鎖和閂(internal locks and latches),保護 數據庫的內部結構。
DML鎖的目的在於保證併發狀況下的數據完整性,。在Oracle數據庫中,DML鎖主要包括TM鎖和TX鎖,其中TM鎖稱爲表級鎖,TX鎖稱爲事務鎖或行級鎖。
當Oracle 執行DML語句時,系統自動在所要操做的表上申請TM類型的鎖。當TM鎖得到後,系統再自動申請TX類型的鎖,並將實際鎖定的數據行的鎖標誌位進行置位。這樣在事務加鎖前檢查TX鎖相容性時就不用再逐行檢查鎖標誌,而只需檢查TM鎖模式的相容性便可,大大提升了系統的效率。TM鎖包括了SS、SX、S、X 等多種模式,在數據庫中用0-6來表示。不一樣的SQL操做產生不一樣類型的TM鎖。
在數據行上只有X鎖(排他鎖)。在 Oracle數據庫中,當一個事務首次發起一個DML語句時就得到一個TX鎖,該鎖保持到事務被提交或回滾。當兩個或多個會話在表的同一條記錄上執行 DML語句時,第一個會話在該條記錄上加鎖,其餘的會話處於等待狀態。當第一個會話提交後,TX鎖被釋放,其餘會話才能夠加鎖。
當Oracle數據庫發生TX鎖等待時,若是不及時處理經常會引發Oracle數據庫掛起,或致使死鎖的發生,產生ORA-60的錯誤。這些現象都會對實際應用產生極大的危害,如長時間未響應,大量事務失敗等。
1、悲觀封鎖
鎖在用戶修改以前就發揮做用:
Select ..for update(nowait)
Select * from tab1 for update
用戶發出這條命令以後,oracle將會對返回集中的數據創建行級封鎖,以防止其餘用戶的修改。
若是此時其餘用戶對上面返回結果集的數據進行dml或ddl操做都會返回一個錯誤信息或發生阻塞。
2、樂觀鎖
樂觀的認爲數據在select出來到update進取並提交的這段時間數據不會被更改。這裏面有一種潛在的危險就是因爲被選出的結果集並無被鎖定,是存在一種可能被其餘用戶更改的可能。所以Oracle仍然建議是用悲觀封鎖,由於這樣會更安全。
表1 Oracle的TM鎖類型 |
|||
鎖模式 |
鎖描述 |
解釋 |
SQL操做 |
0 |
none |
|
|
1 |
NULL |
空 |
Select |
2 |
SS(Row-S) |
行級共享鎖,其餘對象只能查詢這些數據行 |
Select for update、Lock for update、Lock row share |
3 |
SX(Row-X) |
行級排它鎖,在提交前不容許作DML操做 |
Insert、Update、Delete、Lock row share |
4 |
S(Share) |
共享鎖 |
Create index、Lock share |
5 |
SSX(S/Row-X) |
共享行級排它鎖 |
Lock share row exclusive |
6 |
X(Exclusive) |
排它鎖 |
Alter table、Drop able、Drop index、Truncate table 、Lock exclusive |
1、內部級封鎖
內部級封鎖是用於保護ORACLE內部結構,由系統內部實現,用戶不能訪問,所以咱們沒必要對此作過多的瞭解。
2、DDL級封鎖(字典/語法分析封鎖)
DDL級封鎖也是由ORACLE RDBMS來控制,它用於保護數據字典和數據定義改變時的一致性和完整性。它是系統在對SQL定義語句做語法分析時自動地加鎖,無需用戶幹予。字典/語法分析封鎖共分三類:
(1)、字典操做鎖:用於對字典操做時,鎖住數據字典,此封鎖是獨佔的,從而保護任何一個時刻僅能對一個字典操做。
(2)、字典定義鎖:用於防止在進行字典操做時又進行語法分析,這樣能夠避免在查詢字典的同時改動某個表的結構。
(3)、表定義鎖:用於 一個SQL語句正當訪問某個表時,防止字典中與該表有關的項目被修改。
3、DML級封鎖
DML級封鎖用於控制併發事務中的數據操縱,保證數據的一致性和完整性,其封鎖對象能夠是表或行。
對用戶的數據操縱,Oracle能夠自動爲操縱的數據進行封鎖,但若是有操縱受權,則爲知足併發操縱的須要另外實施封鎖。DML封鎖可由一個用戶進程以顯式的方式加鎖,也可經過某些SQL語句隱含方式實現。
DML鎖有以下三種封鎖方式:
(1)、共享封鎖方式(SHARE)
(2)、獨佔封鎖方式(EXCLUSIVE)
(3)、共享更新封鎖(SHARE UPDATE)
一、共享方式的表封鎖
共享方式的表封鎖是對錶中的全部數據進行封鎖,該鎖用於保護查詢數據的一致性,防止其它用戶對已封鎖的表進行更更新。其它用戶只能對該表再施加共享方式的鎖,而不能再對該表施加獨佔方式的封鎖,共享更新鎖能夠再施加,但不容許持有共享更新封鎖的進程作更新。共享該表的全部用戶只能查詢表中的數據,但不能更新。
二、獨佔方式表封鎖
獨佔方式表封鎖是用於封鎖表中的全部數據,擁有該獨佔方式表封鎖的用戶,便可以查詢該表,又能夠更新該表,其它的用戶不能再對該表施加任何封鎖(包括共享、獨佔或共享更新封鎖)。其它用戶雖然不能更新該表,但能夠查詢該表。
三、共享更新封鎖方式
共享更新封鎖是對一個表的一行或多行進行封鎖,於是也稱做行級封鎖。表級封鎖雖然保證了數據的一致性,但卻減弱了操做數據的並行性。行級封鎖確保在用戶取得被更新的行到該行進行更新這段時間內不被其它用戶所修改。於是行級鎖便可保證數據的一致性又能提升數據操做的迸發性。
SOAP:簡單對象訪問協議,簡單對象訪問協議(SOAP)是一種輕量的、簡單的、基於 XML 的協議,它被設計成在 WEB 上交換結構化的和固化的信息。 SOAP 能夠和現存的許多因特網協議和格式結合使用,包括超文本傳輸協議( HTTP),簡單郵件傳輸協議(SMTP),多用途網際郵件擴充協議(MIME)。它還支持從消息系統到遠程過程調用(RPC)等大量的應用程序。
SOAP 包括四個部分:
SOAP 封裝:它定義了一個框架 , 該框架描述了消息中的內容是什麼,誰應當處理它以及它是可選的仍是必須的。
SOAP 編碼規則:它定義了一種序列化的機制,用於交換應用程序所定義的數據類型的實例。
SOAP RPC 表示:它定義了用於表示遠程過程調用和應答的協定。
SOAP 綁定:定義了一種使用底層傳輸協議來完成在節點間交換SOAP封裝的約定。
SOAP 消息基本上是從發送端到接收端的單向傳輸,但它們經常結合起來執行相似於請求 / 應答的模式。全部的 SOAP 消息都使用 XML 編碼。一條 SOAP 消息就是一個包含有一個必需的 SOAP 的封裝包,一個可選的 SOAP 標頭和一個必需的 SOAP 體塊的 XML 文檔。
協議結構
SOAP 消息格式:
SOAP 標頭
<SOAP-ENV: Envelope Attributes>
<SOAP-ENV:Body Attributes>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
目前主要在web服務中運用。
如:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<funName xmlns="http://tempuri.org/">
<parm1>value1</parm1>
<parm2>value2</parm2>
</funName>
</soap:Body>
</soap:Envelope>
SOAP 通信協議使用 HTTP 來發送XML 格式的信息。HTTP與RPC 的協議很類似,它簡單、 配置普遍,而且對防火牆比其它協議更容易發揮做用。HTTP 請求通常由 Web 服務器軟件(如 IIS 和Apache)來處理, 但愈來愈多的應用服務器產品正在支持HTTP。XML 做爲一個更好的網絡數據表達方式( NDR)。SOAP 把 XML 的使用代碼化爲請求和響應參數編碼模式, 並用HTTP 做傳輸。具體地講, 一個SOAP 方法能夠簡單地看做遵循SOAP編碼規則的HTTP請求和響應, 一個 SOAP 終端則能夠看做一個基於HTTP 的URL, 它用來識別方法調用的目標。像CORBA/ IIOP同樣, SOAP不須要具體的對象綁定到一個給定的終端, 而是由具體實現程序來決定怎樣把對象終端標識符映像到服務器端的對象。
SOAP是一種基於XML的協議,它用於在分佈式環境中發送消息,並執行遠程過程調用。使用SOAP,不用考慮任何特定的傳輸協議(儘管一般選用HTTP協議), 就能使數據序列化。用SOAP來構建平臺與語言中性的互操做系統是一個好的選擇。總之,SOAP和 Web服務已爲在XML上構建分佈式應用程序基礎結構所需的一切都考慮好了。經過解決COM和Java組件對象模型之間的衝突,SOAP把多個平臺在訪問數據時所出現的 不兼容性問題減至最少。
forward僅是容器中控制權的轉向,在客戶端瀏覽器地址欄中不會顯示出轉向後的地址;
redirect則是徹底的跳轉,瀏覽器將會獲得跳轉的地址,並從新發送請求連接。這樣,從瀏覽器的地址欄中能夠看到跳轉後的連接地址。
因此,forward更加高效,在forward能夠知足須要時,儘可能使用forward()方法,而且,這樣也有助於隱藏實際的連接。