jvm詳細:https://www.cnblogs.com/wowinsincere/p/13191698.html
JVM是可運行Java代碼的假想計算機, 包括一套字節碼指令集、一組寄存器、一個棧、一個垃圾回收,堆 和 一個存儲方法域。 JVM 是運行在操做系統之上的,它與硬件沒有直接的交互。
咱們都知道Java 源文件,經過編譯器,可以生產相應的.Class 文件,也就是字節碼文件, 而字節碼文件又經過Java 虛擬機中的解釋器,編譯成特定機器上的機器碼 。 也就是以下: ① Java 源文件—->編譯器—->字節碼文件 ② 字節碼文件—->JVM—->機器碼 每一種平臺的解釋器是不一樣的,可是實現的虛擬機是相同的,這也就是Java 爲何可以跨平臺的緣由了 , 當一個程序從開始運行,這時虛擬機就開始實例化了,多個程序啓動就會存在多個虛擬機實例。 程序退出或者關閉,則虛擬機實例消亡,多個虛擬機實例之間數據不能共享。
線程html
這裏所說的線程指程序執行過程當中的一個線程實體。JVM 容許一個應用併發執行多個線程。 Hotspot JVM 中的Java 線程與原生操做系統線程有直接的映射關係。 當線程本地存儲、緩衝區分配、同步對象、棧、程序計數器等準備好之後,就會建立一個操做系統原生線程。 Java 線程結束,原生線程隨之被回收。操做系統負責調度全部線程,並把它們分配到任何可用的 CPU 上。 當原生線程初始化完畢,就會調用 Java 線程的 run() 方法。 當線程結束時,會釋放原生線程和 Java 線程的全部資源。
Hotspot JVM 後臺運行的系統線程主要有下面幾個:前端
虛擬機線程(VM thread) | 這個線程等待 JVM 到達安全點操做出現。這些操做必需要在獨立的線程裏執行,由於當堆修改沒法進行時,線程都須要 JVM 位於安全點。這些操做的類型有:stop-the-world垃圾回收、線程棧 dump、線程暫停、線程偏向鎖(biased locking)解除。 |
---|---|
週期性任務線程 | 這線程負責定時器事件(也就是中斷),用來調度週期性操做的執行。 |
GC 線程 | 這些線程支持 JVM 中不一樣的垃圾回收活動。 |
編譯器線程 | 這些線程在運行時將字節碼動態編譯成本地平臺相關的機器碼。 |
信號分發線程 | 這個線程接收發送到 JVM 的信號並調用適當的 JVM 方法處理。 |
JVM 內存區域java
垃圾回收與算法mysql
netty框架linux
byte[] 或者char[] streamnginx
buffer Channel程序員
* 流的體系結構: * 抽象基類 字節流(或文件流) * InputStream FileInputStream { read(byte[] buffer) } * OutputStream FileOutputStream { write(byte[] buffer,0,len) } * Reader FileReader { read(char[] buffer) } * Writer FileWriter { write(char[] buffer,0,len) }
緩衝流(處理流的一種) * BufferedInputStream { read(byte[] buffer} * BufferedOutputStream {write(byte[] buffer,0,len) /flush()} * BufferedReader { read(char[] buffer) /readLine() } * BufferedWriter { write(char[] buffer,0,len) /flush() }
char byteweb
緩衝流做用:提供流的讀取、寫入的速度面試
1.轉換流:屬於字符流,做用:提供了在字節流和字符流之間的轉換。ajax
InputStreamReader:將一個字節的輸入流轉換爲字符的輸入流。
OutputStreamWriter:將一個字符的輸出流轉換爲字節的輸出流。
2.解碼:字節、字節數組 --->字符數組、字符串
編碼:字符數組、字符串 --->字節、字節數組
3.字符集
序列化: * Person須要知足以下的要求,方可序列化 * 1.須要實現接口:Serializable * 2.當前類提供一個全局常量:serialVersionUID * 3.除了當前Person類須要實現Serializable接口以外, * 還必須保證其內部全部屬性也必須是可序列化的。(默認狀況下,基本數據類型可序列化) * ObjectOutputStream和ObjectInputStream不能序列化static和transient修飾的成員變量。 做用: ObjectOutputStream:內存中的對象 --->存儲中的文件、經過網絡傳輸出去:序列化過程 ObjectInputStream:存儲中的文件、經過網絡接收過來--->內存中的對象:反序列化過程
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
最傳統的一種IO 模型,即在讀寫數據過程當中會發生阻塞現象。 當用戶線程發出IO 請求以後,內核會去查看數據是否就緒,若是沒有就緒就會等待數據就緒,而用戶線程就會處於阻塞狀態,用戶線程交出CPU。 當數據就緒以後,內核會將數據拷貝到用戶線程,並返回結果給用戶線程,用戶線程才解除block 狀態。 典型的阻塞IO 模型的例子爲:data = socket.read();若是數據沒有就緒,就會一直阻塞在read 方法。
當用戶線程發起一個read 操做後,並不須要等待,而是立刻就獲得了一個結果。 若是結果是一個error 時,它就知道數據尚未準備好,因而它能夠再次發送read 操做。 一旦內核中的數據準備好了,而且又再次收到了用戶線程的請求,那麼它立刻就將數據拷貝到了用戶線程, 而後返回。因此事實上,在非阻塞IO 模型中,用戶線程須要不斷地詢問內核數據是否就緒, 也就說非阻塞IO不會交出CPU,而會一直佔用CPU。 典型的非阻塞IO 模型通常以下: while(true){ data = socket.read(); if(data!= error){ 處理數據 break; } } 可是對於非阻塞IO 就有一個很是嚴重的問題,在while 循環中須要不斷地去詢問內核數據是否就緒, 這樣會致使CPU佔用率很是高,所以通常狀況下不多使用while 循環這種方式來讀取數據。
多路複用IO 模型是目前使用得比較多的模型。Java NIO 實際上就是多路複用IO。 在多路複用IO模型中,會有一個線程不斷去輪詢多個socket 的狀態,只有當socket 真正有讀寫事件時, 才真正調用實際的IO 讀寫操做。由於在多路複用IO 模型中,只須要使用一個線程就能夠管理多個socket, 系統不須要創建新的進程或者線程,也沒必要維護這些線程和進程,而且只有在真正有socket 讀寫事件進行時, 纔會使用IO 資源,因此它大大減小了資源佔用。在Java NIO 中,是經過selector.select()去查詢每一個通道是否有到達事件, 若是沒有事件,則一直阻塞在那裏,所以這種方式會致使用戶線程的阻塞。多路複用IO 模式,經過一個線程就能夠管理多個socket, 只有當socket 真正有讀寫事件發生纔會佔用資源來進行實際的讀寫操做。所以,多路複用IO 比較適合鏈接數比較多的狀況。 另外多路複用IO 爲什麼比非阻塞IO 模型的效率高是由於在非阻塞IO 中,不斷地詢問socket 狀態時經過用戶線程去進行的, 而在多路複用IO 中,輪詢每一個socket 狀態是內核在進行的,這個效率要比用戶線程要高的多。 不過要注意的是,多路複用IO 模型是經過輪詢的方式來檢測是否有事件到達,而且對到達的事件逐一進行響應。 所以對於多路複用IO 模型來講,一旦事件響應體很大,那麼就會致使後續的事件遲遲得不處處理,而且會影響新的事件輪詢。
在信號驅動IO 模型中,當用戶線程發起一個IO 請求操做,會給對應的socket 註冊一個信號函數, 而後用戶線程會繼續執行,當內核數據就緒時會發送一個信號給用戶線程,用戶線程接收到信號以後, 便在信號函數中調用IO 讀寫操做來進行實際的IO 請求操做。
異步IO 模型纔是最理想的IO 模型,在異步IO 模型中,當用戶線程發起read 操做以後, 馬上就能夠開始去作其它的事。而另外一方面,從內核的角度,當它受到一個asynchronous read 以後, 它會馬上返回,說明read 請求已經成功發起了,所以不會對用戶線程產生任何block。而後, 內核會等待數據準備完成,而後將數據拷貝到用戶線程,當這一切都完成以後,內核會給用戶線程發送一個信號, 告訴它read 操做完成了。也就說用戶線程徹底不須要實際的整個IO 操做是如何進行的,只須要先發起一個請求, 當接收內核返回的成功信號時表示IO 操做已經完成,能夠直接去使用數據了。 也就說在異步IO 模型中,IO 操做的兩個階段都不會阻塞用戶線程,這兩個階段都是由內核自動完成, 而後發送一個信號告知用戶線程操做已完成。用戶線程中不須要再次調用IO 函數進行具體的讀寫。 這點是和信號驅動模型有所不一樣的,在信號驅動模型中,當用戶線程接收到信號表示數據已經就緒, 而後須要用戶線程調用IO 函數進行實際的讀寫操做;而在異步IO 模型中,收到信號表示IO 操做已經完成, 不須要再在用戶線程中調用IO 函數進行實際的讀寫操做。
NIO 主要有三大核心部分:Channel(通道),Buffer(緩衝區), Selector。 傳統IO 基於字節流和字符流進行操做, 而NIO 基於Channel 和Buffer(緩衝區)進行操做,數據老是從通道讀取到緩衝區中, 或者從緩衝區寫入到通道中。Selector(選擇區)用於監聽多個通道的事件(好比:鏈接打開,數據到達)。 所以,單個線程能夠監聽多個數據通道。 NIO 和傳統IO 之間第一個最大的區別是,IO 是面向流的,NIO 是面向緩衝區的。
Java IO 面向流意味着每次從流中讀一個或多個字節,直至讀取全部字節,它們沒有被緩存在任何 地方。此外,它不能先後移動流中的數據。若是須要先後移動從流中讀取的數據,須要先將它緩存到一個緩衝區。 NIO 的緩衝導向方法不一樣。數據讀取到一個它稍後處理的緩衝區,須要時可在緩衝區中先後移動。 這就增長了處理過程當中的靈活性。可是,還須要檢查是否該緩衝區中包含全部您須要處理的數據。 並且,需確保當更多的數據讀入緩衝區時,不要覆蓋緩衝區裏還沒有處理的數據。
IO 的各類流是阻塞的。這意味着,當一個線程調用read() 或 write()時,該線程被阻塞, 直到有一些數據被讀取,或數據徹底寫入。該線程在此期間不能再幹任何事情了。 NIO 的非阻塞模式, 使一個線程從某通道發送請求讀取數據,可是它僅能獲得目前可用的數據,若是目前沒有數據可用時, 就什麼都不會獲取。而不是保持線程阻塞,因此直至數據變的能夠讀取以前,該線程能夠繼續作其餘的事情。 非阻塞寫也是如此。一個線程請求寫入一些數據到某通道,但不須要等待它徹底寫入, 這個線程同時能夠去作別的事情。 線程一般將非阻塞IO 的空閒時間用於在其它通道上執行IO 操做, 因此一個單獨的線程如今能夠管理多個輸入和輸出通道(channel)。
首先說一下Channel,國內大多翻譯成「通道」。Channel 和IO 中的Stream(流)是差很少一個等級的。 只不過Stream 是單向的,譬如:InputStream, OutputStream,而Channel 是雙向的, 既能夠用來進行讀操做,又能夠用來進行寫操做。 NIO 中的Channel 的主要實現有: 1. FileChannel 2. DatagramChannel 3. SocketChannel 4. ServerSocketChannel 這裏看名字就能夠猜出個因此然來:分別能夠對應文件IO、UDP 和TCP(Server 和Client)。 下面演示的案例基本上就是圍繞這4 個類型的Channel 進行陳述的。
Buffer,故名思意,緩衝區,其實是一個容器,是一個連續數組。Channel 提供從文件、 網絡讀取數據的渠道,可是讀取或寫入的數據都必須經由Buffer。
上面的圖描述了從一個客戶端向服務端發送數據,而後服務端接收數據的過程。客戶端發送數據時, 必須先將數據存入Buffer 中,而後將Buffer 中的內容寫入通道。 服務端這邊接收數據必須經過Channel 將數據讀入到Buffer 中,而後再從Buffer 中取出數據來處理。 在NIO 中,Buffer 是一個頂層父類,它是一個抽象類,經常使用的Buffer 的子類有: ByteBuffer、IntBuffer、 CharBuffer、 LongBuffer、 DoubleBuffer、FloatBuffer、 ShortBuffer
Selector 類是NIO 的核心類,Selector 可以檢測多個註冊的通道上是否有事件發生,若是有事件發生, 便獲取事件而後針對每一個事件進行相應的響應處理。這樣一來,只是用一個單線程就能夠管理多個通道, 也就是管理多個鏈接。這樣使得只有在鏈接真正有讀寫事件發生時,纔會調用函數來進行讀寫, 就大大地減小了系統開銷,而且沒必要爲每一個鏈接都建立一個線程,不用去維護多個線程,而且避免了多線程之間的上下文切換致使的開銷。
* Collection接口:單列集合,用來存儲一個一個的對象 * list接口:存儲有序的、可重複的數據。 -->"動態"數組,替換原有的數組 * * 面試題:ArrayList、LinkedList、Vector三種的異同? * 同:三個類都是實現了List接口,存儲數據的特色相同:存儲有序的、可重複的數據 * 不一樣:ArrayList:做爲List接口的主要實現類;線程不安全的,效率高;底層使用Object[] elementDate存儲 * LinkedList:對於頻繁的插入、刪除操做,使用此類效率比ArrayList高;底層使用雙向鏈表存儲 * Vector:做爲List接口的古老實現類;線程安全的,效率低;底層使用Object[] elementDate存儲。 * * Set接口:存儲無序的、不可重複的數據 * HashSet:做爲Set接口的主要實現類;線程不安全的;能夠存儲null值 * LinkedHashSet:做爲HashSet的子類;遍歷其內部數據時,能夠按照添加的順序遍歷。 * 對於頻繁的遍歷操做,LinkedHashSet效率高於HashSet。 * TreeSet:能夠按照添加對象的指定屬性,進行排序。 * 以HashSet爲例說明: * 1.無序性:不等於隨機性。存儲的數據在底層數組中並不是按照數組索引的順序添加,而是根據數據的哈希值決定的。 * 2.不可重複性:保證添加的元素按照equals()判斷時,不能返回true。即:相同的元素只能添加一個。 * * 添加元素的過程:以HashSet爲例: * 咱們向HashSet中添加元素a,首先調用元素a所在類的hashCode()方法,計算a的哈希值, * 此哈希值接着經過某種算法計算出在HashSet底層數組中的存放位置(即爲:索引位置),判斷 * 數組此位置上是否已經有元素: * 若是此位置上沒有其餘元素,則元素a添加成功。 -->狀況1 * 若是此位置上有其餘元素b(或以鏈表形式存在的多個元素),則比較元素a與元素b的hash值: * 若是hash值不相同,則元素a添加成功。->狀況2 * 若是hash值相同,進而須要調用元素a所在類的equals()方法: * equals()返回true,元素a添加失敗 * equals()返回false,則元素a添加成功。->狀況3 * * 對於添加成功的狀況2和狀況3而言:元素a與已經存在指定索引位置上數據以鏈表的方式存儲。 * jdk 7:元素a放到數組中,指向原來的元素。 * jdk 8:原來的元素在數組中,指向元素a. 七上八下。 * HashSet底層:數組+鏈表的結構。 * 要求:向set中添加的數據,其所在的類必定要重寫hashCode()和equals() * 重寫的hashCode()和equals()儘量保持一致性:相等的對象必須具備相等的散列碼 * * LinkedHashSet的使用 * LinkedHashSet做爲HashSet的子類,在添加數據的同時,每一個數據還維護了兩個引用,記錄此數據前一個數據和後一個數據。 * 優勢:對於頻繁的遍歷操做,LinkedHashSet效率高於HashSet。 * * TreeSet: * 1.向TreeSet中添加的數據,要求是相同類的對象。 * 2.兩種排序方式:天然排序(實現Comparable接口)和定製排序(Comparator) * 3.天然排序中,比較兩個對象是否相同的標準爲:compareTo()返回0,再也不是equals()。 * 3.定製排序中,比較兩個對象是否相同的標準爲:compare()返回0,再也不是equals()。
天然排序(實現Comparable接口):
定製排序(Comparator):
一.Map的實現類結構: * | ----Map:雙列數據,存儲key-value對的數據 --相似於高中的函數:y = f(x) * |----HashMap:做爲Map的主要實現類;線程不安全的,效率高;存儲null的key和value * |----LinkedHashMap:保證在遍歷map元素時,能夠按照添加的順序實現遍歷。 * 緣由:在原有的HashMap底層結構基礎上,添加了一對指針,指向前一個和後一個元素, * 對於頻繁的遍歷操做,此類執行效率高於HashMap。 * |----TreeMap:保證按照添加的key-value對進行排序,實現排序遍歷。此時考慮key的天然排序或定製排序。 * 底層使用紅黑樹 * |----Hashtable:做爲古老的實現類;線程安全的,效率低;不能存儲null的key和value。 * |----Properties:經常使用來處理配置文件。key和value都是String類型。 * * HashMap的底層:數組+鏈表(jdk7及以前) * 數組+鏈表+紅黑樹(jdk8) * * 面試題: * 1.HashMap的底層實現原理? * 2.HashMap和Hashtable的異同? * 3.CurrentHashMap與Hashtable的異同? * * 2、Map結構的理解: * Map中的key:無序的、不可重複的,使用Set存儲全部的key --->key所在的類要重寫equals()和hashCode()(以HashMap爲例) * Map中的value:無序的、可重複的,使用Collection存儲全部的value --->value所在的類要重寫equals() * 一個鍵值對:key-value構成了一個Entry對象。 * Map中的entry:無序的、不可重複的,使用Set存儲全部的entry。 * 3、HashMap的底層實現原理: * 以jdk7爲例說明: * HashMap map = new HashMap();在實例化之後,底層建立了長度是16的一維數組Entry[] table. * ...可能已經執行過屢次put... * map.put(key1,value1): * 首先,調用key1所在類的hashCode()計算key1哈希值,此哈希值通過某種算數計算之後,獲得在Entry數組中存放位置。 * 若是此位置上的數據爲空,此時的key1-value1添加成功。----狀況1 * 若是此位置上數據不爲空,(意味着此位置上存在一個或多個數據(以鏈表形式存在)), * 比較key1和已經存在的一個或多個數據的哈希值: * 若是key1的哈希值與已經存在的數據的哈希值都不相同,此時key1-value1添加成功。----狀況2 * 若是key1的哈希值和已經存在的某一個數據(key2-value2)的哈希值相同,繼續比較:調用key1所在類的equals(key2)方法,比較: * 若是equals()返回false:此時key1-value1添加成功。----狀況3 * 若是equals()返回true:使用value1替換value2值。 * 補充:關於狀況2和狀況3:此時key1-value1和原來的數據以鏈表的方式存儲。 * 在不斷的添加過程當中,會涉及到擴容問題,當超出臨界值(且要存放的位置非空),擴容。默認的擴容方式:擴容爲原來容量的2倍,並將原來的數據複製過來。 * * jdk8 相比較jdk7在底層實現方面的不一樣: * 1.new HashMap():底層沒有建立一個長度爲16的數組。 * 2.jdk 8底層的數組是:Node[],而非Entry[] * 3.首次調用put()方法時,底層建立長度爲16的數組 * 4.jdk7底層結構只有:數組+鏈表。jdk8中底層結構:數組+鏈表+紅黑樹。 * 當數組的某一個索引位置上的元素以鏈表形式存在的數據個數 > 8 且當前數組的長度 > 64時, * 此時此索引位置上的全部數據改成使用紅黑樹存儲。 * * DEFAULT_INITIAL_CAPACITY:HashMap的默認容量,16 * DEFAULT_LOAD_FACTOR::HashMap的默認加載因子:0.75 * threshold:擴容的臨界值,=容量*填充因子:16*0.75 => 12 * TREEIFY_THRESHOLD:Bucket中鏈表長度大於該默認值,轉化爲紅黑樹:8 * MIN_TREEIFY_CAPACITY:桶中的Node被樹化時最小的hash表容量:64 * * 4、LinkedHashMap的底層實現原理(瞭解) * 源碼中: * static class Entry<K,V> extends HashMap.Node</K,V>{ * Entry<K,V>before,after;//可以記錄添加的元素的前後順序 * }
HashMap hashMap = new HashMap(); hashMap.put("AA",33); hashMap.put("AB",14); hashMap.put(44,535); //遍歷全部的key集:keySet() Set set = hashMap.keySet(); Iterator iterator = set.iterator(); while(iterator.hasNext()){ System.out.println(iterator.next()); } //遍歷全部的value集:values() Collection values = hashMap.values(); for(Object obj:values){ System.out.println(obj); } //遍歷全部的key-value //entrySet() Set entrySet = hashMap.entrySet(); Iterator iterator1 = entrySet.iterator(); while(iterator1.hasNext()){ Object obj = iterator1.next(); Map.Entry entry = (Map.Entry) obj; System.out.println(entry.getKey() + "---" + entry.getValue()); } //方式二: Set keySet = hashMap.keySet(); Iterator iterator2 = keySet.iterator(); while(iterator2.hasNext()){ Object key = iterator2.next(); Object value = hashMap.get(key); } key.hashCode(),Math.abs(key.hashcode()%15)
集合類存放於Java.util 包中,主要有3 種:set(集)、list(列表包含Queue)和map(映射)。 1. Collection:Collection 是集合List、Set、Queue 的最基本的接口。 2. Iterator:迭代器,能夠經過迭代器遍歷集合中的數據 3. Map:是映射表的基礎接口
Collections工具類:操做collection、Map的工具類
Collections類中提供了多個synchronizedXxx()方法,該方法可以使將指定集合包裝成線程同步的集合, 從而能夠解決多線程併發訪問集合時的線程安全問題。
List list = new ArrayList(); list.add(56); list.add(0); list.add(66); list.add(-76); List dest = Arrays.asList(new Object[list.size()]); System.out.println(dest.size()+"dest:"+dest); Collections.copy(dest,list); System.out.println(dest); List synchronizedList = Collections.synchronizedList(list);
Java 的List 是很是經常使用的數據類型。List 是有序的Collection。Java List 一共三個實現類:
分別是ArrayList、Vector 和LinkedList。
ArrayList 是最經常使用的List 實現類,內部是經過數組實現的,它容許對元素進行快速隨機訪問。 數組的缺點是每一個元素之間不能有間隔,當數組大小不知足時須要增長存儲能力,就要將已經有數組的數據複製到新的存儲空間中。 當從ArrayList 的中間位置插入或者刪除元素時,須要對數組進行復制、移動、代價比較高。 所以,它適合隨機查找和遍歷,不適合插入和刪除。
Vector 與ArrayList 同樣,也是經過數組實現的,不一樣的是它支持線程的同步, 即某一時刻只有一個線程可以寫Vector,避免多線程同時寫而引發的不一致性,但實現同步須要很高的花費, 所以,訪問它比訪問ArrayList 慢。
LinkedList 是用鏈表結構存儲數據的,很適合數據的動態插入和刪除,隨機訪問和遍歷速度比較慢。 另外,他還提供了List 接口中沒有定義的方法,專門用於操做表頭和表尾元素,能夠看成堆棧、隊列和雙向隊列使用。
Set 注重獨一無二的性質,該體系集合用於存儲無序(存入和取出的順序不必定相同)元素,值不能重複。 對象的相等性本質是對象hashCode 值(java 是依據對象的內存地址計算出的此序號)判斷的, 若是想要讓兩個不一樣的對象視爲相等的,就必須覆蓋Object 的hashCode 方法和equals 方法。
哈希表邊存放的是哈希值。HashSet 存儲元素的順序並非按照存入時的順序(和List 顯然不一樣) 而是按照哈希值來存的因此取數據也是按照哈希值取得。元素的哈希值是經過元素的hashcode 方法來獲取的, HashSet 首先判斷兩個元素的哈希值,若是哈希值同樣,接着會比較equals 方法 若是 equls 結果爲true , HashSet 就視爲同一個元素。若是equals 爲false 就不是同一個元素。 哈希值相同equals 爲false 的元素是怎麼存儲呢,就是在一樣的哈希值下順延(能夠認爲哈希值相同的元素放在一個哈希桶中)。 也就是哈希同樣的存一列。 如圖1 表示hashCode 值不相同的狀況; 圖2 表示hashCode 值相同,但equals 不相同的狀況。 HashSet 經過hashCode 值來肯定元素在內存中的位置。一個hashCode 位置上能夠存放多個元素。
1. TreeSet()是使用二叉樹的原理對新add()的對象按照指定的順序排序(升序、降序), 每增長一個對象都會進行排序,將對象插入的二叉樹指定的位置。 2. Integer 和String 對象均可以進行默認的TreeSet 排序,而自定義類的對象是不能夠的, 本身定義的類必須實現Comparable 接口,而且覆寫相應的compareTo()函數,才能夠正常使用。 3. 在覆寫compare()函數時,要返回相應的值才能使TreeSet 按照必定的規則來排序 4. 比較此對象與指定對象的順序。若是該對象小於、等於或大於指定對象,則分別返回負整數、零或正整數。
對於LinkedHashSet 而言, 它繼承與HashSet 、又基於LinkedHashMap 來實現的。 LinkedHashSet 底層使用LinkedHashMap 來保存全部元素,它繼承與HashSet, 其全部的方法操做上又與HashSet 相同,所以LinkedHashSet 的實現上很是簡單,只提供了四個構造方法, 並經過傳遞一個標識參數,調用父類的構造器,底層構造一個LinkedHashMap 來實現, 在相關操做上與父類HashSet 的操做相同,直接調用父HashSet 的方法便可。
key.hashCode(),Math.abs(key.hashcode()%15) HashMap 根據鍵的hashCode 值存儲數據,大多數狀況下能夠直接定位到它的值,於是具備很快的訪問速度, 但遍歷順序倒是不肯定的。 HashMap 最多隻容許一條記錄的鍵爲null,容許多條記錄的值爲null。 HashMap 非線程安全,即任一時刻能夠有多個線程同時寫HashMap,可能會致使數據的不一致。若是須要知足線程安全, 能夠用 Collections 的synchronizedMap 方法使HashMap 具備線程安全的能力,或者使用ConcurrentHashMap。 咱們用下面這張圖來介紹HashMap 的結構。
大方向上,HashMap 裏面是一個數組,而後數組中每一個元素是一個單向鏈表。 上圖中,每一個綠色的實體是嵌套類 Entry 的實例,Entry 包含四個屬性:key, value, hash 值和用於單向鏈表的 next。 1. capacity:當前數組容量,始終保持 2^n,能夠擴容,擴容後數組大小爲當前的 2 倍。 2. loadFactor:負載因子,默認爲 0.75。 3. threshold:擴容的閾值,等於 capacity * loadFactor
Java8 對 HashMap 進行了一些修改,最大的不一樣就是利用了紅黑樹,因此其由 數組+鏈表+紅黑樹 組成。 根據 Java7 HashMap 的介紹,咱們知道,查找的時候,根據 hash 值咱們可以快速定位到數組的具體下標, 可是以後的話,須要順着鏈表一個個比較下去才能找到咱們須要的,時間複雜度取決於鏈表的長度,爲 O(n)。 爲了下降這部分的開銷,在 Java8 中,當鏈表中的元素超過了 8 個之後,會將鏈表轉換爲紅黑樹, 在這些位置進行查找的時候能夠下降時間複雜度爲 O(logN)。
Segment段 ConcurrentHashMap 和 HashMap 思路是差很少的,可是由於它支持併發操做,因此要複雜一些。 整個 ConcurrentHashMap 由一個個 Segment 組成,Segment 表明」部分「或」一段「的意思, 因此不少地方都會將其描述爲分段鎖。注意,行文中,我不少地方用了「槽」來表明一個segment。 線程安全(Segment繼承ReentrantLock加鎖) 簡單理解就是,ConcurrentHashMap 是一個 Segment 數組,Segment 經過繼承ReentrantLock 來進行加鎖, 因此每次須要加鎖的操做鎖住的是一個 segment,這樣只要保證每一個 Segment 是線程安全的,也就實現了全局的線程安全。 並行度(默認16) concurrencyLevel:並行級別、併發數、Segment 數,怎麼翻譯不重要,理解它。默認是 16, 也就是說 ConcurrentHashMap 有 16 個 Segments,因此理論上,這個時候,最多能夠同時支持 16 個線程併發寫, 只要它們的操做分別分佈在不一樣的 Segment 上。這個值能夠在初始化的時候設置爲其餘值,可是一旦初始化之後, 它是不能夠擴容的。再具體到每一個 Segment 內部,其實每一個 Segment 很像以前介紹的 HashMap, 不過它要保證線程安全,因此處理起來要麻煩些。 Java8 實現(引入了紅黑樹) Java8 對 ConcurrentHashMap 進行了比較大的改動,Java8 也引入了紅黑樹。
Hashtable 是遺留類,不少映射的經常使用功能與HashMap 相似,不一樣的是它承自Dictionary 類, 而且是線程安全的,任一時間只有一個線程能寫Hashtable,併發性不如ConcurrentHashMap,由於ConcurrentHashMap 引入了分段鎖。 Hashtable 不建議在新代碼中使用,不須要線程安全的場合能夠用HashMap 替換,須要線程安全的場合能夠用ConcurrentHashMap 替換。
TreeMap 實現SortedMap 接口,可以把它保存的記錄根據鍵排序,默認是按鍵值的升序排序, 也能夠指定排序的比較器,當用Iterator 遍歷TreeMap 時,獲得的記錄是排過序的。 若是使用排序的映射,建議使用TreeMap。 在使用TreeMap 時,key 必須實現Comparable 接口或者在構造TreeMap 傳入自定義的Comparator, 不然會在運行時拋出java.lang.ClassCastException 類型的異常。 參考:https://www.ibm.com/developerworks/cn/java/j-lo-tree/index.html
LinkedHashMap 是HashMap 的一個子類,保存了記錄的插入順序,在用Iterator 遍歷 LinkedHashMap 時,先獲得的記錄確定是先插入的,也能夠在構造時帶參數,按照訪問次序排序。
HashMap不是線程安全的,HashTable是線程安全。 HashMap容許空(null)的鍵和值(key),HashTable則不容許。 HashMap性能優於Hashtable。 Map 1.Map是一個以鍵值對存儲的接口。Map下有兩個具體的實現,分別是HashMap和HashTable. 2.HashMap是線程非安全的,HashTable是線程安全的,因此HashMap的效率高於HashTable. 3.HashMap容許鍵或值爲空,而HashTable不容許鍵或值爲空.
java併發知識庫
一、繼承Thread類 Thread 類本質上是實現了Runnable 接口的一個實例,表明一個線程的實例。啓動線程的惟一方 法就是經過Thread 類的start()實例方法。start()方法是一個native 方法,它將啓動一個新線 程,並執行run()方法。 public class MyThread extends Thread { public void run() { System.out.println("MyThread.run()"); } } MyThread myThread1 = new MyThread(); myThread1.start(); 二、實現Runnable接口 若是本身的類已經extends 另外一個類,就沒法直接extends Thread,此時,能夠實現一個Runnable 接口。 public class MyThread extends OtherClass implements Runnable { public void run() { System.out.println("MyThread.run()"); } } //啓動MyThread,須要首先實例化一個Thread,並傳入本身的MyThread 實例: MyThread myThread = new MyThread(); Thread thread = new Thread(myThread); thread.start(); //事實上,當傳入一個Runnable target 參數給Thread 後,Thread 的run()方法就會調用 target.run() public void run() { if (target != null) { target.run(); } }
三、ExecutorService、Callable<Class>、Future 有返回值線程 有返回值的任務必須實現Callable 接口,相似的,無返回值的任務必須Runnable 接口。 執行Callable 任務後,能夠獲取一個Future 的對象,在該對象上調用get 就能夠獲取到Callable 任務返回的Object 了, 再結合線程池接口ExecutorService 就能夠實現傳說中有返回結果的多線程了。 //建立一個線程池 ExecutorService pool = Executors.newFixedThreadPool(taskSize); // 建立多個有返回值的任務 List<Future> list = new ArrayList<Future>(); for (int i = 0; i < taskSize; i++) { Callable c = new MyCallable(i + " "); // 執行任務並獲取Future 對象 Future f = pool.submit(c); list.add(f); } // 關閉線程池 pool.shutdown(); // 獲取全部併發任務的運行結果 for (Future f : list) { // 從Future 對象上獲取任務的返回值,並輸出到控制檯 System.out.println("res:" + f.get().toString()); }
四、基於線程池的方式 線程和數據庫鏈接這些資源都是很是寶貴的資源。 那麼每次須要的時候建立,不須要的時候銷燬,是很是浪費資源的。 那麼咱們就能夠使用緩存的策略,也就是使用線程池。 // 建立線程池 ExecutorService threadPool = Executors.newFixedThreadPool(10); while(true) { threadPool.execute(new Runnable() { // 提交多個線程任務,並執行 @Override public void run() { System.out.println(Thread.currentThread().getName() + " is running .."); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } }); }
Java 裏面線程池的頂級接口是Executor,可是嚴格意義上講Executor 並非一個線程池, 而只是一個執行線程的工具。真正的線程池接口是ExecutorService。
一、newCachedThreadPool 建立一個可根據須要建立新線程的線程池,可是在之前構造的線程可用時將重用它們。 對於執行不少短時間異步任務的程序而言,這些線程池一般可提升程序性能。 調用 execute 將重用之前構造的線程(若是線程可用)。若是現有線程沒有可用的,則建立一個新線程並添加到池中。 終止並從緩存中移除那些已有 60 秒鐘未被使用的線程。所以,長時間保持空閒的線程池不會使用任何資源。 二、newFixedThreadPool 建立一個可重用固定線程數的線程池,以共享的無界隊列方式來運行這些線程。 在任意點,在大多數 nThreads 線程會處於處理任務的活動狀態。若是在全部線程處於活動狀態時提交附加任務, 則在有可用線程以前,附加任務將在隊列中等待。若是在關閉前的執行期間因爲失敗而致使任何線程終止, 那麼一個新線程將代替它執行後續的任務(若是須要)。在某個線程被顯式地關閉以前,池中的線程將一直存在。 三、newScheduledThreadPool 建立一個線程池,它可安排在給定延遲後運行命令或者按期地執行。 ScheduledExecutorService scheduledThreadPool= Executors.newScheduledThreadPool(3); scheduledThreadPool.schedule(newRunnable(){ @Override public void run() { System.out.println("延遲三秒"); } }, 3, TimeUnit.SECONDS); scheduledThreadPool.scheduleAtFixedRate(newRunnable(){ @Override public void run() { System.out.println("延遲1 秒後每三秒執行一次"); } },1,3,TimeUnit.SECONDS); 四、newSingleThreadExecutor Executors.newSingleThreadExecutor()返回一個線程池(這個線程池只有一個線程), 這個線程池能夠在線程死後(或發生異常時)從新啓動一個線程來替代原來的線程繼續執行下去!
當線程被建立並啓動之後,它既不是一啓動就進入了執行狀態,也不是一直處於執行狀態。 在線程的生命週期中, 它要通過新建(New)、就緒(Runnable)、運行(Running)、阻塞(Blocked)和死亡(Dead)5 種狀態。 尤爲是當線程啓動之後,它不可能一直"霸佔"着CPU 獨自運行,因此CPU 須要在多條線程之間切換, 因而線程狀態也會屢次在運行、阻塞之間切換 一、新建狀態(NEW) 當程序使用new 關鍵字建立了一個線程以後,該線程就處於新建狀態,此時僅由JVM 爲其分配內存,並初始化其成員變量的值 二、就緒狀態(RUNNABLE) 當線程對象調用了start()方法以後,該線程處於就緒狀態。Java 虛擬機會爲其建立方法調用棧和程序計數器,等待調度運行。 三、運行狀態(RUNNING) 若是處於就緒狀態的線程得到了CPU,開始執行run()方法的線程執行體,則該線程處於運行狀態。 四、阻塞狀態(BLOCKED) 阻塞狀態是指線程由於某種緣由放棄了cpu 使用權,也即讓出了cpu timeslice,暫時中止運行。 直到線程進入可運行(runnable)狀態,纔有機會再次得到cpu timeslice 轉到運行(running)狀態。 阻塞的狀況分三種: 等待阻塞(o.wait->等待隊列): 運行(running)的線程執行o.wait()方法,JVM 會把該線程放入等待隊列(waitting queue)中。 同步阻塞(lock->鎖池): 運行(running)的線程在獲取對象的同步鎖時,若該同步鎖被別的線程佔用,則JVM 會把該線程放入鎖池(lock pool)中。 其餘阻塞(sleep/join): 運行(running)的線程執行Thread.sleep(long ms)或t.join()方法,或者發出了I/O 請求時, JVM 會把該線程置爲阻塞狀態。當sleep()狀態超時、join()等待線程終止或者超時、 或者I/O處理完畢時,線程從新轉入可運行(runnable)狀態。 五、線程死亡(DEAD) 線程會如下面三種方式結束,結束後就是死亡狀態。 正常結束 1. run()或call()方法執行完成,線程正常結束。 異常結束 2. 線程拋出一個未捕獲的Exception 或Error。 調用 stop 3. 直接調用該線程的stop()方法來結束該線程—該方法一般容易致使死鎖,不推薦使用
一、正常運行結束 程序運行結束,線程自動結束。 二、使用退出標誌退出線程 通常run()方法執行完,線程就會正常結束,然而,經常有些線程是伺服線程。它們須要長時間的運行, 只有在外部某些條件知足的狀況下,才能關閉這些線程。使用一個變量來控制循環,例如: 最直接的方法就是設一個boolean 類型的標誌,並經過設置這個標誌爲true 或false 來控制while 循環是否退出,代碼示例: public class ThreadSafe extends Thread { public volatile boolean exit = false; public void run() { while (!exit){ //do something } } } 定義了一個退出標誌exit,當exit 爲true 時,while 循環退出,exit 的默認值爲false.在定義exit時, 使用了一個Java 關鍵字volatile,這個關鍵字的目的是使exit 同步,也就是說在同一時刻只 能由一個線程來修改exit 的值。 三、Interrupt方法結束線程 使用interrupt()方法來中斷線程有兩種狀況: 1. 線程處於阻塞狀態:如使用了sleep,同步鎖的wait,socket 中的receiver,accept 等方法時, 會使線程處於阻塞狀態。當調用線程的interrupt()方法時,會拋出InterruptException 異常。 阻塞中的那個方法拋出這個異常,經過代碼捕獲該異常,而後break 跳出循環狀態, 從而讓咱們有機會結束這個線程的執行。一般不少人認爲只要調用interrupt 方法線程就會結束, 其實是錯的, 必定要先捕獲InterruptedException 異常以後經過break 來跳出循環, 才能正常結束run 方法。 2. 線程未處於阻塞狀態:使用isInterrupted()判斷線程的中斷標誌來退出循環。 當使用interrupt()方法時,中斷標誌就會置true,和使用自定義的標誌來控制循環是同樣的道理。 public class ThreadSafe extends Thread { public void run() { while (!isInterrupted()){ //非阻塞過程當中經過判斷中斷標誌來退出 try{ Thread.sleep(5*1000);//阻塞過程捕獲中斷異常來退出 }catch(InterruptedException e){ e.printStackTrace(); break;//捕獲到異常以後,執行break 跳出循環 } } } } 四、stop方法終止線程(線程不安全) 程序中能夠直接使用thread.stop()來強行終止線程,可是stop 方法是很危險的,就象忽然關 閉計算機電源,而不是按正常程序關機同樣,可能會產生不可預料的結果,不安全主要是: thread.stop()調用以後,建立子線程的線程就會拋出ThreadDeatherror 的錯誤,而且會釋放子 線程所持有的全部鎖。通常任何進行加鎖的代碼塊,都是爲了保護數據的一致性,若是在調用 thread.stop()後致使了該線程所持有的全部鎖的忽然釋放(不可控制),那麼被保護數據就有可能呈 現不一致性,其餘線程在使用這些被破壞的數據時,有可能致使一些很奇怪的應用程序錯誤。因 此,並不推薦使用stop 方法來終止線程。
1. 對於sleep()方法,咱們首先要知道該方法是屬於Thread 類中的。而wait()方法,則是屬於Object 類中的。 2. sleep()方法致使了程序暫停執行指定的時間,讓出cpu 該其餘線程,可是他的監控狀態依然保持者, 當指定的時間到了又會自動恢復運行狀態。 3. 在調用sleep()方法的過程當中,線程不會釋放對象鎖。 4. 而當調用wait()方法的時候,線程會放棄對象鎖,進入等待此對象的等待鎖定池, 只有針對此對象調用notify()方法後本線程才進入對象鎖定池準備獲取對象鎖進入運行狀態。
start與run區別
1. start()方法來啓動線程,真正實現了多線程運行。這時無需等待run 方法體代碼執行完畢, 能夠直接繼續執行下面的代碼。 2. 經過調用Thread 類的start()方法來啓動一個線程, 這時此線程是處於就緒狀態, 並無運行。 3. 方法run()稱爲線程體,它包含了要執行的這個線程的內容,線程就進入了運行狀態, 開始運行run 函數當中的代碼。 Run 方法運行結束, 此線程終止。而後CPU 再調度其它線程。
1. 定義:守護線程--也稱「服務線程」,他是後臺線程,它有一個特性, 即爲用戶線程 提供公共服務,在沒有用戶線程可服務時會自動離開。 2. 優先級:守護線程的優先級比較低,用於爲系統中的其它對象和線程提供服務。 3. 設置:經過setDaemon(true)來設置線程爲「守護線程」; 將一個用戶線程設置爲守護線程的方式是在 線程對象建立 以前 用線程對象的setDaemon 方法。 4. 在Daemon 線程中產生的新線程也是Daemon 的。 5. 線程則是JVM 級別的,以Tomcat 爲例,若是你在Web 應用中啓動一個線程, 這個線程的生命週期並不會和Web 應用程序保持同步。 也就是說,即便你中止了Web 應用,這個線程依舊是活躍的。 6. example: 垃圾回收線程就是一個經典的守護線程,當咱們的程序中再也不有任何運行的Thread, 程序就不會再產生垃圾,垃圾回收器也就無事可作,因此當垃圾回收線程是JVM 上僅剩的線程時, 垃圾回收線程會自動離開。它始終在低級別的狀態中運行,用於實時監控和管理系統中的可回收資源。 7. 生命週期:守護進程(Daemon)是運行在後臺的一種特殊進程。它獨立於控制終端而且周 期性地執行某種任務或等待處理某些發生的事件。也就是說守護線程不依賴於終端,可是依 賴於系統,與系統「同生共死」。當JVM 中全部的線程都是守護線程的時候,JVM 就能夠退 出了;若是還有一個或以上的非守護線程則JVM 不會退出。
一、樂觀鎖 樂觀鎖是一種樂觀思想,即認爲讀多寫少,遇到併發寫的可能性低,每次去拿數據的時候都認爲別人不會修改, 因此不會上鎖,可是在更新的時候會判斷一下在此期間別人有沒有去更新這個數據,採起在寫時先讀出當前版本號, 而後加鎖操做(比較跟上一次的版本號,若是同樣則更新),若是失敗則要重複讀-比較-寫的操做。 java 中的樂觀鎖基本都是經過CAS 操做實現的, CAS 是一種更新的原子操做,比較當前值跟傳入值是否同樣,同樣則更新,不然失敗。 CAS:Compare and Swap,即比較再交換。 對CAS的理解,CAS是一種無鎖算法,CAS有3個操做數,內存值V,舊的預期值A,要修改的新值B。 當且僅當預期值A和內存值V相同時,將內存值V修改成B,不然什麼都不作。 二、悲觀鎖 悲觀鎖是就是悲觀思想,即認爲寫多,遇到併發寫的可能性高,每次去拿數據的時候都認爲別人會修改, 因此每次在讀寫數據的時候都會上鎖,這樣別人想讀寫這個數據就會block 直到拿到鎖。 java 中的悲觀鎖就是Synchronized,AQS 框架下的鎖則是先嚐試cas 樂觀鎖去獲取鎖,獲取不到, 纔會轉換爲悲觀鎖,如RetreenLock。 三、自旋鎖 自旋鎖原理很是簡單,若是持有鎖的線程能在很短期內釋放鎖資源, 那麼那些等待競爭鎖的線程就不須要作內核態和用戶態之間的切換進入阻塞掛起狀態,它們只須要等一等(自旋), 等持有鎖的線程釋放鎖後便可當即獲取鎖,這樣就避免用戶線程和內核的切換的消耗。 線程自旋是須要消耗cup 的,說白了就是讓cup 在作無用功,若是一直獲取不到鎖,那線程也不能一直佔用cup 自旋作無用功,因此須要設定一個自旋等待的最大時間。 若是持有鎖的線程執行的時間超過自旋等待的最大時間扔沒有釋放鎖, 就會致使其它爭用鎖的線程在最大等待時間內仍是獲取不到鎖,這時爭用線程會中止自旋進入阻塞狀態。 自旋鎖的優缺點 自旋鎖儘量的減小線程的阻塞,這對於鎖的競爭不激烈,且佔用鎖時間很是短的代碼塊來講性能能大幅度的提高, 由於自旋的消耗會小於線程阻塞掛起再喚醒的操做的消耗,這些操做會致使線程發生兩次上下文切換! 可是若是鎖的競爭激烈,或者持有鎖的線程須要長時間佔用鎖執行同步塊,這時候就不適合使用自旋鎖了, 由於自旋鎖在獲取鎖前一直都是佔用cpu 作無用功,佔着XX 不XX,同時有大量線程在競爭一個鎖,會致使獲取鎖的時間很長, 線程自旋的消耗大於線程阻塞掛起操做的消耗,其它須要cup 的線程又不能獲取到cpu,形成cpu 的浪費。 因此這種狀況下咱們要關閉自旋鎖; 自旋鎖時間閥值(1.6引入了適應性自旋鎖) 自旋鎖的目的是爲了佔着CPU 的資源不釋放,等到獲取到鎖當即進行處理。可是如何去選擇自旋的執行時間呢? 若是自旋執行時間太長,會有大量的線程處於自旋狀態佔用CPU 資源,進而會影響總體系統的性能。所以自旋的週期選的額外重要! JVM 對於自旋週期的選擇,jdk1.5 這個限度是必定的寫死的,在1.6 引入了適應性自旋鎖, 適應性自旋鎖意味着自旋的時間不在是固定的了,而是由前一次在同一個鎖上的自旋時間以及鎖的擁有者的狀態來決定, 基本認爲一個線程上下文切換的時間是最佳的一個時間,同時JVM 還針對當前CPU 的負荷狀況作了較多的優化, 若是平均負載小於CPUs 則一直自旋,若是有超過(CPUs/2)個線程正在自旋,則後來線程直接阻塞, 若是正在自旋的線程發現Owner 發生了變化則延遲自旋時間(自旋計數)或進入阻塞,若是CPU 處於節電模式則中止自旋, 自旋時間的最壞狀況是CPU的存儲延遲(CPU A 存儲了一個數據,到CPU B 得知這個數據直接的時間差), 自旋時會適當放棄線程優先級之間的差別。 自旋鎖的開啓 JDK1.6 中-XX:+UseSpinning 開啓; -XX:PreBlockSpin=10 爲自旋次數; JDK1.7 後,去掉此參數,由jvm 控制; 四、Synchronized同步鎖 synchronized 它能夠把任意一個非NULL 的對象看成鎖。他屬於獨佔式的悲觀鎖,同時屬於可重 入鎖。 Synchronized做用範圍 1.做用於方法時,鎖住的是對象的實例(this); 2. 看成用於靜態方法時,鎖住的是Class 實例,又由於Class 的相關數據存儲在永久帶PermGen (jdk1.8 則是metaspace),永久帶是全局共享的,所以靜態方法鎖至關於類的一個全局鎖,會鎖全部調用該方法的線程; 3. synchronized 做用於一個對象實例時,鎖住的是全部以該對象爲鎖的代碼塊。它有多個隊列, 當多個線程一塊兒訪問某個對象監視器的時候,對象監視器會將這些線程存儲在不一樣的容器中。 Synchronized 核心組件 1) Wait Set:哪些調用wait 方法被阻塞的線程被放置在這裏; 2) Contention List:競爭隊列,全部請求鎖的線程首先被放在這個競爭隊列中; 3) Entry List:Contention List 中那些有資格成爲候選資源的線程被移動到Entry List 中; 4) OnDeck:任意時刻,最多隻有一個線程正在競爭鎖資源,該線程被成爲OnDeck; 5) Owner:當前已經獲取到所資源的線程被稱爲Owner; 6) !Owner:當前釋放鎖的線程。 Synchronized實現 1. JVM 每次從隊列的尾部取出一個數據用於鎖競爭候選者(OnDeck),可是併發狀況下,ContentionList 會被大量的併發線程進行CAS 訪問, 爲了下降對尾部元素的競爭,JVM會將一部分線程移動到EntryList 中做爲候選競爭線程。 2. Owner 線程會在unlock 時,將ContentionList 中的部分線程遷移到EntryList 中,並指定 EntryList 中的某個線程爲OnDeck 線程(通常是最早進去的那個線程)。 3. Owner 線程並不直接把鎖傳遞給OnDeck 線程,而是把鎖競爭的權利交給OnDeck,OnDeck 須要從新競爭鎖。這樣雖然犧牲了一些公平性, 可是能極大的提高系統的吞吐量,在JVM 中,也把這種選擇行爲稱之爲「競爭切換」。 4. OnDeck 線程獲取到鎖資源後會變爲Owner 線程,而沒有獲得鎖資源的仍然停留在EntryList中。若是Owner 線程被wait 方法阻塞, 則轉移到WaitSet 隊列中,直到某個時刻經過notify或者notifyAll 喚醒,會從新進去EntryList 中。 5. 處於ContentionList、EntryList、WaitSet 中的線程都處於阻塞狀態,該阻塞是由操做系統 來完成的(Linux 內核下采用pthread_mutex_lock 內核函數實現的)。 6. Synchronized 是非公平鎖。 Synchronized 在線程進入ContentionList 時,等待的線程會先嚐試自旋獲取鎖,若是獲取不到就進入ContentionList, 這明顯對於已經進入隊列的線程是不公平的,還有一個不公平的事情就是自旋獲取鎖的線程還可能直接搶佔OnDeck 線程的鎖資源。 參考:https://blog.csdn.net/zqz_zqz/article/details/70233767 7. 每一個對象都有個monitor 對象,加鎖就是在競爭monitor 對象,代碼塊加鎖是在先後分別加 上monitorenter 和monitorexit 指令來實現的,方法加鎖是經過一個標記位來判斷的 8. synchronized 是一個重量級操做,須要調用操做系統相關接口,性能是低效的,有可能給線 程加鎖消耗的時間比有用操做消耗的時間更多。 9. Java1.6,synchronized 進行了不少的優化,有適應自旋、鎖消除、鎖粗化、輕量級鎖及偏向 鎖等,效率有了本質上的提升。在以後推出的Java1.7 與1.8 中,均對該關鍵字的實現機理作了優化。引入了偏向鎖和輕量級鎖。都是在對象頭中有標記位,不須要通過操做系統加鎖。 10. 鎖能夠從偏向鎖升級到輕量級鎖,再升級到重量級鎖。這種升級過程叫作鎖膨脹; 11. JDK 1.6 中默認是開啓偏向鎖和輕量級鎖,能夠經過-XX:-UseBiasedLocking 來禁用偏向鎖。 五、ReentrantLock ReentantLock 繼承接口Lock 並實現了接口中定義的方法,他是一種可重入鎖,除了能完成synchronized 所能完成的全部工做外, 還提供了諸如可響應中斷鎖、可輪詢鎖請求、定時鎖等避免多線程死鎖的方法。 Lock 接口的主要方法 1. void lock(): 執行此方法時, 若是鎖處於空閒狀態, 當前線程將獲取到鎖. 相反, 若是鎖已經 被其餘線程持有, 將禁用當前線程, 直到當前線程獲取到鎖. 2. boolean tryLock():若是鎖可用, 則獲取鎖, 並當即返回true, 不然返回false. 該方法和 lock()的區別在於, tryLock()只是"試圖"獲取鎖, 若是鎖不可用, 不會致使當前線程被禁用,當前線程仍然繼續往下執行代碼. 而lock()方法則是必定要獲取到鎖, 若是鎖不可用, 就一直等待, 在未得到鎖以前,當前線程並不繼續向下執行. 3. void unlock():執行此方法時, 當前線程將釋放持有的鎖. 鎖只能由持有者釋放, 若是線程 並不持有鎖, 卻執行該方法, 可能致使異常的發生. 4. Condition newCondition():條件對象,獲取等待通知組件。該組件和當前的鎖綁定, 當前線程只有獲取了鎖,才能調用該組件的await()方法,而調用後,當前線程將縮放鎖。 5. getHoldCount() :查詢當前線程保持此鎖的次數,也就是執行此線程執行lock 方法的次數。 6. getQueueLength():返回正等待獲取此鎖的線程估計數,好比啓動10 個線程,1 個線程得到鎖,此時返回的是9 7. getWaitQueueLength:(Condition condition)返回等待與此鎖相關的給定條件的線程估計數。 好比10 個線程,用同一個condition 對象,而且此時這10 個線程都執行了condition 對象的await 方法,那麼此時執行此方法返回10 8. hasWaiters(Condition condition) : 查詢是否有線程等待與此鎖有關的給定條件(condition),對於指定contidion 對象,有多少線程執行了condition.await 方法 9. hasQueuedThread(Thread thread):查詢給定線程是否等待獲取此鎖 10. hasQueuedThreads():是否有線程等待此鎖 11. isFair():該鎖是否公平鎖 12. isHeldByCurrentThread(): 當前線程是否保持鎖鎖定,線程的執行lock 方法的先後分別是false 和true 13. isLock():此鎖是否有任意線程佔用 14. lockInterruptibly():若是當前線程未被中斷,獲取鎖 15. tryLock():嘗試得到鎖,僅在調用時鎖未被線程佔用,得到鎖 16. tryLock(long timeout TimeUnit unit):若是鎖在給定等待時間內沒有被另外一個線程保持,則獲取該鎖。 非公平鎖 JVM 按隨機、就近原則分配鎖的機制則稱爲不公平鎖,ReentrantLock 在構造函數中提供了是否公平鎖的初始化方式,默認爲非公平鎖。 非公平鎖實際執行的效率要遠遠超出公平鎖,除非程序有特殊須要,不然最經常使用非公平鎖的分配機制。 公平鎖 公平鎖指的是鎖的分配機制是公平的,一般先對鎖提出獲取請求的線程會先被分配到鎖,ReentrantLock 在構造函數中提供了是否公平鎖的初始化方式來定義公平鎖。 ReentrantLock 與synchronized 1. ReentrantLock 經過方法lock()與unlock()來進行加鎖與解鎖操做,與synchronized 會被JVM 自動解鎖機制不一樣,ReentrantLock 加鎖後須要手動進行解鎖。 爲了不程序出現異常而沒法正常解鎖的狀況,使用ReentrantLock 必須在finally 控制塊中進行解鎖操做。 2. ReentrantLock 相比synchronized 的優點是可中斷、公平鎖、多個鎖。這種狀況下須要 使用ReentrantLock。 ReentrantLock 實現: public class MyService { private Lock lock = new ReentrantLock(); //Lock lock=new ReentrantLock(true);//公平鎖 //Lock lock=new ReentrantLock(false);//非公平鎖 private Condition condition=lock.newCondition();//建立Condition public void testMethod() { try { lock.lock();//lock 加鎖 //1:wait 方法等待: //System.out.println("開始wait"); condition.await(); //經過建立Condition 對象來使線程wait,必須先執行lock.lock 方法得到鎖 //:2:signal 方法喚醒 condition.signal();//condition 對象的signal 方法能夠喚醒wait 線程 for (int i = 0; i < 5; i++) { System.out.println("ThreadName=" + Thread.currentThread().getName()+ (" " + (i + 1))); } } catch (InterruptedException e) { e.printStackTrace(); }finally{ lock.unlock(); } } } Condition 類和 Object 類鎖方法區別 1. Condition 類的awiat 方法和Object 類的wait 方法等效 2. Condition 類的signal 方法和Object 類的notify 方法等效 3. Condition 類的signalAll 方法和Object 類的notifyAll 方法等效 4. ReentrantLock 類能夠喚醒指定條件的線程,而object 的喚醒是隨機的 tryLock 和 lock 和 lockInterruptibly 的區別 1. tryLock 能得到鎖就返回true,不能就當即返回false,tryLock(long timeout,TimeUnitunit), 能夠增長時間限制,若是超過該時間段還沒得到鎖,返回false 2. lock 能得到鎖就返回true,不能的話一直等待得到鎖 3. lock 和lockInterruptibly,若是兩個線程分別執行這兩個方法,但此時中斷這兩個線程,lock 不會拋出異常,而lockInterruptibly 會拋出異常。 Semaphore 信號量 Semaphore 是一種基於計數的信號量。它能夠設定一個閾值,基於此,多個線程競爭獲取許可信號,作完本身的申請後歸還,超過閾值後, 線程申請許可信號將會被阻塞。Semaphore 能夠用來構建一些對象池,資源池之類的,好比數據庫鏈接池實現互斥鎖(計數器爲1) 咱們也能夠建立計數爲1 的Semaphore,將其做爲一種相似互斥鎖的機制,這也叫二元信號量,表示兩種互斥狀態。 代碼實現 它的用法以下: // 建立一個計數閾值爲5 的信號量對象 // 只能5 個線程同時訪問 Semaphore semp = new Semaphore(5); try { // 申請許可 semp.acquire(); try { // 業務邏輯 } catch (Exception e) { } finally { // 釋放許可 semp.release(); } } catch (InterruptedException e) { } Semaphore ReentrantLock Semaphore 基本能完成ReentrantLock 的全部工做,使用方法也與之相似,經過acquire()與release()方法來得到和釋放臨界資源。 經實測,Semaphone.acquire()方法默認爲可響應中斷鎖,與ReentrantLock.lockInterruptibly()做用效果一致,也就是說在等待臨界資源的過程當中能夠被 Thread.interrupt()方法中斷。 此外,Semaphore 也實現了可輪詢的鎖請求與定時鎖的功能,除了方法名tryAcquire 與tryLock不一樣,其使用方法與ReentrantLock 幾乎一致。 Semaphore 也提供了公平與非公平鎖的機制,也可在構造函數中進行設定。 Semaphore 的鎖釋放操做也由手動進行,所以與ReentrantLock 同樣,爲避免線程因拋出異常而 沒法正常釋放鎖的狀況發生,釋放鎖的操做也必須在finally 代碼塊中完成。 AtomicInteger 首先說明, 此處AtomicInteger,一個提供原子操做的Integer的類,常見的還有AtomicBoolean、AtomicInteger、AtomicLong、AtomicReference 等, 他們的實現原理相同,區別在與運算對象類型的不一樣。使人興奮地,還能夠經過AtomicReference<V>將一個對象的全部操做轉化成原子操做。 咱們知道,在多線程程序中,諸如++i 或 i++等運算不具備原子性,是不安全的線程操做之一。 一般咱們會使用synchronized 將該操做變成一個原子操做,但JVM 爲此類操做特地提供了一些同步類,使得使用更方便,且使程序運行效率變得更高。 經過相關資料顯示,一般AtomicInteger的性能是ReentantLock 的好幾倍。 可重入鎖(遞歸鎖) 本文裏面講的是廣義上的可重入鎖,而不是單指JAVA 下的ReentrantLock。可重入鎖,也叫作遞歸鎖,指的是同一線程外層函數得到鎖以後, 內層遞歸函數仍然有獲取該鎖的代碼,但不受影響。在JAVA 環境下 ReentrantLock 和synchronized 都是 可重入鎖。 公平鎖與非公平鎖 公平鎖(Fair) 加鎖前檢查是否有排隊等待的線程,優先排隊等待的線程,先來先得 非公平鎖(Nonfair) 加鎖時不考慮排隊等待問題,直接嘗試獲取鎖,獲取不到自動到隊尾等待 1. 非公平鎖性能比公平鎖高5~10 倍,由於公平鎖須要在多核的狀況下維護一個隊列 2. Java 中的synchronized 是非公平鎖,ReentrantLock 默認的lock()方法採用的是非公平鎖。 ReadWriteLock 讀寫鎖 爲了提升性能,Java 提供了讀寫鎖,在讀的地方使用讀鎖,在寫的地方使用寫鎖,靈活控制,若是沒有寫鎖的狀況下, 讀是無阻塞的,在必定程度上提升了程序的執行效率。讀寫鎖分爲讀鎖和寫鎖,多個讀鎖不互斥,讀鎖與寫鎖互斥,這是由jvm 本身控制的,你只要上好相應的鎖便可。 讀鎖 若是你的代碼只讀數據,能夠不少人同時讀,但不能同時寫,那就上讀鎖 寫鎖 若是你的代碼修改數據,只能有一我的在寫,且不能同時讀取,那就上寫鎖。總之,讀的時候上讀鎖,寫的時候上寫鎖! Java 中讀寫鎖有個接口java.util.concurrent.locks.ReadWriteLock,也有具體的實現ReentrantReadWriteLock。 共享鎖和獨佔鎖 java 併發包提供的加鎖模式分爲獨佔鎖和共享鎖。 獨佔鎖 獨佔鎖模式下,每次只能有一個線程能持有鎖,ReentrantLock 就是以獨佔方式實現的互斥鎖。 獨佔鎖是一種悲觀保守的加鎖策略,它避免了讀/讀衝突,若是某個只讀線程獲取鎖,則其餘讀線 程都只能等待,這種狀況下就限制了沒必要要的併發性,由於讀操做並不會影響數據的一致性。 共享鎖 共享鎖則容許多個線程同時獲取鎖,併發訪問共享資源,如:ReadWriteLock。共享鎖則是一種樂觀鎖, 它放寬了加鎖策略,容許多個執行讀操做的線程同時訪問共享資源。 1. AQS 的內部類Node 定義了兩個常量SHARED 和EXCLUSIVE,他們分別標識 AQS 隊列中等待線程的鎖獲取模式。 2. java 的併發包中提供了ReadWriteLock,讀-寫鎖。它容許一個資源能夠被多個讀操做訪問,或者被一個寫操做訪問,但二者不能同時進行。 重量級鎖(Mutex Lock) Synchronized 是經過對象內部的一個叫作監視器鎖(monitor)來實現的。可是監視器鎖本質又是依賴於底層的操做系統的Mutex Lock 來實現的。 而操做系統實現線程之間的切換這就須要從用戶態轉換到核心態,這個成本很是高,狀態之間的轉換須要相對比較長的時間,這就是爲何Synchronized 效率低的緣由。 所以,這種依賴於操做系統Mutex Lock 所實現的鎖咱們稱之爲「重量級鎖」。JDK 中對Synchronized 作的種種優化,其核心都是爲了減小這種重量級鎖的使用。 JDK1.6 之後,爲了減小得到鎖和釋放鎖所帶來的性能消耗,提升性能,引入了「輕量級鎖」和「偏向鎖」。 輕量級鎖 鎖的狀態總共有四種:無鎖狀態、偏向鎖、輕量級鎖和重量級鎖。 鎖升級 隨着鎖的競爭,鎖能夠從偏向鎖升級到輕量級鎖,再升級的重量級鎖(可是鎖的升級是單向的,也就是說只能從低到高升級,不會出現鎖的降級)。 「輕量級」是相對於使用操做系統互斥量來實現的傳統鎖而言的。可是,首先須要強調一點的是,輕量級鎖並非用來代替重量級鎖的, 它的本意是在沒有多線程競爭的前提下,減小傳統的重量級鎖使用產生的性能消耗。在解釋輕量級鎖的執行過程以前, 先明白一點,輕量級鎖所適應的場景是線程交替執行同步塊的狀況,若是存在同一時間訪問同一鎖的狀況,就會致使輕量級鎖膨脹爲重量級鎖。 偏向鎖 Hotspot 的做者通過以往的研究發現大多數狀況下鎖不只不存在多線程競爭,並且老是由同一線程屢次得到。 偏向鎖的目的是在某個線程得到鎖以後,消除這個線程鎖重入(CAS)的開銷,看起來讓這個線程獲得了偏護。 引入偏向鎖是爲了在無多線程競爭的狀況下儘可能減小沒必要要的輕量級鎖執行路徑,由於輕量級鎖的獲取及釋放依賴屢次CAS 原子指令, 而偏向鎖只須要在置換ThreadID 的時候依賴一次CAS 原子指令(因爲一旦出現多線程競爭的狀況就必須撤銷偏向鎖, 因此偏向鎖的撤銷操做的性能損耗必須小於節省下來的CAS 原子指令的性能消耗)。上面說過,輕量級鎖是爲了在線程交替執行同步塊時提升性能, 而偏向鎖則是在只有一個線程執行同步塊時進一步提升性能。 分段鎖 分段鎖也並不是一種實際的鎖,而是一種思想ConcurrentHashMap 是學習分段鎖的最好實踐 鎖優化 減小鎖持有時間 只用在有線程安全要求的程序上加鎖 減小鎖粒度 將大對象(這個對象可能會被不少線程訪問),拆成小對象,大大增長並行度,下降鎖競爭。 下降了鎖的競爭,偏向鎖,輕量級鎖成功率纔會提升。最最典型的減少鎖粒度的案例就是ConcurrentHashMap。 鎖分離 最多見的鎖分離就是讀寫鎖ReadWriteLock,根據功能進行分離成讀鎖和寫鎖,這樣讀讀不互斥,讀寫互斥,寫寫互斥,即保證了線程安全, 又提升了性能,具體也請查看[高併發Java五]JDK 併發包1。讀寫分離思想能夠延伸,只要操做互不影響,鎖就能夠分離。 好比LinkedBlockingQueue 從頭部取出,從尾部放數據 鎖粗化 一般狀況下,爲了保證多線程間的有效併發,會要求每一個線程持有鎖的時間儘可能短,即在使用完公共資源後,應該當即釋放鎖。 可是,凡事都有一個度,若是對同一個鎖不停的進行請求、同步和釋放,其自己也會消耗系統寶貴的資源,反而不利於性能的優化 。 鎖消除 鎖消除是在編譯器級別的事情。在即時編譯器時,若是發現不可能被共享的對象,則能夠消除這些對象的鎖操做,多數是由於程序員編碼不規範引發。 參考:https://www.jianshu.com/p/39628e1180a9
線程相關的基本方法有wait,notify,notifyAll,sleep,join,yield 等。 1.線程等待(wait) 調用該方法的線程進入WAITING 狀態,只有等待另外線程的通知或被中斷纔會返回,須要注意的是調用wait()方法後,會釋放對象的鎖。 所以,wait 方法通常用在同步方法或同步代碼塊中。 2. 線程睡眠(sleep) sleep 致使當前線程休眠,與wait 方法不一樣的是sleep 不會釋放當前佔有的鎖,sleep(long)會致使 線程進入TIMED-WATING 狀態,而wait()方法會致使當前線程進入WATING 狀態 3. 線程讓步(yield) yield 會使當前線程讓出CPU 執行時間片,與其餘線程一塊兒從新競爭CPU 時間片。通常狀況下,優先級高的線程有更大的可能性成功競爭獲得CPU 時間片, 但這又不是絕對的,有的操做系統對線程優先級並不敏感。 4. 線程中斷(interrupt) 中斷一個線程,其本意是給這個線程一個通知信號,會影響這個線程內部的一箇中斷標識位。這個線程自己並不會所以而改變狀態(如阻塞,終止等)。 1. 調用interrupt()方法並不會中斷一個正在運行的線程。也就是說處於Running 狀態的線程並不會由於被中斷而被終止,僅僅改變了內部維護的中斷標識位而已。 2. 若調用sleep()而使線程處於TIMED-WATING 狀態,這時調用interrupt()方法,會拋出InterruptedException,從而使線程提早結束TIMED-WATING 狀態。 3. 許多聲明拋出InterruptedException 的方法(如Thread.sleep(long mills 方法)),拋出異常前,都會清除中斷標識位, 因此拋出異常後,調用isInterrupted()方法將會返回false。 4. 中斷狀態是線程固有的一個標識位,能夠經過此標識位安全的終止線程。好比,你想終止一個線程thread 的時候,能夠調用thread.interrupt()方法, 在線程的run 方法內部能夠根據thread.isInterrupted()的值來優雅的終止線程。 5. Join 等待其餘線程終止 join() 方法,等待其餘線程終止,在當前線程中調用一個線程的 join() 方法,則當前線程轉爲阻塞狀態, 回到另外一個線程結束,當前線程再由阻塞狀態變爲就緒狀態,等待 cpu 的寵幸。 6. 爲何要用 join()方法? 不少狀況下,主線程生成並啓動了子線程,須要用到子線程返回的結果,也就是須要主線程須要在子線程結束後再結束,這時候就要用到 join() 方法。 System.out.println(Thread.currentThread().getName() + "線程運行開始!"); Thread6 thread1 = new Thread6(); thread1.setName("線程B"); thread1.join(); System.out.println("這時thread1 執行完畢以後才能執行主線程"); 7. 線程喚醒(notify) Object 類中的 notify() 方法,喚醒在此對象監視器上等待的單個線程,若是全部線程都在此對象上等待,則會選擇喚醒其中一個線程, 選擇是任意的,並在對實現作出決定時發生,線程經過調用其中一個 wait() 方法,在對象的監視器上等待,直到當前的線程放棄此對象上的鎖定, 才能繼續執行被喚醒的線程,被喚醒的線程將以常規方式與在該對象上主動同步的其餘全部線程進行競爭。相似的方法還有 notifyAll() , 喚醒再次監視器上等待的全部線程。 8. 其餘方式 1. sleep():強迫一個線程睡眠N毫秒。 2. isAlive(): 判斷一個線程是否存活。 3. join(): 等待線程終止。 4. activeCount(): 程序中活躍的線程數。 5. enumerate(): 枚舉程序中的線程。 6. currentThread(): 獲得當前線程。 7. isDaemon(): 一個線程是否爲守護線程。 8. setDaemon(): 設置一個線程爲守護線程。(用戶線程和守護線程的區別在於,是否等待主線程依賴於主線程結束而結束) 9. setName(): 爲線程設置一個名稱。 10. wait(): 強迫一個線程等待。 11. notify(): 通知一個線程繼續運行。 12. setPriority(): 設置一個線程的優先級。 13. getPriority()::得到一個線程的優先級。 查看JAVA核心面試知識整理-最全@www.java1234.com.pdf
CountDownLantch
CyclicBarrier
概念 若是某個方法不能按照正常的途徑完成任務,就能夠經過另外一種路徑退出方法。在這種狀況下會拋出一個封裝了錯誤信息的對象。 此時,這個方法會馬上退出同時不返回任何值。另外,調用這個方法的其餘代碼也沒法繼續執行,異常處理機制會將代碼執行交給異常處理器。
異常分類 Throwable 是 Java 語言中全部錯誤或異常的超類。下一層分爲Error 和Exception 1. Error 類是指java 運行時系統的內部錯誤和資源耗盡錯誤。應用程序不會拋出該類對象。若是 出現了這樣的錯誤,除了告知用戶,剩下的就是盡力使程序安全的終止。 Exception(RuntimeException、CheckedException) 2. Exception 又有兩個分支, 一個是運行時異常RuntimeException , 一個是CheckedException。 RuntimeException 如: NullPointerException 、ClassCastException ; 一個是檢查異常CheckedException,如I/O 錯誤致使的IOException、SQLException。 RuntimeException 是那些可能在 Java 虛擬機正常運行期間拋出的異常的超類。 若是出現RuntimeException,那麼必定是程序員的錯誤. 運行時異常: 都是RuntimeException類及其子類異常: IndexOutOfBoundsException 索引越界異常 ArithmeticException:數學計算異常 NullPointerException:空指針異常 ArrayOutOfBoundsException:數組索引越界異常 ClassNotFoundException:類文件未找到異常 ClassCastException:造型異常(類型轉換異常) 檢查異常CheckedException:通常是外部錯誤,這種異常都發生在編譯階段,Java 編譯器會強制程序去捕獲此類異常, 即會出現要求你把這段可能出現異常的程序進行try catch,該類異常通常包括幾個方面: 1. 試圖在文件尾部讀取數據 2. 試圖打開一個錯誤格式的URL 3. 試圖根據給定的字符串查找class 對象,而這個字符串表示的類並不存在
異常的處理方式 遇到問題不進行具體處理,而是繼續拋給調用者(throw,throws)拋出異常有三種形式,一是throw,一個throws,還有一種系統自動拋異常。 public static void main(String[] args) { String s = "abc"; if(s.equals("abc")) { throw new NumberFormatException(); } else { System.out.println(s); } } int div(int a,int b) throws Exception{ return a/b; } try catch 捕獲異常針對性處理方式
位置不一樣 1. throws 用在函數上,後面跟的是異常類,能夠跟多個;而throw 用在函數內,後面跟的是異常對象。 功能不一樣 2. throws 用來聲明異常,讓調用者只知道該功能可能出現的問題,能夠給出預先的處理方式;throw 拋出具體的問題對象, 執行到throw,功能就已經結束了,跳轉到調用者,並將具體的問題對象拋給調用者。也就是說throw 語句獨立存在時,下面不要定義其餘語句,由於執行不到。 3. throws 表示出現異常的一種可能性,並不必定會發生這些異常;throw 則是拋出了異常,執行throw 則必定拋出了某種異常對象。 4. 二者都是消極處理異常的方式,只是拋出或者可能拋出異常,可是不會由函數去處理異常,真正的處理異常由函數的上層調用處理。
1. 動態語言 動態語言,是指程序在運行時能夠改變其結構:新的函數能夠引進,已有的函數能夠被刪除等結構上的變化。 好比常見的JavaScript 就是動態語言,除此以外Ruby,Python 等也屬於動態語言,而C、C++則不屬於動態語言。從反射角度說JAVA 屬於半動態語言。 2. 反射機制概念 (運行狀態中知道類全部的屬性和方法) 在Java 中的反射機制是指在運行狀態中,對於任意一個類都可以知道這個類全部的屬性和方法;而且對於任意一個對象, 都可以調用它的任意一個方法;這種動態獲取信息以及動態調用對象方法的功能成爲Java 語言的反射機制。 3. 反射的應用場合 編譯時類型和運行時類型 在Java 程序中許多對象在運行是都會出現兩種類型:編譯時類型和運行時類型。 編譯時的類型由聲明對象時實用的類型來決定,運行時的類型由實際賦值給對象的類型決定 。如: Person p=new Student(); 其中編譯時類型爲Person,運行時類型爲Student。 編譯時類型沒法獲取具體方法 程序在運行時還可能接收到外部傳入的對象,該對象的編譯時類型爲Object,可是程序有須要調用該對象的運行時類型的方法。 爲了解決這些問題,程序須要在運行時發現對象和類的真實信息。然而,若是編譯時根本沒法預知該對象和類屬於哪些類, 程序只能依靠運行時信息來發現該對象和類的真實信息,此時就必須使用到反射了。 4.Java 反射API 反射 API用來生成JVM中的類、接口或者對象的信息。 1. Class 類:反射的核心類,能夠獲取類的屬性,方法等信息。 2. Field 類:Java.lang.reflect 包中的類,表示類的成員變量,能夠用來獲取和設置類之中的屬性值。 3. Method 類: Java.lang.reflec 包中的類,表示類的方法,它能夠用來獲取類中的方法信息或者執行方法。 4. Constructor 類: Java.lang.reflec 包中的類,表示類的構造方法。 5. 反射使用步驟(獲取Class 對象、調用對象方法) 1. 獲取想要操做的類的Class 對象,他是反射的核心,經過Class 對象咱們能夠任意調用類的方法。 2. 調用Class 類中的方法,既就是反射的使用階段。 3. 使用反射API 來操做這些信息。 6. 獲取Class 對象的3 種方法 調用某個對象的getClass()方法 Person p=new Person(); Class clazz=p.getClass(); 調用某個類的 class屬性類型獲取該類對應的Class對象 Class clazz=Person.class; 使用Class類中的forName()靜態方法(最安全/性能最好) Class clazz=Class.forName("類的全路徑"); (最經常使用) 當咱們得到了想要操做的類的Class 對象後,能夠經過Class 類中的方法獲取並查看該類中的方法和屬性。 //獲取Person 類的Class 對象 Class clazz=Class.forName("reflection.Person"); //獲取Person 類的全部方法信息 Method[] method=clazz.getDeclaredMethods(); for(Method m:method){ System.out.println(m.toString()); } //獲取Person 類的全部成員屬性信息 Field[] field=clazz.getDeclaredFields(); for(Field f:field){ System.out.println(f.toString()); } //獲取Person 類的全部構造方法信息 Constructor[] constructor=clazz.getDeclaredConstructors(); for(Constructor c:constructor){ System.out.println(c.toString()); } 7. 建立對象的兩種方法 Class對象的 newInstance() 1. 使用Class 對象的newInstance()方法來建立該Class 對象對應類的實例,可是這種方法要求該Class 對象對應的類有默認的空構造器。 調用Constructor對象的 newInstance() 2. 先使用Class 對象獲取指定的Constructor 對象,再調用Constructor 對象的newInstance()方法來建立 Class 對象對應類的實例, 經過這種方法能夠選定構造方法建立實例。 //獲取Person 類的Class 對象 Class clazz=Class.forName("reflection.Person"); //使用.newInstane 方法建立對象 Person p=(Person) clazz.newInstance(); //獲取構造方法並建立對象 Constructor c=clazz.getDeclaredConstructor(String.class,String.class,int.class); //建立對象並設置屬性 Person p1=(Person) c.newInstance("李四","男",20);
一、概念 Annotation(註解)是Java 提供的一種對元程序中元素關聯信息和元數據(metadata)的途徑和方法。 Annatation(註解)是一個接口,程序能夠經過反射來獲取指定程序中元素的Annotation對象,而後經過該Annotation 對象來獲取註解中的元數據信息。 二、4種標準元註解 元註解的做用是負責註解其餘註解。 Java5.0 定義了4 個標準的meta-annotation 類型,它們被用來提供對其它 annotation 類型做說明。 1.@Target 修飾的對象範圍 @Target 說明了Annotation 所修飾的對象範圍: Annotation 可被用於 packages、types(類、接口、枚舉、Annotation 類型)、類型成員(方法、構造方法、成員變量、枚舉值)、 方法參數和本地變量(如循環變量、catch 參數)。在Annotation 類型的聲明中使用了target 可更加明晰其修飾的目標 2.@Retention 定義 被保留的時間長短 Retention 定義了該Annotation 被保留的時間長短:表示須要在什麼級別保存註解信息,用於描述註解的生命週期 (即:被描述的註解在什麼範圍內有效),取值(RetentionPoicy)由: SOURCE:在源文件中有效(即源文件保留) CLASS:在class 文件中有效(即class 保留) RUNTIME:在運行時有效(即運行時保留) 3.@Documented 描述-javadoc @ Documented 用於描述其它類型的annotation 應該被做爲被標註的程序成員的公共API,所以能夠被例如javadoc 此類的工具文檔化。 4.@Inherited 闡述了某個被標註的類型是被繼承的 @Inherited 元註解是一個標記註解,@Inherited 闡述了某個被標註的類型是被繼承的。 若是一個使用了@Inherited 修飾的annotation 類型被用於一個class,則這個annotation 將被用於該class 的子類。 3. 註解處理器 若是沒有用來讀取註解的方法和工做,那麼註解也就不會比註釋更有用處了。使用註解的過程當中,很重要的一部分就是建立於使用註解處理器。 Java SE5 擴展了反射機制的API,以幫助程序員快速的構造自定義註解處理器。下面實現一個註解處理器。 /1:*** 定義註解*/ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface FruitProvider { /**供應商編號*/ public int id() default -1; /*** 供應商名稱*/ public String name() default ""; /** * 供應商地址*/ public String address() default ""; } //2:註解使用 public class Apple { @FruitProvider(id = 1, name = "陝西紅富士集團", address = "陝西省西安市延安路") private String appleProvider; public void setAppleProvider(String appleProvider) { this.appleProvider = appleProvider; } public String getAppleProvider() { return appleProvider; } } /3:*********** 註解處理器 ***************/ public class FruitInfoUtil { public static void getFruitInfo(Class<?> clazz) { String strFruitProvicer = "供應商信息:"; Field[] fields = clazz.getDeclaredFields();//經過反射獲取處理註解 for (Field field : fields) { if (field.isAnnotationPresent(FruitProvider.class)) { FruitProvider fruitProvider = (FruitProvider)field.getAnnotation(FruitProvider.class); //註解信息的處理地方 strFruitProvicer = " 供應商編號:" + fruitProvider.id() + " 供應商名稱:" + fruitProvider.name() + " 供應商地址:"+ fruitProvider.address(); System.out.println(strFruitProvicer); } } } } public class FruitRun { public static void main(String[] args) { FruitInfoUtil.getFruitInfo(Apple.class); /***********輸出結果***************/ // 供應商編號:1 供應商名稱:陝西紅富士集團 供應商地址:陝西省西安市延 } }
Java 類中不只能夠定義變量和方法,還能夠定義類,這樣定義在類內部的類就被稱爲內部類。根據定義的方式不一樣,內部類分爲靜態內部類,成員內部類,局部內部類,匿名內部類四種。 1.靜態內部類 定義在類內部的靜態類,就是靜態內部類。 public class Out { private static int a; private int b; public static class Inner { public void print() { System.out.println(a); } } } 1. 靜態內部類能夠訪問外部類全部的靜態變量和方法,即便是private 的也同樣。 2. 靜態內部類和通常類一致,能夠定義靜態變量、方法,構造方法等。 3. 其它類使用靜態內部類須要使用「外部類.靜態內部類」方式,以下所示: Out.Inner inner = new Out.Inner();inner.print(); 4. Java集合類HashMap內部就有一個靜態內部類Entry。Entry是HashMap存放元素的抽象,HashMap 內部維護Entry 數組用了存放元素, 可是Entry 對使用者是透明的。像這種和外部類關係密切的,且不依賴外部類實例的,均可以使用靜態內部類。 2.成員內部類 定義在類內部的非靜態類,就是成員內部類。成員內部類不能定義靜態方法和變量(final 修飾的除外)。 這是由於成員內部類是非靜態的,類初始化的時候先初始化靜態成員,若是容許成員內部類定義靜態變量,那麼成員內部類的靜態變量初始化順序是有歧義的。 public class Out { private static int a; private int b; public class Inner { public void print() { System.out.println(a); System.out.println(b); } } } 3.局部內部類(定義在方法中的類) 定義在方法中的類,就是局部類。若是一個類只在某個方法中使用,則能夠考慮使用局部類。 public class Out { private static int a; private int b; public void test(final int c) { final int d = 1; class Inner { public void print() { System.out.println(c); } } } } 4. 匿名內部類(要繼承一個父類或者實現一個接口、直接使用new來生成一個對象的引用) 匿名內部類咱們必需要繼承一個父類或者實現一個接口,固然也僅能只繼承一個父類或者實現一個接口。 同時它也是沒有class 關鍵字,這是由於匿名內部類是直接使用new 來生成一個對象的引用。 public abstract class Bird { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public abstract int fly(); } public class Test { public void test(Bird bird){ System.out.println(bird.getName() + "可以飛 " + bird.fly() + "米"); } public static void main(String[] args) { Test test = new Test(); test.test(new Bird() { public int fly() { return 10000; } public String getName() { return "大雁"; } }); } }
泛型提供了編譯時類型安全檢測機制,該機制容許程序員在編譯時檢測到非法的類型。 泛型的本質是參數化類型,也就是說所操做的數據類型被指定爲一個參數。 好比咱們要寫一個排序方法,可以對整型數組、字符串數組甚至其餘任何類型的數組進行排序,咱們就能夠使用Java 泛型。 1. 泛型方法(<E>) 你能夠寫一個泛型方法,該方法在調用時能夠接收不一樣類型的參數。根據傳遞給泛型方法的參數類型,編譯器適當地處理每個方法調用。 // 泛型方法 printArray public static < E > void printArray( E[] inputArray ){ for ( E element : inputArray ){ System.out.printf( "%s ", element ); } } 1. <? extends T>表示該通配符所表明的類型是T 類型的子類。 2. <? super T>表示該通配符所表明的類型是T 類型的父類。 2. 泛型類<T> 泛型類的聲明和非泛型類的聲明相似,除了在類名後面添加了類型參數聲明部分。 和泛型方法同樣,泛型類的類型參數聲明部分也包含一個或多個類型參數,參數間用逗號隔開。 一個泛型參數,也被稱爲一個類型變量,是用於指定一個泛型類型名稱的標識符。由於他們接受一個或多個參數,這些類被稱爲參數化的類或參數化的類型。 public class Box<T> { private T t; public void add(T t) { this.t = t; } public T get() { return t; } } 3. 類型通配符? 類型通配符通常是使用? 代替具體的類型參數。例如 List<?> 在邏輯上是List<String>,List<Integer> 等全部List<具體類型實參>的父類。 4. 類型擦除 Java 中的泛型基本上都是在編譯器這個層次來實現的。在生成的Java 字節代碼中是不包含泛型中的類型信息的。 使用泛型的時候加上的類型參數,會被編譯器在編譯的時候去掉。這個過程就稱爲類型擦除。如在代碼中定義的List<Object>和List<String>等類型, 在編譯以後都會變成List。JVM看到的只是List,而由泛型附加的類型信息對JVM來講是不可見的。 類型擦除的基本過程也比較簡單,首先是找到用來替換類型參數的具體類。這個具體類通常 是Object。若是指定了類型參數的上界的話,則使用這個上界。把代碼中的類型參數都替換成具體的類。
泛型的使用 * 1.jdk 5.0新增的特性 * 2.在集合中使用泛型: * 總結:1.集合接口或集合類在jdk5.0時都修改成帶泛型的結構。 * 2.在實例化集合類時,能夠指明具體的泛型類型 * 3.指明完之後,在集合類或接口中凡是定義類或接口時,內部結構(好比:方法、構造器、屬性等)使用到類的泛型的位置,都指定爲實例化的泛型。 * 好比:add(E e) --->實例化之後:add(Integer e) * 4.注意點:泛型的類型必須是類,不能是基本數據類型。須要用到基本數據類型的位置,必須是包裝類 * 5.若是實例化時,沒有指明泛型的類型。默認類型爲java.lang.Object類型。 * 3.如何自定義泛型結構:泛型類、泛型接口;泛型方法 * 泛型方法:在方法中出現了泛型的結構,泛型參數與類的泛型參數沒有任何關係。 泛型方法所屬的類是否是泛型類都沒有關係。 能夠聲明爲靜態的。緣由:泛型參數是在調用方法時肯定的。並不是在實例化類時肯定。
保存(持久化)對象及其狀態到內存或者磁盤 Java 平臺容許咱們在內存中建立可複用的Java 對象,但通常狀況下,只有當JVM 處於運行時,這些對象纔可能存在, 即,這些對象的生命週期不會比JVM 的生命週期更長。但在現實應用中,就可能要求在JVM中止運行以後可以保存(持久化)指定的對象,並在未來從新讀取被保存的對象。 Java 對象序列化就可以幫助咱們實現該功能。 序列化對象以字節數組保持-靜態成員不保存 使用Java 對象序列化,在保存對象時,會把其狀態保存爲一組字節,在將來,再將這些字節組裝成對象。 必須注意地是,對象序列化保存的是對象的」狀態」,即它的成員變量。由此可知,對象序列化不會關注類中的靜態變量。 序列化用戶遠程對象傳輸 除了在持久化對象時會用到對象序列化以外,當使用RMI(遠程方法調用),或在網絡中傳遞對象時,都會用到對象序列化。 Java 序列化API 爲處理對象序列化提供了一個標準機制,該API 簡單易用。 Serializable 實現序列化 在Java 中,只要一個類實現了java.io.Serializable 接口,那麼它就能夠被序列化。 ObjectOutputStream 和 ObjectInputStream 對對象進行序列化及反序列化 經過ObjectOutputStream 和ObjectInputStream 對對象進行序列化及反序列化。 writeObject 和 readObject 自定義序列化策略 在類中增長writeObject 和 readObject 方法能夠實現自定義序列化策略。 序列化ID 虛擬機是否容許反序列化,不只取決於類路徑和功能代碼是否一致,一個很是重要的一點是兩個類的序列化 ID 是否一致(就是 private static final long serialVersionUID) 序列化並不保存靜態變量 序列化子父類說明 要想將父類對象也序列化,就須要讓父類也實現Serializable 接口。 Transient 關鍵字阻止該變量被序列化到文件中 1. 在變量聲明前加上Transient 關鍵字,能夠阻止該變量被序列化到文件中, 在被反序列化後,transient 變量的值被設爲初始值,如 int 型的是 0,對象型的是 null。 2. 服務器端給客戶端發送序列化對象數據,對象中有一些數據是敏感的,好比密碼字符串等,但願對該密碼字段在序列化時, 進行加密,而客戶端若是擁有解密的密鑰,只有在客戶端進行反序列化時,才能夠對密碼進行讀取,這樣能夠必定程度保證序列化對象的數據安全。
將一個對象的引用複製給另一個對象,一共有三種方式。 第一種方式是直接賦值,第二種方式是淺拷貝,第三種是深拷貝。因此你們知道了哈,這三種概念實際上都是爲了拷貝對象。 1. 直接賦值複製 直接賦值。在Java 中,A a1 = a2,咱們須要理解的是這實際上覆制的是引用,也就是說a1 和a2 指向的是同一個對象。 所以,當a1 變化的時候,a2 裏面的成員變量也會跟着變化。 2. 淺複製(複製引用但不復制引用的對象) 建立一個新對象,而後將當前對象的非靜態字段複製到該新對象,若是字段是值類型的,那麼對該字段執行復制; 若是該字段是引用類型的話,則複製引用但不復制引用的對象。所以,原始對象及其副本引用同一個對象。 class Resume implements Cloneable{ public Object clone() { try { return (Resume)super.clone(); } catch (Exception e) { e.printStackTrace(); return null; } } } 3. 深複製(複製對象和其應用對象) 深拷貝不只複製對象自己,並且複製對象包含的引用指向的全部對象。 class Student implements Cloneable { String name; int age; Professor p; Student(String name, int age, Professor p) { this.name = name; this.age = age; this.p = p; } public Object clone() { Student o = null; try { o = (Student) super.clone(); } catch (CloneNotSupportedException e) { System.out.println(e.toString()); } o.p = (Professor) p.clone(); return o; } } 4. 序列化(深clone 一中實現) 在Java 語言裏深複製一個對象,經常能夠先使對象實現Serializable 接口, 而後把對象(實際上只是對象的一個拷貝)寫到一個流裏,再從流裏讀出來,即可以重建對象。
* 方法的參數傳遞機制: * 一、形參是基本數據類型 * 傳遞數據值 * 二、實參是引用數據類型 * 傳遞地址值 * 特殊的類型:String、包裝類等對象不可變性 * 考點 * 一、就近原則 * 二、變量的分類 * 成員變量:類變量、實例變量 * 局部變量 * 三、非靜態代碼塊的執行:每次建立實例對象都會執行 * 四、方法的調用規則:調用一次執行一次 * * 局部變量與成員變量的區別 * 一、聲明的位置 * 局部變量:方法體{}中,形參,代碼塊{}中 * 成員變量:類中方法外 * 類變量:有static修飾 * 實例變量:沒有static修飾 * 二、修飾符 * 局部變量:final * 成員變量:public、protected、private、final、static、volatile、transient * 三、值存儲的位置 * 局部變量:棧 * 實例變量:堆 * 類變量:方法區 * 四、做用域 * 局部變量:從聲明處開始,到所屬的}結束 * 實例變量:在當前類中"this."(有時this.能夠缺省),在其餘類中"對象名."訪問 * 類變量:在當前類中"類名."(有時類名.能夠省略),在其餘類中"類名."或"對象名."訪問 * 五、生命週期 * 局部變量:每個線程,每一次調用執行都是新的生命週期 * 實例變量:隨着對象的建立而初始化,隨着對象的被回收而消亡,每個對象的實例變量是獨立的 * 類變量:隨着類的初始化而初始化,隨着類的卸載而消亡,該類的全部對象的類變量是共享的。
valueOf(); lastIndexOf();join(,);isEmpty();indexOf() toString();hashCode();getChars();getBytes();equals() trim();contains();concat();cahrAt() substring(); split(); replace(); length();
/** * 餓漢式:直接建立對象,不存在線程安全問題 * 1.直接實例化餓漢式(簡潔直觀) * @author Administrator * */ public class Singleton1 { public static final Singleton1 INSTANCE = new Singleton1(); private Singleton1(){ } } /* * 一、構造器私有化 * 二、用一個靜態變量保存這個惟一的實例 * 三、提供一個靜態方法,獲取這個實例對象 */ public class Singleton4 { private static Singleton4 instance; private Singleton4(){ } public static Singleton4 getInstance(){ if(instance == null){ synchronized (Singleton4.class) { if(instance ==null){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } instance = new Singleton4(); } } } return instance; } }
推薦視頻地址:https://www.bilibili.com/video/BV1nx411g7ja?from=search&seid=15231419141912161282
IoC
<bean id="" class=""></bean>
DI
<bean id="" class=""> <property name="" ref=""></property> </bean>
xml配置
實例化方式
3種實例化方式: 一、默認構造 <bean id="" class=""> 二、靜態工廠 <bean id="" class="" factory-method="靜態方法"></bean> 三、實例工廠 <bean id = "工廠實例id" class=""></bean> <bean id ="" class="" factory-bean="工廠實例id" factory-method="普通方法"></bean>
BeanFactory 和 FactoryBean 對比?
BeanFactory:工廠,用於生成任意bean。 FactoryBean:特殊bean,用於生成另外一個特定的bean。例如:ProxyFactoryBean ,此工廠bean用於生產代理。<bean id="" class="....ProxyFactoryBean"> 得到代理對象實例。AOP使用
做用域
<bean id="" class="" scope=""></bean> 單例:singleton:默認值。當IOC容器-建立就會建立bean的實例,並且是單例的,每次獲得的都是同一個 多例:prototype:原型的。當IOC容器-建立再也不實例化該bean,每次調用getBean方法時再實例化該bean request:每次請求實例化一個bean session:在一次會話中共享一個bean
生命週期
<bean id="" class="" init-method="初始化方法名稱" destroy-method="銷燬的方法名稱"></bean> 後處理bean: BeanPostProcessor 前方法:postProcessBeforeInitialization 後方法:postProcessAfterInitialization 將後處理的實現類註冊給spring:<bean class="MyBeanPostProcessor"></bean>
屬性注入:構造方法、setter、、集合
依賴注入方式: 手動注入: 基於xml裝配:構造方法:<constructor-arg></constructor-arg> name value/ref; index type; setter方法: <bean> <property name="" value=""> value=""等同於<value>1234</value> </property> <property name="" ref="另外一個bean"> ref="另外一個bean"等同於<ref bean="id"></ref> </property> </bean> 集合: <bean id="" class=""> <property name=""></property> </bean> 基於註解裝配: 1.@Component取代<bean class=""> @Component("id") 取代 <bean id="" class=""> 2. web層:@Control 取代<bean class=""> service層:@Service dao層:@Repository 3. 普通值:@Value("") 引用值: 方式1:按照【類型】注入 @Autowired 方式2:按照【名稱】注入1 @Autowired @Qualifier("名稱") 方式3:按照【名稱】注入2 @Resource("名稱") 4.生命週期 初始化:@PostConstruct 銷燬:@PreDestroy 5.做用域 @Scope("prototype") 多例 <!-- 組件掃描,掃描含有註解的類 --> <context:component-scan base-package=""></context:component-scan> 自動注入: @Autowired 默認不生效。爲了生效,須要在xml配置:<context:annotation-config> 總結: 註解1:<context:component-scan base-package=" "> 註解2:<context:annotation-config> 1.通常狀況兩個註解不一塊兒使用。 2.「註解1」掃描含有註解(@Component 等)類,注入註解自動生效。 「註解2」只在xml和註解(注入)混合使用時,使注入註解生效。
集合的注入都是給<property>添加子標籤 數組:<array> List:<list> Set:<set> Map:<map> ,map存放k/v 鍵值對,使用<entry>描述 Properties:<props> <prop key=""></prop> 【】 普通數據:<value> 引用數據:<ref> <bean id="" class=""> <property name=""> <array> <value>1</value> <value>2</value> </array> </property> <property name="mapData"> <map> <entry key="jack" value="傑克"></entry> <entry> <key><value>rose</value></key> <value>肉絲</value> </entry> </map> </property> </bean> p命名空間:簡化<property> <bean p:屬性名="普通值" p:屬性名-ref="引用值"> 注意聲明命名空間 SpEL:<property name="" value="#{表達式}"> #{123} #{'abc'} #{beanId.propName?.methodName()} #{T(類).靜態方法|字段}
aop底層將採用代理機制進行實現。 接口 + 實現類:spring採用 jdk 的動態代理Proxy。 有接口 實現類:spring 採用 cglib字節碼加強。 無接口
1. target目標類:須要被代理的類。例如:UserService 2. Joinpoint鏈接點:所謂鏈接點是指那些可能被攔截到的方法。例如:全部的方法 3. PointCut切入點:已經被加強的鏈接點。例如:addUser() 4. advice通知/加強,加強代碼。例如:after、before 5. Weaving織入:是指把加強advice應用到目標對象target來建立新的代理對象proxy的過程. 6. proxy代理類 7. Aspect切面:是切入點pointcut和通知advice的結合 一個線是一個特殊的面。 一個切入點和一個通知,組成成一個特殊的面。
jdk動態代理:Proxy.newProxyInstance 參數1:loader ,類加載器 參數2:Class[] interfaces 代理類須要實現的全部接口 參數3:InvocationHandler 處理類,接口,必須進行實現類,通常採用匿名內部 提供 invoke 方法,代理類的每個方法執行時,都將調用一次invoke 參數31:Object proxy :代理對象 參數32:Method method : 代理對象當前執行的方法的描述對象(反射) 參數33:Object[] args :方法實際參數
CGLIB字節碼加強 核心:hibernate-distribution-3.6.10.Final\lib\bytecode\cglib\cglib-2.2.jar 依賴:struts-2.3.15.3\apps\struts2-blank\WEB-INF\lib\asm-3.3.jar spring-core..jar 已經整合以上兩個內容 代理類 ,採用cglib,底層建立目標類的子類 1 核心類:Enhancer enhancer = new Enhancer(); 2 肯定父類 enhancer.setSuperclass(userService.getClass()); 3 設置回調函數 ,enhancer.setCallback(new MethodInterceptor() MethodInterceptor接口 等效 jdk InvocationHandler接口 intercept() 等效 jdk invoke() 參數一、參數二、參數3:以invoke同樣 ;參數4:methodProxy 方法的代理 執行代理類的父類methodProxy.invokeSuper(proxy, args) 4 enhance.creat()。
public class MyBeanFactory { public static UserService createService(){ //1 目標類 final UserService userService = new UserServiceImpl(); //2切面類 final MyAspect myAspect = new MyAspect(); /* 3 代理類:將目標類(切入點)和 切面類(通知) 結合 --> 切面 * Proxy.newProxyInstance * 參數1:loader ,類加載器,動態代理類 運行時建立,任何類都須要類加載器將其加載到內存。 * 通常狀況:當前類.class.getClassLoader(); * 目標類實例.getClass().get... * 參數2:Class[] interfaces 代理類須要實現的全部接口 * 方式1:目標類實例.getClass().getInterfaces() ;注意:只能得到本身接口,不能得到父元素接口 * 方式2:new Class[]{UserService.class} * 例如:jdbc 驅動 --> DriverManager 得到接口 Connection * 參數3:InvocationHandler 處理類,接口,必須進行實現類,通常採用匿名內部 * 提供 invoke 方法,代理類的每個方法執行時,都將調用一次invoke * 參數31:Object proxy :代理對象 * 參數32:Method method : 代理對象當前執行的方法的描述對象(反射) * 執行方法名:method.getName() * 執行方法:method.invoke(對象,實際參數) * 參數33:Object[] args :方法實際參數 * */ UserService proxService = (UserService)Proxy.newProxyInstance( MyBeanFactory.class.getClassLoader(), userService.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //前執行 myAspect.before(); //執行目標類的方法 Object obj = method.invoke(userService, args); //後執行 myAspect.after(); return obj; } }); return proxService; } }
public class MyBeanFactory { public static UserServiceImpl createService(){ //1 目標類 final UserServiceImpl userService = new UserServiceImpl(); //2切面類 final MyAspect myAspect = new MyAspect(); // 3.代理類 ,採用cglib,底層建立目標類的子類 //3.1 核心類 Enhancer enhancer = new Enhancer(); //3.2 肯定父類 enhancer.setSuperclass(userService.getClass()); /* 3.3 設置回調函數 , MethodInterceptor接口 等效 jdk InvocationHandler接口 * intercept() 等效 jdk invoke() * 參數一、參數二、參數3:以invoke同樣 * 參數4:methodProxy 方法的代理 */ enhancer.setCallback(new MethodInterceptor(){ @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { //前 myAspect.before(); //執行目標類的方法 Object obj = method.invoke(userService, args); // * 執行代理類的父類 ,執行目標類 (目標類和代理類 父子關係) methodProxy.invokeSuper(proxy, args); //後 myAspect.after(); return obj; } }); //3.4 建立代理 UserServiceImpl proxService = (UserServiceImpl) enhancer.create(); return proxService; } }
讓spring 建立代理對象,從spring容器中手動的獲取代理對象 核心:4+1 和 AOP:AOP聯盟(規範)、spring-aop (實現) 切面類中肯定通知 public class MyAspect implements MethodInterceptor環繞通知 spring配置:ProxyFactoryBean interfaces : 肯定接口們 經過<array>能夠設置多個值 target :肯定目標類 interceptorNames : 通知 切面類的名稱,類型String[],若是設置一個值 value="" optimize :強制使用cglib
![img](D:\Program Files\Typora\my_typora\image\java核心面試整理\clip_image001.png)
spring配置(半自動)
目標類 public interface UserService { public void addUser(); public void updateUser(); public void deleteUser(); } 10.4.2 切面類 /** * 切面類中肯定通知,須要實現不一樣接口,接口就是規範,從而就肯定方法名稱。 * * 採用「環繞通知」 MethodInterceptor */ public class MyAspect implements MethodInterceptor { @Override public Object invoke(MethodInvocation mi) throws Throwable { System.out.println("前3"); //手動執行目標方法 Object obj = mi.proceed(); System.out.println("後3"); return obj; } }
spring配置
<!-- 1 建立目標類 --> <bean id="userServiceId" class="com.itheima.b_factory_bean.UserServiceImpl"></bean> <!-- 2 建立切面類 --> <bean id="myAspectId" class="com.itheima.b_factory_bean.MyAspect"></bean> <!-- 3 建立代理類 * 使用工廠bean FactoryBean ,底層調用 getObject() 返回特殊bean * ProxyFactoryBean 用於建立代理工廠bean,生成特殊代理對象 interfaces : 肯定接口們 經過<array>能夠設置多個值 只有一個值時,value="" target : 肯定目標類 interceptorNames : 通知 切面類的名稱,類型String[],若是設置一個值 value="" optimize :強制使用cglib <property name="optimize" value="true"></property> 底層機制 若是目標類有接口,採用jdk動態代理 若是沒有接口,採用cglib 字節碼加強 若是聲明 optimize = true ,不管是否有接口,都採用cglib --> <bean id="proxyServiceId" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="interfaces" value="com.itheima.b_factory_bean.UserService"></property> <property name="target" ref="userServiceId"></property> <property name="interceptorNames" value="myAspectId"></property> </bean>
![img](D:\Program Files\Typora\my_typora\image\java核心面試整理\clip_image002.jpg)
從spring容器得到目標類,若是配置aop,spring將自動生成代理。 要肯定目標類,aspectj 切入點表達式,導入jar包 spring-framework-3.0.2.RELEASE-dependencies\org.aspectj\com.springsource.org.aspectj.weaver\1.6.8.RELEASE 從spring容器得到目標類,若是配置aop,spring將自動生成代理。 導入jar:com.springsource.org.aspectj.weaver\1.6.8.RELEASE 1 導入命名空間 xmlns:aop 2 使用 <aop:config>進行配置 proxy-target-class="true" 聲明時使用cglib代理 <aop:pointcut> 切入點 ,從目標對象得到具體方法 <aop:advisor> 特殊的切面,只有一個通知 和 一個切入點 advice-ref 通知引用 pointcut-ref 切入點引用 3 切入點表達式 execution(* com.itheima.c_spring_aop.*.*(..)) 選擇方法 返回值任意 包 類名任意 方法名任意 參數任意
spring AOP編程(全自動)
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 1 建立目標類 --> <bean id="userServiceId" class="com.itheima.c_spring_aop.UserServiceImpl"></bean> <!-- 2 建立切面類(通知) --> <bean id="myAspectId" class="com.itheima.c_spring_aop.MyAspect"></bean> <!-- 3 aop編程 3.1 導入命名空間 3.2 使用 <aop:config>進行配置 proxy-target-class="true" 聲明時使用cglib代理 <aop:pointcut> 切入點 ,從目標對象得到具體方法 <aop:advisor> 特殊的切面,只有一個通知 和 一個切入點 advice-ref 通知引用 pointcut-ref 切入點引用 3.3 切入點表達式 execution(* com.itheima.c_spring_aop.*.*(..)) 選擇方法 返回值任意 包 類名任意 方法名任意 參數任意 --> <aop:config proxy-target-class="true"> <aop:pointcut expression="execution(* com.itheima.c_spring_aop.*.*(..))" id="myPointCut"/> <aop:advisor advice-ref="myAspectId" pointcut-ref="myPointCut"/> </aop:config> </beans>
AspectJ是一個基於Java語言的AOP框架 Spring2.0之後新增了對AspectJ切點表達式支持 @AspectJ 是AspectJ1.5新增功能,經過JDK5註解技術,容許直接在Bean類中定義切面 新版本Spring框架,建議使用AspectJ方式來開發AOP 主要用途:自定義開發 11.2 切入點表達式【掌握】 1.execution() 用於描述方法 【掌握】 語法:execution(修飾符 返回值 包.類.方法名(參數) throws異常) 修飾符,通常省略 public 公共方法 * 任意 返回值,不能省略 void 返回沒有值 String 返回值字符串 * 任意 包,[省略] com.itheima.crm 固定包 com.itheima.crm.*.service crm包下面子包任意 (例如:com.itheima.crm.staff.service) com.itheima.crm.. crm包下面的全部子包(含本身) com.itheima.crm.*.service.. crm包下面任意子包,固定目錄service,service目錄任意包 類,[省略] UserServiceImpl 指定類 *Impl 以Impl結尾 User* 以User開頭 * 任意 方法名,不能省略 addUser 固定方法 add* 以add開頭 *Do 以Do結尾 * 任意 (參數) () 無參 (int) 一個整型 (int ,int) 兩個 (..) 參數任意 throws ,可省略,通常不寫。 綜合1 execution(* com.itheima.crm.*.service..*.*(..)) 綜合2 <aop:pointcut expression="execution(* com.itheima.*WithCommit.*(..)) || execution(* com.itheima.*Service.*(..))" id="myPointCut"/> 2.within:匹配包或子包中的方法(瞭解) within(com.itheima.aop..*) 3.this:匹配實現接口的代理對象中的方法(瞭解) this(com.itheima.aop.user.UserDAO) 4.target:匹配實現接口的目標對象中的方法(瞭解) target(com.itheima.aop.user.UserDAO) 5.args:匹配參數格式符合標準的方法(瞭解) args(int,int) 6.bean(id) 對指定的bean全部的方法(瞭解) bean('userServiceId')
導入4個jar: aop聯盟規範 spring aop 實現 aspect 規範 spring aspect 實現 <aop:config> <aop:aspect ref="myAspectId"> <aop:pointcut expression="" > <aop:before method="myBefore" pointcut= "expression()"/> <aop:around method="myAround" pointcut-ref="myPointCut"/> <aop:after-returning method="" pointcut-ref="myPointCut" returning="ret" /> <context:component-scan base-package=""> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> @Pointcut("execution(* com.itheima.d_aspect.b_anno.UserServiceImpl.*(..))") @Around(value = "myPointCut()") @AfterReturning(value="myPointCut()" ,returning="ret")
![img](D:\Program Files\Typora\my_typora\image\java核心面試整理\00000001.png)
AOP聯盟通知類型 AOP聯盟爲通知Advice定義了org.aopalliance.aop.Advice Spring按照通知Advice在目標類方法的鏈接點位置,能夠分爲5類 • 前置通知 org.springframework.aop.MethodBeforeAdvice • 在目標方法執行前實施加強 • 後置通知 org.springframework.aop.AfterReturningAdvice • 在目標方法執行後實施加強 • 環繞通知 org.aopalliance.intercept.MethodInterceptor • 在目標方法執行先後實施加強 • 異常拋出通知 org.springframework.aop.ThrowsAdvice • 在方法拋出異常後實施加強 • 引介通知 org.springframework.aop.IntroductionInterceptor • 在目標類中添加一些新的方法和屬性 環繞通知,必須手動執行目標方法 try{ //前置通知 //執行目標方法 //後置通知 } catch(){ //拋出異常通知 }
1.目標類:接口 + 實現 2.切面類:編寫多個通知,採用aspectj 通知名稱任意(方法名任意) 3.aop編程,將通知應用到目標類 切面類 /** * 切面類,含有多個通知 */ public class MyAspect { public void myBefore(JoinPoint joinPoint){ System.out.println("前置通知 : " + joinPoint.getSignature().getName()); } public void myAfterReturning(JoinPoint joinPoint,Object ret){ System.out.println("後置通知 : " + joinPoint.getSignature().getName() + " , -->" + ret); } public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{ System.out.println("前"); //手動執行目標方法 Object obj = joinPoint.proceed(); System.out.println("後"); return obj; } public void myAfterThrowing(JoinPoint joinPoint,Throwable e){ System.out.println("拋出異常通知 : " + e.getMessage()); } public void myAfter(JoinPoint joinPoint){ System.out.println("最終通知"); } }
spring配置 <!-- 1 建立目標類 --> <bean id="userServiceId" class="com.itheima.d_aspect.a_xml.UserServiceImpl"></bean> <!-- 2 建立切面類(通知) --> <bean id="myAspectId" class="com.itheima.d_aspect.a_xml.MyAspect"></bean> <!-- 3 aop編程 <aop:aspect> 將切面類 聲明「切面」,從而得到通知(方法) ref 切面類引用 <aop:pointcut> 聲明一個切入點,全部的通知均可以使用。 expression 切入點表達式 id 名稱,用於其它通知引用 --> <aop:config> <aop:aspect ref="myAspectId"> <aop:pointcut expression="execution(* com.itheima.d_aspect.a_xml.UserServiceImpl.*(..))" id="myPointCut"/> <!-- 3.1 前置通知 <aop:before method="" pointcut="" pointcut-ref=""/> method : 通知,及方法名 pointcut :切入點表達式,此表達式只能當前通知使用。 pointcut-ref : 切入點引用,能夠與其餘通知共享切入點。 通知方法格式:public void myBefore(JoinPoint joinPoint){ 參數1:org.aspectj.lang.JoinPoint 用於描述鏈接點(目標方法),得到目標方法名等 例如: <aop:before method="myBefore" pointcut-ref="myPointCut"/> --> <!-- 3.2後置通知 ,目標方法後執行,得到返回值 <aop:after-returning method="" pointcut-ref="" returning=""/> returning 通知方法第二個參數的名稱 通知方法格式:public void myAfterReturning(JoinPoint joinPoint,Object ret){ 參數1:鏈接點描述 參數2:類型Object,參數名 returning="ret" 配置的 例如: <aop:after-returning method="myAfterReturning" pointcut-ref="myPointCut" returning="ret" /> --> <!-- 3.3 環繞通知 <aop:around method="" pointcut-ref=""/> 通知方法格式:public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{ 返回值類型:Object 方法名:任意 參數:org.aspectj.lang.ProceedingJoinPoint 拋出異常 執行目標方法:Object obj = joinPoint.proceed(); 例如: <aop:around method="myAround" pointcut-ref="myPointCut"/> --> <!-- 3.4 拋出異常 <aop:after-throwing method="" pointcut-ref="" throwing=""/> throwing :通知方法的第二個參數名稱 通知方法格式:public void myAfterThrowing(JoinPoint joinPoint,Throwable e){ 參數1:鏈接點描述對象 參數2:得到異常信息,類型Throwable ,參數名由throwing="e" 配置 例如: <aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointCut" throwing="e"/> --> <!-- 3.5 最終通知 --> <aop:after method="myAfter" pointcut-ref="myPointCut"/> </aop:aspect> </aop:config>
替換bean <!-- 1.掃描 註解類 --> <context:component-scan base-package="com.itheima.d_aspect.b_anno"> </context:component-scan> @Service("userServiceId") public class UserServiceImpl implements UserService{} @Component @Aspect//聲明切面 public class MyAspect{} 替換aop <!-- 2.肯定 aop註解生效 --> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> //切入點當前有效 @Before("execution(* com.itheima.d_aspect.b_anno.UserServiceImpl.*(..))") public void myBefore(JoinPoint joinPoint){ System.out.println("前置通知 : " + joinPoint.getSignature().getName()); } //聲明公共切入點 @Pointcut("execution(* com.itheima.d_aspect.b_anno.UserServiceImpl.*(..))") private void myPointCut(){ } @AfterReturning(value="myPointCut()" ,returning="ret") public void myAfterReturning(JoinPoint joinPoint,Object ret){ System.out.println("後置通知 : " + joinPoint.getSignature().getName() + " , -->" + ret); } @Around(value = "myPointCut()") public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{ System.out.println("前"); //手動執行目標方法 Object obj = joinPoint.proceed(); System.out.println("後"); return obj; } @AfterThrowing(value="execution(* com.itheima.d_aspect.b_anno.UserServiceImpl.*(..))" ,throwing="e") public void myAfterThrowing(JoinPoint joinPoint,Throwable e){ System.out.println("拋出異常通知 : " + e.getMessage()); }
切面類
/** * 切面類,含有多個通知 */ @Component @Aspect public class MyAspect { //切入點當前有效 // @Before("execution(* com.itheima.d_aspect.b_anno.UserServiceImpl.*(..))") public void myBefore(JoinPoint joinPoint){ System.out.println("前置通知 : " + joinPoint.getSignature().getName()); } //聲明公共切入點 @Pointcut("execution(* com.itheima.d_aspect.b_anno.UserServiceImpl.*(..))") private void myPointCut(){ } // @AfterReturning(value="myPointCut()" ,returning="ret") public void myAfterReturning(JoinPoint joinPoint,Object ret){ System.out.println("後置通知 : " + joinPoint.getSignature().getName() + " , -->" + ret); } // @Around(value = "myPointCut()") public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{ System.out.println("前"); //手動執行目標方法 Object obj = joinPoint.proceed(); System.out.println("後"); return obj; } // @AfterThrowing(value="execution(* com.itheima.d_aspect.b_anno.UserServiceImpl.*(..))" ,throwing="e") public void myAfterThrowing(JoinPoint joinPoint,Throwable e){ System.out.println("拋出異常通知 : " + e.getMessage()); } @After("myPointCut()") public void myAfter(JoinPoint joinPoint){ System.out.println("最終通知"); } }
spring配置
<!-- 1.掃描 註解類 --> <context:component-scan base-package="com.itheima.d_aspect.b_anno"> </context:component-scan> <!-- 2.肯定 aop註解生效 --> <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
導入jar包 核心:4+1 aop : 4 (aop聯盟、spring aop、aspectj規範、spring aspect) 數據庫:2 (jdbc/tx) 驅動:mysql oracle 鏈接池:c3p0 proxool DBCP
事務的屬性:
一、propagation:用來設置事務的傳播行爲 事務的傳播行爲:一個方法運行在了一個開啓了事務的方法中,當前方法時使用原來的事務仍是開啓一個新的事務 -Propagation.REQUIRED:默認值,使用原來的事務。required -Propagation.REQUIRES_NEW:將原來的事務掛起,開啓一個新的事務requires_new 二、isolation:用來設置事務的隔離級別 -Isolation.REPEATABLE_READ:可重複讀,MYSQL默認的隔離級別 repeatable_read -Isonlation.READ_COMMITTED:讀已提交,ORACLE默認的隔離級別,開發時一般使用的隔離級別
手動管理事務
spring底層使用 TransactionTemplate 事務模板進行操做 1.service 須要得到 TransactionTemplate 2.spring 配置模板,並注入給service 3.模板須要注入事務管理器 4.配置事務管理器:DataSourceTransactionManager ,須要注入DataSource
ACID(isolation,) 1.原子性: 整個事務當中全部的操做,要麼所有成功,要麼所有失敗. 2.一致性: 在事務開始以前和事務結束以後,數據庫的信息必定是正確的. 3.隔離性: 一個事務的成功或失敗對於其餘事務是沒有任何影響的,2個事務之間是互相獨⽴立的. 4.持久性: 在事務完成之後,這個事務所對數據庫的操做會永久保存在數據庫當中,不會被回滾.
1.讀未提交(read uncommitted):也就是髒讀,事務能夠讀取其餘事務未提交的數據 2.讀已提交(read committed):一個事務讀取到另外一個事務已提交的數據(解決了髒讀問題.oracle默認) 3.可重複讀(repeatable read):在一個事務中讀取到的數據始終保持一致,不管另外一個事務是否提交 (解決髒讀、不可重複讀,mysql默認) 4.可串行化(serializable):同時只能執行一個事務,至關於事務中的單線程 default
多個事務存在是怎麼處理的策略 propagation required須要:若是存在一個事務,則支持當前事務,若是沒有則開啓。 supports支持:若是存在一個事務,支持當前事務,若是沒有事務,則非事務的執行 mandatory必要的:若是已經存在一個事務,支持當前事務,若是沒有一個活動的事務,則拋出異常 requires_new:老是開啓一個新的事務,若是一個事務已經存在,則將這個存在的事務掛起。 not_support:老是非事務的執行,並掛起任何存在的事務。 never 毫不:老是非事務地執行,若是存在一個活動事務,則拋出異常 nested 嵌套的:若是有就嵌套,沒有就開啓事務。
spring 提供用於操做JDBC工具類,相似:DBUtils。 依賴 鏈接池DataSource (數據源) //1 建立數據源(鏈接池) dbcp BasicDataSource dataSource = new BasicDataSource(); //2 建立模板 JdbcTemplate jdbcTemplate = new JdbcTemplate(); jdbcTemplate.setDataSource(dataSource); <!-- 建立模板 ,須要注入數據源--> <bean id="jdbcTemplateId" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSourceId"></property> </bean> <!-- 建立數據源 c3p0--> <bean id="dataSourceId" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="com.mysql.jdbc.Driver"></property> <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/ee19_spring_day02"></property> <property name="user" value="root"></property> <property name="password" value="1234"></property> </bean> <!-- 建立數據源 dbcp--> <bean id="dataSourceId" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost:3306/ee19_spring_day02"> <property name="username" value="root"></property> <property name="password" value="1234"></property> </bean> 使用JdbcDaoSupport <!-- 配置dao * dao 繼承 JdbcDaoSupport,以後只須要注入數據源,底層將自動建立模板 --> <bean id="userDaoId" class="com.itheima.e_jdbcdaosupport.UserDao"> <property name="dataSource" ref="dataSourceId"></property> </bean>
properties文件 jdbc.driverClass=com.mysql.jdbc.Driver jdbc.jdbcUrl=jdbc:mysql://localhost:3306/ee19_spring_day02 jdbc.user=root jdbc.password=1234 spring配置 <!-- 加載配置文件 "classpath:"前綴表示 src下 在配置文件以後經過 ${key} 得到內容 --> <context:property-placeholder location="classpath:com/itheima/f_properties/jdbcInfo.properties"/> <!-- 建立數據源 c3p0--> <bean id="dataSourceId" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.driverClass}"></property> <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property> <property name="user" value="${jdbc.user}"></property> <property name="password" value="${jdbc.password}"></property> </bean>
三個頂級接口 PlatformTransactionManager 平臺事務管理器,spring要管理事務,必須使用事務管理器 進行事務配置時,必須配置事務管理器。 TransactionDefinition:事務詳情(事務定義、事務屬性),spring用於肯定事務具體詳情, 例如:隔離級別、是否只讀、超時時間等 進行事務配置時,必須配置詳情。spring將配置項封裝到該對象實例。 TransactionStatus:事務狀態,spring用於記錄當前事務運行狀態。例如:是否有保存點,事務是否完成。 spring底層根據狀態進行相應操做。 常見的事務管理器 DataSourceTransactionManager ,jdbc開發時事務管理器,採用JdbcTemplate HibernateTransactionManager,hibernate開發時事務管理器,整合hibernate TransactionStatus getTransaction(TransactionDefinition definition) ,事務管理器 經過「事務詳情」,得到「事務狀態」,從而管理事務。 void commit(TransactionStatus status) 根據狀態提交 void rollback(TransactionStatus status) 根據狀態回滾
手動管理事務(瞭解) spring底層使用 TransactionTemplate 事務模板進行操做。 操做 1.service 須要得到 TransactionTemplate 2.spring 配置模板,並注入給service 3.模板須要注入事務管理器 4.配置事務管理器:DataSourceTransactionManager ,須要注入DataSource dao層: import org.springframework.jdbc.core.support.JdbcDaoSupport; public class AccountDaoImple extends JdbcDaoSupport implements AccountDao { @Override public void out(String outer, int money) { this.getJdbcTemplate().update("update account set money = money - ? where name = ?",money,outer); } @Override public void in(String inner, int money) { this.getJdbcTemplate().update("update account set money = money + ? where name = ?", money,inner); } } service層: import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; public class AccountServiceImpl implements AccountService { private AccountDao accountDao; public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } //須要spring注入模板 private TransactionTemplate transactionTemplate; public void setTransactionTemplate(TransactionTemplate transactionTemplate) { this.transactionTemplate = transactionTemplate; } @Override public void transfer(final String outer,final String inner,final Integer money) { transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus arg0) { accountDao.out(outer, money); //斷電 // int i = 1/0; accountDao.in(inner, money); } }); } } spring配置 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 建立數據源 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="com.mysql.jdbc.Driver"></property> <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/study"></property> <property name="user" value="root"></property> <property name="password" value="10086"></property> </bean> <!-- 配置dao --> <bean id="accountDaoImple" class="cn.lm.tx01.AccountDaoImple"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 配置service --> <bean id="accountServiceImplId" class="cn.lm.tx01.AccountServiceImpl"> <property name="accountDao" ref="accountDaoImple"></property> <property name="transactionTemplate" ref="transactionTemplate"></property> </bean> <!-- 建立模板 --> <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"> <property name="transactionManager" ref="txManager"></property> </bean> <!-- 配置事務管理器 ,管理器須要事務,事務從Connection得到,鏈接從鏈接池DataSource得到 --> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> </beans>
spring提供 管理事務的代理工廠bean TransactionProxyFactoryBean 1.getBean() 得到代理對象 2.spring 配置一個代理 Dao層 import org.springframework.jdbc.core.support.JdbcDaoSupport; public class AccountDaoImple extends JdbcDaoSupport implements AccountDao { @Override public void out(String outer, int money) { this.getJdbcTemplate().update("update account set money = money - ? where name = ?",money,outer); } @Override public void in(String inner, int money) { this.getJdbcTemplate().update("update account set money = money + ? where name = ?", money,inner); } } Service類 public class AccountServiceImpl implements AccountService { private AccountDao accountDao; public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } @Override public void transfer(String outer, String inner, int money) { accountDao.out(outer, money); int i = 1 / 0; accountDao.in(inner, money); } } spring配置 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 建立數據源 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="com.mysql.jdbc.Driver"></property> <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/study"></property> <property name="user" value="root"></property> <property name="password" value="10086"></property> </bean> <!-- 配置dao --> <bean id="accountDaoImple" class="cn.lm.tx02.AccountDaoImple"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 配置service --> <bean id="accountServiceImplId" class="cn.lm.tx02.AccountServiceImpl"> <property name="accountDao" ref="accountDaoImple"></property> </bean> <!-- 配置事務管理器 --> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 4 service 代理對象 4.1 proxyInterfaces 接口 4.2 target 目標類 4.3 transactionManager 事務管理器 4.4 transactionAttributes 事務屬性(事務詳情) prop.key :肯定哪些方法使用當前事務配置 prop.text:用於配置事務詳情 格式:PROPAGATION,ISOLATION,readOnly,-Exception,+Exception 傳播行爲 隔離級別 是否只讀 異常回滾 異常提交 例如: <prop key="transfer">PROPAGATION_REQUIRED,ISOLATION_DEFAULT</prop> 默認傳播行爲,和隔離級別 <prop key="transfer">PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly</prop> 只讀 <prop key="transfer">PROPAGATION_REQUIRED,ISOLATION_DEFAULT,+java.lang.ArithmeticException</prop> 有異常扔提交 --> <bean id="proxyAccountService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <property name="proxyInterfaces" value="cn.lm.tx02.AccountService"></property> <property name="target" ref="accountServiceImplId"></property> <property name="transactionManager" ref="txManager"></property> <property name="transactionAttributes"> <props> <prop key="transfer">PROPAGATION_REQUIRED,ISOLATION_DEFAULT</prop> </props> </property> </bean> </beans>
在spring xml 配置aop 自動生成代理,進行事務的管理 1.配置管理器 2.配置事務詳情 3.配置aop Dao import org.springframework.jdbc.core.support.JdbcDaoSupport; public class AccountDaoImple extends JdbcDaoSupport implements AccountDao { @Override public void out(String outer, int money) { this.getJdbcTemplate().update("update account set money = money - ? where name = ?",money,outer); } @Override public void in(String inner, int money) { this.getJdbcTemplate().update("update account set money = money + ? where name = ?", money,inner); } } Service public class AccountServiceImpl implements AccountService { private AccountDao accountDao; public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } @Override public void transfer(String outer, String inner, int money) { accountDao.out(outer, money); int i = 1 / 0; accountDao.in(inner, money); } } 15.3.4.3 配置文件 <!-- 建立數據源 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="com.mysql.jdbc.Driver"></property> <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/study"></property> <property name="user" value="root"></property> <property name="password" value="10086"></property> </bean> <!-- 配置dao --> <bean id="accountDaoImple" class="cn.lm.tx03_xml.AccountDaoImple"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 配置service --> <bean id="accountServiceImplId" class="cn.lm.tx03_xml.AccountServiceImpl"> <property name="accountDao" ref="accountDaoImple"></property> </bean> <!-- 配置事務管理器 --> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 配置 事務詳情(事務通知) , 在aop篩選基礎上,對ABC三個肯定使用什麼樣的事務。例如:AC讀寫、B只讀 等 <tx:attributes> 用於配置事務詳情(屬性屬性) <tx:method name=""/> 詳情具體配置 propagation 傳播行爲 , REQUIRED:必須;REQUIRES_NEW:必須是新的 isolation 隔離級別 --> <tx:advice id="txAdvice" transaction-manager="txManager"> <tx:attributes> <tx:method name="transfer" propagation="REQUIRED" isolation="DEFAULT"/> </tx:attributes> </tx:advice> <!-- AOP編程,目標類有ABCD(4個鏈接點),切入點表達式 肯定加強的鏈接器,從而得到切入點:ABC --> <aop:config> <aop:advisor advice-ref="txAdvice" pointcut="execution(* cn.lm.tx03_xml.*.*(..))"/> </aop:config> </beans>
1.配置事務管理器,將並事務管理器交予spring 2.在目標類或目標方法添加註解便可 @Transactional Service層 import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @Transactional(propagation=Propagation.REQUIRED,isolation=Isolation.DEFAULT) public class AccountServiceImpl implements AccountService { private AccountDao accountDao; public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } @Override public void transfer(String outer, String inner, int money) { accountDao.out(outer, money); //int i = 1 / 0; accountDao.in(inner, money); } } <!-- 4 事務管理 --> <!-- 4.1 事務管理器 --> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 4.2 將管理器交予spring * transaction-manager 配置事務管理器 * proxy-target-class true : 底層強制使用cglib 代理 --> <tx:annotation-driven transaction-manager="txManager"/>
導入jar包 基本 :4+1 測試:spring-test...jar 1.讓Junit通知spring加載配置文件 2.讓spring容器自動進行注入 修改測試類 @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations="classpath:applicationContext.xml") public class TestApp { @Autowired //與junit整合,不須要在spring xml配置掃描 private AccountService accountService; @Test public void demo01(){ // String xmlPath = "applicationContext.xml"; // ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath); // AccountService accountService = (AccountService) applicationContext.getBean("accountService"); accountService.transfer("jack", "rose", 1000); } }
0.導入jar包 spring-web.xml 1.tomcat啓動加載配置文件 servlet --> init(ServletConfig) --> <load-on-startup>2 filter --> init(FilterConfig) --> web.xml註冊過濾器自動調用初始化 listener --> ServletContextListener --> servletContext對象監聽【】 spring提供監聽器 ContextLoaderListener --> web.xml <listener><listener-class>.... 若是隻配置監聽器,默認加載xml位置:/WEB-INF/applicationContext.xml java.io.FileNotFoundException: 2.肯定配置文件位置,經過系統初始化參數 ServletContext 初始化參數 web.xml <context-param> <param-name>contextConfigLocation <param-value>classpath:applicationContext.xml
<!-- 肯定配置文件位置 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> <!-- 配置spring 監聽器,加載xml配置文件 --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
談談你對Spring的理理解 Spring 是一個輕量級開源的JAVAEE 框架, 它的核心是: IOC(控制反轉) DI(注入) AOP(面向切面) 控制反轉(IOC):是面向對象編程中的一種設計原則,用來下降程序代碼之間的耦合度,使整個程序體系結構更加靈活, 與此同時將類的建立和依賴關係寫在配置文件裏,由配置文件注入,達到鬆耦合的效果。與此同時IOC 也稱爲DI(依賴注入), 依賴注入是一種開發模式;依賴注入提倡使用接口編程; 依賴注入使得能夠開發各個組件,而後根據組件之間的依賴關係注入組裝。 IOC(控制反轉): 把建立對象的權利交給Spring, Spring容器使用工廠模式爲咱們建立了所須要的對象,直接調用就能夠了 (線程安全的) DI(注入) : Spring使用JavaBean對象的Set方法或者構造方法將其屬性自動設置所須要的值; AOP(面向切面) : 將一個對象橫向抽成一個切面,對這個切面進行一些如權限控制,事務管理,記錄日誌等,底層是動態代理; AOP 實現流程 一、aop:config 自定義標籤解析 二、自定義標籤解析時封裝對應的aop 入口類,類的類型就是BeanPostProcessor 接口類型 三、Bean 實例化過程當中會執行到aop 入口類中 四、在aop 入口類中,判斷當前正在實例化的類是否在pointcut 中,pointcut 能夠理解爲一個模糊匹配,是一個joinpoint 的集合 五、若是當前正在實例化的類在pointcut 中,則返回該bean 的代理類,同時把全部配置的advice 封裝成MethodInterceptor 對象加入到容器中,封裝成一個過濾器鏈 六、代理對象調用,jdk 動態代理會調到invocationHandler 中,cglib 型代理調到MethodInterceptor 的callback 類中,而後在invoke 方法中執行過濾器鏈。 Spring 框架中如何基於AOP 實現的事務管理? 事務管理,是一個切面。在aop 環節中,其餘環節都同樣,事務管理就是由Spring 提供的advice,既是TransactionInterceptor, 它同樣的會在過濾器鏈中被執行到,這個TransactionInterceptor 過濾器類是經過解析<tx:advice>自定義標籤獲得的。 Spring的啓動過程 1.建立一個全局的上下文環境,這個上下文就是ServletContext 2.在web容器啓動時,會觸發容器初始化事件,Spring 會建立一個上下文,這個上下文被稱爲根上下文,就是WebApplicationContext 3.監聽器器初始完畢後,開始初始化web.html配置中的Servlet Spring中的幾種設計模式? 1.單例模式:Spring的配置文件中設置bean默認爲單例模式, Spring中有兩種方式,若目標對象實現了若干接口,Spring使用JDK的類代理沒有實現任何接口,Spring使⽤cglib類的子類 2.模板方法模式:用來解決代碼重複問題. 3.前端控制器模式:Spring提供了前端控制器DispatherServlet來對請求進行分發 4.視圖幫助(view) : spring提供了一系列列的JSP標籤,高效幫助將分散的代碼整合在視圖中 5.依賴注入:DI貫穿BeanFactory/ApplicationContext接口的核心理念 6.工廠模式: spring中使用beanFactory來建立對象實例. bean的生命週期 單例對象: 生命週期 1.對象出生: 當應用加載,建立容器時,對象就被建立了 2.對象活着:只要容器在,對象就一直活着 3.對象死亡:當容器銷燬時,對象就死亡了 多例對象:生命週期 1.對象出生:當使用對象時,建立新的對象實例(Bean) 2.對象活着:只要對象在使用中就一直活着 3.對象死亡:當對象長時間不使用,Java 的GC 就會自動回收 BeanFactory和FactoryBean的區別? BeanFactory是IOC最基本的容器,負責生產和管理bean,它爲其餘具體的IOC容器提供了最基本的規範 FactoryBean是一個接口,在IOC容器中bean實現了FactoryBean以後,經過getBean(String BeanName)獲取到Bean對象 spring的事務傳播特性? 1.PROPAGATION_REQUIRED : 若是存在一個事務,則支持當前事務,若是沒有事務則開啓 2.PROPAGATION_SUPPORTS : 若是存在一個事務,則支持當前事務,若是沒有事務,則非事務執行 3.PROPAGATION_MANDATORY: 若是存在一個事務,則支持當前事務,若是沒有一個活動的事務,則拋出異常 4.PROPAGATION_REQUIRES_NEW : 老是開啓一個新事務,若是一個事務已經存在,則將這個存在的事務掛起 5.PROPAGATION_NOT_SUPPORTED : 老是非事務執行,並掛起任何事務 6.PROPAGATION_NEVER : 老是非事務執行,若是存在一個事務則拋出異常 7.PROPAGATION_NESTED : 若是一個活動的事務存在,則運行在一個嵌套的事務中,若是沒有活動事務,則按TransactionDefinition.PROPAGATION_REQUIRED執行 nested
1 setter注入
SpringMVC的工做原理: 1.用戶發起請求找到DispatchServlet(控制器器) 2.DispatchServlet對URL進行解析,獲得請求資源標識符(URL)而後根據URL調用HandlerMapping將請求映射處處理器HandlerExcutionChain 3.DispatchServlet根據得到Handler選擇一個具體的HandlerAdapter適配處理器 4.Handler對數據處理完成返回一個ModelAndView()對象給DisPatchServlet(控制器) 5.Handler返回的ModelAndView()只是邏輯視圖並不是具體的視圖,DispatcherSevlet經過ViewResolver視圖解析器將邏輯視圖轉換成真正的視圖View 6.DispatcherServle經過model 解析出 ModelAndView()中的參數進行解析最終返回一個具體的view 並返回給客戶端 DispatcherServlet、HandlerMapping、HandlerAdapter和ViewResolver等對象協同工做 SpringMVC常⽤用註解都有哪些? @requestMapping : 用於請求URL 路徑 @requestBody : 接收http請求的json格式,將json格式轉換成java對象 @responseBody : 將controller⽅方法返回的對象轉換成json 響應給客戶端 springMvc的優化? 1.controller盡量使用單例,能夠減小建立對象和回收對象的開銷, 2.處理request的方法形參上加@RequestParam註解,能夠避免springmvc使用asm框架讀取class文件獲取方法參數名的過程
1.首先,要在web.xml裏面配置SpringMVC的核心控制器,DispatcherServlet,對指定的後綴請求進行攔截。 2.Controller層要加 @Controller註解,代表該類是MVC的控制層。 3.建立Service接口,給接口加上註解 @Component或者 @Service 代表這是Service業務處理層 4.在Controller層聲明Service變量(屬性),給變量(屬性) 加上 @Autowired註解,經過自動綁定機制將Service注入到Controller。 (注:@Autowired默認是ByType,若是想根據屬性名注入,那麼就再加上註解 @Resource(name="屬性名")) 5.在Controller層的方法上加上註解 @RequestMapping("requestAddress") 代表該方法的請求地址 6.Dao層要加上註解 @Repository 代表這是數據庫持久層 7.一樣將dao實例注入到service層中。 8.配置視圖解析器 "InternalResourceViewResolver",對處理後的跳轉進行統一配置。
1.核心控制器不一樣 springMVC的核心控制器是servlet,struts2是Filter (核心控制器的主要做用是處理全部的請求) 2.控制器實例,springMVC會比struts2快一些(理論上)。 springMVC是基於方法設計,而struts2是基於對象。 struts2每次發送請求都會實例一個action,每一個action都會被注入屬性,而springMVC更像servlet, 只有一個實例,每次執行對應的方法便可。(注意,因爲是單例實例,因此應當避免全局變量的修改,這樣會產生線程安全問題) 3.管理方式:大部分公司的核心架構中,會用到spring,而springMVC又是spring中的一個模塊, 因此spring對於springMVC的控制器管理更加簡單方便,並且提供了全註解方式進行管理,各類功能的註解都比較全面, 使用簡單,而struts2須要採用xml不少的配置參數來管理(雖然也能夠採用註解,但幾乎沒有公司這麼作)。 4.參數傳遞:Struts2中自身提供多種參數接受,其實都是經過(ValueStack)進行傳遞和賦值,而springMVC是經過方法的參數進行接收。 5.學習難度:struts有不少的技術點,好比攔截器,值棧及OMG表達式,學習成本較高,springMVC比較簡單,較少的時間就能夠上手。 6.interceptor的實現機制:struts有本身的interceptor機制,springMVC用的是獨立的AOP方式。 7.springMVC處理ajax請求,直接經過返回數據,方法中使用註解@ResponseBody,springMVC自動把對象轉換爲json數據,而struts2是經過插件的方式進行處理。 在springMVC流行起來以前,struts2在MVC框架中佔核心地位,隨着springMVC的出現,springMVC慢慢的取代struts2,可是不少企業都是原來搭建的框架,使用struts2較多。
①工做原理 在Struts2 框架中的處理大概分爲如下幾個步驟: 1)客戶端初始化一個指向 Servlet 容器(例如 Tomcat )的請求 2)這個請求通過一系列的過濾器 Filter(這些過濾器中有一個叫作ActionContextCleanUp 的可選過濾器, 這個過濾器對於 Struts2 和其餘框架的集成頗有幫助,例如:SiteMesh Plugin) 3)接着 FilterDispatcher被調用,FilterDispatcher詢問ActionMapper來決定這個請求是否須要調用某個Action 4)若是 ActionMapper決定須要調用某個Action,FilterDispatcher 把請求的處理交給 ActionProxy 5)ActionProxy 經過 Configuration Manager詢問框架的配置文件,找到須要調用的 Action 類 6)ActionProxy 建立一個 ActionInvocation 的實例。 7)ActionInvocation 實例使用命名模式來調用,在調用 Action 的過程先後,涉及到相關攔截器( Intercepter )的調用。 8)一旦 Action 執行完畢, ActionInvocation 負責根據 struts.xml 中的配置找到對應的返回結果。 返回結果一般是(但不老是,也多是另外的一個 Action 鏈)一個須要被表示的 JSP 或者 FreeMarker 的模版。 在表示的過程當中能夠使用 Struts2 框架中繼承的標籤。在這個過程當中須要涉及到 ActionMapper 。 ②工做流程: 1)客戶端在瀏覽器中輸入一個 url 地址。 2)這個 url 請求經過 http 協議發送給 tomcat 。 3)tomcat 根據 url 找到對應項目裏面的 web.xml 文件。 4)在 web.xml 裏面會發現有 struts2 的配置。 5)而後會找到 struts2 對應的 struts.xml 配置文件。 6)根據 url 解析 struts.xml 配置文件就會找到對應的 class 。 7)調用完 class 返回一個字 String ,根據 struts.xml 返回到對應的 jsp 。
模板技術 ,通常用於頁面靜態化
freemarker:擴展名:*.ftl
velocity :擴展名 *.vm
一、在ssh框架中是怎麼整合spring? 首先在web.xml中經過ContextLoaderListener來融入spring,並加載spring的相關配置文件 二、在ssh框架中是怎麼整合struts2? 配置sturts2的前端總控制器filterDispatcher來過濾相關的 請求而且加載struts.xml action繼承ActionSupport,而後經過引入struts-spring-plugin.jar包而且根據配置文件中service的id生成get,set方法來注入service層。 三、在ssh框架中是怎麼整合hibernate? 經過spring中的配置文件加載hibernate.cfg.xml文件從而融入hibernate dao層繼承於HibernateDaoSupport,而且在dao的配置文件中注入sessionFactory
1.spring
基礎:4+1 , beans、core、context、expression , commons-logging (struts已經導入) AOP:aop聯盟、spring aop 、aspect規範、spring aspect db:jdbc、tx 測試:test web開發:spring web 驅動:mysql 鏈接池:c3p0 整合包 spring整合hibernate: spring orm struts 整合spring:struts2-spring-plugin-2.3.15.3.jar 刪除重複jar包 字節碼加強的jar包 2.hibernate:spring orm %h%\hibernate3.jar 核心 %h%\lib\required 必須
%h%\lib\jpa jpa規範 (java persistent api 持久api),hibernate註解開發 @Entity @Id 等 l 整合log4j 導入 log4j...jar (struts已經導入) 整合(過渡):slf4j-log4j12-1.7.5.jar
二級緩存 核心:ehcache-1.5.0.jar 依賴: backport-util-concurrent-2.1.jar commons-logging (存在) 3.struts struts-2.3.15.3\apps\struts2-blank\WEB-INF\lib
Javabean public class User { private Integer id; private String username; private String password; private Integer age; 映射文件 <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="com.itheima.domain.User" table="t_user"> <id name="id"> <generator class="native"></generator> </id> <property name="username"></property> <property name="password"></property> <property name="age"></property> </class> </hibernate-mapping> dao層 spring提供 HibernateTemplate 用於操做PO對象,相似Hibernate Session對象。 public class UserDaoImpl implements UserDao { //須要spring注入模板 private HibernateTemplate hibernateTemplate; public void setHibernateTemplate(HibernateTemplate hibernateTemplate) { this.hibernateTemplate = hibernateTemplate; } @Override public void save(User user) { this.hibernateTemplate.save(user); } } service層 public class UserServiceImpl implements UserService { private UserDao userDao; public void setUserDao(UserDao userDao) { this.userDao = userDao; } @Override public void register(User user) { userDao.save(user); } } hibernate.cfg.xml <session-factory> <!-- 1基本4項 --> <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property> <property name="hibernate.connection.url">jdbc:mysql:///ee19_spring_day03</property> <property name="hibernate.connection.username">root</property> <property name="hibernate.connection.password">1234</property> <!-- 2 配置方言 --> <property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property> <!-- 3 sql語句 --> <property name="hibernate.show_sql">true</property> <property name="hibernate.format_sql">true</property> <!-- 4 自動生成表(通常沒用) --> <property name="hibernate.hbm2ddl.auto">update</property> <!-- 5本地線程綁定 --> <property name="hibernate.current_session_context_class">thread</property> <!-- 導入映射文件 --> <mapping resource="com/itheima/domain/User.hbm.xml"/> </session-factory> applicationContext.xml <!-- 1 加載hibenrate.cfg.xml 得到SessionFactory * configLocation肯定配置文件位置 --> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="configLocation" value="classpath:hibernate.cfg.xml"></property> </bean> <!-- 2建立模板 * 底層使用session,session 有sessionFactory得到 --> <bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate"> <property name="sessionFactory" ref="sessionFactory"></property> </bean> <!-- 3 dao --> <bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"> <property name="hibernateTemplate" ref="hibernateTemplate"></property> </bean> <!-- 4 service --> <bean id="userService" class="com.itheima.service.impl.UserServiceImpl"> <property name="userDao" ref="userDao"></property> </bean> <!-- 5 事務管理 --> <!-- 5.1 事務管理器 :HibernateTransactionManager --> <bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager" > <property name="sessionFactory" ref="sessionFactory"></property> </bean> <!-- 5.2 事務詳情 ,給ABC進行具體事務設置 --> <tx:advice id="txAdvice" transaction-manager="txManager"> <tx:attributes> <tx:method name="register"/> </tx:attributes> </tx:advice> <!-- 5.3 AOP編程,ABCD 篩選 ABC --> <aop:config> <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.itheima.service..*.*(..))"/> </aop:config>
刪除hibernate.cfg.xml文件,但須要保存文件內容,將其配置spring中 修改dao層,繼承HibernateDaoSupport <!-- 1.1加載properties文件 --> <!-- 1.2 配置數據源 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="com.mysql.jdbc.Driver"></property> <property name="jdbcUrl" value="jdbc:mysql:///ee19_spring_day03"></property> <property name="user" value="root"></property> <property name="password" value="1234"></property> </bean> <!-- 1.3配置 LocalSessionFactoryBean,得到SessionFactory * configLocation肯定配置文件位置 <property name="configLocation" value="classpath:hibernate.cfg.xml"></property> 1)dataSource 數據源 2)hibernateProperties hibernate其餘配置項 3) 導入映射文件 mappingLocations ,肯定映射文件位置,須要「classpath:」 ,支持通配符 【】 <property name="mappingLocations" value="classpath:com/itheima/domain/User.hbm.xml"></property> <property name="mappingLocations" value="classpath:com/itheima/domain/*.hbm.xml"></property> mappingResources ,加載執行映射文件,從src下開始 。不支持通配符* <property name="mappingResources" value="com/itheima/domain/User.hbm.xml"></property> mappingDirectoryLocations ,加載指定目錄下的,全部配置文件 <property name="mappingDirectoryLocations" value="classpath:com/itheima/domain/"></property> mappingJarLocations , 從jar包中得到映射文件 --> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="dataSource" ref="dataSource"></property> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop> <prop key="hibernate.show_sql">true</prop> <prop key="hibernate.format_sql">true</prop> <prop key="hibernate.hbm2ddl.auto">update</prop> <prop key="hibernate.current_session_context_class">thread</prop> </props> </property> <property name="mappingLocations" value="classpath:com/itheima/domain/*.hbm.xml"></property> </bean> / 底層須要SessionFactory,自動建立HibernateTemplate模板 public class UserDaoImpl extends HibernateDaoSupport implements UserDao { @Override public void save(User user) { this.getHibernateTemplate().save(user); } } spring 刪除模板,給dao注入SessionFactory <!-- 3 dao --> <bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"> <property name="sessionFactory" ref="sessionFactory"></property> </bean>
編寫action類,並將其配置給spring ,spring能夠注入service 2.編寫struts.xml 3.表單jsp頁面 4.web.xml 配置 1.肯定配置文件contextConfigLocation 2.配置監聽器 ContextLoaderListener 3.配置前端控制器 StrutsPrepareAndExecuteFitler
18.4.1 action類 通用 public class UserAction extends ActionSupport implements ModelDriven<User> { //1 封裝數據 private User user = new User(); @Override public User getModel() { return user; } //2 service private UserService userService; public void setUserService(UserService userService) { this.userService = userService; } 功能 /** * 註冊 * @return */ public String register(){ userService.register(user); return "success"; } 18.4.2 spring配置 <!-- 6 配置action --> <bean id="userAction" class="com.itheima.web.action.UserAction" scope="prototype"> <property name="userService" ref="userService"></property> </bean> 18.4.3 struts配置 <struts> <!-- 開發模式 --> <constant name="struts.devMode" value="true" /> <package name="default" namespace="/" extends="struts-default"> <!-- 底層自動從spring容器中經過名稱得到內容, getBean("userAction") --> <action name="userAction_*" class="userAction" method="{1}"> <result name="success">/messag.jsp</result> </action> </package> </struts> 18.4.4 jsp表單 <form action="${pageContext.request.contextPath}/userAction_register" method="post"> 用戶名:<input type="text" name="username"/> <br/> 密碼:<input type="password" name="password"/> <br/> 年齡:<input type="text" name="age"/> <br/> <input type="submit" /> </form> 18.4.5 配置web.xml <!-- 1 肯定spring xml位置 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> <!-- 2 spring監聽器 --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- 3 struts 前端控制器 --> <filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
刪除spring action配置 struts <action class="全限定類名"> 要求:Action類中,必須提供service名稱與 spring配置文件一致。(若是名稱同樣,將自動注入)
<package name="default" namespace="/" extends="struts-default"> <!-- 底層自動從spring容器中經過名稱得到內容, getBean("userAction") --> <action name="userAction_*" class="com.itheima.web.action.UserAction" method="{1}"> <result name="success">/messag.jsp</result> </action> </package>
分析: 1. struts 配置文件 default.properties ,常量配置文件 struts-default.xml ,默認核心配置文件 struts-plugins.xml ,插件配置文件 struts.xml,自定義核心配置文件 常量的使用,後面配置項,將覆蓋前面的。 2.default.properties ,此配置文件中肯定 按照【名稱】自動注入 /org/apache/struts2/default.properties 3. struts-plugins.xml ,struts整合spring <constant name="struts.objectFactory" value="spring" /> struts的action將有spring建立 總結,以後action有spring建立,並按照名稱自動注入
首先經過configuration去加載hibernate.cfg.xml這個配置文件,根據配置文件的信息去建立sessionFactory,sessionFactory是線程安全的, 是一個session工廠,用來建立session,session是線程不安全的,至關於jdbc的connection,最後經過session去進行數據庫的各類操做, 在進行操做的時候經過transaction進行事務的控制。
1 .Configuration接口的做用是對Hibernate進行配置,以及對它進行啓動。(加載hibernate.cfg.xml)並建立一個SessionFactory對象。 2 .SessionFactory接口 SessionFactory接口負責初始化Hibernate。它充當數據存儲源的代理,並負責建立Session對象。SessionFactory是線程安全的。 3 .Session接口 Session(會話)接口是Hibernate應用使用的主要接口。Session接口負責執行被持久化對象的CRUD操做(增刪改查)。 Session對象是非線程安全的。Session 至關於jdbc的connection 4 .Query與Criteria接口 總之Query和Criteria接口負責執行各類數據庫查詢。 5 .Transaction接口 Transaction(事務)負責操做相關的事務。
一、hibernate和jdbc主要區別就是,hibernate先檢索緩存中的映射對象( 即hibernate操做的是對象),而jdbc則是直接操做數據庫. 二、Hibernate是JDBC的輕量級的對象封裝,它是一個獨立的對象持久層框架。Hibernate能夠用在任何JDBC能夠使用的場合 三、Hibernate是一個和JDBC密切關聯的框架,因此Hibernate的兼容性和JDBC驅動,和數據庫都有必定的關係, 可是和使用它的Java程序,和App Server沒有任何關係,也不存在兼容性問題。 四、若是正確的使用JDBC技術,它的執行效率必定比hibernate要好,由於hibernate是基於jdbc的技術. 五、JDBC使用的是SQL語句,Hibernate使用的是HQL語句,可是HQL語句最終還會隱式轉換成SQL語句執行。
開啓事務 session.beginTransaction();
執行相關的操做,若是成功則session.getTransaction().commit();
執行操做失敗則 session.getTransaction.rollback();
Transient(臨時) new 一個初始化對象後,並無在數據庫裏保存數據,處於臨時狀態; Persistent(持久化) 當執行save()方法,調用session.close()方法以前,內存中的對象與數據庫有對應關係處於持久化狀態; Detached(託管/遊離) 當執行session.close()以後,處於託管狀態; 狀態的轉換 處於託管狀態下,調用update()方法後,轉換爲持久化狀態; 在持久化狀態下,執行delete()方法後,轉換爲臨時狀態; 在未初始化對象以前,調用get(),load(),find(),iterate()以後,直接進入持久化狀態。
hibernate分爲一級緩存即session緩存也叫事務級別的緩存以及二級緩存sessionFactory即應用級別的緩存,還有查詢緩存即三級緩存. 一級緩存的生命週期和session的生命週期保持一致,hibernate默認就啓用了一級緩存,不能將其關閉, 能夠經過session.clear()和session.evict(object)來管理一級緩存。其中get,load,iterate都會使用一級緩存,一級緩存緩存的是對象。 二級緩存的生命週期和sessionFactory的生命週期保持一致,能夠跨session,被多個session共享, hibernate3默認開啓二級緩存,也能夠手動開啓並指定緩存插件如ehcache,oscache等。二級緩存也只能緩存對象。 三級緩存也叫查詢緩存,查詢緩存是針對普通屬性結果集的緩存,對實體對象的結果集只緩存id。 對query.list()起做用,query.iterate不起做用,也就是query.iterate不使用查詢緩存
1. #{}和${}的區別是什麼? #{}是預編譯處理,${}是字符串替換。 Mybatis 在處理#{}時,會將sql 中的#{}替換爲?號,調用PreparedStatement 的set方法來賦值; Mybatis 在處理${}時,就是把${}替換成變量的值。 使用#{}能夠有效的防止SQL 注入,提升系統安全性。 2. 一般一個Xml 映射文件,都會寫一個Dao 接口與之對應,請問,這個Dao 接口的工做原理是什麼?Dao 接口裏的方法,參數不一樣時,方法能重載嗎? Dao 接口,就是人們常說的Mapper 接口,接口的全限名,就是映射文件中的namespace的值,接口的方法名, 就是映射文件中MappedStatement 的id 值,接口方法內的參數,就是傳遞給sql 的參數。 Mapper 接口是沒有實現類的,當調用接口方法時,接口全限名+方法名拼接字符串做爲key 值,可惟必定位一個MappedStatement, 舉例: com.mybatis3.mappers.StudentDao.findStudentById,能夠惟一找到namespace 爲com.mybatis3.mappers.StudentDao 下面id = findStudentById 的MappedStatement。 在Mybatis 中,每個<select>、<insert>、<update>、<delete>標籤,都會被解析爲一個MappedStatement 對象。 Dao 接口裏的方法,是不能重載的,由於是全限名+方法名的保存和尋找策略。 Dao 接口的工做原理是JDK 動態代理,Mybatis 運行時會使用JDK 動態代理爲Dao接口生成代理proxy 對象, 代理對象proxy 會攔截接口方法, 轉而執行MappedStatement 所表明的sql,而後將sql 執行結果返回。 3. Mybatis 是如何進行分頁的?分頁插件的原理是什麼? Mybatis 使用RowBounds 對象進行分頁,它是針對ResultSet 結果集執行的內存分頁,而非物理分頁, 能夠在sql 內直接書寫帶有物理分頁的參數來完成物理分頁功能,也能夠使用分頁插件來完成物理分頁。 分頁插件的基本原理是使用Mybatis 提供的插件接口,實現自定義插件,在插件的攔截方法內攔截待執行的sql, 而後重寫sql,根據dialect 方言,添加對應的物理分頁語句和物理分頁參數。 4. Mybatis 是如何將sql 執行結果封裝爲目標對象並返回的?都有哪些映射形式? 第一種是使用<resultMap>標籤,逐必定義列名和對象屬性名之間的映射關係。 第二種是使用sql 列的別名功能,將列別名書寫爲對象屬性名,好比T_NAME AS NAME,對象屬性名通常是name, 小寫,可是列名不區分大小寫,Mybatis 會忽略列名大小寫,智能找到與之對應對象屬性名,你甚至能夠寫成T_NAME AS NaMe,Mybatis同樣能夠正常工做。 有了列名與屬性名的映射關係後,Mybatis 經過反射建立對象,同時使用反射給對象的屬性逐一賦值並返回,那些找不到映射關係的屬性,是沒法完成賦值的。 5. Xml 映射文件中,除了常見的select|insert|update|delete 標籤以外,還有哪些標籤? 注:這道題出自京東面試官。 還有不少其餘的標籤, 加上動態sql 的9 個標籤, trim|where|set|foreach|if|choose|when|otherwise|bind 等,其中爲sql 片斷標籤,經過標籤引入sql 片斷,爲不支持自增的主鍵生成策略標籤。 6. 簡述Mybatis 的插件運行原理,以及如何編寫一個插件 Mybatis 僅能夠編寫針對ParameterHandler 、ResultSetHandler 、StatementHandler、Executor 這4 種接口的插件,Mybatis 使用JDK 的動態代理, 爲須要攔截的接口生成代理對象以實現接口方法攔截功能,每當執行這4 種接口對象的方法時,就會進入攔截方法,具體就是InvocationHandler 的invoke()方法,當 然,只會攔截那些你指定須要攔截的方法。實現Mybatis 的Interceptor 接口並複寫intercept()方法,而後在給插件編寫註解,指定要攔截哪個接口的哪些方法便可, 記住,還須要在配置文件中配置你編寫的插件。 7. 一級、二級緩存 1) 一級緩存: 基於PerpetualCache 的HashMap 本地緩存, 其存儲做用域爲Session,當Session flush 或close 以後,該Session 中的全部Cache 就將清空。 2)二級緩存與一級緩存其機制相同,默認也是採用PerpetualCache,HashMap 存儲, 不一樣在於其存儲做用域爲Mapper(Namespace), 而且可自定義存儲源, 如 Ehcache。要開啓二級緩存,你須要在你的SQL 映射文件中添加一行:<cache/> 3 ) 對於緩存數據更新機制, 當某一個做用域( 一級緩存Session/ 二級緩存Namespaces)的進行了C/U/D 操做後,默認該做用域下全部select 中的緩存將被clear。 8. Mybatis 是否支持延遲加載?若是支持,它的實現原理是什麼? Mybatis 僅支持association 關聯對象和collection 關聯集合對象的延遲加載,association 指的就是一對一,collection 指的就是一對多查詢。 在Mybatis 配置文件中,能夠配置是否啓用延遲加載lazyLoadingEnabled=true|false。它的原理是,使用CGLIB 建立目標對象的代理對象, 當調用目標方法時,進入攔截器方法,好比調用a.getB().getName(),攔截器invoke()方法發現a.getB()是null值, 那麼就會單獨發送事先保存好的查詢關聯B 對象的sql,把B 查詢上來,而後調用a.setB(b),因而a 的對象b 屬性就有值了, 接着完成a.getB().getName()方法的調用。這就是延遲加載的基本原理。 9. Mybatis 映射文件中,若是A 標籤經過include 引用了B 標籤的內容,請問,B 標籤可否定義在A 標籤的後面,仍是說必須定義在A 標籤的前面? 雖然Mybatis 解析Xml 映射文件是按照順序解析的,可是,被引用的B 標籤依然能夠定義在任何地方,Mybatis 均可以正確識別。 原理是,Mybatis 解析A 標籤,發現A 標籤引用了B 標籤,可是B 標籤還沒有解析到,尚不存在,此時,Mybatis 會將A 標籤標記爲未解析狀態, 而後繼續解析餘下的標籤,包含B 標籤,待全部標籤解析完畢,Mybatis 會從新解析那些被標記爲未解析的標籤,此時再解析A 標籤時, B 標籤已經存在,A 標籤也就能夠正常解析完成了。 10. 簡述Mybatis 的Xml 映射文件和Mybatis 內部數據結構之間的映射關係? Mybatis 將全部Xml 配置信息都封裝到All-In-One 重量級對象Configuration 內部。在Xml 映射文件中,<parameterMap>標籤會被解析爲ParameterMap 對象, 其每一個子元素會被解析爲ParameterMapping 對象。<resultMap>標籤會被解析爲ResultMap 對象,其每一個子元素會被解析爲ResultMapping 對象。每個<select>、 <insert>、<update>、<delete>標籤均會被解析爲MappedStatement 對象,標籤內的sql 會被解析爲BoundSql 對象。
Mybatis 中有一級緩存和二級緩存,默認狀況下一級緩存是開啓的,並且是不能關閉的。 一級緩存是指SqlSession 級別的緩存,當在同一個SqlSession 中進行相同的SQL 語句查詢時, 第二次之後的查詢不會從數據庫查詢,而是直接從緩存中獲取,一級緩存最多緩存1024 條SQL。 二級緩存是指能夠跨SqlSession 的緩存。是mapper 級別的緩存,對於mapper 級別的緩存不一樣的sqlsession 是能夠共享的。
1.Mybatis 的一級緩存原理(sqlsession 級別) 第一次發出一個查詢sql,sql 查詢結果寫入sqlsession 的一級緩存中,緩存使用的數據結構是一個map。 key:MapperID+offset+limit+Sql+全部的入參 value:用戶信息 同一個sqlsession 再次發出相同的sql,就從緩存中取出數據。若是兩次中間出現commit 操做(修改、添加、刪除), 本sqlsession 中的一級緩存區域所有清空,下次再去緩存中查詢不到因此要從數據庫查詢,從數據庫查詢到再寫入緩存。 2.二級緩存原理(mapper 基本) 二級緩存的範圍是mapper 級別(mapper 同一個命名空間),mapper 以命名空間爲單位建立緩存數據結構,結構是map。 mybatis 的二級緩存是經過CacheExecutor 實現的。CacheExecutor實際上是Executor 的代理對象。全部的查詢操做, 在CacheExecutor 中都會先匹配緩存中是否存在,不存在則查詢數據庫。 key:MapperID+offset+limit+Sql+全部的入參 具體使用須要配置: 1. Mybatis 全局配置中啓用二級緩存配置 2. 在對應的Mapper.xml 中配置cache 節點 <setting name="cacheEnabled"value="true"/> 3. 在對應的select 查詢節點中添加useCache=true 清空緩存 flushCache="true" <settings> <!-- 開啓二級緩存的支持 --> <setting name="cacheEnabled" value="true"/> </settings>
相同點: 都是Java中ORM框架,屏蔽jdbc,api的底層訪問細節,咱們不用與jdbc api打交道就能夠完成對數據庫的持久化操做。 jdbc api編程流程固定,還將sql語句與Java代碼混雜在了一塊兒,常常須要拼湊sql語句,細節很繁瑣。 ibatis(mybatis)的好處:屏蔽jdbc api的底層訪問細節,將sql語句與Java代碼進行分離,提供了將結果集自動封裝成爲實體對象和對象的集合的功能。 queryforList返回對象集合,用queryforObject返回單個對象; 提供了自動將實體對象的屬性傳遞給sql語句的參數。 hibernate的好處: hibernate是一個全自動的ORM映射工具,它能夠自動生存sql語句,並執行並返回Java結果。 不一樣點: 1.hibernate要比ibatis(mybatis)功能強大不少,由於hibernate自動生存sql語句。 2.ibatis(mybatis)須要咱們本身在xml配置文件中寫sql語句,hibernate咱們沒法直接控制該語句, 咱們就沒法去寫特定的高效的sql,對於一些不太複雜的sql查詢,hibernate能夠很好地幫咱們完成,可是,對於複雜的查詢, hibernate就很難適應了,這時候用ibatis就是不錯的選擇,由於ibatis仍是由咱們本身寫sql語句。 3.ibatis要比hibernate簡單得多,ibatis是面向sql的,不用考慮對象間一些複雜的映射關係。 ———————————————————————————————————————————————————————————————————————— Hibernate是一個開放源代碼的對象關係映射框架,它對JDBC進行了很是輕量級的對象封裝,創建對象與數據庫表的映射。是一個全自動的、徹底面向對象的持久層框架。 Mybatis是一個開源對象關係映射框架,原名:ibatis,2010年由谷歌接管之後改名。是一個半自動化的持久層框架。 1 相同點 Hibernate與MyBatis均可以是經過SessionFactoryBuider由XML配置文件生成SessionFactory,而後由SessionFactory 生成Session,最後由Session來開啓執行事務和SQL語句。 其中SessionFactoryBuider,SessionFactory,Session的生命週期都是差很少的。Hibernate和MyBatis都支持JDBC和JTA事務處理。 2 二者區別 2.1 開發方面 在項目開發過程中,就速度而言: hibernate開發中,sql語句已經被封裝,直接能夠使用,加快系統開發; Mybatis 屬於半自動化,sql須要手工完成,稍微繁瑣; 可是,凡事都不是絕對的,若是對於龐大複雜的系統項目來講,發雜語句較多,選擇hibernate 就不是一個好方案。 2.2 sql優化方面 Hibernate 自動生成sql,有些語句較爲繁瑣,會多消耗一些性能; Mybatis 手動編寫sql,能夠避免不須要的查詢,提升系統性能; 2.3 對象管理比對 Hibernate 是完整的對象-關係映射的框架,開發工程中,無需過多關注底層實現,只要去管理對象便可; Mybatis 須要自行管理 映射關係; 2.4 緩存方面 相同點: Hibernate和Mybatis的二級緩存除了採用系統默認的緩存機制外,均可以經過實現你本身的緩存或爲其餘第三方緩存方案,建立適配器來徹底覆蓋緩存行爲。 不一樣點: Hibernate的二級緩存配置在SessionFactory生成的配置文件中進行詳細配置,而後再在具體的表-對象映射中配置是那種緩存。 MyBatis的二級緩存配置都是在每一個具體的表-對象映射中進行詳細配置,這樣針對不一樣的表能夠自定義不一樣的緩存機制。 而且Mybatis能夠在命名空間中共享相同的緩存配置和實例,經過Cache-ref來實現。 比較: Hibernate 具備良好的管理機制,用戶不須要關注SQL,若是二級緩存出現髒數據,系統會保存; Mybatis 在使用的時候要謹慎,避免緩存CAche 的使用。 Hibernate優點 Hibernate的DAO層開發比MyBatis簡單,Mybatis須要維護SQL和結果映射。 Hibernate對對象的維護和緩存要比MyBatis好,對增刪改查的對象的維護要方便。 Hibernate數據庫移植性很好,MyBatis的數據庫移植性很差,不一樣的數據庫須要寫不一樣SQL。 Hibernate有更好的二級緩存機制,能夠使用第三方緩存。MyBatis自己提供的緩存機制不佳。 Mybatis優點 MyBatis能夠進行更爲細緻的SQL優化,能夠減小查詢字段。 MyBatis容易掌握,而Hibernate門檻較高。 一句話總結 Mybatis:小巧、方便、高效、簡單、直接、半自動化 Hibernate:強大、方便、高效、複雜、間接、全自動化
Spring Boot 原理 Spring Boot 是由Pivotal 團隊提供的全新框架,其設計目的是用來簡化新Spring 應用的初始搭建以及開發過程。 該框架使用了特定的方式來進行配置,從而使開發人員再也不須要定義樣板化的配置。 經過這種方式,Spring Boot 致力於在蓬勃發展的快速應用開發領域(rapid applicationdevelopment)成爲領導者。其特色以下: 一、建立獨立的Spring應用程序 二、嵌入的Tomcat,無需部署WAR文件 三、簡化Maven配置 四、自動配置Spring 五、提供生產就緒型功能,如指標,健康檢查和外部配置 六、絕對沒有代碼生成和對XML沒有要求配置 經常使用註解 @SpringBootApplication 掃描到Configuration類並把它加入到程序的上下文,啓動類 @EnableAutoConfiguration 自動配置 @ComponetScan 組件掃描,可自動發現和裝配一些Bean @Autowierd 自動導入 @PathVariable 獲取參數
1 MyISAM存儲引擎 MyISAM引擎是MySQL數據庫最經常使用的; 它管理的表具備如下特性: 使用三個文件表示每一個表: a) 格式文件 — 存儲表的結構(mytable.frm) b) 數據文件 — 存儲表的數據(mytable.MYD) c) 索引文件 — 存儲表的索引(mytable.MYI) 可轉換爲壓縮、叧讀表來節省空間 2 InnoDB存儲引擎 InnoDB存儲引擎是MySQL數據庫的缺省引擎; 它管理的表具體有如下特徵: a) 每一個InnoDB表在數據庫目錄中以.frm格式文件表示 b) InnoDB表空間tablespace被用於存儲表的內容 c) 提供一組用來記錄事務性活勱的日誌文件 d) 用COMMIT(提交)、SAVEPOINT及ROLLBACK(回滾)支持事務處理 e) 提供所有ACID兼容 f) 在MySQL服務器崩潰後提供自動恢復 g) 多版本(MVCC)和行級鎖定 h) 支持外鍵及引用的完整性,包括級聯更新和刪除 3 MEMORY存儲引擎 使用MEMORY存儲引擎的表,由於數據存儲在內存中,且行的長度固定,因此使得MEMORY存儲引擎很是快; MEMORY存儲引擎管理的表具備下列特徵: a) 在數據庫目錄內,每一個表均以.frm格式文件表示; b) 表數據及索引被存儲在內存中; c) 表級鎖機制; d) 字段屬性不能包含TEXT或BLOB字段; MEMORY存儲引擎之前被稱爲HEAP引擎; 選擇合適的存儲引擎 MyISAM表最適合於大量的數據讀而少許數據更新的混合操做。MyISAM表的另外一種適用情形是使用壓縮的只讀表。 若是查詢中包含較多的數據更新操做,應使用InnoDB。其行級鎖機制和多版本的支持爲數據讀取和更新的混合提供了良好的併發機制。 使用MEMORY存儲引擎存儲非永久須要的數據,或者是可以從基於磁盤的表中從新生成的數據。
1) 開啓事務:start transaction 2) 結束事務:end transaction 3) 提交事務:commit transaction 4) 回滾事務:rollback transaction 隔離性有四個隔離級別 isolation 髒讀 不可重複讀 幻象讀 1) read uncommitted 讀未提交 2) read committed 讀已提交 3) repeatable read 可重複讀 MySQL數據庫管理系統默認隔離級別 4) serializable 串行化 查看當前會話級隔離級別 select @@tx_isolation; select @@session.tx_isolation; 查看當前全局隔離級別: @@global.tx_isolation; select @@global.tx_isolation;
索引(Index)是幫助DBMS高效獲取數據的數據結構。 分類:普通索引/惟一索引/主鍵索引/全文索引 普通索引:容許重複的值出現 惟一索引:除了不能有重複的記錄外,其它和普通索引同樣(用戶名、用戶身份證、email,tel) 主鍵索引:是隨着設定主鍵而建立的,也就是把某個列設爲主鍵的時候,數據庫就會給改列建立索引。這就是主鍵索引.惟一且沒有null值 全文索引:用來對錶中的文本域(char,varchar,text)進行索引, 全文索引針對MyIsam explain select * from articles where match(title,body) against(‘database’);【會使用全文索引】
ORACLE數據庫具備如下特色: 1 支持多用戶、大事務量的事務處理 2 數據安全性和完整性控制 3 支持分佈式數據處理 4 可移植性 數據文件dbf 數據文件是數據庫的物理存儲單位。數據庫的數據是存儲在表空間中的,真正是在某一個或者多個數據文件中。 而一個表空間能夠由一個或多個數據文件組成,一個數據文件只能屬於一個表空間。一旦數據文件被加入到某個表空間後,就不能刪除這個文件, 若是要刪除某個數據文件,只能刪除其所屬於的表空間才行。 表空間 表空間是Oracle 對物理數據庫上相關數據文件( ORA 或者 DBF 文件)的邏輯映射。 一個數據庫在邏輯上被劃分紅一到若干個表空間,每一個表空間包含了在邏輯上相關聯的一組結構。每一個數據庫至少有一個表空間 稱之爲 system 表空間。 每一個表空間由同一磁盤上的一個或多個文件組成,這些文件叫數據文件。一個數據文件只能屬於一個表空間。 VARCHAR2(30),DATE,NUMBER,CHAR(4),NUMBER(10,2) 數據類型: 1.字符型 1 CHAR : 固定長度的字符類型,最多存儲 2000 個字節 2 VARCHAR2 : 可變長度的字符類型,最多存儲 4000 個字節 LONG : 大文本類型。大文本類型。最大能夠存儲最大能夠存儲22個個GG 2.數值型數值型 NUMBER : 數值類型數值類型 例如: NUMBER (5) 最大能夠存的數爲 99999 NUMBER(5,2) 最大能夠存的數爲 999.99 3.日期型 1 DATE :日期時間型,精確到秒 2 TIMESTAMP :精確到秒的小數點後 9 位 4.二進制型(大數據類型)二進制型(大數據類型) CLOB : 存儲字符 最大能夠存 4 個 G BLOB :存儲圖像、聲音、 視頻等二進制數據 最多能夠存 4 個 G 序列是 ORACLE 提供的用於產生一系列惟一數字的 數據庫對象 。 select 序列名稱 .nextval from dual 提取下一個值 select 序列名稱 .currval from dual 提取當前值
1 普通索引 create index 索引名稱 on 表名 列名; 2 惟一索引 create unique index 索引名稱 on 表名 列名; 3 複合索引 create index 索引名稱 on 表名 列名 列名...; 4 反向鍵索引 create index 索引名稱 on 表名 列名 reverse; 應用場景:當某個字段的值爲連續增加的值,若是構建標準索引,會造成歪脖子樹。這樣會增長查詢的層數,性能會降低。 創建反向鍵索引,能夠使索引的值變得不規則,從而使索引樹可以均勻分佈。 5 位圖索引 語法:create bitmap index 索引名稱 on 表名 列名; 使用場景:位圖索引適合建立在低基數列上位圖索引不直接存儲ROWID ,而是存儲字節位到 ROWID 的映射 優勢:減小響應時間,節省空間佔用
在Oracle中,數據完整性能夠使用約束、觸發器、應用程序(過程、函數)三種方法來實現, 在這三種方法中,由於約束易於維護,而且具備最好的性能,因此做爲維護數據完整性的首選。 約束的分類 not null(非空) unique(惟一) primary key(主鍵) foreign key(外鍵) check 存儲過程:是大型的SQL語句集,用於在大型數據庫系統中完成特定的功能。 存儲在數據庫中,編譯後永久有效,用戶經過指定存儲過程的名稱並指定參數(若是存儲過程具備參數)來執行。 觸發器: 觸發器是用戶得以在關係表上的一類有事件驅動的數據庫對象,也是一種保證數據完整性的方法; 觸發器一旦定義,無需用戶調用,任何對錶的修改操做均由數據庫服務器自動激活相應的觸發器。 觸發器的主要做用是實現主鍵和外鍵不能保證的複雜的參照關係性和數據的一致性,從而保護表中數據; 觸發器與存儲過程的惟一區別是觸發器不能執行EXECUTE語句調用,而是在用戶執行Transact-SQL語句時自動觸發執行。
redis是一個key-value的nosql數據庫,先存到內存中,會根據必定的策略持久化到磁盤,即便斷電時也不會丟失數據,支持的數據類型比較多。 主要用來作緩存數據庫的數據和web集羣時看成中央緩存存放session 使用場景: 緩存:把常常須要查詢的,不多修改的數據,放到讀取速度很快的空間(內存),以便下次訪問時減小訪問時間,減輕壓力。 計數器:redis中的計數器是原子性的內存操做。能夠解決庫存溢出問題,進銷系統庫存溢出。 session緩存服務器:web集羣時做爲session緩存服務器緩存隊列等。 String 字符串 hash 鍵值對 list 鏈表 set 集合 zset 有序集合 Redis 持久化? RDB 持久化 (默認) : 在指定的時間間隔內將內存中的數據集快照寫入磁盤。 dump.rdb 在指定的時間間隔內生成數據集的時間點快照,適用於進行備份,能夠最大化Redis性能 AOF 持久化 : 記錄服務器執行的全部寫操做命令,並在服務器啓動時,經過從新執行這些命令來還原數據集, 使用AOF持久化會讓Redis變的很是耐久,對於相同數量的數據集而言,AOF文件一般是要大於RDB文件,AOF在運行效率上每每會慢於RDB。 appendonly.aof 觸發RDB快照 1 在指定的時間間隔內,執行指定次數的寫操做 2 執行save(阻塞, 只管保存快照,其餘的等待) 或者是bgsave (異步)命令 3 執行flushall 命令,清空數據庫全部數據,意義不大。 4 執行shutdown 命令,保證服務器正常關閉且不丟失任何數據,意義...也不大。 對於RDB來講,提供了三種機制:save、bgsave、自動化。 自動觸發是由咱們的配置文件來完成的。在redis.conf配置文件中。 二者的區別: RDB持久化是指在指定的時間間隔以內將內存中的數據集快照寫入磁盤,實際操做過程是fork一個子程序,現將數據集寫入臨時文件.寫入成功後,在替換以前的文件,用二進制壓縮存儲 AOF持久化以日誌的形式記錄服務器所處理的每個寫/刪除操做,查詢操做不會記錄,以文本形式記錄,能夠打開文本查看詳細的操做記錄。
java動態代理 目標類 切面類 proxy.newProxyInstance( 類加載器, 代理類須要實現的全部接口(數組), new InvocationHandler(){ @Override public Object invoke(代理對象,方法,方法的實際參數)throws throwable{ method.invoke(userService,args); } } ); CGLIB字節碼加強 public class MyBeanFactory {; public static UserServiceImpl createService(){; 目標類 final UserServiceImpl userservice = new UserServiceImpl(); 代理類 final MyAspect aspect = new MyAspect(); 核心類:Enhance enhance = new Enhance(); 獲取代理類的子類:enhance.setSuperClass(userservice.getClass()); 設置回調函數:enhance.callback(new MethodInterceptor(){ @Override public Object intercept(Object proxy,Method method,Object[] args,方法的代理 methodProxy){ 前方法 目標方法 Object obj = method.invoke(userservice,args); 執行代理類的父類 methodProxy.invokeSuper(proxy,args); 後方法 return obj; } }); 建立代理: UserServiceImpl proxyservice = (UserServiceImpl)enhance.create(); return proxyservice;
簡單地說,就是一個變量和常量的關係。 StringBuffer 對象的內容能夠修改; 而String 對象一旦產生後就不能夠被修改,從新賦值實際上是兩個對象。 StringBuffer 的內部實現方式和String 不一樣, StringBuffer 在進行字符串處理時,不生成新的對象,在內存使用上要優於String 類。 因此在實際使用時,若是常常須要對一個字符串進行修改,例如插入、刪除等操做,使用StringBuffer 要更加適合一些。 Sting StringBuilder StringBuffer 區別: 1.String 是內容不可變的字符串,String底層使用了一個不可變的字符數組(final char value[]); 2.StirngBuilder StringBuffer,是內容能夠改變的字符串,底層(char[] value) 最經典的就是拼接字符串。 1.String進行拼接String c ="a"+"b"; 2.StringBuilder或者StringBuffer; StringBuilder sb = new StringBuilder(); sb.apend("a").apend("b"); 拼接字符串不能使用String進行拼接,要使用StirngBuilder StringBuffer。 StringBuilder是線程不安全的,效率較高。默認長度爲16 StringBuffer 是線程安全的,效率較低。初始長度爲16
Java 提供和支持建立抽象類和接口。 它們的實現有共同點,不一樣點在於: 接口中全部的方法隱含的都是抽象的。而抽象類則能夠同時包含抽象和非抽象的方法。 類能夠實現不少個接口,可是隻能繼承一個抽象類 類能夠不實現抽象類和接口聲明的全部方法,固然,在這種狀況下,類也必須得聲明成是抽象的。 抽象類能夠在不提供接口方法實現的狀況下實現接口。 Java 接口中聲明的變量默認都是final 的。抽象類能夠包含非final 的變量。 Java 接口中的成員函數默認是public 的。抽象類的成員函數能夠是private , protected 或者是public 。 接口是絕對抽象的,不能夠被實例化。抽象類也不能夠被實例化,可是,若是它包含main 方法的話是能夠 被調用的。 抽象類(abstract class)和接⼝(interface)有什麼異同? 成員區別: 抽象類成員能夠是變量或常量,擁有構造方法,能夠有抽象方法和非抽象⽅法; 接口⽐抽象更抽象 接⼝的成員變量量只能是常量,沒有構造方法,都是抽象⽅法. 關係區別: 抽象類子類使用extends關鍵字來繼承,只能單繼承或者多重繼承; 接口使用implements來實現,能夠多實現 設計理念: 抽象類的方法能夠由public,protected和defalut修飾符; ⽽而接口默認修飾符只有public;
cookie 是Web服務器發送給瀏覽器的一塊信息。 瀏覽器會在本地文件中給每個Web服務器存儲cookie。之後瀏覽器在給特定的Web服務器發請求的時候,同時會發送全部爲該服務器存儲的cookie。 session 和cookie 的區別: 不管客戶端瀏覽器作怎麼樣的設置,session 都應該能正常工做。 客戶端能夠選擇禁用cookie ,可是session 仍然是可以工做的,由於客戶端沒法禁用服務端的session 。 在存儲的數據量方面session 和cookies 也是不同的。 session 可以存儲任意的Java 對象, cookie 只能存儲String 類型的對象。
session和cookie都是會話(session)跟蹤技術。 cookie經過在客戶端記錄信息肯定用戶身份,session經過在服務器端記錄信息肯定用戶身份。 可是session的實現依賴於cookie,sessionID(session的惟一標識符須要存放在客戶端)。 cookie和session的區別: 1.cookie數據存放在客戶的瀏覽器上,session數據存放在服務器上。 2.cookie不是很安全,別人能夠分析存放在本地的cookie並進行cookie欺騙,考慮到安全應該使用session。 3.session會在必定的時間內保存在服務器上。當訪問增多,會比較佔用服務器性能,考慮到服務器性能方面,應該使用cookie。 4.單個cookie保存的數據不能超過4k,不少瀏覽器限制一個站點最多保存20個cookie。 5.因此建議: 將登陸信息等重要信息存放在session其餘信息若是須要保留,能夠放在cookie中。 好比購物車最好使用cookie,可是cookie是能夠在客戶端禁用的,這時候咱們要使用cookie+數據庫的方式實現,當從cookie中不能取出數據時,就從數據庫獲取。
sendRedirect() 方法會建立一個新的請求,而forward() 方法只是把請求轉發到一個新的目標上。 重定向(redirect) 之後,以前請求做用域範圍之內的對象就失效了, 由於會產生一個新的請求,而轉發(forwarding) 之後,以前請求做用域範圍之內的對象仍是能訪問的。通常認爲sendRedirect() 比forward() 要慢 1.forward是服務器端的轉向,而redirect是客戶端的跳轉。 2.使用forward瀏覽器的地址不會發生改變,而redirect會發生改變。 3.forward是一次請求中完成的,而redirect是從新發起請求。 4.forward是在服務器端完成,而不用客戶端從新發起請求,效率較高。
1)模型Model:應用對象。 模型是應用程序的主體部分。模型表明了業務數據和業務邏輯; 當數據發生改變時,它要負責通知視圖部分;一個模型能爲多個視圖提供數據。 因爲同一個模型能夠被多個視圖重用,因此提升了應用的可重用性。 (2)視圖View:數據的展示。 視圖是用戶看到並與之交互的界面。視圖向用戶顯示相關的數據,並能接收用戶的輸入數據,可是它並不進行任何實際的業務處理。 視圖能夠向模型查詢業務狀態,但不能改變模型。視圖還能接受模型發出的數據更新事件,從而對用戶界面進行同步更新。 (3)控制器Controller :邏輯處理、控制實體數據在視圖上展現、調用模型處理業務請求。 當 Web 用戶單擊 Web 頁面中的提交按鈕來發送 HTML 表單時,控制器接收請求並調用相應的模型組件去 處理請求,而後調用相應的視圖來顯示模型返回的數據。
(1)Transient( 瞬態) : 一個實體經過new操做符建立後,沒有和Hibernate 的Session 創建關係,也沒有手動賦值過該實體的持久化標識(持久化標識能夠認爲映射表的主鍵)。 此時該實體中的任何屬性的更新都不會反映到數據庫表中。 (2)persistent( 持久態) : 當一個實體和Hibernate 的Session 建立了關係,並獲取了持久化標識,並且在Hibernate 的Session 生命週期內存在。 此時針對該實體任何屬性的更改都會直接影響到數據庫表中一條記錄對應字段的更新,也即與對應數據庫表保持同步。 (3)Detached( 遊歷態) : 當一個實體和Hibernate 的Session 建立了關係,並獲取了持久化標識,而此時Hibernate 的Session 的命週期結束,實體的持久化標識沒有被改動過。 針對該實體的任何屬性的修改都不會及時反映到數據庫表中。
List (有序可重複) arrayList : 基於動態數組的數據結構 (有序,可重複,關注的是索引,查詢速度快,默認長度10 ,擴容的長度爲當前長度的1.5倍) linkedList : 基於鏈表的數據結構(增刪操做較多) Set (無序,不可重複) HashSet(不能存儲重複的元素,存儲順序和取出的順序不必定一致Set集合沒有索引,默認長度16 ,當長度的負載因子達到0.75 會⾃自動擴容,擴容的長度爲當前長度乘以*2 Map (無序,不可重複 ,查詢快, 非同步的線程是不安全的 能夠存null值) HashMap : 底層數組加鏈表 存儲的格式是鍵值對 它根據鍵的HashCode值存儲數據 (JDK1.8 之後當鏈表長度大於8時,會轉成紅黑數,默認長度爲16 ,當負載因子達到0.75,就會自動擴容,擴容的長度爲當前長度*2 , 構造一個空的 HashMap 在把原來的HashMap 存⼊新的HashMap當中, 因此他存儲的位置可能會有變化 可否讓HashMap同步? HashMap能夠經過下面的語句進行同步: Map m = Collections.synchronizeMap(hashMap); Hashtable (不不容許存null值,同步的,因此線程是安全的) HashSet和HashMap的區別 *HashMap* *HashSet* HashMap實現了Map接口; HashSet實現了Set接⼜ HashMap儲存鍵值對 HashSet僅僅存儲對象 使⽤put()⽅法將元素放⼊map中 使⽤add()⽅法將元素放⼊set中
七、final、finally、finalize的區別?
final : 用於聲明屬性,方法和類, 分別表示屬性不可被更改,⽅法不可覆蓋, 被修飾的類不可被繼承 finally : 異常處理語句結構的一部分,表示老是執行. (通常用來釋放資源) finalize : Object 類的一個方法, 在垃圾收集器執行的時候回調用被回收對象的此方法,能夠覆蓋此方法提供垃圾收集時的其餘資源回收,例如關閉文件等.
clone() :建立並返回此對象的⼀一個副本(克隆) equals():判斷是否相等 finalize():垃圾回收器方法 getClass():返回類對象 hashCode():返回哈希碼值 notify():喚醒在此對象等待的單個線程 notifyAll():喚醒在此對象等待的全部線程 toString():返回的對象用字符串來表示 wait():是線程等待
1.就緒(Runnable):線程準備運行,不必定立刻開始運行; 2.運行中(Running):進程正在執行線程的代碼; 3.等待中(Waiting):線程處於阻塞的狀態,等待外部的處理結束; 4.睡眠中(Sleep):線程被強制睡眠; 5.I/O阻塞(BlockedonI/O):等待I/O操做完成。 6.同步阻塞(BlockedonSynchronization):等待獲取鎖。 7.死亡(Dead):線程完成了了執⾏行行。
反射就是在運行狀態當中,對於任意的一個類,可以獲取這個類的全部屬性和方法,對於任何一個對象, 都能調用它的任意一個方法,這種動態獲取的信息及動態調用對象方法的功能稱爲反射; 反射機制的優勢就是能夠實現動態的建立對象和編譯,體現出很大的靈活性,缺點是對性能有影響. 獲取字節碼有三種⽅方法: 1. 根據類名: 類名.class; 2.根據對象: 對象.class; 3.根據全限定名 : Class.forName(全限定類名)
建立型模式(5種):工廠方法模式,抽象工廠模式,單例例模式,建造者模式,原型模式。 結構型模式(7種):適配器模式,裝飾器模式,代理模式,外觀模式,橋接模式,組合模式,享元模式。 行爲型模式(11種):策略模式、模板方法模式、觀察者模式、迭代子模式、責任鏈模式、命令模式、備忘錄模式、狀態模式、訪問者模式、中介者模式、解釋器器模式。 單例設計模式的通常定義: 一個類中只容許有一個實例; 實現思路 : 讓類的構造方法私有化, 同時提供一個靜態方法去實例化這個類; 懶漢式: 當你是使用這個對象的時候它纔會建立這個對象,時間換空間(線程不不安全的) 餓漢式: 當這個類初始化的時候就會建立好這個對象,空間換時間 (線程是安全的,推薦使用) 簡單⼯廠設計模式的通常定義: 簡單工廠又稱爲靜態工廠,由工廠對象決定建立哪個產品對象. 實現思路: 寫一個,讓他製造出咱們想要的對象. 適配器設計模式的通常定義: 某類繼承這個適配器,從而實現咱們須要實現的方法; 實現思路:經過寫一個適配器類,里面寫了全部的抽象方法,但這些方法是空的,而且適配器類要定義成抽象的, 若是適配器類能夠本身實現就沒有意義了,適配器的做用,繼承適配器,簡化操做. 模板設計模式的通常定義:定義一個算法骨架將具體實現交給子類去實現, 實現思路:在類中定義一個抽象方法,具體實現交給子類去實現; 裝飾者設計模式的通常定義:就是給一個對象增長一些新功能,並且是動態的; 實現思路: 要求裝飾對象和被裝飾對象實現同一個接口,裝飾對象持有被裝飾對象的實例;
第一步:Class.forName()加載數據庫鏈接驅動; 第二步:DriverManager.getConnection()獲取數據鏈接對象; 第三步:根據SQL獲取sql會話對象,有2種⽅方式 Statement、PreparedStatement; 第四步:執行SQL處理結果集,執行SQL前若是有參數值就設置參數值setXXX(); 第五步:關閉結果集、關閉會話、關閉鏈接
① GET請求的數據會附在URL以後(就是把數據放置在HTTP協議頭中),以?分割URL和傳輸數據,參數之間以&相連. ② GET方式提交的數據最多隻能是1024字節,理論上POST沒有限制,可傳較大量的數據。 ③ POST的安全性要比GET的安全性高。 經過GET提交數據,用戶名和密碼將明文出如今URL上, (1)登陸頁面有可能被瀏覽器緩存 (2)其餘人查看瀏覽器的歷史紀錄,那麼別人就能夠拿到你的帳號和密碼了. GET和POST請求都是http的提交方式,用戶經過不一樣的http的提交方式完成對資源(url)的操做。 具體點來說get通常用於獲取/查詢 資源信息,而post通常用於更新資源信息。 http定義了與服務器交互的不一樣方法,最基本的方法有四種,分別是get,post,put,delete URL全稱是資源描述符,咱們能夠這樣認爲: 一個URL地址,它用於描述一個網絡上的資源,而http中的get,post,put,delete就對應着這個資源的查,改,增,刪 4個操做。 具體點來說get通常用於獲取/查詢 資源信息,而post通常用於更新資源信息。 1.get請求提交的數據會在地址欄顯示出來,而post請求不會在地址欄顯示出來。 get提交,請求的數據會附在URL以後(就是把數據放置在http協議中),以?分割URL和傳輸數據,多個參數用&鏈接; post提交是把提交的數據放置在http包的包體中。所以,get提交的數據會在地址欄中顯示出來,而post提交,地址欄不會改變。 2.傳輸數據的大小 get請求因爲瀏覽器對地址長度的限制而致使傳輸的數據有限制。 而post請求不會由於地址長度限制而致使傳輸數據限制。 3.安全性,post的安全性要比get的安全性要高。 因爲get方法的數據會在地址中呈現,因此能夠經過歷史記錄找到密碼等關鍵信息。
利用Redis作session共享 ; 方案是重寫服務器中的HttpSession和HttpservletRequest, 首先實現HttpSession接口, 重寫session全部的方法,將session以hash值得方式存在Redis中,一個session的key就是sessionID, setAtrribute重寫以後就是更新Redis中的數據,getAttribute重寫以後就是Redis中的數據,等須要將 HttpSession 的接口一一實現。 解決方案一:基於Nginx的ip_hash 負載均衡 其實就是對請求過來的ip地址對你的多少臺可用的服務器進行取模,而後就會把你的請求經過Nginx的反向代理給分發到對應的服務器上。 (這裏會把可用的服務器放到一個數組中,若是取模獲得的結果是幾,就把請求分到服務器數組中的下標爲幾 的服務器上) 一、優勢:實現簡單,對應用無侵入性,無額外開銷。 二、缺點:一旦某個web服務器重啓或宕機,相對應的session的數據將會丟失,且其對nginx負載均衡的能力進行了弱化。 解決方案二:基於Tomcat的session複製 這個解決方案其實就是當用戶請求的時候,把產生的sessionID給複製到系統全部的服務器中, 這樣就能保證當用戶請求的時候從服務器A可能調用到服務器B上的模塊的時候,也能保證服務B也有該用戶的sessionID,這樣就不會再次讓用戶進行再次登陸操做了。也就解決問題了。 代碼實現:修個Tomcat配置文件 一、修改server.xml中的Cluster節點; 二、修改應用web.xml,增長節點:<distributable/> 一、優勢:對應用無侵入性,重啓或宕機不會形成session的丟失。 二、缺點:須要依賴支持session複製的web服務器,在數據量很大的狀況下不只佔用網絡資源,並且會致使延遲。只適用於web服務器比較少且session數據量少的狀況。 解決方案三:使用Redis作緩存session的統一緩存 這種方案呢,其實就是把每次用戶的請求的時候生成的sessionID給放到Redis的服務器上。 而後在基於Redis的特性進行設置一個失效時間的機制,這樣就能保證用戶在咱們設置的Redis中的session失效時間內,都不須要進行再次登陸。 代碼實現:修改應用的配置文件 一、增長redis client和spring session的依賴; 二、修改web.xml,增長filter:springSessionRFilter; 三、修改spring配置文件,在容器中注入spring session和Redis相關的bean; 解決方案四: 其實還能夠把session放到cookie中去,由於每次用戶請求的時候,都會把本身的cookie放到請求中, 因此這樣就能保證每次用戶請求的時候都能保證用戶在分佈式環境下,也不會在進行二次登錄。
tomcat的server.xml
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"channelSendOptions="8"> <Manager className="org.apache.catalina.ha.session.DeltaManager" expireSessionsOnShutdown="false" notifyListenersOnReplication="true" /> <Channel className="org.apache.catalina.tribes.group.GroupChannel"> <Membership className="org.apache.catalina.tribes.membership.McastService" bind="127.0.0.1" address="228.0.0.4" <!--保留ip,用於廣播-->port="45564" frequency="500" dropTime="3000"/> <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver" address="auto"port="4001" <!--若是是在同一臺機器上的兩個tomcat作負載,則此端口則不能重複--> autoBind="100" selectorTimeout="5000" maxThreads="6"/> <Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter"> <Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/> </Sender> <Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/> <Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor"/> </Channel> <Valve className="org.apache.catalina.ha.tcp.ReplicationValve" filter=""/> <Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/> <Deployer className="org.apache.catalina.ha.deploy.FarmWarDeployer" tempDir="/tmp/war-temp/" deployDir="/tmp/war-deploy/" watchDir="/tmp/war-listen/" watchEnabled="false"/> <ClusterListener className="org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener"/> <ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/> </Cluster>
jsp本質就是一個servlet,每一個jsp頁面都是一個servlet 的實例 Servlet是由java提供用於開發web服務器應用程序的一個組件,用來生成動態內容 jsp和Servlet有什麼區別 : 1.jsp是html頁面中內嵌的java代碼,側重頁面顯示 2.Servlet是html代碼和java代碼分離,側重邏輯控制,MVC設計思想中JSP位於視圖層,servlet位於控制層
Servlet(Sever Applet),全稱Java servlet,是用Java編寫的服務器端程序。 servlet是指任何實現了這個servlet接口的類。 其主要功能在於交互式地瀏覽和修改數據,生成動態web內容。servlet運行於支持Java的應用服務器中。 HttpServlet重寫doGet和doPost方法或者你也能夠重寫service方法完成對get和post請求的響應。
jsp和Servlet的相同點和不一樣點? jsp是servlet技術的擴展,全部的jsp文件都會被翻譯爲一個繼承HttpServlet的類,也就是說jsp最終也是一個servlet。這個servlet對外提供服務。 servlet和jsp最主要的不一樣點在於,jsp側重於視圖,servlet主要用於控制邏輯。 servlet的應用邏輯是在Java文件中,而且徹底從表示層中的HTML裏分離開來,(servlet若是要實現HTML的功能,必須使用Writer輸出對應的html) 而jsp的狀況是Java和HTML能夠組合成一個擴展名爲.jsp的文件。
servlet有良好的生存期的定義,包括加載和實例化、初始化、處理請求以及服務結束,這個生存週期由javax.servlet.Servlet接口的init,service和destroy方法表達。 加載servlet的class-->實例化servlet-->調用servlet的init完成初始化--> 響應請求(servlet的service方法)其中(doget,dopost方法) -->servlet容器關閉時(servlet的destroy方法)
9個內置對象: request 用戶端請求,此請求會包含來自get/post請求的參數 response 網頁傳回用戶端的迴應 pageContext 網頁的屬性在這裏進行管理 session 與請求有關的會話期 application servlet 正在執行的內容 out 用來傳送回應的輸出 config servlet 架構部件 page jsp 網頁自己 exception 針對錯誤網頁,未捕捉的例外 九大隱式對象 輸入/輸出對象: request response out 做用域通訊對象: session application pageContext Servlet 對象: page config 錯誤對象: exception
jsp傳遞值 request,session,application,cookie
(1)pageContext,在當前jsp頁面有效,跳到其它頁面失效 (2)request,指一次請求範圍內有效,從http請求到服務器處理結束,返回響應的整個過程。在這個過程當中使用forward(請求轉發)方式跳轉多個jsp,在這些頁面里你均可以使用這個變量 (3)session,指當前會話有效範圍,瀏覽器從打開到關閉過程當中,轉發、重定向都可以使用 (4)application context域-指只能在同一個web中使用,服務器未關閉或者重啓,數據就有效
Ajax 是一種建立交互式網頁應用的網頁開發技術, 經過異步模式,提高了用戶體驗,優化了瀏覽器和服務器之間傳輸,減小不必要的數據往返,減小了寬帶的佔用. 最大的特色是能夠實現局部刷新,在不更新整個頁面的前提下維護數據.
建立目錄和移除目錄:mkdir rmdir 建立文件: touch 文件名稱 打包並壓縮:tar -zcvf 解壓壓縮包: tar -xvf 查找字符串:grep 顯示當前所在目錄:pwd 建立空文件:touch 編輯器:vim vi 刪除:rm -rf 修改: mv 目錄名稱 新目錄名稱 動態打印日誌信息:tail –f 日誌文件 列出文件列表:ls 【參數 -a -l】
1.當只要一行數據的時候實例 limit 1 2.選擇正確的數據庫引擎, MyISAM 適用於一些大量查詢的應用, InnoDB的寫操做比較優秀 3.⽤用not exists代替not in 4.充分使⽤用索引, B-TREE 仍然是最高效的索引之中的一個 5.用 NOSQL 的方式使用 MYSQL
1.存儲過程只在建立時進行編譯,之後每次執行存儲過程都不須要從新編譯,而通常sql語句每執行一次就編譯一次,所以使用存儲過程能夠大大提升數據庫執行速度。 2.一般,複雜的業務邏輯須要不少條sql語句。這些語句要分別從客戶機發送到服務器,當客戶機和服務器之間的操做不少時, 將產生大量的網絡傳輸,若是將這些操做放在一個存儲過程當中,那麼客戶機和服務器之間的網絡傳輸就會大大減小,下降了網絡負載。 3.存儲過程建立一次即可以重複使用,從而能夠減小數據庫開發人員的工做量。 4.安全性高,存儲過程能夠屏蔽對底層數據庫對象的直接訪問,使用execute權限調用存儲過程,無需擁有訪問底層數據庫對象的顯式權限。
大多數咱們都使用PreparedStatement代替 1.PreparedStatement是預編譯的,比Statement速度快 2.代碼的可讀性和可維護性要好。 雖然使用PreparedStatement來代替statement會使代碼多出幾行,但這樣的代碼不管從可讀性仍是可維護性上來講,都比直接用statement的代碼高不少檔次。 3.安全性 PreparedStatement 能夠防止sql注入攻擊,而statement卻不能 (若是使用預編譯語言你傳入的任何內容都不會和原來的語句發生任何匹配的關係,只要全使用預編譯語言,就不用對傳入的數據作任何過濾, 而若是使用普通的statement,有可能要對drop等作費盡心思的判斷和過濾)
1.數據庫設計方面 a.對查詢進行優化,盡量避免全表掃描 b.應盡量避免在where子句中對字段使用null判斷 c.索引並不是越多越好,索引當然能夠提升select的效率,但同時也下降了insert及update操做 d.盡量使用數字型字段,是由於引擎在處理查詢和鏈接時會逐個比較字符串中每一個字符,而對於數字一次就能夠了 e.避免頻繁建立和刪除臨時表,以減小系統表資源的消耗 2.語句句⽅方⾯面 f.應盡量避免在where子句中使用!= 或<.> 操做符,或者是or來鏈接條件, 不然將引擎放棄使用索引而使用全表掃描 g.任何地方都不要使用 select * from T , 用具體的字段來代替(*), 不要返回用不到的字段 h.用not exists代替not in 3.java方面 i.合理利用內存,有的數據要緩存. 優化10 大策略 策略1.儘可能全值匹配 當創建了索引列後,能在wherel 條件中使用索引的儘可能所用。 策略2.最佳左前綴法則 若是索引了多列,要遵照最左前綴法則。指的是查詢從索引的最左前列開始而且不跳過索引中的列。 策略3.不在索引列上作任何操做 不在索引列上作任何操做(計算、函數、(自動or 手動)類型轉換),會致使索引失效而轉向全表掃描 策略4.範圍條件放最後 中間有範圍查詢會致使後面的索引列所有失效 策略5.覆蓋索引儘可能用 儘可能使用覆蓋索引(指一個查詢語句的執行只用從索引中就可以取得,沒必要從數據表中讀取,),減小select * 策略6.不等於要慎用 mysql 在使用不等於(!= 或者<>)的時候沒法使用索引會致使全表掃描,若是必定要須要使用不等於,請用覆蓋索引 策略7.Null/Not 有影響 注意null/not null 對索引的可能影響 一、自定定義爲NOT NULL 在字段爲not null 的狀況下,使用is null 或is not null 會致使索引失效 解決方式:覆蓋索引 二、自定義爲NULL 或者不定義 Is not null 的狀況會致使索引失效 解決方式:覆蓋索引 策略8.Like 查詢要小心 like 以通配符開頭('%abc...')mysql 索引失效會變成全表掃描的操做 解決方式:覆蓋索引 策略9.字符類型加引號 字符串不加單引號索引失效 解決方式:請加引號 策略10.OR 改UNION 效率高 解決方式:若是必定要用OR,那麼使用覆蓋索引
內鏈接 : 也被稱爲天然鏈接,只有兩個表相同的數據纔會出如今結果集中. 外連接不僅包含符合鏈接條件的行, 1.左外連接(左邊的表不加限制) 2.右外連接(右邊的表不加限制) 3.全外連接(左右兩表都不加限制)
HTTP : 是一個客戶端和服務端請求和答應的標準 HTTPS : 是以安全爲目的的HTTP通道,簡單講師HTTP的安全版 區別: 1: HTTPS協議須要到CA申請證書,通常免費的較少,於是要收取必定的費用 2:HTTP是超文本傳輸協議,信息是明文傳輸,HTTPS則是具備安全性的SSL加密傳輸協議 3:HTTP和HTTPS使用的是徹底不同的鏈接方式,用的端口也不同樣,HTTP : 80 HTTPS : 443 4.HTTP鏈接很簡單,是無狀態的, HTTPS協議是由SSL+HTTP協議構建的可進行加密傳輸,身份驗證的網絡協議,比HTTP協議安全 HTTP常⻅見的狀態碼有哪些 200 OK //客戶端請求成功 301 Moved Permanently(永久移除),請求的URL已移走。Response中應該包含一個Location URL, 說明資源現 在所處的位置 302 found 重定向 400 Bad Request //客戶端請求語法錯誤,不能被服務器所理解 401 Unauthorized //請求未經受權,這個狀態碼必須和WWW-Authenticate報頭域一塊兒使用 403 Forbidden //服務器接受到請求,可是拒絕提供服務 404 Not Found //請求資源不存在, 輸入了錯誤URL 500 Internal Server Error //服務器內部錯誤 503 Server Unavailable // 服務器當前不能處理客戶端請求,一段時間後可能恢復正常
TCP用於可靠傳輸的狀況, 應用於文件傳輸, 重要狀態更新等場景 UDP用於對高速傳輸和實時性要求較高的通訊領域。廣播 TCP協議 TCP協議全稱: 傳輸控制協議, 顧名思義, 就是要對數據的傳輸進行必定的控制. TCP(Transmission Control Protocol,傳輸控制協議)是面向鏈接的協議,也就是說,在收發數據前,必須和對方創建可靠的鏈接。 三次握手 第一次: 客戶端 - - > 服務器 此時服務器知道了客戶端要創建鏈接了。 請求的SYN報文 第二次: 客戶端 < - - 服務器 此時客戶端知道服務器收到鏈接請求了。 確認應答(ACK)和同步序列號(SYN) 第三次: 客戶端 - - > 服務器 此時服務器知道客戶端收到了本身的迴應。 確認應答(ACK) 到這裏, 就能夠認爲客戶端與服務器已經創建了鏈接. 四次揮手 第一次: 當主機A完成數據傳輸後,將控制位FIN置1,提出中止TCP鏈接的請求 ; 第二次: 主機B收到FIN後對其做出響應,確認這一方向上的TCP鏈接將關閉,將ACK置1; 第三次: 由B端再提出反方向的關閉請求,將FIN置1 ; 第四次: 主機A對主機B的請求進行確認,將ACK置1,雙方向的關閉結束.。 爲何創建鏈接是三次握手,關閉鏈接確是四次揮手呢? 創建鏈接的時候,服務器在LISTEN狀態下,收到創建鏈接請求的SYN報文後,把ACK和SYN放在一個報文裏發送給客戶端。 而關閉鏈接時,服務器收到對方的FIN報文時,僅僅表示對方再也不發送數據了可是還能接收數據,而本身也未必所有數據都發送給對方了, 因此己方能夠當即關閉,也能夠發送一些數據給對方後,再發送FIN報文給對方來表示贊成如今關閉鏈接,所以,己方ACK和FIN通常都會分開發送,從而致使多了一次。 TCP協議規定,主動關閉鏈接的一方要處於TIME_ WAIT狀態,等待2*MSL(maximum segment lifetime)的時間後才能回到CLOSED狀態.
源端口號/目的端口號: 表示數據從哪一個進程來, 到哪一個進程去. 32位序號: 4位首部長度: 表示該tcp報頭有多少個4字節(32個bit) 6位保留: 顧名思義, 先保留着, 以防萬一 6位標誌位 URG: 標識緊急指針是否有效 ACK: 標識確認序號是否有效 PSH: 用來提示接收端應用程序馬上將數據從tcp緩衝區讀走 RST: 要求從新創建鏈接. 咱們把含有RST標識的報文稱爲復位報文段 SYN: 請求創建鏈接. 咱們把含有SYN標識的報文稱爲同步報文段 FIN: 通知對端, 本端即將關閉. 咱們把含有FIN標識的報文稱爲結束報文段 16位窗口大小: 16位檢驗和: 由發送端填充, 檢驗形式有CRC校驗等. 若是接收端校驗不經過, 則認爲數據有問題. 此處的校驗和不光包含TCP首部, 也包含TCP數據部分. 16位緊急指針: 用來標識哪部分數據是緊急數據. 選項和數據暫時忽略
1:建立SqlSessionFactory 2:經過SqlSessionFactory建立SqlSession 3:經過SqlSession執⾏行行數據庫操做 4:調用session.commit() 提交事務 5.調用session.close() 關閉會話
一、 new
二、 反射
三、 反序列化
四、 Clone
內存泄露 (memory leak),是指應用程序在申請內存後,沒法釋放已經申請的內存空間. 一次內存泄露危害能夠忽略,但若是任其發展最終會致使內存溢出(out of memory).如讀取文件後流要進行及時的關閉以及對數據庫鏈接的釋放。 內存泄露是指你的應用使用資源以後沒有及時釋放,致使應用內存中持有了不須要的資源,這是一種狀態描述。 並且一般都是因爲內存泄露致使堆棧內存不斷增大,從而引起內存溢出。 內存溢出(out of memory)是指應用程序在申請內存時,沒有足夠的內存空間供其使用。如咱們在項目中對於大批量數據的導入,採用分段批量提交的方式。 指你的應用的內存已經不能知足正常使用了,堆棧已經達到系統設置的最大值,進而致使崩潰,這事一種結果描述。
1.dom4j 2.sax 3.jaxb 4.jdom 5.dom 1.dom4j dom4j是一個Java的XML API,相似於jdom,用來讀寫XML文件的。dom4j是一個很是優秀的Java XML API, 具備性能優異、功能強大和極端易用使用的特色,同時它也是一個開放源代碼的軟件。 2.sax SAX(simple API for XML)是一種XML解析的替代方法。相比於DOM,SAX是一種速度更快,更有效的方法。 它逐行掃描文檔,一邊掃描一邊解析。並且相比於DOM,SAX能夠在解析文檔的任意時刻中止解析,但任何事物都有其相反的一面,對於SAX來講就是操做複雜。 3.jaxb JAXB(Java Architecture for XML Binding) 是一個業界的標準,是一項能夠根據XML Schema產生Java類的技術。 該過程當中,JAXB也提供了將XML實例文檔反向生成Java對象樹的方法,並能將Java對象樹的內容從新寫到XML實例文檔。 從另外一方面來說,JAXB提供了快速而簡便的方法將XML模式綁定到Java表示,從而使得Java開發者在Java應用程序中能方便地結合XML數據和處理函數。 二、dom4j 與 sax 之間的對比:【注:必須掌握!】 dom4j不適合大文件的解析,由於它是一會兒將文件加載到內存中,因此有可能出現內存溢出, sax是基於事件來對xml進行解析的,因此他能夠解析大文件的xml 也正是由於如此,因此dom4j能夠對xml進行靈活的增刪改查和導航,而sax沒有這麼強的靈活性 因此sax常常是用來解析大型xml文件,而要對xml文件進行一些靈活(crud)操做就用dom4j
Final是一個修飾符: 當final修飾一個變量的時候,變量變成一個常量,它不能被二次賦值 當final修飾的變量爲靜態變量(即由static修飾)時,必須在聲明這個變量的時候給它賦值 當final修飾方法時,該方法不能被重寫 當final修飾類時,該類不能被繼承 Final不能修飾抽象類,由於抽象類中會有須要子類實現的抽象方法,(抽象類中能夠有抽象方法,也能夠有普通方法,當一個抽象類中沒有抽象方法時,這個抽象類也就沒有了它存在的必要) Final不能修飾接口,由於接口中有須要其實現類來實現的方法 Finally: Finally只能與try/catch語句結合使用,finally語句塊中的語句必定會執行,而且會在return,continue,break關鍵字以前執行 finalize: Finalize是一個方法,屬於java.lang.Object類,finalize()方法是GC(garbage collector垃圾回收)運行機制的一部分,finalize()方法是在GC清理它所從屬的對象時被調用的
針對於重複提交的總體解決方案: 1.用redirect來解決重複提交的問題 2.點擊一次以後,按鈕失效 3.經過loading 4.自定義重複提交過濾器 5.解決struts2重複提交 能夠結合s:token標籤來解決重複提交問題 利用token的原理: 1.在前端的jsp頁面中加入s:token標籤,在訪問該頁面時就會生成 隱藏域,該隱藏域中包含一個隨機生成的字符串,並把該字符串 存入session中 2.在struts2的配置文件中加入token攔截器後,當正常訪問action 的時候,會從session中取出該字符串,而後和頁面隱藏域中提交 字符串作對比,若是一致則正常執行並刪除session中存儲的字符串。 1.經過JavaScript屏蔽提交按鈕(不推薦) 2.給數據庫增長惟一鍵約束(簡單粗暴) 3.利用Session防止表單重複提交(推薦) 4.使用AOP自定義切入實現
1.JSP include動做 jsp:include 動做 以「<jsp: 動做名 」 開始,以「</jsp:動做名> 」 結束 好比:<jsp:include page=" Filename" /> 2.JSP指令:<%@ include%><%@ %> 以「<%@ 」 開始,以「%> 」 結束。好比: <%@ include file = " Filename" %> 3.JSP輸出表達式:<%= %><%=Java表達式 %> 輸出變量的值,後邊不能加<%= ; %> 4.JSP Scriptlet【腳本】:<% ;%> <% Java 代碼 %> 例子: <% Calendar now = Calendar.getInstance(); %> 5.JSP聲明:<%! %> <%! 函數或者方法 %> 例子: <%! String getHello(String name) { return "Hi," + name + "!"; } %> 6.迭代標籤:<c:foreach> Jstl中的核心標籤(core) 7.JSP註釋: <!-- 這是註釋,但客戶端能夠查看到 --> <%-- 這也是註釋,但客戶端不能查看到 --%> 8.el表達式:${} 9.jsp:include動做是在運行時動態包含。 @include指令是在編譯時包含。 它們兩個都只能包含本項目的相關文件,不能包含其餘項目的。 若是要包含其餘項目的文件能夠使用c:import
select * from (select * from (select s.*,rownum rn from student s ) where rn<=5) where rn>0
select * from teacher where order by id limit 5,4;