2012年3月15日
版權(quán)信息: 可以任意轉(zhuǎn)載, 轉(zhuǎn)載時(shí)請(qǐng)務(wù)必以超鏈接形式標(biāo)明文章原文出處, 即下面的聲明.
原文出處:http://blog.chenlb.com/2009/06/java-classloader-architecture.html
jvm classLoader architecture:
- Bootstrap ClassLoader/啟動(dòng)類(lèi)加載器
主要負(fù)責(zé)jdk_home/lib目錄下的核心 api 或 -Xbootclasspath 選項(xiàng)指定的jar包裝入工作。 - Extension ClassLoader/擴(kuò)展類(lèi)加載器
主要負(fù)責(zé)jdk_home/lib/ext目錄下的jar包或 -Djava.ext.dirs 指定目錄下的jar包裝入工作。 - System ClassLoader/系統(tǒng)類(lèi)加載器
主要負(fù)責(zé)java -classpath/-Djava.class.path所指的目錄下的類(lèi)與jar包裝入工作。 - User Custom ClassLoader/用戶(hù)自定義類(lèi)加載器(java.lang.ClassLoader的子類(lèi))
在程序運(yùn)行期間, 通過(guò)java.lang.ClassLoader的子類(lèi)動(dòng)態(tài)加載class文件, 體現(xiàn)java動(dòng)態(tài)實(shí)時(shí)類(lèi)裝入特性。
類(lèi)加載器的特性:
- 每個(gè)ClassLoader都維護(hù)了一份自己的名稱(chēng)空間, 同一個(gè)名稱(chēng)空間里不能出現(xiàn)兩個(gè)同名的類(lèi)。
- 為了實(shí)現(xiàn)java安全沙箱模型頂層的類(lèi)加載器安全機(jī)制, java默認(rèn)采用了 " 雙親委派的加載鏈 " 結(jié)構(gòu)。

classloader-architecture

classloader-class-diagram
類(lèi)圖中, BootstrapClassLoader是一個(gè)單獨(dú)的java類(lèi), 其實(shí)在這里, 不應(yīng)該叫他是一個(gè)java類(lèi)。因?yàn)椋呀?jīng)完全不用java實(shí)現(xiàn)了。它是在jvm啟動(dòng)時(shí), 就被構(gòu)造起來(lái)的, 負(fù)責(zé)java平臺(tái)核心庫(kù)。
自定義類(lèi)加載器加載一個(gè)類(lèi)的步驟

classloader-load-class
ClassLoader 類(lèi)加載邏輯分析, 以下邏輯是除 BootstrapClassLoader 外的類(lèi)加載器加載流程:
- // 檢查類(lèi)是否已被裝載過(guò)
- Class c = findLoadedClass(name);
- if (c == null ) {
- // 指定類(lèi)未被裝載過(guò)
- try {
- if (parent != null ) {
- // 如果父類(lèi)加載器不為空, 則委派給父類(lèi)加載
- c = parent.loadClass(name, false );
- } else {
- // 如果父類(lèi)加載器為空, 則委派給啟動(dòng)類(lèi)加載加載
- c = findBootstrapClass0(name);
- }
- } catch (ClassNotFoundException e) {
- // 啟動(dòng)類(lèi)加載器或父類(lèi)加載器拋出異常后, 當(dāng)前類(lèi)加載器將其
- // 捕獲, 并通過(guò)findClass方法, 由自身加載
- c = findClass(name);
- }
- }
線程上下文類(lèi)加載器
java默認(rèn)的線程上下文類(lèi)加載器是 系統(tǒng)類(lèi)加載器(AppClassLoader)。
- // Now create the class loader to use to launch the application
- try {
- loader = AppClassLoader.getAppClassLoader(extcl);
- } catch (IOException e) {
- throw new InternalError(
- "Could not create application class loader" );
- }
-
- // Also set the context class loader for the primordial thread.
- Thread.currentThread().setContextClassLoader(loader);
以上代碼摘自sun.misc.Launch的無(wú)參構(gòu)造函數(shù)Launch()。
使用線程上下文類(lèi)加載器, 可以在執(zhí)行線程中, 拋棄雙親委派加載鏈模式, 使用線程上下文里的類(lèi)加載器加載類(lèi).
典型的例子有, 通過(guò)線程上下文來(lái)加載第三方庫(kù)jndi實(shí)現(xiàn), 而不依賴(lài)于雙親委派.
大部分java app服務(wù)器(jboss, tomcat..)也是采用contextClassLoader來(lái)處理web服務(wù)。
還有一些采用 hotswap 特性的框架, 也使用了線程上下文類(lèi)加載器, 比如 seasar (full stack framework in japenese).
線程上下文從根本解決了一般應(yīng)用不能違背雙親委派模式的問(wèn)題.
使java類(lèi)加載體系顯得更靈活.
隨著多核時(shí)代的來(lái)臨, 相信多線程開(kāi)發(fā)將會(huì)越來(lái)越多地進(jìn)入程序員的實(shí)際編碼過(guò)程中. 因此,
在編寫(xiě)基礎(chǔ)設(shè)施時(shí), 通過(guò)使用線程上下文來(lái)加載類(lèi), 應(yīng)該是一個(gè)很好的選擇。
當(dāng)然, 好東西都有利弊. 使用線程上下文加載類(lèi), 也要注意, 保證多根需要通信的線程間的類(lèi)加載器應(yīng)該是同一個(gè),
防止因?yàn)椴煌念?lèi)加載器, 導(dǎo)致類(lèi)型轉(zhuǎn)換異常(ClassCastException)。
為什么要使用這種雙親委托模式呢?
- 因?yàn)檫@樣可以避免重復(fù)加載,當(dāng)父親已經(jīng)加載了該類(lèi)的時(shí)候,就沒(méi)有必要子ClassLoader再加載一次。
- 考慮到安全因素,我們?cè)囅胍幌拢绻皇褂眠@種委托模式,那我們就可以隨時(shí)使用自定義的String來(lái)動(dòng)態(tài)替代java核心api中定義類(lèi)型,這樣會(huì)存在非常大的安全隱患,而雙親委托的方式,就可以避免這種情況,因?yàn)镾tring已經(jīng)在啟動(dòng)時(shí)被加載,所以用戶(hù)自定義類(lèi)是無(wú)法加載一個(gè)自定義的ClassLoader。
java動(dòng)態(tài)載入class的兩種方式:
- implicit隱式,即利用實(shí)例化才載入的特性來(lái)動(dòng)態(tài)載入class
- explicit顯式方式,又分兩種方式:
- java.lang.Class的forName()方法
- java.lang.ClassLoader的loadClass()方法
用Class.forName加載類(lèi)
Class.forName使用的是被調(diào)用者的類(lèi)加載器來(lái)加載類(lèi)的。
這種特性, 證明了java類(lèi)加載器中的名稱(chēng)空間是唯一的, 不會(huì)相互干擾。
即在一般情況下, 保證同一個(gè)類(lèi)中所關(guān)聯(lián)的其他類(lèi)都是由當(dāng)前類(lèi)的類(lèi)加載器所加載的。
- public static Class forName(String className)
- throws ClassNotFoundException {
- return forName0(className, true , ClassLoader.getCallerClassLoader());
- }
-
- /** Called after security checks have been made. */
- private static native Class forName0(String name, boolean initialize,
- ClassLoader loader)
- throws ClassNotFoundException;
上面中 ClassLoader.getCallerClassLoader 就是得到調(diào)用當(dāng)前forName方法的類(lèi)的類(lèi)加載器
static塊在什么時(shí)候執(zhí)行?
- 當(dāng)調(diào)用forName(String)載入class時(shí)執(zhí)行,如果調(diào)用ClassLoader.loadClass并不會(huì)執(zhí)行.forName(String,false,ClassLoader)時(shí)也不會(huì)執(zhí)行.
- 如果載入Class時(shí)沒(méi)有執(zhí)行static塊則在第一次實(shí)例化時(shí)執(zhí)行.比如new ,Class.newInstance()操作
- static塊僅執(zhí)行一次
各個(gè)java類(lèi)由哪些classLoader加載?
- java類(lèi)可以通過(guò)實(shí)例.getClass.getClassLoader()得知
- 接口由AppClassLoader(System ClassLoader,可以由ClassLoader.getSystemClassLoader()獲得實(shí)例)載入
- ClassLoader類(lèi)由bootstrap loader載入
NoClassDefFoundError和ClassNotFoundException
- NoClassDefFoundError:當(dāng)java源文件已編譯成.class文件,但是ClassLoader在運(yùn)行期間在其搜尋路徑load某個(gè)類(lèi)時(shí),沒(méi)有找到.class文件則報(bào)這個(gè)錯(cuò)
- ClassNotFoundException:試圖通過(guò)一個(gè)String變量來(lái)創(chuàng)建一個(gè)Class類(lèi)時(shí)不成功則拋出這個(gè)異常
一:使用場(chǎng)景
1)使用的地方:樹(shù)形結(jié)構(gòu),分支結(jié)構(gòu)等
2)使用的好處:降低客戶(hù)端的使用,為了達(dá)到元件與組合件使用的一致性,增加了元件的編碼
3)使用后的壞處:代碼不容易理解,需要你認(rèn)真去研究,發(fā)現(xiàn)元件與組合件是怎么組合的
二:一個(gè)實(shí)際的例子
畫(huà)圖形,這個(gè)模式,稍微要難理解一點(diǎn),有了例子就說(shuō)明了一切,我畫(huà)的圖是用接口做的,代碼實(shí)現(xiàn)是抽象類(lèi)為基類(lèi),你自己選擇了,接口也可以。

1)先建立圖形元件
package com.mike.pattern.structure.composite;
/**
* 圖形元件
*
* @author taoyu
*
* @since 2010-6-23
*/
public abstract class Graph {
/**圖形名稱(chēng)*/
protected String name;
public Graph(String name){
this.name=name;
}
/**畫(huà)圖*/
public abstract void draw()throws GraphException;
/**添加圖形*/
public abstract void add(Graph graph)throws GraphException;
/**移掉圖形*/
public abstract void remove(Graph graph)throws GraphException;
}
2)建立基礎(chǔ)圖形圓
package com.mike.pattern.structure.composite;
import static com.mike.util.Print.print;
/**
* 圓圖形
*
* @author taoyu
*
* @since 2010-6-23
*/
public class Circle extends Graph {
public Circle(String name){
super(name);
}
/**
* 圓添加圖形
* @throws GraphException
*/
@Override
public void add(Graph graph) throws GraphException {
throw new GraphException("圓是基礎(chǔ)圖形,不能添加");
}
/**
* 圓畫(huà)圖
*/
@Override
public void draw()throws GraphException {
print(name+"畫(huà)好了");
}
/**
* 圓移掉圖形
*/
@Override
public void remove(Graph graph)throws GraphException {
throw new GraphException("圓是基礎(chǔ)圖形,不能移掉");
}
}
3)建立基礎(chǔ)圖形長(zhǎng)方形
package com.mike.pattern.structure.composite;
import static com.mike.util.Print.print;
/**
* 長(zhǎng)方形
*
* @author taoyu
*
* @since 2010-6-23
*/
public class Rectangle extends Graph {
public Rectangle(String name){
super(name);
}
/**
* 長(zhǎng)方形添加
*/
@Override
public void add(Graph graph) throws GraphException {
throw new GraphException("長(zhǎng)方形是基礎(chǔ)圖形,不能添加");
}
/**
* 畫(huà)長(zhǎng)方形
*/
@Override
public void draw() throws GraphException {
print(name+"畫(huà)好了");
}
@Override
public void remove(Graph graph) throws GraphException {
throw new GraphException("長(zhǎng)方形是基礎(chǔ)圖形,不能移掉");
}
}
4)最后簡(jiǎn)歷組合圖形
package com.mike.pattern.structure.composite;
import java.util.ArrayList;
import java.util.List;
import static com.mike.util.Print.print;
/**
* 圖形組合體
*
* @author taoyu
*
* @since 2010-6-23
*/
public class Picture extends Graph {
private List<Graph> graphs;
public Picture(String name){
super(name);
/**默認(rèn)是10個(gè)長(zhǎng)度*/
graphs=new ArrayList<Graph>();
}
/**
* 添加圖形元件
*/
@Override
public void add(Graph graph) throws GraphException {
graphs.add(graph);
}
/**
* 圖形元件畫(huà)圖
*/
@Override
public void draw() throws GraphException {
print("圖形容器:"+name+" 開(kāi)始創(chuàng)建");
for(Graph g : graphs){
g.draw();
}
}
/**
* 圖形元件移掉圖形元件
*/
@Override
public void remove(Graph graph) throws GraphException {
graphs.remove(graph);
}
}
5)最后測(cè)試
public static void main(String[] args)throws GraphException {
/**畫(huà)一個(gè)圓,圓里包含一個(gè)圓和長(zhǎng)方形*/
Picture picture=new Picture("立方體圓");
picture.add(new Circle("圓"));
picture.add(new Rectangle("長(zhǎng)方形"));
Picture root=new Picture("怪物圖形");
root.add(new Circle("圓"));
root.add(picture);
root.draw();
}
6)使用心得:的確降低了客戶(hù)端的使用情況,讓整個(gè)圖形可控了,當(dāng)是你要深入去理解,才真名明白采用該模式的含義,不太容易理解。
一:使用場(chǎng)景
1)使用的地方:我想使用兩個(gè)不同類(lèi)的方法,這個(gè)時(shí)候你需要把它們組合起來(lái)使用
2)目前使用的情況:我會(huì)把兩個(gè)類(lèi)用戶(hù)組合的方式放到一起,編程思想think in java里已經(jīng)提到個(gè),能盡量用組合就用組合,繼承一般考慮再后。
3)使用后的好處:你不需要改動(dòng)以前的代碼,只是新封裝了一新類(lèi),由這個(gè)類(lèi)來(lái)提供兩個(gè)類(lèi)的方法,這個(gè)時(shí)候:一定會(huì)想到facade外觀模式,本來(lái)是多個(gè)類(lèi)使用的情況,我新封裝成一個(gè)類(lèi)來(lái)使用,而這個(gè)類(lèi)我采用組合的方式來(lái)包裝新的方法。我的理解是,設(shè)計(jì)模式本身就是為了幫助解決特定的業(yè)務(wù)場(chǎng)景而故意把模式劃分對(duì)應(yīng)的模式類(lèi)別,其實(shí)大多數(shù)情況,都解決了同樣的問(wèn)題,這個(gè)時(shí)候其實(shí)沒(méi)有必要過(guò)多的糾纏到模式的名字上了,你有好的注意,你甚至取一個(gè)新的名字來(lái)概括這樣的使用場(chǎng)景。
4)使用的壞處:適配器模式,有兩種方式來(lái)實(shí)現(xiàn)。一個(gè)是組合一個(gè)是繼承,我覺(jué)得,首先應(yīng)該考慮組合,能用組合就不要用繼承,這是第一個(gè)。第二個(gè),你采用繼承來(lái)實(shí)現(xiàn),那肯定會(huì)加大繼承樹(shù)結(jié)構(gòu),如果你的繼承關(guān)系本身就很復(fù)雜了,這肯定會(huì)加大繼承關(guān)系的維護(hù),不有利于代碼的理解,或則更加繁瑣。繼承是為了解決重用的為題而出現(xiàn)的,所以我覺(jué)得不應(yīng)該濫用繼承,有機(jī)會(huì)可以考慮同樣別的方案。
二:一個(gè)實(shí)際的例子
關(guān)聯(lián)營(yíng)銷(xiāo)的例子,用戶(hù)購(gòu)買(mǎi)完商品后,我又推薦他相關(guān)別的商品
由于減少代碼,方法我都不采用接口,直接由類(lèi)來(lái)提供,代碼只是一個(gè)范例而已,都精簡(jiǎn)了。
1)創(chuàng)建訂單信息
public class Order {
private Long orderId;
private String nickName;
public Order(Long orderId,String nickName){
this.orderId=orderId;
this.nickName=nickName;
}
/**
* 用戶(hù)下訂單
*/
public void insertOrder(){
}
}
2)商品信息
public class Auction {
/**商品名稱(chēng)*/
private String name;
/**制造商*/
private String company;
/**制造日期*/
private Date date;
public Auction(String name,String company, Date date){
this.name=name;
this.company=company;
this.date=date;
}
/**
* 推廣的商品列表
*/
public void commendAuction(){
}
}
3)購(gòu)物
public class Trade {
/**用戶(hù)訂單*/
private Order order;
/**商品信息*/
private Auction auction;
public Trade(Order order ,Auction auction){
this.order=order;
this.auction=auction;
}
/**
* 用戶(hù)產(chǎn)生訂單以及后續(xù)的事情
*/
public void trade(){
/**下訂單*/
order.insertOrder();
/**關(guān)聯(lián)推薦相關(guān)的商品*/
auction.commendAuction();
}
}
4)使用心得:其實(shí)外面采用了很多繼承的方式,order繼承auction之后,利用super .inserOrder()再加一個(gè)auction.recommendAuction(),實(shí)際上大同小異,我到覺(jué)得采用組合更容易理解以及代碼更加優(yōu)美點(diǎn)。
一:使用場(chǎng)景
1)使用到的地方:如果你想創(chuàng)建類(lèi)似汽車(chē)這樣的對(duì)象,首先要?jiǎng)?chuàng)建輪子,玻璃,桌椅,發(fā)動(dòng)機(jī),外廓等,這些部件都創(chuàng)建好后,最后創(chuàng)建汽車(chē)成品,部件的創(chuàng)建和汽車(chē)的組裝過(guò)程本身都很復(fù)雜的情況,希望把部件的創(chuàng)建和成品的組裝分開(kāi)來(lái)做,這樣把要做的事情分割開(kāi)來(lái),降低對(duì)象實(shí)現(xiàn)的復(fù)雜度,也降低以后成本的維護(hù),把汽車(chē)的部件創(chuàng)建和組裝過(guò)程獨(dú)立出兩個(gè)對(duì)應(yīng)的工廠來(lái)做,有點(diǎn)類(lèi)似建立兩個(gè)對(duì)應(yīng)的部件創(chuàng)建工廠和汽車(chē)組裝工廠兩個(gè)工廠,而工廠只是創(chuàng)建一個(gè)成品,并沒(méi)有把里面的步驟也獨(dú)立出來(lái),應(yīng)該說(shuō)Builder模式比工廠模式又進(jìn)了一步。
2)采用Builder模式后的好處:把一個(gè)負(fù)責(zé)的對(duì)象的創(chuàng)建過(guò)程分解,把一個(gè)對(duì)象的創(chuàng)建分成兩個(gè)對(duì)象來(lái)負(fù)責(zé)創(chuàng)建,代碼更有利于維護(hù),可擴(kuò)性比較好。
3)采用Builder模式后的壞處:實(shí)現(xiàn)起來(lái),對(duì)應(yīng)的接口以及部件的對(duì)象的創(chuàng)建比較多,代碼相對(duì)來(lái)講,比較多了,估計(jì)剛開(kāi)始你會(huì)有點(diǎn)暈,這個(gè)可以考慮代碼精簡(jiǎn)的問(wèn)題,增加代碼的可讀性。
二:一個(gè)實(shí)際的例子
汽車(chē)的組裝
1)首先創(chuàng)建汽車(chē)這個(gè)成品對(duì)象,包含什么的成員
public class Car implements Serializable{
/**
* 汽車(chē)序列號(hào)
*/
private static final long serialVersionUID = 1L;
/**汽車(chē)輪子*/
private Wheel wheel;
/**汽車(chē)發(fā)動(dòng)機(jī)*/
private Engine engine;
/**汽車(chē)玻璃*/
private Glass glass;
/**汽車(chē)座椅*/
private Chair chair;
public Wheel getWheel() {
return wheel;
}
public void setWheel(Wheel wheel) {
this.wheel = wheel;
}
public Engine getEngine() {
return engine;
}
public void setEngine(Engine engine) {
this.engine = engine;
}
public Glass getGlass() {
return glass;
}
public void setGlass(Glass glass) {
this.glass = glass;
}
public Chair getChair() {
return chair;
}
public void setChair(Chair chair) {
this.chair = chair;
}
}
2)創(chuàng)建對(duì)應(yīng)汽車(chē)零部件
public class Wheel {
public Wheel(){
print("--汽車(chē)輪子構(gòu)建完畢--");
}
}
public class Engine {
public Engine(){
print("--汽車(chē)發(fā)動(dòng)機(jī)構(gòu)建完畢--");
}
}
public class Glass {
public Glass(){
print("--汽車(chē)玻璃構(gòu)建完畢--");
}
}
public class Chair {
public Chair(){
print("--汽車(chē)座椅構(gòu)建完畢--");
}
}
3)開(kāi)始重點(diǎn)了,汽車(chē)成品的組裝過(guò)程
public interface Builder {
/**組裝汽車(chē)輪子*/
public void buildWheel();
/**組裝汽車(chē)發(fā)動(dòng)機(jī)*/
public void buildEngine();
/**組裝汽車(chē)玻璃*/
public void buildGlass();
/**組裝汽車(chē)座椅*/
public void buildChair();
/**返回組裝好的汽車(chē)*/
public Car getCar();
}
以及實(shí)現(xiàn)類(lèi)
public class CarBuilder implements Builder {
/**汽車(chē)成品*/
private Car car;
public CarBuilder(){
car=new Car();
}
/**組裝汽車(chē)輪子*/
@Override
public void buildChair() {
car.setChair(new Chair());
}
/**組裝汽車(chē)發(fā)動(dòng)機(jī)*/
@Override
public void buildEngine() {
car.setEngine(new Engine());
}
/**組裝汽車(chē)玻璃*/
@Override
public void buildGlass() {
car.setGlass(new Glass());
}
/**組裝汽車(chē)座椅*/
@Override
public void buildWheel() {
car.setWheel(new Wheel());
}
/**返回組裝好的汽車(chē)*/
@Override
public Car getCar() {
buildChair();
buildEngine();
buildGlass();
buildWheel();
print("--整個(gè)汽車(chē)構(gòu)建完畢--");
return car;
}
}
4)最后汽車(chē)創(chuàng)建測(cè)試
public static void main(String[] args) {
/**創(chuàng)建汽車(chē)組裝*/
Builder carBuilder=new CarBuilder();
Car car=carBuilder.getCar();
}
最后輸出:
--汽車(chē)座椅構(gòu)建完畢--
--汽車(chē)發(fā)動(dòng)機(jī)構(gòu)建完畢--
--汽車(chē)玻璃構(gòu)建完畢--
--汽車(chē)輪子構(gòu)建完畢--
--整個(gè)汽車(chē)構(gòu)建完畢--
5)體會(huì)心得:Builder模式實(shí)際的重點(diǎn)就把汽車(chē)的組裝過(guò)程和零部件的生產(chǎn)分開(kāi)來(lái)實(shí)現(xiàn),零部件的生成主要靠自己的對(duì)象來(lái)實(shí)現(xiàn),我上面只是在構(gòu)造函數(shù)里創(chuàng)建了,比較簡(jiǎn)單,而重點(diǎn)汽車(chē)的組裝則交給CarBuilder來(lái)實(shí)現(xiàn),最終由builder來(lái)先負(fù)責(zé)零部件的創(chuàng)建,最后返回出成品的汽車(chē)。
一:使用場(chǎng)景
1)經(jīng)常使用的地方:一個(gè)類(lèi)只有一個(gè)實(shí)例,eg:頁(yè)面訪問(wèn)統(tǒng)計(jì)pv,統(tǒng)計(jì)的個(gè)數(shù)就只能保證一個(gè)實(shí)例的統(tǒng)計(jì)。
2)我們目前使用的情況:比如我想創(chuàng)建一個(gè)對(duì)象,這個(gè)對(duì)象希望只有一份實(shí)例的維護(hù),在內(nèi)存的保存也只有一份,也就是在同一個(gè)jvm的java堆里只保存一份實(shí)例對(duì)象,所以你會(huì)想一辦法,在創(chuàng)建這個(gè)對(duì)象的時(shí)候,就已經(jīng)能保證只有一份。
3)怎么改進(jìn):定義該對(duì)象的時(shí)候,就保證是同一份實(shí)例,比如:定義為私有構(gòu)造函數(shù),防止通過(guò)new的方式可以創(chuàng)建對(duì)象,然后在對(duì)象里定義一個(gè)靜態(tài)的私有成員(本身對(duì)象的一個(gè)實(shí)例),然后再創(chuàng)建一個(gè)外面訪問(wèn)該對(duì)象的方法就好了。
4)改進(jìn)的好處:代碼在編譯代碼這個(gè)級(jí)別就被控制了,不至于在jvm里運(yùn)行的時(shí)候才來(lái)保證,把唯一實(shí)例的創(chuàng)建保證在編譯階段;jvm里內(nèi)存只有一份,從而內(nèi)存占有率更低,以及更方便java垃圾回收
5)改進(jìn)后的壞處:只能是代碼稍微需要更多點(diǎn),其實(shí)大家最后發(fā)現(xiàn)改進(jìn)后的壞處,都是代碼定義比之間要多一點(diǎn),但以后的維護(hù)代碼就降下來(lái)了,也短暫的代碼量偏大來(lái)?yè)Q取以后代碼的精簡(jiǎn)。
二:一個(gè)實(shí)際的例子
總體的例子
package com.mike.pattern.singleton;
/**
* 總統(tǒng)
*
* @author taoyu
*
* @since 2010-6-22
*/
public class President {
private President(){
System.out.println("總統(tǒng)已經(jīng)選舉出來(lái)了");
}
/**總統(tǒng)只有一個(gè)*/
private static President president=new President();
/**
* 返回總統(tǒng)
*/
public static President getPresident(){
return president;
}
/**
* 總統(tǒng)宣布選舉成功
*/
public void announce(){
System.out.println("偉大的中國(guó)人民,我將成你們新的總統(tǒng)");
}
}
/**
* @param args
*/
public static void main(String[] args) {
President president=President.getPresident();
president.announce();
}
1.使用場(chǎng)景
1)子類(lèi)過(guò)多,不容易管理;構(gòu)造對(duì)象過(guò)程過(guò)長(zhǎng);精簡(jiǎn)代碼創(chuàng)建;
2)目前我們代碼情況: 編寫(xiě)代碼的時(shí)候,我們經(jīng)常都在new對(duì)象,創(chuàng)建一個(gè)個(gè)的對(duì)象,而且還有很多麻煩的創(chuàng)建方式,eg:HashMap<String,Float> grade=new HashMap<String,Float>(),這樣的代碼創(chuàng)建方式太冗長(zhǎng)了,難道你沒(méi)有想過(guò)把這個(gè)創(chuàng)建變的短一點(diǎn)么,比如:HashMap<String,Float>grade=HashMapFactory.new(),可以把你創(chuàng)建精簡(jiǎn)一點(diǎn);你也可以還有別的需求,在創(chuàng)建對(duì)象的時(shí)候,你需要不同的情況,創(chuàng)建統(tǒng)一種類(lèi)別的對(duì)象,eg:我想生成不同的汽車(chē),創(chuàng)建小轎車(chē),創(chuàng)建卡車(chē),創(chuàng)建公交汽車(chē)等等,都屬于同種類(lèi)別:汽車(chē),你難道沒(méi)有想過(guò),我把這些創(chuàng)建的對(duì)象在一個(gè)工廠里來(lái)負(fù)責(zé)創(chuàng)建,我把創(chuàng)建分開(kāi)化,交給一人來(lái)負(fù)責(zé),這樣可以讓代碼更加容易管理,創(chuàng)建方式也可以簡(jiǎn)單點(diǎn)。
比如:Car BMW=CarFactory.create(bmw); 把創(chuàng)建new由一個(gè)統(tǒng)一負(fù)責(zé),這樣管理起來(lái)相當(dāng)方便
3)怎么改進(jìn):這個(gè)時(shí)候,你會(huì)想到,創(chuàng)建這樣同類(lèi)別的東西,我把這個(gè)權(quán)利分出去,讓一個(gè)人來(lái)單獨(dú)管理,它只負(fù)責(zé)創(chuàng)建我的對(duì)象這個(gè)事情,所以你單獨(dú)簡(jiǎn)歷一個(gè)對(duì)象來(lái)創(chuàng)建同類(lèi)的對(duì)象,這個(gè)時(shí)候,你想這個(gè)東西有點(diǎn)像工廠一樣,生成同樣的產(chǎn)品,所以取了個(gè)名字:工廠模式,顧名思義,只負(fù)責(zé)對(duì)象的創(chuàng)建
4)改進(jìn)后的好處:代碼更加容易管理了,代碼的創(chuàng)建要簡(jiǎn)潔很多。
5)改進(jìn)后的壞處:那就是你需要單獨(dú)加一個(gè)工廠對(duì)象來(lái)負(fù)責(zé)創(chuàng)建,多需要寫(xiě)點(diǎn)代碼。
2.一個(gè)實(shí)際的例子
創(chuàng)建寶馬汽車(chē)與奔馳汽車(chē)的例子
1)先提取出一個(gè)汽車(chē)的公用接口Car
public interface Car{
/**行駛*/
public void drive();
}
2)寶馬和奔馳汽車(chē)對(duì)象
public class BMWCar implements Car {
/**
* 汽車(chē)發(fā)動(dòng)
*/
public void drive(){
System.out.println("BMW Car drive");
}
}
public class BengCar implements Car {
/**
* 汽車(chē)發(fā)動(dòng)
*/
public void drive(){
System.out.println("BengChi Care drive");
}
}
3)單獨(dú)一個(gè)汽車(chē)工廠來(lái)負(fù)責(zé)創(chuàng)建
public class FactoryCar {
/**
* 制造汽車(chē)
*
* @param company 汽車(chē)公司
* @return 汽車(chē)
* @throws CreateCarException 制造汽車(chē)失敗異常
*/
public static Car createCar(Company company)throws CreateCarException{
if(company==Company.BMW){
return new BMWCar();
}else if(company==Company.Beng){
return new BengCar();
}
return null;
}
}
4)最后的代碼實(shí)現(xiàn):
Car BMWCar=FactoryCar.createCar(Company.BMW);
BMWCar.drive();
1. 我說(shuō)下我對(duì)設(shè)計(jì)模式的理解:任何一樣事物都是因?yàn)橛行枨蟮尿?qū)動(dòng)才誕生的,所以設(shè)計(jì)模式也不例外,我們平時(shí)在編寫(xiě)代碼的時(shí)候,隨著時(shí)間的深入,發(fā)現(xiàn)很多代碼很難維護(hù),可擴(kuò)展性級(jí)差,以及代碼的效率也比較低,這個(gè)時(shí)候你肯定會(huì)想辦法讓代碼變的優(yōu)美又能解決你項(xiàng)目中的問(wèn)題,所以在面向?qū)ο笳Z(yǔ)言里,你肯定會(huì)去發(fā)現(xiàn)很多可以重用的公用的方法,比如:接口的存在,你自然就想到了,讓你定義的方法與你的實(shí)現(xiàn)分開(kāi),也可以很方便把不同的類(lèi)與接口匹配起來(lái),形成了一個(gè)公用的接口,你會(huì)發(fā)現(xiàn)這樣做,好處會(huì)是非常多的,解決了你平時(shí)想把代碼的申明與邏輯實(shí)現(xiàn)的分開(kāi)。
2. 這個(gè)時(shí)候,你發(fā)現(xiàn)了,本身面向?qū)ο蟮恼Z(yǔ)言里,已經(jīng)暗藏了很多好處,你肯定會(huì)仔細(xì)去分析面向?qū)ο筮@個(gè)語(yǔ)言,認(rèn)真去挖掘里面更多的奧秘,最后,你發(fā)現(xiàn)了,原來(lái)你可以把面向?qū)ο蟮奶匦蕴崛〕梢粋€(gè)公用的實(shí)現(xiàn)案例,這些案例里能幫助你解決你平時(shí)編寫(xiě)代碼的困擾,而這樣一群人,就是所謂gof的成員,他們從平時(shí)設(shè)計(jì)建筑方面找到了靈感,建筑的設(shè)計(jì)也可以公用化以及重用化,所以他們也提取了相關(guān)的軟件設(shè)計(jì)方面的公用案例,也就有了下面的相關(guān)的所謂23種設(shè)計(jì)模式,而里面這么多模式,你也可以把他們歸類(lèi)起來(lái),最后發(fā)現(xiàn)就幾類(lèi)模式:創(chuàng)建,結(jié)構(gòu),行為等模式類(lèi)別,而這些現(xiàn)成的方案,也可以在實(shí)際應(yīng)用中充分發(fā)揮作用,隨著大家的使用以及理解,發(fā)現(xiàn)其實(shí)這些所謂的模式里,你的確可以讓你的代碼變的更加優(yōu)美與簡(jiǎn)練。
3. 我比較喜歡把代碼變的更加優(yōu)美與簡(jiǎn)練,優(yōu)美的代碼就是一看就懂,結(jié)構(gòu)很清晰,而簡(jiǎn)歷就是一目了然,又可以解決你的問(wèn)題,就是代碼又少效率又高,所以平時(shí)要養(yǎng)成寫(xiě)java doc的習(xí)慣,這樣的代碼才為清晰,所以才會(huì)更加優(yōu)美。
4. 這些就是我對(duì)設(shè)計(jì)模式的理解,所以這么好的寶貝,我們不去深入的了解,的確可惜了,這就叫站到巨人的肩膀上.....
一:網(wǎng)絡(luò)配置
1.關(guān)掉防火墻
1) 重啟后生效
開(kāi)啟: chkconfig iptables on
關(guān)閉: chkconfig iptables off
2) 即時(shí)生效,重啟后失效
開(kāi)啟: service iptables start
關(guān)閉: service iptables stop
2.下載軟件
wget curl
3.安裝和解壓
安裝 rpm -ivh
升級(jí) rpm -Uvh
卸載 rpm -e
tar -zxvf
二:網(wǎng)卡設(shè)置
1、 設(shè)置ip地址(即時(shí)生效,重啟失效)
#ifconfig eth0 ip地址 netmask 子網(wǎng)掩碼
2、 設(shè)置ip地址(重啟生效,永久生效)
#setup
3、 通過(guò)配置文件設(shè)置ip地址(重啟生效,永久生效)
#vi /etc/sysconfig/network-scripts/ifcfg-eth0
DEVICE=eth0 #設(shè)備名,與文件同名。
ONBOOT=yes #在系統(tǒng)啟動(dòng)時(shí),啟動(dòng)本設(shè)備。
BOOTPROTO=static
IPADDR=202.118.75.91 #此網(wǎng)卡的IP地址
NETMASK=255.255.255.0 #子網(wǎng)掩碼
GATEWAY=202.118.75.1 #網(wǎng)關(guān)IP
MACADDR=00:02:2D:2E:8C:A8 #mac地址
4、 重啟網(wǎng)絡(luò)服務(wù)
#service network restart //重啟所有網(wǎng)卡
5、 禁用網(wǎng)卡,啟動(dòng)網(wǎng)卡
#ifdown eth0
#ifup eth0
6、 屏蔽網(wǎng)卡,顯示網(wǎng)卡
#ifconfig eth0 down
#ifconfig eth0 up
7、 配置DNS客戶(hù)端(最多三個(gè))
#vi /etc/resolv.conf
nameserver 202.99.96.68
8、更改主機(jī)名(即時(shí)生效)
#hostname 主機(jī)名
9、更改主機(jī)名(重啟計(jì)算機(jī)生效,永久生效)
#vi /etc/sysconfig/network
HOSTNAME=主機(jī)名
三:兩臺(tái)linux拷貝命令:scp
1.安裝scp:yum install openssh-clients
2.scp -r 本地用戶(hù)名@IP地址:文件名1 遠(yuǎn)程用戶(hù)名@IP地址:文件名2
摘要: 作者:NetSeek http://www.linuxtone.org (IT運(yùn)維專(zhuān)家網(wǎng)|集群架構(gòu)|性能調(diào)優(yōu))歡迎轉(zhuǎn)載,轉(zhuǎn)載時(shí)請(qǐng)務(wù)必以超鏈接形式標(biāo)明文章原始出處和作者信息及本聲明.首發(fā)時(shí)間: 2008-11-25 更新時(shí)間:2009-1-14目 錄一、 Nginx 基礎(chǔ)知識(shí)二、 Nginx 安裝及調(diào)試三、 Nginx Rewrite四、 Nginx Redirect五、 Nginx 目錄自動(dòng)加斜線...
閱讀全文
一:quartz簡(jiǎn)介 OpenSymphony 的Quartz提供了一個(gè)比較完美的任務(wù)調(diào)度解決方案。 Quartz 是個(gè)開(kāi)源的作業(yè)調(diào)度框架,定時(shí)調(diào)度器,為在 Java 應(yīng)用程序中進(jìn)行作業(yè)調(diào)度提供了簡(jiǎn)單卻強(qiáng)大的機(jī)制。
Quartz中有兩個(gè)基本概念:作業(yè)和觸發(fā)器。作業(yè)是能夠調(diào)度的可執(zhí)行任務(wù),觸發(fā)器提供了對(duì)作業(yè)的調(diào)度
二:quartz spring配置詳解- 為什么不適用java.util.Timer結(jié)合java.util.TimerTask
1.主要的原因,適用不方便,特別是制定具體的年月日時(shí)分的時(shí)間,而quartz使用類(lèi)似linux上的cron配置,很方便的配置每隔時(shí)間執(zhí)行觸發(fā)。
2.其次性能的原因,使用jdk自帶的Timer不具備多線程,而quartz采用線程池,性能上比timer高出很多。
在spring里主要分為兩種使用方式:第一種,也是目前使用最多的方式,spring提供的MethodInvokingJobDetailFactoryBean代理類(lèi),通過(guò)雷利類(lèi)直接調(diào)用任務(wù)類(lèi)的某個(gè)函數(shù);第二種,程序里實(shí)現(xiàn)quartz接口,quartz通過(guò)該接口進(jìn)行調(diào)度。
主要講解通過(guò)spring提供的代理類(lèi)MethodInvokingJobDetailFactoryBean 1.業(yè)務(wù)邏輯類(lèi):業(yè)務(wù)邏輯是獨(dú)立的,本身就與quartz解耦的,并沒(méi)有深入進(jìn)去,這對(duì)業(yè)務(wù)來(lái)講是很好的一個(gè)方式。
public class TestJobTask{ /**
*業(yè)務(wù)邏輯處理
*/ public void service(){
/**業(yè)務(wù)邏輯*/ 


..
}
}
2.增加一個(gè)線程池 <!-- 線程執(zhí)行器配置,用于任務(wù)注冊(cè) --><bean id="executor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<property name="corePoolSize" value="10" />
<property name="maxPoolSize" value="100" />
<property name="queueCapacity" value="500" />
</bean>
3.定義業(yè)務(wù)邏輯類(lèi)
<!-- 業(yè)務(wù)對(duì)象 --><bean id="testJobTask" class="com.mike.scheduling.TestJobTask" />
4.增加quartz調(diào)用業(yè)務(wù)邏輯
<!-- 調(diào)度業(yè)務(wù) --><bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="testJobTask" />
<property name="targetMethod" value="service" />
</bean>
5.增加調(diào)用的觸發(fā)器,觸發(fā)的時(shí)間,有兩種方式:
第一種觸發(fā)時(shí)間,采用類(lèi)似linux的cron,配置時(shí)間的表示發(fā)出豐富 <bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean"> <property name="jobDetail" ref="jobDetail" />
<property name="cronExpression" value="10 0/1 * * * ?" />
</bean>
Cron表達(dá)式“10 */1 * * * ?”意為:從10秒開(kāi)始,每1分鐘執(zhí)行一次 第二種,采用比較簡(jiǎn)話(huà)的方式,申明延遲時(shí)間和間隔時(shí)間
<bean id="taskTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean"> <property name="jobDetail" ref="jobDetail" />
<property name="startDelay" value="10000" />
<property name="repeatInterval" value="60000" />
</bean>
延遲10秒啟動(dòng),然后每隔1分鐘執(zhí)行一次 6.開(kāi)始調(diào)用
<!-- 設(shè)置調(diào)度 --><bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="cronTrigger" />
</list>
</property>
<property name="taskExecutor" ref="executor" />
</bean>
7.結(jié)束:?jiǎn)?dòng)容器即可,已經(jīng)將spring和quartz結(jié)合完畢。 Cron常用的表達(dá)式 "0 0 12 * * ?" 每天中午12點(diǎn)觸發(fā)"0 15 10 ? * *" 每天上午10:15觸發(fā)
"0 15 10 * * ?" 每天上午10:15觸發(fā)
"0 15 10 * * ? *" 每天上午10:15觸發(fā)
"0 15 10 * * ? 2005" 2005年的每天上午10:15觸發(fā)
"0 * 14 * * ?" 在每天下午2點(diǎn)到下午2:59期間的每1分鐘觸發(fā)
"0 0/5 14 * * ?" 在每天下午2點(diǎn)到下午2:55期間的每5分鐘觸發(fā)
"0 0/5 14,18 * * ?" 在每天下午2點(diǎn)到2:55期間和下午6點(diǎn)到6:55期間的每5分鐘觸發(fā)
"0 0-5 14 * * ?" 在每天下午2點(diǎn)到下午2:05期間的每1分鐘觸發(fā)
"0 10,44 14 ? 3 WED" 每年三月的星期三的下午2:10和2:44觸發(fā)
"0 15 10 ? * MON-FRI" 周一至周五的上午10:15觸發(fā)
"0 15 10 15 * ?" 每月15日上午10:15觸發(fā)
"0 15 10 L * ?" 每月最后一日的上午10:15觸發(fā)
"0 15 10 ? * 6L" 每月的最后一個(gè)星期五上午10:15觸發(fā)
"0 15 10 ? * 6L 2002-2005" 2002年至2005年的每月的最后一個(gè)星期五上午10:15觸發(fā)
"0 15 10 ? * 6#3" 每月的第三個(gè)星期五上午10:15觸發(fā)
三:quartz原理
根據(jù)上面spring的配置,我們就比較清楚quartz的內(nèi)部情況,下面我們主要詳解配置涉及到的每個(gè)點(diǎn) 1.我們先從最后一個(gè)步驟看起,
SchedulerFactoryBean ,scheduler的工廠實(shí)現(xiàn),里面可以生產(chǎn)出對(duì)應(yīng)的多個(gè)jobDetail和trigger,每個(gè)jobDetail對(duì)應(yīng)trigger代表一個(gè)任務(wù)
Quartz的SchedulerFactory是標(biāo)準(zhǔn)的工廠類(lèi),不太適合在Spring環(huán)境下使用。此外,為了保證Scheduler能夠感知 Spring容器的生命周期,完成自動(dòng)啟動(dòng)和關(guān)閉的操作,必須讓Scheduler和Spring容器的生命周期相關(guān)聯(lián)。以便在Spring容器啟動(dòng)后, Scheduler自動(dòng)開(kāi)始工作,而在Spring容器關(guān)閉前,自動(dòng)關(guān)閉Scheduler。為此,Spring提供 SchedulerFactoryBean,這個(gè)FactoryBean大致?lián)碛幸韵碌墓δ埽?nbsp; 1)以更具Bean風(fēng)格的方式為Scheduler提供配置信息;
2)讓Scheduler和Spring容器的生命周期建立關(guān)聯(lián),相生相息;
3)通過(guò)屬性配置部分或全部代替Quartz自身的配置文件。
2.jobDetail,表示一個(gè)可執(zhí)行的業(yè)務(wù)調(diào)用
3.trigger:調(diào)度的時(shí)間計(jì)劃,什么時(shí)候,每隔多少時(shí)間可執(zhí)行等時(shí)間計(jì)劃
4.ThreadPoolTaskExecutor,線程池,用來(lái)并行執(zhí)行每個(gè)對(duì)應(yīng)的job,提高效率,這也是上面提到不推薦使用jdk自身timer的一個(gè)很重要的原因
一:事務(wù)的概念
事務(wù)必須服從ISO/IEC所制定的ACID原則。ACID是原子性(atomicity)、一致性(consistency)、隔離性(isolation)和持久性(durability)的縮寫(xiě) 事務(wù)的原子性:表示事務(wù)執(zhí)行過(guò)程中的任何失敗都將導(dǎo)致事務(wù)所做的任何修改失效。
一致性表示當(dāng)事務(wù)執(zhí)行失敗時(shí),所有被該事務(wù)影響的數(shù)據(jù)都應(yīng)該恢復(fù)到事務(wù)執(zhí)行前的狀態(tài)。
隔離性表示在事務(wù)執(zhí)行過(guò)程中對(duì)數(shù)據(jù)的修改,在事務(wù)提交之前對(duì)其他事務(wù)不可見(jiàn)。
持久性表示已提交的數(shù)據(jù)在事務(wù)執(zhí)行失敗時(shí),數(shù)據(jù)的狀態(tài)都應(yīng)該正確。 二:事務(wù)的場(chǎng)景
1.與銀行相關(guān)的業(yè)務(wù),重要的數(shù)據(jù),與錢(qián)相關(guān)的內(nèi)容不能出任何錯(cuò)。
2.系統(tǒng)內(nèi)部認(rèn)為重要的數(shù)據(jù),都需要事務(wù)的支持,防止重要數(shù)據(jù)的不一致。
3.具體的業(yè)務(wù)場(chǎng)景:銀行業(yè)務(wù),支付業(yè)務(wù),交易業(yè)務(wù)等。
三:事務(wù)的實(shí)現(xiàn)方式
首先說(shuō)一下事務(wù)的類(lèi)型,主要包含一下三種:JDBC事務(wù),JTA事務(wù),容器事務(wù)
1、JDBC事務(wù) JDBC 事務(wù)是用 Connection 對(duì)象控制的。JDBC Connection 接口( java.sql.Connection )提供了兩種事務(wù)模式:自動(dòng)提交和手工提交。 java.sql.Connection 提供了以下控制事務(wù)的方法: public void setAutoCommit(boolean) public boolean getAutoCommit() public void commit() public void rollback() 使用 JDBC 事務(wù)界定時(shí),您可以將多個(gè) SQL 語(yǔ)句結(jié)合到一個(gè)事務(wù)中。JDBC 事務(wù)的一個(gè)缺點(diǎn)是事務(wù)的范圍局限于一個(gè)數(shù)據(jù)庫(kù)連接。一個(gè) JDBC 事務(wù)不能跨越多個(gè)數(shù)據(jù)庫(kù)。 2、JTA(Java Transaction API)事務(wù) JTA是一種高層的,與實(shí)現(xiàn)無(wú)關(guān)的,與協(xié)議無(wú)關(guān)的API,應(yīng)用程序和應(yīng)用服務(wù)器可以使用JTA來(lái)訪問(wèn)事務(wù)。 JTA允許應(yīng)用程序執(zhí)行分布式事務(wù)處理--在兩個(gè)或多個(gè)網(wǎng)絡(luò)計(jì)算機(jī)資源上訪問(wèn)并且更新數(shù)據(jù),這些數(shù)據(jù)可以分布在多個(gè)數(shù)據(jù)庫(kù)上。JDBC驅(qū)動(dòng)程序的JTA支持極大地增強(qiáng)了數(shù)據(jù)訪問(wèn)能力。 如果計(jì)劃用 JTA 界定事務(wù),那么就需要有一個(gè)實(shí)現(xiàn) javax.sql.XADataSource 、 javax.sql.XAConnection 和 javax.sql.XAResource 接口的 JDBC 驅(qū)動(dòng)程序。一個(gè)實(shí)現(xiàn)了這些接口的驅(qū)動(dòng)程序?qū)⒖梢詤⑴c JTA 事務(wù)。一個(gè) XADataSource 對(duì)象就是一個(gè) XAConnection 對(duì)象的工廠。 XAConnection s 是參與 JTA 事務(wù)的 JDBC 連接。 您將需要用應(yīng)用服務(wù)器的管理工具設(shè)置 XADataSource 。從應(yīng)用服務(wù)器和 JDBC 驅(qū)動(dòng)程序的文檔中可以了解到相關(guān)的指導(dǎo)。 J2EE 應(yīng)用程序用 JNDI 查詢(xún)數(shù)據(jù)源。一旦應(yīng)用程序找到了數(shù)據(jù)源對(duì)象,它就調(diào)用 javax.sql.DataSource.getConnection() 以獲得到數(shù)據(jù)庫(kù)的連接。 XA 連接與非 XA 連接不同。一定要記住 XA 連接參與了 JTA 事務(wù)。這意味著 XA 連接不支持 JDBC 的自動(dòng)提交功能。同時(shí),應(yīng)用程序一定不要對(duì) XA 連接調(diào)用 java.sql.Connection.commit() 或者 java.sql.Connection.rollback() 。相反,應(yīng)用程序應(yīng)該使用 UserTransaction.begin()、 UserTransaction.commit() 和 serTransaction.rollback() 。 3、容器事務(wù) 容器事務(wù)主要是J2EE應(yīng)用服務(wù)器提供的,容器事務(wù)大多是基于JTA完成,這是一個(gè)基于JNDI的,相當(dāng)復(fù)雜的API實(shí)現(xiàn)。相對(duì)編碼實(shí)現(xiàn)JTA事務(wù)管理,我們可以通過(guò)EJB容器提供的容器事務(wù)管理機(jī)制(CMT)完成同一個(gè)功能,這項(xiàng)功能由J2EE應(yīng)用服務(wù)器提供。這使得我們可以簡(jiǎn)單的指定將哪個(gè)方法加入事務(wù),一旦指定,容器將負(fù)責(zé)事務(wù)管理任務(wù)。這是我們土建的解決方式,因?yàn)橥ㄟ^(guò)這種方式我們可以將事務(wù)代碼排除在邏輯編碼之外,同時(shí)將所有困難交給J2EE容器去解決。使用EJB CMT的另外一個(gè)好處就是程序員無(wú)需關(guān)心JTA API的編碼,不過(guò),理論上我們必須使用EJB。 四、三種事務(wù)差異 1、JDBC事務(wù)控制的局限性在一個(gè)數(shù)據(jù)庫(kù)連接內(nèi),但是其使用簡(jiǎn)單。 2、JTA事務(wù)的功能強(qiáng)大,事務(wù)可以跨越多個(gè)數(shù)據(jù)庫(kù)或多個(gè)DAO,使用也比較復(fù)雜。 3、容器事務(wù),主要指的是J2EE應(yīng)用服務(wù)器提供的事務(wù)管理,局限于EJB應(yīng)用使用。
五:詳解事務(wù)
1.首先看一下目前使用最多的spring事務(wù),目前spring配置分為聲明式事務(wù)和編程式事務(wù)。
1)編程式事務(wù)
主要是實(shí)現(xiàn)接口PlatformTransactionManager
實(shí)現(xiàn)了事務(wù)管理的接口有非常多,這里主要講DataSourceTransactionManager和數(shù)據(jù)庫(kù)jdbc相關(guān)的事務(wù)處理方式
之前有接觸過(guò)hadoop,但都比較淺顯,對(duì)立面的東東不是很清楚!
打算后面在hadoop上花時(shí)間把里面的內(nèi)容,好好學(xué)學(xué),這篇博客將在后面陸續(xù)更新hadoop學(xué)習(xí)筆記。
一:Mina概要 Apache Mina是一個(gè)能夠幫助用戶(hù)開(kāi)發(fā)高性能和高伸縮性網(wǎng)絡(luò)應(yīng)用程序的框架。它通過(guò)Java nio技術(shù)基于TCP/IP和UDP/IP協(xié)議提供了抽象的、事件驅(qū)動(dòng)的、異步的API。
如下的特性:
1、 基于Java nio的TCP/IP和UDP/IP實(shí)現(xiàn)
基于RXTX的串口通信(RS232)
VM 通道通信
2、通過(guò)filter接口實(shí)現(xiàn)擴(kuò)展,類(lèi)似于Servlet filters
3、low-level(底層)和high-level(高級(jí)封裝)的api:
low-level:使用ByteBuffers
High-level:使用自定義的消息對(duì)象和解碼器
4、Highly customizable(易用的)線程模式(MINA2.0 已經(jīng)禁用線程模型了):
單線程
線程池
多個(gè)線程池
5、基于java5 SSLEngine的SSL、TLS、StartTLS支持
6、負(fù)載平衡
7、使用mock進(jìn)行單元測(cè)試
8、jmx整合
9、基于StreamIoHandler的流式I/O支持
10、IOC容器的整合:Spring、PicoContainer
11、平滑遷移到Netty平臺(tái)
二:實(shí)踐 首先講一下客戶(hù)端的通信過(guò)程:
1.通過(guò)SocketConnector同服務(wù)器端建立連接
2.鏈接建立之后I/O的讀寫(xiě)交給了I/O Processor線程,I/O Processor是多線程的
3.通過(guò)I/O Processor讀取的數(shù)據(jù)經(jīng)過(guò)IoFilterChain里所有配置的IoFilter,IoFilter進(jìn)行消息的過(guò)濾,格式的轉(zhuǎn)換,在這個(gè)層面可以制定一些自定義的協(xié)議
4.最后IoFilter將數(shù)據(jù)交給Handler進(jìn)行業(yè)務(wù)處理,完成了整個(gè)讀取的過(guò)程
5.寫(xiě)入過(guò)程也是類(lèi)似,只是剛好倒過(guò)來(lái),通過(guò)IoSession.write寫(xiě)出數(shù)據(jù),然后Handler進(jìn)行寫(xiě)入的業(yè)務(wù)處理,處理完成后交給IoFilterChain,進(jìn)行消息過(guò)濾和協(xié)議的轉(zhuǎn)換,最后通過(guò)I/O Processor將數(shù)據(jù)寫(xiě)出到socket通道
IoFilterChain作為消息過(guò)濾鏈
1.讀取的時(shí)候是從低級(jí)協(xié)議到高級(jí)協(xié)議的過(guò)程,一般來(lái)說(shuō)從byte字節(jié)逐漸轉(zhuǎn)換成業(yè)務(wù)對(duì)象的過(guò)程
2.寫(xiě)入的時(shí)候一般是從業(yè)務(wù)對(duì)象到字節(jié)byte的過(guò)程
IoSession貫穿整個(gè)通信過(guò)程的始終
客戶(hù)端通信過(guò)程

1.創(chuàng)建服務(wù)器
package com.gewara.web.module.base;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.charset.Charset;
import org.apache.mina.core.service.IoAcceptor;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
/**
* Mina服務(wù)器
*
* @author mike
*
* @since 2012-3-15
*/
public class HelloServer {
private static final int PORT = 8901;// 定義監(jiān)聽(tīng)端口
public static void main(String[] args) throws IOException{
// 創(chuàng)建服務(wù)端監(jiān)控線程
IoAcceptor acceptor = new NioSocketAcceptor();
// 設(shè)置日志記錄器
acceptor.getFilterChain().addLast("logger", new LoggingFilter());
// 設(shè)置編碼過(guò)濾器
acceptor.getFilterChain().addLast("codec",new ProtocolCodecFilter(new TextLineCodecFactory(Charset.forName("UTF-8"))));
// 指定業(yè)務(wù)邏輯處理器
acceptor.setHandler(new HelloServerHandler());
// 設(shè)置端口號(hào)
acceptor.setDefaultLocalAddress(new InetSocketAddress(PORT));
// 啟動(dòng)監(jiān)聽(tīng)線程
acceptor.bind();
}
}
2.創(chuàng)建服務(wù)器端業(yè)務(wù)邏輯
package com.gewara.web.module.base;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IoSession;
/**
* 服務(wù)器端業(yè)務(wù)邏輯
*
* @author mike
*
* @since 2012-3-15
*/public class HelloServerHandler
extends IoHandlerAdapter {
@Override
/**
* 連接創(chuàng)建事件
*/ public void sessionCreated(IoSession session){
// 顯示客戶(hù)端的ip和端口
System.out.println(session.getRemoteAddress().toString());
}
@Override
/**
* 消息接收事件
*/ public void messageReceived(IoSession session, Object message)
throws Exception{
String str = message.toString();
if (str.trim().equalsIgnoreCase("quit")){
// 結(jié)束會(huì)話(huà)
session.close(
true);
return;
}
// 返回消息字符串
session.write("Hi Client!");
// 打印客戶(hù)端傳來(lái)的消息內(nèi)容
System.out.println("Message written

" + str);
}
}
3.創(chuàng)建客戶(hù)端
package com.gewara.web.module.base;
import java.net.InetSocketAddress;
import java.nio.charset.Charset;
import org.apache.mina.core.future.ConnectFuture;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.nio.NioSocketConnector;
/**
* Mina客戶(hù)端
*
* @author mike
*
* @since 2012-3-15
*/
public class HelloClient {
public static void main(String[] args){
// 創(chuàng)建客戶(hù)端連接器.
NioSocketConnector connector = new NioSocketConnector();
// 設(shè)置日志記錄器
connector.getFilterChain().addLast("logger", new LoggingFilter());
// 設(shè)置編碼過(guò)濾器
connector.getFilterChain().addLast("codec",
new ProtocolCodecFilter(new TextLineCodecFactory(Charset.forName("UTF-8"))));
// 設(shè)置連接超時(shí)檢查時(shí)間
connector.setConnectTimeoutCheckInterval(30);
// 設(shè)置事件處理器
connector.setHandler(new HelloClientHandler());
// 建立連接
ConnectFuture cf = connector.connect(new InetSocketAddress("192.168.2.89", 8901));
// 等待連接創(chuàng)建完成
cf.awaitUninterruptibly();
// 發(fā)送消息
cf.getSession().write("Hi Server!");
// 發(fā)送消息
cf.getSession().write("quit");
// 等待連接斷開(kāi)
cf.getSession().getCloseFuture().awaitUninterruptibly();
// 釋放連接
connector.dispose();
}
}
4.客戶(hù)端業(yè)務(wù)邏輯
package com.gewara.web.module.base;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IoSession;
public class HelloClientHandler extends IoHandlerAdapter {
@Override
/**
* 消息接收事件
*/
public void messageReceived(IoSession session, Object message) throws Exception{
//顯示接收到的消息
System.out.println("server message:"+message.toString());
}
}
5.先啟動(dòng)服務(wù)器端,然后啟動(dòng)客戶(hù)端
2012-03-15 14:45:41,456 INFO logging.LoggingFilter - CREATED
/192.168.2.89:2691
2012-03-15 14:45:41,456 INFO logging.LoggingFilter - OPENED
2012-03-15 14:45:41,487 INFO logging.LoggingFilter - RECEIVED: HeapBuffer[pos=0 lim=11 cap=2048: 48 69 20 53 65 72 76 65 72 21 0A]
2012-03-15 14:45:41,487 DEBUG codec.ProtocolCodecFilter - Processing a MESSAGE_RECEIVED
for session 1
Message written

Hi Server!
2012-03-15 14:45:41,487 INFO logging.LoggingFilter - SENT: HeapBuffer[pos=0 lim=0 cap=0: empty]
2012-03-15 14:45:41,487 INFO logging.LoggingFilter - RECEIVED: HeapBuffer[pos=0 lim=5 cap=2048: 71 75 69 74 0A]
2012-03-15 14:45:41,487 DEBUG codec.ProtocolCodecFilter - Processing a MESSAGE_RECEIVED
for session 1
2012-03-15 14:45:41,487 INFO logging.LoggingFilter - CLOSED
三:分析源碼
1.首先看服務(wù)器
// 創(chuàng)建服務(wù)端監(jiān)控線程
IoAcceptor acceptor = new NioSocketAcceptor();
// 設(shè)置日志記錄器
acceptor.getFilterChain().addLast("logger", new LoggingFilter());
// 設(shè)置編碼過(guò)濾器
acceptor.getFilterChain().addLast("codec",new ProtocolCodecFilter(new TextLineCodecFactory(Charset.forName("UTF-8"))));
// 指定業(yè)務(wù)邏輯處理器
acceptor.setHandler(new HelloServerHandler());
// 設(shè)置端口號(hào)
acceptor.setDefaultLocalAddress(new InetSocketAddress(PORT));
// 啟動(dòng)監(jiān)聽(tīng)線程
acceptor.bind();
1)先創(chuàng)建NioSocketAcceptor nio的接收器,談到Socket就要說(shuō)到Reactor模式 當(dāng)前分布式計(jì)算 Web Services盛行天下,這些網(wǎng)絡(luò)服務(wù)的底層都離不開(kāi)對(duì)socket的操作。他們都有一個(gè)共同的結(jié)構(gòu):1. Read request2. Decode request3. Process service 4. Encode reply5. Send reply 
但這種模式在用戶(hù)負(fù)載增加時(shí),性能將下降非常的快。我們需要重新尋找一個(gè)新的方案,保持?jǐn)?shù)據(jù)處理的流暢,很顯然,事件觸發(fā)機(jī)制是最好的解決辦法,當(dāng)有事件發(fā)生時(shí),會(huì)觸動(dòng)handler,然后開(kāi)始數(shù)據(jù)的處理。
Reactor模式類(lèi)似于AWT中的Event處理。
Reactor模式參與者
1.Reactor 負(fù)責(zé)響應(yīng)IO事件,一旦發(fā)生,廣播發(fā)送給相應(yīng)的Handler去處理,這類(lèi)似于AWT的thread
2.Handler 是負(fù)責(zé)非堵塞行為,類(lèi)似于AWT ActionListeners;同時(shí)負(fù)責(zé)將handlers與event事件綁定,類(lèi)似于AWT addActionListener

并發(fā)系統(tǒng)常采用reactor模式,簡(jiǎn)稱(chēng)觀察者模式,代替常用的多線程處理方式,利用有限的系統(tǒng)的資源,提高系統(tǒng)的吞吐量。
可以看一下這篇文章,講解的很生動(dòng)具體,一看就明白reactor模式的好處
http://daimojingdeyu.iteye.com/blog/828696 Reactor模式是編寫(xiě)高性能網(wǎng)絡(luò)服務(wù)器的必備技術(shù)之一,它具有如下的優(yōu)點(diǎn): 1)響應(yīng)快,不必為單個(gè)同步時(shí)間所阻塞,雖然Reactor本身依然是同步的; 2)編程相對(duì)簡(jiǎn)單,可以最大程度的避免復(fù)雜的多線程及同步問(wèn)題,并且避免了多線程/進(jìn)程的切換開(kāi)銷(xiāo); 3)可擴(kuò)展性,可以方便的通過(guò)增加Reactor實(shí)例個(gè)數(shù)來(lái)充分利用CPU資源; 4)可復(fù)用性,reactor框架本身與具體事件處理邏輯無(wú)關(guān),具有很高的復(fù)用性; 2)其次,再說(shuō)說(shuō)NIO的基本原理和使用 NIO 有一個(gè)主要的類(lèi)Selector,這個(gè)類(lèi)似一個(gè)觀察者,只要我們把需要探知的socketchannel告訴Selector,我們接著做別的事情,當(dāng)有事件發(fā)生時(shí),他會(huì)通知我們,傳回一組 SelectionKey,我們讀取這些Key,就會(huì)獲得我們剛剛注冊(cè)過(guò)的socketchannel,然后,我們從這個(gè)Channel中讀取數(shù)據(jù),放心,包準(zhǔn)能夠讀到,接著我們可以處理這些數(shù)據(jù)。
Selector內(nèi)部原理實(shí)際是在做一個(gè)對(duì)所注冊(cè)的channel的輪詢(xún)?cè)L問(wèn),不斷的輪詢(xún)(目前就這一個(gè)算法),一旦輪詢(xún)到一個(gè)channel有所注冊(cè)的事情發(fā)生,比如數(shù)據(jù)來(lái)了,他就會(huì)站起來(lái)報(bào)告,交出一把鑰匙,讓我們通過(guò)這把鑰匙(SelectionKey表示 SelectableChannel 在 Selector 中的注冊(cè)的標(biāo)記。 )來(lái)讀取這個(gè)channel的內(nèi)容。