
2008年10月8日
posted @
2010-10-29 13:51 xiaoxinchen 閱讀(1781) |
評論 (0) |
編輯 收藏
眾所周知,
Linux動態(tài)庫的默認搜索路徑是/lib和/usr/lib。動態(tài)庫被創(chuàng)建后,一般都復(fù)制到這兩個目錄中。當程序執(zhí)行時需要某動態(tài)庫,并且該動態(tài)庫還未加載到內(nèi)存中,則系統(tǒng)會自動到這兩個默認搜索路徑中去查找相應(yīng)的動態(tài)庫文件,然后加載該文件到內(nèi)存中,這樣程序就可以使用該動態(tài)庫中的函數(shù),以及該動態(tài)庫的其它資源了。在Linux
中,動態(tài)庫的搜索路徑除了默認的搜索路徑外,還可以通過以下三種方法來指定。
方法一:在配置文件/etc/ld.so.conf中指定動態(tài)庫搜索路徑。
可以通過編輯配置文件/etc/ld.so.conf來指定動態(tài)庫的搜索路徑,該文件中每行為一個動態(tài)庫搜索路徑。每次編輯完該文件后,都必須運行命令ldconfig使修改后的配置生效。我們通過例1來說明該方法。
例1:
我們通過以下命令用源程序pos_conf.c(見程序1)來創(chuàng)建動態(tài)庫
libpos.so,詳細創(chuàng)建過程請參考文[1]。
# gcc -c pos_conf.c
# gcc -shared -fPCI -o
libpos.so pos_conf.o
#
#include <stdio.h>
void
pos()
{
printf("/root/test/conf/lib\n");
}
程序1: pos_conf.c
接著通過以下命令編譯main.c(見程序2)生成目標程序pos。
# gcc -o pos main.c -L. -lpos
#
void pos();
int main()
{
pos();
return 0;
}
程序2: main.c
然后把庫文件移動到目錄/root/test/conf/lib中。
# mkdir -p /root/test/conf/lib
# mv
libpos.so /root/test/conf/lib
#
最后編輯配置文件/etc/ld.so.conf,在該文件中追加一行"/root/test/conf/lib"。
運行程序pos試試。
# ./pos
./pos: error while loading
shared libraries: libpos.so: cannot open shared object file: No such file or
directory
#
出錯了,系統(tǒng)未找到動態(tài)庫libpos.so。找找原因,原來在編輯完配置文件/etc/ld.so.conf后,沒有運行命令ldconfig,所以剛才的修改還未生效。我們運行l(wèi)dconfig后再試試。
# ldconfig
# ./pos /root/test/conf/lib
#
程序pos運行成功,并且打印出正確結(jié)果。
方法二:通過環(huán)境變量LD_LIBRARY_PATH指定動態(tài)庫搜索路徑(!)。
通過設(shè)定環(huán)境變量LD_LIBRARY_PATH也可以指定動態(tài)庫搜索路徑。當通過該環(huán)境變量指定多個動態(tài)庫搜索路徑時,路徑之間用冒號":"分隔。
不過LD_LIBRARY_PATH的設(shè)定作用是全局的,過多的使用可能會影響到其他應(yīng)用程序的運行,所以多用在調(diào)試。(LD_LIBRARY_PATH的缺陷和使用準則,可以參考《Why
LD_LIBRARY_PATH is
bad》)。通常情況下推薦還是使用gcc的-R或-rpath選項來在編譯時就指定庫的查找路徑,并且該庫的路徑信息保存在可執(zhí)行文件中,運行時它會直接到該路徑查找?guī)欤苊饬耸褂肔D_LIBRARY_PATH環(huán)境變量查找。
下面通過例2來說明本方法。
例2:
我們通過以下命令用源程序pos_env.c(見程序3)來創(chuàng)建動態(tài)庫libpos.so。
# gcc -c pos_env.c
# gcc -shared -fPCI -o
libpos.so pos_env.o
#
#include <stdio.h>
void
pos()
{
printf("/root/test/env/lib\n");
}
程序3: pos_env.c
測試用的可執(zhí)行文件pos可以使用例1中的得到的目標程序pos,不需要再次編譯。因為pos_conf.c中的函數(shù)pos和pos_env.c中的函數(shù)pos
函數(shù)原型一致,且動態(tài)庫名相同,這就好比修改動態(tài)庫pos后重新創(chuàng)建該庫一樣。這也是使用動態(tài)庫的優(yōu)點之一。
然后把動態(tài)庫libpos.so移動到目錄/root/test/conf/lib中。
# mkdir -p /root/test/env/lib
# mv
libpos.so /root/test/env/lib
#
我們可以使用export來設(shè)置該環(huán)境變量,在設(shè)置該環(huán)境變量后所有的命令中,該環(huán)境變量都有效。
例如:
# export
LD_LIBRARY_PATH=/root/test/env/lib
#
但本文為了舉例方便,使用另一種設(shè)置環(huán)境變量的方法,既在命令前加環(huán)境變量設(shè)置,該環(huán)境變量只對該命令有效,當該命令執(zhí)行完成后,該環(huán)境變量就無效了。如下述命令:
# LD_LIBRARY_PATH=/root/test/env/lib ./pos
/root/test/env/lib
#
程序pos運行成功,并且打印的結(jié)果是"/root/test/env/lib",正是程序pos_env.c中的函數(shù)pos的運行結(jié)果。因此程序pos搜索到的動態(tài)庫是/root/test/env/lib/libpos.so。
方法三:在編譯目標代碼時指定該程序的動態(tài)庫搜索路徑。
還可以在編譯目標代碼時指定程序的動態(tài)庫搜索路徑。這是通過gcc 的參數(shù)"-Wl,-rpath,"指定(如例3所示)。當指定多個動態(tài)庫搜索路徑時,路徑之間用冒號":"分隔。
例3:
我們通過以下命令用源程序pos.c(見程序4)來創(chuàng)建動態(tài)庫libpos.so。
# gcc -c pos.c
# gcc -shared -fPCI -o
libpos.so pos.o
#
#include <stdio.h>
void
pos()
{
printf("./\n");
}
程序4: pos.c
因為我們需要在編譯目標代碼時指定可執(zhí)行文件的動態(tài)庫搜索路徑,所以需要用gcc命令重新編譯源程序main.c(見程序2)來生成可執(zhí)行文件pos。
# gcc -o pos main.c -L. -lpos
-Wl,-rpath,./
#
再運行程序pos試試。
# ./pos ./
#
程序pos運行成功,輸出的結(jié)果正是pos.c中的函數(shù)pos的運行結(jié)果。因此程序pos搜索到的動態(tài)庫是./libpos.so。
以上介紹了三種指定動態(tài)庫搜索路徑的方法,加上默認的動態(tài)庫搜索路徑/lib和/usr/lib,共五種動態(tài)庫的搜索路徑,那么它們搜索的先后順序是什么呢?
在 介紹上述三種方法時,分別創(chuàng)建了動態(tài)庫./libpos.so、
/root/test/env/lib/libpos.so和/root/test/conf/lib/libpos.so。我們再用源程序
pos_lib.c(見程序5)來創(chuàng)建動態(tài)庫/lib/libpos.so,用源程序pos_usrlib.c(見程序6)來創(chuàng)建動態(tài)庫
/usr/lib/libpos.so。
#include <stdio.h>
void
pos()
{
printf("/lib\n");
}
程序5: pos_lib.c
#include <stdio.h>
void
pos()
{
printf("/usr/lib\n");
}
程序6: pos_usrlib.c
這樣我們得到五個動態(tài)庫libpos.so,這些動態(tài)庫的名字相同,且都包含相同函數(shù)原型的公用函數(shù)pos。但存儲的位置不同和公用函數(shù)pos
打印的結(jié)果不同。每個動態(tài)庫中的公用函數(shù)pos都輸出該動態(tài)庫所存放的位置。這樣我們可以通過執(zhí)行例3中的可執(zhí)行文件pos得到的結(jié)果不同獲知其搜索到了哪個動態(tài)庫,從而獲得第1個動態(tài)庫搜索順序,然后刪除該動態(tài)庫,再執(zhí)行程序pos,獲得第2個動態(tài)庫搜索路徑,再刪除第2個被搜索到的動態(tài)庫,如此往復(fù),將可得到Linux搜索動態(tài)庫的先后順序。程序pos執(zhí)行的輸出結(jié)果和搜索到的動態(tài)庫的對應(yīng)關(guān)系如表1所示:
程序pos輸出結(jié)果 |
使用的動態(tài)庫 |
對應(yīng)的動態(tài)庫搜索路徑指定方式 |
./ |
./libpos.so |
編譯目標代碼時指定的動態(tài)庫搜索路徑 |
/root/test/env/lib |
/root/test/env/lib/libpos.so |
環(huán)境變量LD_LIBRARY_PATH指定的動態(tài)庫搜索路徑 |
/root/test/conf/lib |
/root/test/conf/lib/libpos.so |
配置文件/etc/ld.so.conf中指定的動態(tài)庫搜索路徑 |
/lib |
/lib/libpos.so |
默認的動態(tài)庫搜索路徑/lib |
/usr/lib |
/usr/lib/libpos.so |
默認的動態(tài)庫搜索路徑/usr/lib |
表1: 程序pos輸出結(jié)果和動態(tài)庫的對應(yīng)關(guān)系
創(chuàng)建各個動態(tài)庫,并放置在相應(yīng)的目錄中。測試環(huán)境就準備好了。執(zhí)行程序pos,并在該命令行中設(shè)置環(huán)境變量LD_LIBRARY_PATH。
# LD_LIBRARY_PATH=/root/test/env/lib ./pos
./
#
根據(jù)程序pos的輸出結(jié)果可知,最先搜索的是編譯目標代碼時指定的動態(tài)庫搜索路徑。然后我們把動態(tài)庫./libpos.so刪除了,再運行上述命令試試。
# rm libpos.so
rm: remove regular file
`libpos.so'? y
# LD_LIBRARY_PATH=/root/test/env/lib ./pos
/root/test/env/lib
#
根據(jù)程序pos的輸出結(jié)果可知,第2個動態(tài)庫搜索的路徑是環(huán)境變量LD_LIBRARY_PATH指定的。我們再把/root/test/env/lib/libpos.so刪除,運行上述命令。
# rm /root/test/env/lib/libpos.so
rm:
remove regular file `/root/test/env/lib/libpos.so'? y
#
LD_LIBRARY_PATH=/root/test/env/lib ./pos /root/test/conf/lib
#
第3個動態(tài)庫的搜索路徑是配置文件/etc/ld.so.conf指定的路徑。刪除動態(tài)庫/root/test/conf/lib/libpos.so后再運行上述命令。
# rm /root/test/conf/lib/libpos.so
rm:
remove regular file `/root/test/conf/lib/libpos.so'? y
#
LD_LIBRARY_PATH=/root/test/env/lib ./pos /lib
#
第4個動態(tài)庫的搜索路徑是默認搜索路徑/lib。我們再刪除動態(tài)庫/lib/libpos.so,運行上述命令。
# rm /lib/libpos.so
rm: remove regular
file `/lib/libpos.so'? y
# LD_LIBRARY_PATH=/root/test/env/lib ./pos
/usr/lib
#
最后的動態(tài)庫搜索路徑是默認搜索路徑/usr/lib。
綜合以上結(jié)果可知,動態(tài)庫的搜索路徑搜索的先后順序是:
1.編譯目標代碼時指定的動態(tài)庫搜索路徑;
2.環(huán)境變量LD_LIBRARY_PATH指定的動態(tài)庫搜索路徑;
3.配置文件/etc/ld.so.conf中指定的動態(tài)庫搜索路徑;
4.默認的動態(tài)庫搜索路徑/lib;
5.默認的動態(tài)庫搜索路徑/usr/lib。
在上述1、2、3指定動態(tài)庫搜索路徑時,都可指定多個動態(tài)庫搜索路徑,其搜索的先后順序是按指定路徑的先后順序搜索的。對此本文不再舉例說明,有興趣的讀者可以參照本文的方法驗證。
posted @
2010-09-14 11:03 xiaoxinchen 閱讀(226) |
評論 (0) |
編輯 收藏
序論
我曾發(fā)表過文件輸入輸出的文章,現(xiàn)在覺得有必要再寫一點。文件 I/O 在C++中比烤蛋糕簡單多了。 在這篇文章里,我會詳細解釋ASCII和二進制文件的輸入輸出的每個細節(jié),值得注意的是,所有這些都是用C++完成的。
一、ASCII 輸出
為了使用下面的方法,
你必須包含頭文件<fstream.h>(譯者注:在標準C++中,已經(jīng)使用<fstream>取
代<fstream.h>,所有的C++標準頭文件都是無后綴的。)。這是 <iostream.h>的一個擴展集,
提供有緩沖的文件輸入輸出操作. 事實上, <iostream.h> 已經(jīng)被<fstream.h>包含了,
所以你不必包含所有這兩個文件, 如果你想顯式包含他們,那隨便你。我們從文件操作類的設(shè)計開始, 我會講解如何進行ASCII I/O操作。
如果你猜是"fstream," 恭喜你答對了! 但這篇文章介紹的方法,我們分別使用"ifstream"?和 "ofstream" 來作輸入輸出。
如果你用過標準控制臺流"cin"?和 "cout," 那現(xiàn)在的事情對你來說很簡單。 我們現(xiàn)在開始講輸出部分,首先聲明一個類對象。
ofstream fout;
這就可以了,不過你要打開一個文件的話, 必須像這樣調(diào)用ofstream::open()。
fout.open("output.txt");
你也可以把文件名作為構(gòu)造參數(shù)來打開一個文件.
ofstream fout("output.txt");
這是我們使用的方法, 因為這樣創(chuàng)建和打開一個文件看起來更簡單. 順便說一句, 如果你要打開的文件不存在,它會為你創(chuàng)建一個,
所以不用擔(dān)心文件創(chuàng)建的問題. 現(xiàn)在就輸出到文件,看起來和"cout"的操作很像。 對不了解控制臺輸出"cout"的人, 這里有個例子。
int num = 150;char name[] = "John Doe";fout << "Here is a number: " << num << "\n";fout << "Now here is a string: " << name << "\n";
現(xiàn)在保存文件,你必須關(guān)閉文件,或者回寫文件緩沖. 文件關(guān)閉之后就不能再操作了,
所以只有在你不再操作這個文件的時候才調(diào)用它,它會自動保存文件。 回寫緩沖區(qū)會在保持文件打開的情況下保存文件, 所以只要有必要就使用它。
回寫看起來像另一次輸出, 然后調(diào)用方法關(guān)閉。像這樣:
fout << flush; fout.close();
現(xiàn)在你用文本編輯器打開文件,內(nèi)容看起來是這樣:
Here is a number: 150 Now here is a string: John Doe
很簡單吧! 現(xiàn)在繼續(xù)文件輸入, 需要一點技巧, 所以先確認你已經(jīng)明白了流操作,對 "<<" 和">>" 比較熟悉了, 因為你接下來還要用到他們。繼續(xù)…
二、ASCII 輸入
輸入和"cin" 流很像. 和剛剛討論的輸出流很像, 但你要考慮幾件事情。在我們開始復(fù)雜的內(nèi)容之前, 先看一個文本:
12 GameDev 15.45 L This is really awesome!
為了打開這個文件,你必須創(chuàng)建一個in-stream對象,?像這樣。
ifstream fin("input.txt");
現(xiàn)在讀入前四行. 你還記得怎么用"<<" 操作符往流里插入變量和符號吧?好,?在 "<<" (插入)?操作符之后,是">>" (提取) 操作符. 使用方法是一樣的. 看這個代碼片段.
int number; float real; char letter, word[8]; fin >> number; fin >> word; fin >> real; fin >> letter;
也可以把這四行讀取文件的代碼寫為更簡單的一行。
fin >> number >> word >> real >> letter;
它是如何運作的呢? 文件的每個空白之后, ">>" 操作符會停止讀取內(nèi)容, 直到遇到另一個>>操作符.
因為我們讀取的每一行都被換行符分割開(是空白字符), ">>"
操作符只把這一行的內(nèi)容讀入變量。這就是這個代碼也能正常工作的原因。但是,可別忘了文件的最后一行。
This is really awesome!
如果你想把整行讀入一個char數(shù)組, 我們沒辦法用">>"?操作符,因為每個單詞之間的空格(空白字符)會中止文件的讀取。為了驗證:
char sentence[101]; fin >> sentence;
我們想包含整個句子, "This is really awesome!" 但是因為空白, 現(xiàn)在它只包含了"This". 很明顯, 肯定有讀取整行的方法, 它就是getline()。這就是我們要做的。
fin.getline(sentence, 100);
這是函數(shù)參數(shù). 第一個參數(shù)顯然是用來接受的char數(shù)組. 第二個參數(shù)是在遇到換行符之前,數(shù)組允許接受的最大元素數(shù)量. 現(xiàn)在我們得到了想要的結(jié)果:“This is really awesome!”。
你應(yīng)該已經(jīng)知道如何讀取和寫入ASCII文件了。但我們還不能罷休,因為二進制文件還在等著我們。
三、二進制 輸入輸出
二進制文件會復(fù)雜一點, 但還是很簡單的。
首先你要注意我們不再使用插入和提取操作符(譯者注:<< 和 >> 操作符).
你可以這么做,但它不會用二進制方式讀寫。你必須使用read() 和write() 方法讀取和寫入二進制文件. 創(chuàng)建一個二進制文件, 看下一行。
ofstream fout("file.dat", ios::binary);
這會以二進制方式打開文件, 而不是默認的ASCII模式。首先從寫入文件開始。函數(shù)write() 有兩個參數(shù)。 第一個是指向?qū)ο蟮腸har類型的指針, 第二個是對象的大小(譯者注:字節(jié)數(shù))。 為了說明,看例子。
int number = 30; fout.write((char *)(&number), sizeof(number));
第一個參數(shù)寫做"(char *)(&number)". 這是把一個整型變量轉(zhuǎn)為char
*指針。如果你不理解,可以立刻翻閱C++的書籍,如果有必要的話。第二個參數(shù)寫作"sizeof(number)". sizeof()
返回對象大小的字節(jié)數(shù). 就是這樣!
二進制文件最好的地方是可以在一行把一個結(jié)構(gòu)寫入文件。 如果說,你的結(jié)構(gòu)有12個不同的成員。 用ASCII?文件,你不得不每次一條的寫入所有成員。 但二進制文件替你做好了。 看這個。
struct OBJECT { int number; char letter; } obj; obj.number = 15;obj.letter = ‘M’; fout.write((char *)(&obj), sizeof(obj));
這樣就寫入了整個結(jié)構(gòu)! 接下來是輸入. 輸入也很簡單,因為read()?函數(shù)的參數(shù)和 write()是完全一樣的, 使用方法也相同。
ifstream fin("file.dat", ios::binary); fin.read((char *)(&obj), sizeof(obj));
我不多解釋用法, 因為它和write()是完全相同的。二進制文件比ASCII文件簡單, 但有個缺點是無法用文本編輯器編輯。 接著, 我解釋一下ifstream 和ofstream 對象的其他一些方法作為結(jié)束.
四、更多方法
我已經(jīng)解釋了ASCII文件和二進制文件, 這里是一些沒有提及的底層方法。
檢查文件
你已經(jīng)學(xué)會了open() 和close() 方法, 不過這里還有其它你可能用到的方法。
方法good() 返回一個布爾值,表示文件打開是否正確。
類似的,bad() 返回一個布爾值表示文件打開是否錯誤。 如果出錯,就不要繼續(xù)進一步的操作了。
最后一個檢查的方法是fail(), 和bad()有點相似, 但沒那么嚴重。
讀文件
方法get() 每次返回一個字符。
方法ignore(int,char) 跳過一定數(shù)量的某個字符, 但你必須傳給它兩個參數(shù)。第一個是需要跳過的字符數(shù)。 第二個是一個字符, 當遇到的時候就會停止。 例子,
fin.ignore(100, ‘\n’);
會跳過100個字符,或者不足100的時候,跳過所有之前的字符,包括 ‘\n’。
方法peek() 返回文件中的下一個字符, 但并不實際讀取它。所以如果你用peek() 查看下一個字符, 用get() 在peek()之后讀取,會得到同一個字符, 然后移動文件計數(shù)器。
方法putback(char) 輸入字符, 一次一個, 到流中。我沒有見到過它的使用,但這個函數(shù)確實存在。
寫文件
只有一個你可能會關(guān)注的方法.?那就是 put(char), 它每次向輸出流中寫入一個字符。
打開文件
當我們用這樣的語法打開二進制文件:
ofstream fout("file.dat", ios::binary);
"ios::binary"是你提供的打開選項的額外標志. 默認的, 文件以ASCII方式打開, 不存在則創(chuàng)建, 存在就覆蓋. 這里有些額外的標志用來改變選項。
ios::app |
添加到文件尾 |
ios::ate |
把文件標志放在末尾而非起始。 |
ios::trunc |
默認. 截斷并覆寫文件。 |
ios::nocreate |
文件不存在也不創(chuàng)建。 |
ios::noreplace |
文件存在則失敗。 |
文件狀態(tài)
我用過的唯一一個狀態(tài)函數(shù)是eof(), 它返回是否標志已經(jīng)到了文件末尾。 我主要用在循環(huán)中。 例如, 這個代碼斷統(tǒng)計小寫‘e’ 在文件中出現(xiàn)的次數(shù)。
ifstream fin("file.txt"); char ch; int counter; while (!fin.eof()) { ch = fin.get(); if (ch == ‘e’) counter++; }fin.close();
我從未用過這里沒有提到的其他方法。 還有很多方法,但是他們很少被使用。參考C++書籍或者文件流的幫助文檔來了解其他的方法。
posted @
2010-08-08 17:37 xiaoxinchen 閱讀(190) |
評論 (0) |
編輯 收藏
什么是Socket
Socket接口是TCP/IP網(wǎng)絡(luò)的API,Socket接口定義了許多函數(shù)或例程,程序員可以用它們來開發(fā)TCP/IP網(wǎng)絡(luò)上的應(yīng)用程序。要學(xué)Internet上的TCP/IP網(wǎng)絡(luò)編程,必須理解Socket接口。
Socket接口設(shè)計者最先是將接口放在Unix操作系統(tǒng)里面的。如果了解Unix系統(tǒng)的輸入和輸出的話,就很容易了解Socket了。網(wǎng)絡(luò)的 Socket數(shù)據(jù)傳輸是一種特殊的I/O,Socket也是一種文件描述符。Socket也具有一個類似于打開文件的函數(shù)調(diào)用Socket(),該函數(shù)返 回一個整型的Socket描述符,隨后的連接建立、數(shù)據(jù)傳輸?shù)炔僮鞫际峭ㄟ^該Socket實現(xiàn)的。常用的Socket類型有兩種:流式Socket (SOCK_STREAM)和數(shù)據(jù)報式Socket(SOCK_DGRAM)。流式是一種面向連接的Socket,針對于面向連接的TCP服務(wù)應(yīng)用;數(shù)據(jù) 報式Socket是一種無連接的Socket,對應(yīng)于無連接的UDP服務(wù)應(yīng)用。
Socket建立
為了建立Socket,程序可以調(diào)用Socket函數(shù),該函數(shù)返回一個類似于文件描述符的句柄。socket函數(shù)原型為:
int socket(int domain, int type, int protocol);
domain指明所使用的協(xié)議族,通常為PF_INET,表示互聯(lián)網(wǎng)協(xié)議族(TCP/IP協(xié)議族);type參數(shù)指定socket的類型: SOCK_STREAM 或SOCK_DGRAM,Socket接口還定義了原始Socket(SOCK_RAW),允許程序使用低層協(xié)議;protocol通常賦值"0"。 Socket()調(diào)用返回一個整型socket描述符,你可以在后面的調(diào)用使用它。
Socket描述符是一個指向內(nèi)部數(shù)據(jù)結(jié)構(gòu)的指針,它指向描述符表入口。調(diào)用Socket函數(shù)時,socket執(zhí)行體將建立一個Socket,實際上"建立一個Socket"意味著為一個Socket數(shù)據(jù)結(jié)構(gòu)分配存儲空間。Socket執(zhí)行體為你管理描述符表。
兩個網(wǎng)絡(luò)程序之間的一個網(wǎng)絡(luò)連接包括五種信息:通信協(xié)議、本地協(xié)議地址、本地主機端口、遠端主機地址和遠端協(xié)議端口。Socket數(shù)據(jù)結(jié)構(gòu)中包含這五種信息。
Socket配置
通過socket調(diào)用返回一個socket描述符后,在使用socket進行網(wǎng)絡(luò)傳輸以前,必須配置該socket。面向連接的socket客戶端通過 調(diào)用Connect函數(shù)在socket數(shù)據(jù)結(jié)構(gòu)中保存本地和遠端信息。無連接socket的客戶端和服務(wù)端以及面向連接socket的服務(wù)端通過調(diào)用 bind函數(shù)來配置本地信息。
Bind函數(shù)將socket與本機上的一個端口相關(guān)聯(lián),隨后你就可以在該端口監(jiān)聽服務(wù)請求。Bind函數(shù)原型為:
int bind(int sockfd,struct sockaddr *my_addr, int addrlen);
Sockfd是調(diào)用socket函數(shù)返回的socket描述符,my_addr是一個指向包含有本機IP地址及端口號等信息的sockaddr類型的指針;addrlen常被設(shè)置為sizeof(struct sockaddr)。
struct sockaddr結(jié)構(gòu)類型是用來保存socket信息的:
struct sockaddr {
unsigned short sa_family; /* 地址族, AF_xxx */
char sa_data[14]; /* 14 字節(jié)的協(xié)議地址 */
};
sa_family一般為AF_INET,代表Internet(TCP/IP)地址族;sa_data則包含該socket的IP地址和端口號。
另外還有一種結(jié)構(gòu)類型:
struct sockaddr_in {
short int sin_family; /* 地址族 */
unsigned short int sin_port; /* 端口號 */
struct in_addr sin_addr; /* IP地址 */
unsigned char sin_zero[8]; /* 填充0 以保持與struct sockaddr同樣大小 */
};
這個結(jié)構(gòu)更方便使用。sin_zero用來將sockaddr_in結(jié)構(gòu)填充到與struct sockaddr同樣的長度,可以用bzero()或memset()函數(shù)將其置為零。指向sockaddr_in 的指針和指向sockaddr的指針可以相互轉(zhuǎn)換,這意味著如果一個函數(shù)所需參數(shù)類型是sockaddr時,你可以在函數(shù)調(diào)用的時候?qū)⒁粋€指向 sockaddr_in的指針轉(zhuǎn)換為指向sockaddr的指針;或者相反。
使用bind函數(shù)時,可以用下面的賦值實現(xiàn)自動獲得本機IP地址和隨機獲取一個沒有被占用的端口號:
my_addr.sin_port = 0; /* 系統(tǒng)隨機選擇一個未被使用的端口號 */
my_addr.sin_addr.s_addr = INADDR_ANY; /* 填入本機IP地址 */
通過將my_addr.sin_port置為0,函數(shù)會自動為你選擇一個未占用的端口來使用。同樣,通過將my_addr.sin_addr.s_addr置為INADDR_ANY,系統(tǒng)會自動填入本機IP地址。
注意在使用bind函數(shù)是需要將sin_port和sin_addr轉(zhuǎn)換成為網(wǎng)絡(luò)字節(jié)優(yōu)先順序;而sin_addr則不需要轉(zhuǎn)換。
計算機數(shù)據(jù)存儲有兩種字節(jié)優(yōu)先順序:高位字節(jié)優(yōu)先和低位字節(jié)優(yōu)先。Internet上數(shù)據(jù)以高位字節(jié)優(yōu)先順序在網(wǎng)絡(luò)上傳輸,所以對于在內(nèi)部是以低位字節(jié)優(yōu)先方式存儲數(shù)據(jù)的機器,在Internet上傳輸數(shù)據(jù)時就需要進行轉(zhuǎn)換,否則就會出現(xiàn)數(shù)據(jù)不一致。
下面是幾個字節(jié)順序轉(zhuǎn)換函數(shù):
·htonl():把32位值從主機字節(jié)序轉(zhuǎn)換成網(wǎng)絡(luò)字節(jié)序
·htons():把16位值從主機字節(jié)序轉(zhuǎn)換成網(wǎng)絡(luò)字節(jié)序
·ntohl():把32位值從網(wǎng)絡(luò)字節(jié)序轉(zhuǎn)換成主機字節(jié)序
·ntohs():把16位值從網(wǎng)絡(luò)字節(jié)序轉(zhuǎn)換成主機字節(jié)序
Bind()函數(shù)在成功被調(diào)用時返回0;出現(xiàn)錯誤時返回"-1"并將errno置為相應(yīng)的錯誤號。需要注意的是,在調(diào)用bind函數(shù)時一般不要將端口號置為小于1024的值,因為1到1024是保留端口號,你可以選擇大于1024中的任何一個沒有被占用的端口號。
連接建立
面向連接的客戶程序使用Connect函數(shù)來配置socket并與遠端服務(wù)器建立一個TCP連接,其函數(shù)原型為:
int connect(int sockfd, struct sockaddr *serv_addr,int addrlen);
Sockfd 是socket函數(shù)返回的socket描述符;serv_addr是包含遠端主機IP地址和端口號的指針;addrlen是遠端地質(zhì)結(jié)構(gòu)的長度。 Connect函數(shù)在出現(xiàn)錯誤時返回-1,并且設(shè)置errno為相應(yīng)的錯誤碼。進行客戶端程序設(shè)計無須調(diào)用bind(),因為這種情況下只需知道目的機器 的IP地址,而客戶通過哪個端口與服務(wù)器建立連接并不需要關(guān)心,socket執(zhí)行體為你的程序自動選擇一個未被占用的端口,并通知你的程序數(shù)據(jù)什么時候到 打斷口。
Connect函數(shù)啟動和遠端主機的直接連接。只有面向連接的客戶程序使用socket時才需要將此socket與遠端主機相連。無連接協(xié)議從不建立直接連接。面向連接的服務(wù)器也從不啟動一個連接,它只是被動的在協(xié)議端口監(jiān)聽客戶的請求。
Listen函數(shù)使socket處于被動的監(jiān)聽模式,并為該socket建立一個輸入數(shù)據(jù)隊列,將到達的服務(wù)請求保存在此隊列中,直到程序處理它們。
int listen(int sockfd, int backlog);
Sockfd 是Socket系統(tǒng)調(diào)用返回的socket 描述符;backlog指定在請求隊列中允許的最大請求數(shù),進入的連接請求將在隊列中等待accept()它們(參考下文)。Backlog對隊列中等待 服務(wù)的請求的數(shù)目進行了限制,大多數(shù)系統(tǒng)缺省值為20。如果一個服務(wù)請求到來時,輸入隊列已滿,該socket將拒絕連接請求,客戶將收到一個出錯信息。
當出現(xiàn)錯誤時listen函數(shù)返回-1,并置相應(yīng)的errno錯誤碼。
accept()函數(shù)讓服務(wù)器接收客戶的連接請求。在建立好輸入隊列后,服務(wù)器就調(diào)用accept函數(shù),然后睡眠并等待客戶的連接請求。
int accept(int sockfd, void *addr, int *addrlen);
sockfd是被監(jiān)聽的socket描述符,addr通常是一個指向sockaddr_in變量的指針,該變量用來存放提出連接請求服務(wù)的主機的信息(某 臺主機從某個端口發(fā)出該請求);addrten通常為一個指向值為sizeof(struct sockaddr_in)的整型指針變量。出現(xiàn)錯誤時accept函數(shù)返回-1并置相應(yīng)的errno值。
首先,當accept函數(shù)監(jiān)視的 socket收到連接請求時,socket執(zhí)行體將建立一個新的socket,執(zhí)行體將這個新socket和請求連接進程的地址聯(lián)系起來,收到服務(wù)請求的 初始socket仍可以繼續(xù)在以前的 socket上監(jiān)聽,同時可以在新的socket描述符上進行數(shù)據(jù)傳輸操作。
數(shù)據(jù)傳輸
Send()和recv()這兩個函數(shù)用于面向連接的socket上進行數(shù)據(jù)傳輸。
Send()函數(shù)原型為:
int send(int sockfd, const void *msg, int len, int flags);
Sockfd是你想用來傳輸數(shù)據(jù)的socket描述符;msg是一個指向要發(fā)送數(shù)據(jù)的指針;Len是以字節(jié)為單位的數(shù)據(jù)的長度;flags一般情況下置為0(關(guān)于該參數(shù)的用法可參照man手冊)。
Send()函數(shù)返回實際上發(fā)送出的字節(jié)數(shù),可能會少于你希望發(fā)送的數(shù)據(jù)。在程序中應(yīng)該將send()的返回值與欲發(fā)送的字節(jié)數(shù)進行比較。當send()返回值與len不匹配時,應(yīng)該對這種情況進行處理。
char *msg = "Hello!";
int len, bytes_sent;
……
len = strlen(msg);
bytes_sent = send(sockfd, msg,len,0);
……
recv()函數(shù)原型為:
int recv(int sockfd,void *buf,int len,unsigned int flags);
Sockfd是接受數(shù)據(jù)的socket描述符;buf 是存放接收數(shù)據(jù)的緩沖區(qū);len是緩沖的長度。Flags也被置為0。Recv()返回實際上接收的字節(jié)數(shù),當出現(xiàn)錯誤時,返回-1并置相應(yīng)的errno值。
Sendto()和recvfrom()用于在無連接的數(shù)據(jù)報socket方式下進行數(shù)據(jù)傳輸。由于本地socket并沒有與遠端機器建立連接,所以在發(fā)送數(shù)據(jù)時應(yīng)指明目的地址。
sendto()函數(shù)原型為:
int sendto(int sockfd, const void *msg,int len,unsigned int flags,const struct sockaddr *to, int tolen);
該函數(shù)比send()函數(shù)多了兩個參數(shù),to表示目地機的IP地址和端口號信息,而tolen常常被賦值為sizeof (struct sockaddr)。Sendto 函數(shù)也返回實際發(fā)送的數(shù)據(jù)字節(jié)長度或在出現(xiàn)發(fā)送錯誤時返回-1。
Recvfrom()函數(shù)原型為:
int recvfrom(int sockfd,void *buf,int len,unsigned int flags,struct sockaddr *from,int *fromlen);
from是一個struct sockaddr類型的變量,該變量保存源機的IP地址及端口號。fromlen常置為sizeof (struct sockaddr)。當recvfrom()返回時,fromlen包含實際存入from中的數(shù)據(jù)字節(jié)數(shù)。Recvfrom()函數(shù)返回接收到的字節(jié)數(shù)或 當出現(xiàn)錯誤時返回-1,并置相應(yīng)的errno。
如果你對數(shù)據(jù)報socket調(diào)用了connect()函數(shù)時,你也可以利用send()和recv()進行數(shù)據(jù)傳輸,但該socket仍然是數(shù)據(jù)報socket,并且利用傳輸層的UDP服務(wù)。但在發(fā)送或接收數(shù)據(jù)報時,內(nèi)核會自動為之加上目地和源地址信息。
結(jié)束傳輸
當所有的數(shù)據(jù)操作結(jié)束以后,你可以調(diào)用close()函數(shù)來釋放該socket,從而停止在該socket上的任何數(shù)據(jù)操作:
close(sockfd);
你也可以調(diào)用shutdown()函數(shù)來關(guān)閉該socket。該函數(shù)允許你只停止在某個方向上的數(shù)據(jù)傳輸,而一個方向上的數(shù)據(jù)傳輸繼續(xù)進行。如你可以關(guān)閉某socket的寫操作而允許繼續(xù)在該socket上接受數(shù)據(jù),直至讀入所有數(shù)據(jù)。
int shutdown(int sockfd,int how);
Sockfd是需要關(guān)閉的socket的描述符。參數(shù) how允許為shutdown操作選擇以下幾種方式:
·0-------不允許繼續(xù)接收數(shù)據(jù)
·1-------不允許繼續(xù)發(fā)送數(shù)據(jù)
·2-------不允許繼續(xù)發(fā)送和接收數(shù)據(jù),
·均為允許則調(diào)用close ()
shutdown在操作成功時返回0,在出現(xiàn)錯誤時返回-1并置相應(yīng)errno。
Socket編程實例
代碼實例中的服務(wù)器通過socket連接向客戶端發(fā)送字符串"Hello, you are connected!"。只要在服務(wù)器上運行該服務(wù)器軟件,在客戶端運行客戶軟件,客戶端就會收到該字符串。
該服務(wù)器軟件代碼如下:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>
#define SERVPORT 3333 /*服務(wù)器監(jiān)聽端口號 */
#define BACKLOG 10 /* 最大同時連接請求數(shù) */
main()
{
int sockfd,client_fd; /*sock_fd:監(jiān)聽socket;client_fd:數(shù)據(jù)傳輸socket */
struct sockaddr_in my_addr; /* 本機地址信息 */
struct sockaddr_in remote_addr; /* 客戶端地址信息 */
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("socket創(chuàng)建出錯!"); exit(1);
}
my_addr.sin_family=AF_INET;
my_addr.sin_port=htons(SERVPORT);
my_addr.sin_addr.s_addr = INADDR_ANY;
bzero(&(my_addr.sin_zero),8);
if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) == -1) {
perror("bind出錯!");
exit(1);
}
if (listen(sockfd, BACKLOG) == -1) {
perror("listen出錯!");
exit(1);
}
while(1) {
sin_size = sizeof(struct sockaddr_in);
if ((client_fd = accept(sockfd, (struct sockaddr *)&remote_addr, &sin_size)) == -1) {
perror("accept出錯");
continue;
}
printf("received a connection from %s\n", inet_ntoa(remote_addr.sin_addr));
if (!fork()) { /* 子進程代碼段 */
if (send(client_fd, "Hello, you are connected!\n", 26, 0) == -1)
perror("send出錯!");
close(client_fd);
exit(0);
}
close(client_fd);
}
}
}
服務(wù)器的工作流程是這樣的:首先調(diào)用socket函數(shù)創(chuàng)建一個Socket,然后調(diào)用bind函數(shù)將其與本機地址以及一個本地端口號綁定,然后調(diào)用 listen在相應(yīng)的socket上監(jiān)聽,當accpet接收到一個連接服務(wù)請求時,將生成一個新的socket。服務(wù)器顯示該客戶機的IP地址,并通過 新的socket向客戶端發(fā)送字符串"Hello,you are connected!"。最后關(guān)閉該socket。
代碼實例中的fork()函數(shù)生成一個子進程來處理數(shù)據(jù)傳輸部分,fork()語句對于子進程返回的值為0。所以包含fork函數(shù)的if語句是子進程代碼部分,它與if語句后面的父進程代碼部分是并發(fā)執(zhí)行的。
客戶端程序代碼如下:
#include<stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#define SERVPORT 3333
#define MAXDATASIZE 100 /*每次最大數(shù)據(jù)傳輸量 */
main(int argc, char *argv[]){
int sockfd, recvbytes;
char buf[MAXDATASIZE];
struct hostent *host;
struct sockaddr_in serv_addr;
if (argc < 2) {
fprintf(stderr,"Please enter the server's hostname!\n");
exit(1);
}
if((host=gethostbyname(argv[1]))==NULL) {
herror("gethostbyname出錯!");
exit(1);
}
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){
perror("socket創(chuàng)建出錯!");
exit(1);
}
serv_addr.sin_family=AF_INET;
serv_addr.sin_port=htons(SERVPORT);
serv_addr.sin_addr = *((struct in_addr *)host->h_addr);
bzero(&(serv_addr.sin_zero),8);
if (connect(sockfd, (struct sockaddr *)&serv_addr, \
sizeof(struct sockaddr)) == -1) {
perror("connect出錯!");
exit(1);
}
if ((recvbytes=recv(sockfd, buf, MAXDATASIZE, 0)) ==-1) {
perror("recv出錯!");
exit(1);
}
buf[recvbytes] = '\0';
printf("Received: %s",buf);
close(sockfd);
}
客戶端程序首先通過服務(wù)器域名獲得服務(wù)器的IP地址,然后創(chuàng)建一個socket,調(diào)用connect函數(shù)與服務(wù)器建立連接,連接成功之后接收從服務(wù)器發(fā)送過來的數(shù)據(jù),最后關(guān)閉socket。
函數(shù)gethostbyname()是完成域名轉(zhuǎn)換的。由于IP地址難以記憶和讀寫,所以為了方便,人們常常用域名來表示主機,這就需要進行域名和IP地址的轉(zhuǎn)換。函數(shù)原型為:
struct hostent *gethostbyname(const char *name);
函數(shù)返回為hosten的結(jié)構(gòu)類型,它的定義如下:
struct hostent {
char *h_name; /* 主機的官方域名 */
char **h_aliases; /* 一個以NULL結(jié)尾的主機別名數(shù)組 */
int h_addrtype; /* 返回的地址類型,在Internet環(huán)境下為AF-INET */
int h_length; /* 地址的字節(jié)長度 */
char **h_addr_list; /* 一個以0結(jié)尾的數(shù)組,包含該主機的所有地址*/
};
#define h_addr h_addr_list[0] /*在h-addr-list中的第一個地址*/
當 gethostname()調(diào)用成功時,返回指向struct hosten的指針,當調(diào)用失敗時返回-1。當調(diào)用gethostbyname時,你不能使用perror()函數(shù)來輸出錯誤信息,而應(yīng)該使用herror()函數(shù)來輸出。
無連接的客戶/服務(wù)器程序的在原理上和連接的客戶/服務(wù)器是一樣的,兩者的區(qū)別在于無連接的客戶/服務(wù)器中的客戶一般不需要建立連接,而且在發(fā)送接收數(shù)據(jù)時,需要指定遠端機的地址。
阻塞和非阻塞
阻塞函數(shù)在完成其指定的任務(wù)以前不允許程序調(diào)用另一個函數(shù)。例如,程序執(zhí)行一個讀數(shù)據(jù)的函數(shù)調(diào)用時,在此函數(shù)完成讀操作以前將不會執(zhí)行下一程序語句。當 服務(wù)器運行到accept語句時,而沒有客戶連接服務(wù)請求到來,服務(wù)器就會停止在accept語句上等待連接服務(wù)請求的到來。這種情況稱為阻塞 (blocking)。而非阻塞操作則可以立即完成。比如,如果你希望服務(wù)器僅僅注意檢查是否有客戶在等待連接,有就接受連接,否則就繼續(xù)做其他事情,則 可以通過將Socket設(shè)置為非阻塞方式來實現(xiàn)。非阻塞socket在沒有客戶在等待時就使accept調(diào)用立即返回。
#include <unistd.h>
#include <fcntl.h>
……
sockfd = socket(AF_INET,SOCK_STREAM,0);
fcntl(sockfd,F_SETFL,O_NONBLOCK);
……
通過設(shè)置socket為非阻塞方式,可以實現(xiàn)"輪詢"若干Socket。當企圖從一個沒有數(shù)據(jù)等待處理的非阻塞Socket讀入數(shù)據(jù)時,函數(shù)將立即返 回,返回值為-1,并置errno值為EWOULDBLOCK。但是這種"輪詢"會使CPU處于忙等待方式,從而降低性能,浪費系統(tǒng)資源。而調(diào)用 select()會有效地解決這個問題,它允許你把進程本身掛起來,而同時使系統(tǒng)內(nèi)核監(jiān)聽所要求的一組文件描述符的任何活動,只要確認在任何被監(jiān)控的文件 描述符上出現(xiàn)活動,select()調(diào)用將返回指示該文件描述符已準備好的信息,從而實現(xiàn)了為進程選出隨機的變化,而不必由進程本身對輸入進行測試而浪費 CPU開銷。Select函數(shù)原型為:
int select(int numfds,fd_set *readfds,fd_set *writefds,
fd_set *exceptfds,struct timeval *timeout);
其中readfds、writefds、exceptfds分別是被select()監(jiān)視的讀、寫和異常處理的文件描述符集合。如果你希望確定是否可以 從標準輸入和某個socket描述符讀取數(shù)據(jù),你只需要將標準輸入的文件描述符0和相應(yīng)的sockdtfd加入到readfds集合中;numfds的值 是需要檢查的號碼最高的文件描述符加1,這個例子中numfds的值應(yīng)為sockfd+1;當select返回時,readfds將被修改,指示某個文件 描述符已經(jīng)準備被讀取,你可以通過FD_ISSSET()來測試。為了實現(xiàn)fd_set中對應(yīng)的文件描述符的設(shè)置、復(fù)位和測試,它提供了一組宏:
FD_ZERO(fd_set *set)----清除一個文件描述符集;
FD_SET(int fd,fd_set *set)----將一個文件描述符加入文件描述符集中;
FD_CLR(int fd,fd_set *set)----將一個文件描述符從文件描述符集中清除;
FD_ISSET(int fd,fd_set *set)----試判斷是否文件描述符被置位。
Timeout參數(shù)是一個指向struct timeval類型的指針,它可以使select()在等待timeout長時間后沒有文件描述符準備好即返回。struct timeval數(shù)據(jù)結(jié)構(gòu)為:
struct timeval {
int tv_sec; /* seconds */
int tv_usec; /* microseconds */
};
POP3客戶端實例
下面的代碼實例基于POP3的客戶協(xié)議,與郵件服務(wù)器連接并取回指定用戶帳號的郵件。與郵件服務(wù)器交互的命令存儲在字符串數(shù)組POPMessage中,程序通過一個do-while循環(huán)依次發(fā)送這些命令。
#include<stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#define POP3SERVPORT 110
#define MAXDATASIZE 4096
main(int argc, char *argv[]){
int sockfd;
struct hostent *host;
struct sockaddr_in serv_addr;
char *POPMessage[]={
"USER userid\r\n",
"PASS password\r\n",
"STAT\r\n",
"LIST\r\n",
"RETR 1\r\n",
"DELE 1\r\n",
"QUIT\r\n",
NULL
};
int iLength;
int iMsg=0;
int iEnd=0;
char buf[MAXDATASIZE];
if((host=gethostbyname("your.server"))==NULL) {
perror("gethostbyname error");
exit(1);
}
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){
perror("socket error");
exit(1);
}
serv_addr.sin_family=AF_INET;
serv_addr.sin_port=htons(POP3SERVPORT);
serv_addr.sin_addr = *((struct in_addr *)host->h_addr);
bzero(&(serv_addr.sin_zero),8);
if (connect(sockfd, (struct sockaddr *)&serv_addr,sizeof(struct sockaddr))==-1){
perror("connect error");
exit(1);
}
do {
send(sockfd,POPMessage[iMsg],strlen(POPMessage[iMsg]),0);
printf("have sent: %s",POPMessage[iMsg]);
iLength=recv(sockfd,buf+iEnd,sizeof(buf)-iEnd,0);
iEnd+=iLength;
buf[iEnd]='\0';
printf("received: %s,%d\n",buf,iMsg);
iMsg++;
} while (POPMessage[iMsg]);
close(sockfd);
}
來自:Li
posted @
2010-03-21 21:01 xiaoxinchen 閱讀(226) |
評論 (0) |
編輯 收藏
總體思路是先打成jar再把jar打成exe。主要看1.3和2.3里的內(nèi)容就可以了。
1.將項目打成jar:
1.1
要將項目打包成jar文件,方法很多,可以用Eclipse自帶的打包工具Ant打包,也可以用Eclipse的Export生成jar。經(jīng)過嘗試后,我
不推薦用Ant打包,因為要自己編寫xml腳本語言,還要增加一些外部的jar,所以我打了好幾次都沒打成。
1.2
在這里介紹兩種方法生成jar,第一種是用Eclpise的Export功能。在要打包的項目上擊右鍵,選擇Export,在窗口中選擇Java里的
JAR file。Next后的窗口中已經(jīng)自動選好了要打包的項目,用戶可以點擊加號查看項目里被打包的內(nèi)容。在下面的JAR
file里設(shè)置你打包生成jar文件的輸出目錄,下一步在出現(xiàn)的窗口中選擇Use existing manifest from
workspace,在下面的Main
class后面直接點Browse,它會自動列出你項目中有主函數(shù)main的類。選擇主類后點Finish即可生成jar文件。在此說明一下,這種打包方
法不能把項目中的外部的jar包打進來,因該是也要編寫一些腳本語言,沒往深研究。所以生成后的jar有些是不能執(zhí)行的。
1.3 第二種方法是利用Eclipse的一個第三方插件fatjar生成jar文件,也是本人覺得最簡單最方便的一種生成方式。先從網(wǎng)上下載些
插件,解壓后是一個plugins的文件夾,里面只有一個文件夾,我的是“net.sf.fjep.fatjar_0.0.24”將它copy到
Eclipser plugins文件夾下,此插件就安裝成功了,重啟Eclipse在項目上右擊就會看到多出一個“Build Fat
Jar”在前面有個綠色的“+”號,這時你就可以用此插件打包你的項目了。進去后第一個界面Jar-Name里增入要生成的jar文件名,我的是
“CAMP_fat.jar”。在Main-Class后點Browse像Export一樣它也會列出你項目中的主類,選擇后其它默認即可,Next后會
列出你要打包的所有內(nèi)容,這個插件的優(yōu)勢就是可以將你項目中的外部jar也打進來,有三個先項,其中Export
ANT是生成build.xml腳本文件,方便用戶以后修改腳本,其它兩個按鈕沒用。在這里什么都不點,直接點Finish就可以生成jar文件。
2.將jar打成.exe文件:
2.1
雖然此時的jar文件已經(jīng)可以執(zhí)行了。生成.exe的文件我也是用兩種方法實現(xiàn)的,用到的打包工具是j2ewiz和exe4j,它們的不同會在我下面的介
紹中體現(xiàn)出來。
2.2 首先是j2ewiz,這個軟件是綠色的,不用安裝,解壓后可以直接運行,但這個軟件生成的
.exe文件不是跨平臺的。運行此程序首先就是輸入要打包的jar文件,我們?yōu)g覽JAR選擇我們之前用fatjar生成的“CAMP_fat.jar”項
目文件(詳見1.3),下面那個選項是提示用戶最低要求的JRE版本,一般選1.3。下一步,因為我們的寢室管理系統(tǒng)是圖形界面,所以在這里選
“Windows窗口程序”下一步它也是自動生成要執(zhí)行的主類,你只要選擇就可以。下面的選框可以選擇你啟動程序顯示的圖片。下一步后這個窗可按個人喜好
選擇。下一步,如果你的程序還有什么依賴的外部jar文件,可以從這里加上,但因為之前的fatjar以經(jīng)將我們項目所用的那三個連數(shù)據(jù)庫的外部類打進
CAMP_fat.jar包里了,所以這里不用再添加。如果你之前是用Export打的jar
包,那么這里就需要再把那個三個數(shù)據(jù)庫的包加進來了(詳見1.2)。下一步是添入要生成的.exe文件名,再選一個程序圖標就可以了,下一步后生
成.exe文件,點完成。雙擊生成的.exe文件就能看到運行效果了,這種exe文件還沒有脫離JDK環(huán)境,還不能跨平臺使用,只能用于小組成員測試使
用。
2.3
下面進入最關(guān)鍵的,如何打包跨平臺的.exe文件。用到的軟件是exe4j,我用的是V4.0版的,此軟件需要破解。安裝后運行左窗窗口標有十步,其實打
包過程也非常簡單。第一步完全略過,直接點Next第二步我們選擇“JAR in EXE mode”
就是選擇我們已經(jīng)有制作好的jar文件。第3步上面是項目名稱,可隨便填寫,下面一個寫出你想要將打包后的exe文件輸出的目錄我的是“桌
面\project\”。第4步,由于我的演示程序是圖形的,所以選第一個,如果你的程序是控制臺的,則選擇第二個,Executable
name寫你將要生成的.exe文件的名字,Icon
File可以選擇生成文件的圖標。第5步,先別管上面的,先在下面單擊綠色的“+”號,在彈出的窗口中點Archive,然后找到起初已經(jīng)做好的
CAMP_fat.jar(詳見1.3)文件,"OK"后返回,在下面的Class Path里就出現(xiàn)jar文件路徑后,再在上面Main
Class欄內(nèi)點擊找到main所在的類。第6步,你系統(tǒng)的JRE版本,一般是填個1.3,下面填1.6在這里單擊advanced
options,選擇search
sequence。選這個就是因為我們要把JDK環(huán)境也打包進來,好讓程序能跨平臺使用。首先要從你系統(tǒng)的JDK下的JRE目錄copy到你.exe文件
的輸出目錄下“桌面\project\JRE”,然后回到exe4j中在彈出窗口刪除列表中的所有項。我的是三項,一個注冊表的,一個JAVA環(huán)境變量
的,一個JDK環(huán)境變量的,都不要。然后單擊綠“+”,選擇directory并選擇JRE的根目錄,我的是“桌面\project\JRE”就是
copy后的目錄,選完后exe4j彈出窗口中的Directory里會顯示“.\JRE”。點OK關(guān)閉該窗口,返回exe4j的主窗口,你就可以看到剛
加的路徑。再從主窗口左側(cè)窗口中單擊advanced options,并選擇preferred VM,在彈出的窗口中選擇client
hostspot VM,單擊next按鈕繼續(xù)。7、8步是一些個性設(shè)置默認即可。第9步編譯完后第10步你點那個“Click Here to
Start the Application”按鈕就可以看到程序運行效果了,然后再點”Seave
as”保存一個exe4j生成的一個文件,隨便存哪里都行,和我們的.exe程序無關(guān)。全部制作過程就完工了。
posted @
2010-03-13 18:06 xiaoxinchen 閱讀(3914) |
評論 (0) |
編輯 收藏
首先要弄清楚,在Linux系統(tǒng)中,內(nèi)核為每一個新創(chuàng)建的文件分配一個Inode(索引結(jié)點),每個文件都有一個惟一的inode號。文件屬性保存在索引結(jié)點里,在訪問文件時,索引結(jié)點被復(fù)制到內(nèi)存在,從而實現(xiàn)文件的快速訪問。
鏈接是一種在共享文件和訪問它的用戶的若干目錄項之間建立聯(lián)系的一種方法。Linux中包括兩種鏈接:硬鏈接(Hard Link)和軟鏈接(Soft Link),軟鏈接又稱為符號鏈接(Symbolic link)。
一、軟鏈接(符號鏈接)
軟鏈接克服了硬鏈接的不足,沒有任何文件系統(tǒng)的限制,任何用戶可以創(chuàng)建指向目錄的符號鏈接。因而現(xiàn)在更為廣泛使用,它具有更大的靈活性,甚至可以跨越不同機器、不同網(wǎng)絡(luò)對文件進行鏈接。
建立軟鏈接,只要在ln后面加上選項 –s。
二、硬鏈接
硬鏈接說白了是一個指針,指向文件索引節(jié)點,系統(tǒng)并不為它重新分配inode。可以用:ln命令來建立硬鏈接。語法
ln [options] existingfile newfile
ln[options] existingfile-list directory
用法:
第一種:為”existingfile”創(chuàng)建硬鏈接,文件名為”newfile”。第二種:在”directory”目錄中,
為”existingfile-list”中包含的所有文件創(chuàng)建一個同名的硬鏈接。常用可選[options] –f
無論”newfile”存在與否,都創(chuàng)建鏈接。-n 如果”newfile”已存在,就不創(chuàng)建鏈接。
posted @
2010-03-11 16:07 xiaoxinchen 閱讀(224) |
評論 (0) |
編輯 收藏
(1)Jre 是java runtime environment,
是java程序的運行環(huán)境。既然是運行,當然要包含jvm,也就是大家熟悉的虛擬機啦,
還有所有java類庫的class文件,都在lib目錄下打包成了jar。大家可以自己驗證。至于在windows上的虛擬機是哪個文件呢?
學(xué)過MFC的都知道什么是dll文件吧,那么大家看看jre/bin/client里面是不是有一個jvm.dll呢?那就是虛擬機。
(2)Jdk 是java development kit,是java的開發(fā)工具包,里面包含了各種類庫和工具。當然也包括了另外一個Jre.
那么為什么要包括另外一個Jre呢?而且jdk/jre/bin同時有client和server兩個文件夾下都包含一個jvm.dll。
說明是有兩個虛擬機的。這一點不知道大家是否注意到了呢?
相信大家都知道jdk的bin下有各種java程序需要用到的命令,與jre的bin目錄最明顯的區(qū)別就是jdk下才有javac,這一點很好理解,因為
jre只是一個運行環(huán)境而已。與開發(fā)無關(guān),正因為如此,具備開發(fā)功能的jdk自己的jre下才會同時有client性質(zhì)的jvm和server性質(zhì)的jvm, 而僅僅作為運行環(huán)境的jre下只需要client性質(zhì)的jvm.dll就夠了。
(3)記得在環(huán)境變量path中設(shè)置jdk/bin路徑麼?這應(yīng)該是大家學(xué)習(xí)Java的第一步吧,
老師會告訴大家不設(shè)置的話javac和java是用不了的。確實jdk/bin目錄下包含了所有的命令。可是有沒有人想過我們用的java命令并不是
jdk/bin目錄下的而是jre/bin目錄下的呢?不信可以做一個實驗,大家可以把jdk/bin目錄下的java.exe剪切到別的地方再運行
java程序,發(fā)現(xiàn)了什么?一切OK!
那么有人會問了?我明明沒有設(shè)置jre/bin目錄到環(huán)境變量中啊?
試想一下如果java為了提供給大多數(shù)人使用,他們是不需要jdk做開發(fā)的,只需要jre能讓java程序跑起來就可以了,那么每個客戶還需要手
動去設(shè)置環(huán)境變量多麻煩啊?所以安裝jre的時候安裝程序自動幫你把jre的java.exe添加到了系統(tǒng)變量中,驗證的方法很簡單,大家看到了系統(tǒng)環(huán)境
變量的
path最前面有“%SystemRoot%\system32;%SystemRoot%;”這樣的配置,那么再去Windows/system32下
面去看看吧,發(fā)現(xiàn)了什么?有一個java.exe。
如果強行能夠把jdk/bin挪到system32變量前面,當然也可以迫使使用jdk/jre里面的java,不過除非有必要,我不建議大家這么做。使用單獨的jre跑java程序也算是客戶環(huán)境下的一種測試。
posted @
2010-03-11 12:27 xiaoxinchen 閱讀(278) |
評論 (0) |
編輯 收藏
關(guān)于2009年12月全國大學(xué)英語四、六級考試成績發(fā)布時間的通知:
2009年12月全國大學(xué)英語四、六級考試成績將于
2010年3月3日上午9點發(fā)布。
成績查詢方式
網(wǎng)上免費查分:
網(wǎng)址: cet.99sushe.com
運營商: 99宿舍網(wǎng)
客服電話: 010-58699163轉(zhuǎn)867
收費短信查分(2010年3月3日上午9點開始):
中國移動、聯(lián)通、電信手機用戶:
發(fā)送A 加 15位準考證號到 1066335577
如A123456789012345到
1066335577查詢成績(1元/條,不含通信費)
特別注意:
河北省的中國移動手機用戶:發(fā)送 8 加
15位準考證號到 10661660
如8123456789012345到 10661660
查詢成績(1元/條,不含通信費)
運營商: 空中網(wǎng)
客服電話: 010-68083018
注:2009年12月網(wǎng)考成績發(fā)布方式和日期另行通知。
全國大學(xué)英語四、六級考試委員會辦公室
2010年2月24日
posted @
2010-03-03 00:50 xiaoxinchen 閱讀(237) |
評論 (0) |
編輯 收藏
在許多
平臺中,Browser
控件皆被做為一個必需的控件給出,并提供了DOM接口,用于
訪問Browser的內(nèi)容,相對來說SWT中的Browser控件就比較薄弱,沒有提供DOM的可控制接口,那么,如何和控件所加載的
頁面進行交互呢?比如需要在集成web
應(yīng)用的
環(huán)境中實現(xiàn)模仿登陸、
自動填表等
功能。
SWT中對Browser有不同的實現(xiàn),目前實現(xiàn)的有IE和Mozilla。在Browser的構(gòu)造
函數(shù)中根據(jù)不同的平臺和不同的style
設(shè)置類決定使用哪個類的實現(xiàn)。
org.eclipse.swt.browser.Mozilla org.eclipse.swt.browser.IE 是已經(jīng)實現(xiàn)的,而其他的
org.eclipse.swt.browser.Safari org.eclipse.swt.browser.Voyager
來源:www.va1314.com/bc
則沒有實現(xiàn)。
public Browser (Composite parent, int style) {
super (checkParent (parent), checkStyle (style));
String platform = SWT.getPlatform ();
Display display = parent.getDisplay ();
if ("gtk".equals (platform)) display.setData (NO_INPUT_METHOD, null); //$NON-NLS-1$
String className = null;
if ((style & SWT.MOZILLA) != 0) {
className = "org.eclipse.swt.browser.Mozilla"; //$NON-NLS-1$
} else {
if ("win32".equals (platform) || "wpf".equals (platform)) { //$NON-NLS-1$ $NON-NLS-2$
className = "org.eclipse.swt.browser.IE"; //$NON-NLS-1$
} else if ("motif".equals (platform)) { //$NON-NLS-1$
className = "org.eclipse.swt.browser.Mozilla"; //$NON-NLS-1$
} else if ("gtk".equals (platform)) { //$NON-NLS-1$
className = "org.eclipse.swt.browser.Mozilla"; //$NON-NLS-1$
} else if ("carbon".equals (platform)) { //$NON-NLS-1$
className = "org.eclipse.swt.browser.Safari"; //$NON-NLS-1$
} else if ("photon".equals (platform)) { //$NON-NLS-1$
className = "org.eclipse.swt.browser.Voyager"; //$NON-NLS-1$
} else {
dispose ();
SWT.error (SWT.ERROR_NO_HANDLES);
}
}
try {
Class clazz = Class.forName (className);
webBrowser = (
WebBrowser)clazz.newInstance ();
} catch (ClassNotFoundException e) {
} catch (Illegal
AccessException e) {
} catch (InstantiationException e) {
}
if (webBrowser == null) {
dispose ();
SWT.error (SWT.ERROR_NO_HANDLES);
}
webBrowser.setBrowser (this);
webBrowser.create (parent, style);
}
public Browser (Composite parent, int style) {
super (checkParent (parent), checkStyle (style));
String platform = SWT.getPlatform ();
Display display = parent.getDisplay ();
if ("gtk".equals (platform)) display.setData (NO_INPUT_METHOD, null); //$NON-NLS-1$
String className = null;
if ((style & SWT.MOZILLA) != 0) {
className = "org.eclipse.swt.browser.Mozilla"; //$NON-NLS-1$
} else {
if ("win32".equals (platform) || "wpf".equals (platform)) { //$NON-NLS-1$ $NON-NLS-2$
className = "org.eclipse.swt.browser.IE"; //$NON-NLS-1$
} else if ("motif".equals (platform)) { //$NON-NLS-1$
className = "org.eclipse.swt.browser.Mozilla"; //$NON-NLS-1$
} else if ("gtk".equals (platform)) { //$NON-NLS-1$
className = "org.eclipse.swt.browser.Mozilla"; //$NON-NLS-1$
} else if ("carbon".equals (platform)) { //$NON-NLS-1$
className = "org.eclipse.swt.browser.Safari"; //$NON-NLS-1$
} else if ("photon".equals (platform)) { //$NON-NLS-1$
className = "org.eclipse.swt.browser.Voyager"; //$NON-NLS-1$
} else {
dispose ();
SWT.error (SWT.ERROR_NO_HANDLES);
}
}
try {
Class clazz = Class.forName (className);
webBrowser = (WebBrowser)clazz.newInstance ();
} catch (ClassNotFoundException e) {
} catch (IllegalAccessException e) {
} catch (InstantiationException e) {
}
if (webBrowser == null) {
dispose ();
SWT.error (SWT.ERROR_NO_HANDLES);
}
webBrowser.setBrowser (this);
webBrowser.create (parent, style);
}
其中對IE的實現(xiàn)主要是采用調(diào)用IE的Activex控件,間接加載IE,對Mozilla由于
代碼過多,本人沒有具體研究,其本身開源,有興趣能夠參看。
那么回歸主題,如何實現(xiàn)與Browser控件的交互呢? 其實仔細看Browser控件的API,能夠發(fā)覺一個execute()方法,這個方法適用于在web文檔加載完畢時能夠
運行javascript
code的。這樣的話,交互就變得簡單了,因為javascript是提供dom的支持的,既然能夠調(diào)用javascript,那么就能夠調(diào)用web頁面
中的每個節(jié)點了。控制的問題處理了,可是另外的問題來了。 如何從javascript的code里邊前往
數(shù)據(jù)呢?
比如我需要將一個<input type=text id=textid />的值前往到j(luò)ava
code中。其實采用的方法是很投機的,因為execute()方法前往的結(jié)果是true or
false,那么對它做文章是沒有用的,我們看其他的api,能夠發(fā)覺:addStatusTextListener()方法。
這個方法能夠監(jiān)聽web頁面對于statusbar文本改變的值,并反映在java
code里面,那么我們只需通過javascript把前往的值寫到window.status,那么就能夠在javacode里取到了。
具體代碼請參考下面,對于Browser的承繼重寫,通過getValue能夠取得指定id的html 控件的值,通過setValue能夠設(shè)置值。
view plaincopy to clipboardprint?
import org.eclipse.swt.browser.Browser;
import org.eclipse.swt.browser.StatusTextEvent;
import org.eclipse.swt.browser.StatusTextListener;
import org.eclipse.swt.widgets.Composite;
public class CoolBrowser extends Browser implements StatusTextListener {
private final String DATA = "Browser_Data";
public CoolBrowser(Composite parent, int style) {
super(parent, style);
addStatusTextListener(this);
}
@Override
protected void checkSubclass() {
}
/**
* Get the value of one input control in the web
* @param id
* @return
*/
public String getValue(String id) {
if (execute("var obj = document.getElementById('" + id + "');"
+ "if( obj != null ) window.status=obj.value;")) {
return (String) getData(DATA);
}
return null;
}
/**
* Set the value of the input control
* @param id
* @param value
*/
public void setValue( String id, Object value ){
if (execute("var obj = document.getElementById('" + id + "');"
+ "if( obj != null ) obj.value='" + value + "';")) {
}
}
@Override
public void changed(StatusTextEvent event) {
setData(DATA, event.text);
}
}
posted @
2009-12-29 16:28 xiaoxinchen 閱讀(5284) |
評論 (1) |
編輯 收藏
下載地址:http://www.iplaysoft.com/windows7-msdn-iso.html
posted @
2009-12-20 20:14 xiaoxinchen 閱讀(416) |
評論 (0) |
編輯 收藏
Proxy代理模式是一種結(jié)構(gòu)型設(shè)計模式,主要解決的問題是:在直接訪問對象時帶來的問題,比如說:要訪問的對象在遠程的機器上。在面向?qū)ο笙到y(tǒng)中,有些對象由于某些原因(比如對象創(chuàng)建開銷很大,或者某些操作需要安全控制,或者需要進程外的訪問),直接訪問會給使用者或者系統(tǒng)結(jié)構(gòu)帶來很多麻煩,我們可以在訪問此對象時加上一個對此對象的訪問層。如下圖:
比如說C和A不在一個服務(wù)器上,A要頻繁的調(diào)用C,我們可以在A上做一個代理類Proxy,把訪問C的工作交給Proxy,這樣對于A來說,就好像在直接訪問C的對象。在對A的開發(fā)中我們可以把注意力完全放在業(yè)務(wù)的實現(xiàn)上。
GoF《設(shè)計模式》中說道:為其他對象提供一種代理以控制這個對象的訪問。
Proxy模式的結(jié)構(gòu):
下面通過一個場景來看看Proxy的實現(xiàn),我們要使用代理類型ProxyClass的對象調(diào)用遠程機器上的一個類型LongDistanceClass的對象。
首先我們先模擬一個遠程的類型:為了保持對被代理對象使用的透明性,我們使代理類型和被代理類型同時繼承同一個接口IProxy
接口實現(xiàn):
interface IProxy
{
string Function1();
string Function2();
}
遠程對象實現(xiàn):
/// <summary>
/// 模擬的遠程對象
/// </summary>
public class LongDistanceClass:IProxy
{
#region IProxy 成員
public string Function1()
{
//do someting
return "LongDistanceClass.Function1";
}
public string Function2()
{
//do someting
return "LongDistanceClass.Function2";
}
#endregion
}
接下來就要實現(xiàn)代理類型,使用代理對象訪問模擬的遠程對象,代理類型實現(xiàn)如下:
public class ProxyClass:IProxy
{
#region IProxy 成員
public string Function1()
{
//to access LongDistanceClass.Function1
LongDistanceClass obj = new LongDistanceClass();
return obj.Function1();
}
public string Function2()
{
//to access LongDistanceClass.Function2
LongDistanceClass obj = new LongDistanceClass();
return obj.Function2();
}
#endregion
}
最后實現(xiàn)客戶端代碼:
class Class1
{
[STAThread]
static void Main(string[] args)
{
IProxy pro = new ProxyClass();
Console.WriteLine(pro.Function1());
Console.WriteLine(pro.Function2());
Console.Read();
}
}
運行結(jié)果如下:
LongDistanceClass.Function1
LongDistanceClass.Function2
Proxy模式的要點:
1、“增加一層間接層”是軟件系統(tǒng)中對許多負責(zé)問題的一種常見解決方法。在面向?qū)ο笙到y(tǒng)中,直接使用某些對象會帶來很多問題,作為間接層的proxy對象便是解決這一問題的常用手段。
在我們?nèi)粘5墓ぷ髦幸渤3S玫酱砟J剑热鐚τ谌龑咏Y(jié)構(gòu)或者N- tiers結(jié)構(gòu)中DAL數(shù)據(jù)訪問層,它把對數(shù)據(jù)庫的訪問進行封裝。BLL業(yè)務(wù)層的開發(fā)者只是調(diào)用DAL中的方法來獲得數(shù)據(jù)。
在比如前一段時間看了看AOP和Remoting方面的資料,對于跨越應(yīng)用程序域的訪問,要為客戶應(yīng)用程序提供一個TransparentProxy(透明代理),客戶程序?qū)嶋H上是通過訪問這個代理來訪問實際的類型對象。
2、具體proxy設(shè)計模式的實現(xiàn)方法、實現(xiàn)粒度都相差很大,有些可能對單個對象作細粒度的控制,有些可能對組件模塊提供抽象代理層,在架構(gòu)層次對對象作proxy。
3、proxy并不一定要求保持接口的一致性,只要能夠?qū)崿F(xiàn)間接控制,有時候損及一些透明性是可以接受的。例如上面的那個例子,代理類型ProxyClass和被代理類型LongDistanceClass可以不用繼承自同一個接口,正像GoF《設(shè)計模式》中說的:為其他對象提供一種代理以控制這個對象的訪問。代理類型從某種角度上講也可以起到控制被代理類型的訪問的作用。
posted @
2009-08-04 14:44 xiaoxinchen 閱讀(149) |
評論 (0) |
編輯 收藏
策略模式
是對算法的包裝,是把使用算法的責(zé)任和算法本身分割開來,委派給不同的對象管理。
策略模式通常把一個系列的算法包裝到一系列的策略類里面,作為一個抽象策略類的子類。
一句話來形容:準備一組算法,并將每一個算法封裝起來,使得他們可以互換
策略模式的結(jié)構(gòu)
策略模式涉及到三個角色:
- 環(huán)境角色:持有一個Strategy類(策略類)的引用
- 抽象策略角色:策略類,通常由一個接口或者抽象類實現(xiàn)
- 具體策略角色:包裝了相關(guān)的算法和行為
《三國演義》中的故事
諸葛亮的精囊妙計?三條妙計
走喬國老的后門,求孫國太放人,請孫夫人退兵
趙云?按計行事
環(huán)境角色:趙云?由他來決定選擇策略
抽象策略角色:(接口)精囊妙計?按計行事(抽象方法)
具體策略角色:三條妙計(單獨使用的)
例子:一個策略模式的加減乘除
抽象策略角色: (精囊妙計)? Operation抽象類(oper抽象方法)
具體策略角色: (三條妙計)? 計算乘積,計算除法,計算加法,計算減法
環(huán)境角色: (趙云)? 有一個策略類( Operation )的引用
策略模式的優(yōu)缺點:
優(yōu)點:
1.提供了管理相關(guān)的算法族的辦法。
2.提供了可以替換繼承關(guān)系的辦法。
3.避免使用多重條件轉(zhuǎn)移語句
缺點:
1.客戶端必須知道所有的策略類,并自行決定使用哪一個策略類。
2.造成很多的策略類。
posted @
2009-08-04 12:21 xiaoxinchen 閱讀(236) |
評論 (0) |
編輯 收藏
原則內(nèi)容:OCP原則就是"開-閉原則",一個軟件應(yīng)該對擴展開放,對修改關(guān)閉。
解釋 :在設(shè)計一個模塊的時候,應(yīng)當使得這個模塊可以在不被修改的前提下面被擴展。換言之,應(yīng)該可以在不必修改源代碼的情況下改變這個
模塊的行為。這個原則有2點要求:
×:擴展開放
×:關(guān)閉修改
滿足OCP原則系統(tǒng)的優(yōu)點:
×:通過擴展已有的軟件系統(tǒng),提供新的行為,可以使得軟件系統(tǒng)滿足新需求
×:已有的軟件模塊,特別是重要的抽象層模塊不能做變更,這使得變化中的軟件系統(tǒng)有一定的穩(wěn)定性和延續(xù)性。
如何實現(xiàn)OCP原則:
1、抽象是關(guān)鍵。
對需要設(shè)計的系統(tǒng)進行抽象,在此前提下面,讓抽象的實現(xiàn)機制千變?nèi)f化。這個抽象層必須預(yù)見到所有的可能的擴展,任何實現(xiàn)的改變都不會改變該抽象結(jié)構(gòu)。這樣使得系統(tǒng)的抽象層無需修改,從而滿足OCP原則的第二條-關(guān)閉修改。
2、對可變性的封裝原則
OCP從另一個角度來說,就是EVP(principle of Encapsulation Variation)原則。即找到系統(tǒng)的可變因素,將之封裝起來。這個原則意味著2點:
×:一種可變性不應(yīng)當散落在代碼的很多角落里,而應(yīng)當被封裝到一個對象里面。繼承應(yīng)當被看 做是封裝變化的方法,而不應(yīng)當被認為是從一般的對象生成特殊的對象方法。
×:一種可變性不應(yīng)當與另一種可變性混合在一起。所有的類圖的繼承結(jié)構(gòu)一般不會超過兩層,不然就意味著將兩種不同的可變性混合在一起。
與其他設(shè)計原則的關(guān)系
LSP原則:這個原則是說任何基類出現(xiàn)的地方,子類一定可以出現(xiàn)。
這個原則是對OCP原則的補充,基類和子類的關(guān)系就是抽象化的具體體現(xiàn),所以LSP原則是對實現(xiàn)抽象化的具體步驟的規(guī)范。一般來說,違背了LSP原則,一定違反了OCP原則,反之不一定成立。
CARP原則:這個原則講的是要盡可能的多用合成/聚合,而不是繼承關(guān)系達到復(fù)用的目的。
CARP原則和LSP原則相輔相成。二者都是對實現(xiàn)OCP原則的具體步驟的規(guī)范。前者要求設(shè)計師首先考慮合成/聚合關(guān)系;后者要求在使用繼承關(guān)系的時候,
必須確定這個關(guān)系是符合一定條件的。CARP原則是OCP原則的必要條件,違反了這個原則,就無法使系統(tǒng)實現(xiàn)OCP原則這個目標。
DIP原則:這個原則是說要依賴于抽象,不要依賴于實現(xiàn)。
DIP原則和OCP原則是目標和手段的關(guān)系。OCP是目標,DIP是手段。要想實現(xiàn)OCP原則,必須堅持DIP原則。違反了DIP原則,則不可能達到OCP原則要求。
LoD原則:這個原則講的是一個軟件實體應(yīng)該盡可能少的和其他實體發(fā)生作用。
當一個system面臨功能擴展的時候,其中會有一些模塊,他們需要修改的壓力比其他的模塊要大一些,如果這些模塊是相對孤立的,那么他們就不會將修改的
壓力傳遞給其他模塊。根據(jù)LoD原則設(shè)計的系統(tǒng),在功能需要擴展的時候,會相對容易的做到對修改的關(guān)閉。LoD原則是一條通向OCP原則的道路。
ISP原則:這個原則是說,應(yīng)當為客戶端提供盡可能小的單獨接口,而不要提供大的總接口。ISP原則和LoD原則講的都是一個軟件實體與另一個軟件實體的通訊限制。廣義的LoD原則要求盡可能限制通訊的寬度和深度,ISP原則所限制的是通信寬度。
一個重構(gòu)方法的討論
“將條件轉(zhuǎn)移語句改寫成為多態(tài)性”是一條廣為流傳的代碼重構(gòu)做法。
這一做法本身并不能保證“開-閉”原則,應(yīng)當以“開-閉”原則判斷是否需要改寫成多態(tài)。條件轉(zhuǎn)移并不是錯誤,如果需要,完全可以選擇使用條件轉(zhuǎn)移。
如果一個條件轉(zhuǎn)移語句確實封裝了某種商務(wù)邏輯的可變性,那么將此種可變性封裝起來就符合“開-閉”原則設(shè)計思想了。如果一個條件轉(zhuǎn)移語句沒有涉及重
要的商務(wù)邏輯,或者不會隨著時間的變化而變化,也不意味著任何的可擴展性,那么它就沒有涉及任何有意義的可變性。這時候?qū)⑦@個條件轉(zhuǎn)移語句改寫成多態(tài)性就
是一種沒有意義的浪費。
抽象類應(yīng)當擁有盡可能多的共同代碼
在一個繼承的等級結(jié)構(gòu)中,共同的代碼應(yīng)當盡量向等級結(jié)構(gòu)的上方移動。把重復(fù)的代碼從子類里面移動到超類里面,可以提高代碼的復(fù)用率。在代碼發(fā)生改變時,設(shè)計師之需要修改一個地方。
抽象類應(yīng)當擁有盡可能少的數(shù)據(jù)
與代碼的移動方向相反,數(shù)據(jù)的移動方向是從抽象類到具體類,向等級結(jié)構(gòu)的下方移動。一個對象的數(shù)據(jù)不論是否使用都會占用資源,所以應(yīng)當放到等級結(jié)構(gòu)的低端。
什么時候才應(yīng)當使用繼承復(fù)用
1.子類是超類的一個特殊種類,而不是超類的一個角色,Is-A才符合繼承關(guān)系。
2.永遠不會出現(xiàn)需要將子類換成另一個類的子類的情況。
3.子類具有擴展超類的責(zé)任,而不是具有置換掉(Override)和注銷掉(Nullify)超類的責(zé)任。
4.只有在分類學(xué)角度上有意義時,才可以使用繼承,不要從工具類繼承。
posted @
2009-08-03 23:15 xiaoxinchen 閱讀(975) |
評論 (0) |
編輯 收藏
什么是內(nèi)聚?什么是耦合?
內(nèi)聚是從功能角度來度量模塊內(nèi)的聯(lián)系,一個好的內(nèi)聚模塊應(yīng)當恰好做一件事。它描述
的是模塊內(nèi)的功能聯(lián)系; 耦合是軟件結(jié)構(gòu)中各模塊之間相互連接的一種度量,耦合強弱取決
于模塊間接口的復(fù)雜程度、進入或訪問一個模塊的點以及通過接口的數(shù)據(jù)。
耦合性也稱塊間聯(lián)系。指軟件系統(tǒng)結(jié)構(gòu)中各模塊間相互聯(lián)系緊密程度的一種度量。模塊之間聯(lián)系越緊密,其耦合性就越強,模塊的獨立性則越差。模塊間耦合高低取決于模塊間接口的復(fù)雜性、調(diào)用的方式及傳遞的信息。
2. 內(nèi)聚分為哪幾類?耦合分為哪幾類?
內(nèi)聚有如下的種類,它們之間的內(nèi)聚度由弱到強排列如下:
(1) 偶然內(nèi)聚。模塊中的代碼無法定義其不同功能的調(diào)用。但它使該模塊能執(zhí)行不同
的功能,這種模塊稱為巧合強度模塊。
(2) 邏輯內(nèi)聚。這種模塊把幾種相關(guān)的功能組合在一起, 每次被調(diào)用時,由傳送給模
塊參數(shù)來確定該模塊應(yīng)完成哪一種功能
(3) 時間內(nèi)聚:把需要同時執(zhí)行的動作組合在一起形成的模塊為時間內(nèi)聚模塊。
(4) 過程內(nèi)聚:構(gòu)件或者操作的組合方式是,允許在調(diào)用前面的構(gòu)件或操作之后,馬上調(diào)用后面的構(gòu)件或操作,即使兩者之間沒有數(shù)據(jù)進行傳遞。
(5) 通信內(nèi)聚:指模塊內(nèi)所有處理元素都在同一個數(shù)據(jù)結(jié)構(gòu)上操作(有時稱之為信息內(nèi)聚),或者指各處理使用相同的輸入數(shù)據(jù)或者產(chǎn)生相同的輸出數(shù)據(jù)。
(6) 順序內(nèi)聚:指一個模塊中各個處理元素都密切相關(guān)于同一功能且必須順序執(zhí)行,前一功能元素輸出就是下一功能元素的輸入。
(7) 功能內(nèi)聚:這是最強的內(nèi)聚,指模塊內(nèi)所有元素共同完成一個功能,缺一不可。
耦合可以分為以下幾種,它們之間的耦合度由高到低排列如下:
(1) 內(nèi)容耦合:如果發(fā)生下列情形,兩個模塊之間就發(fā)生了內(nèi)容耦合
一個模塊直接訪問另一個模塊的內(nèi)部數(shù)據(jù)
一個模塊不通過正常入口轉(zhuǎn)到另一模塊內(nèi)部;
兩個模塊有一部分程序代碼重疊(只可能出現(xiàn)在匯編語言中);
一個模塊有多個入口。
(2) 公共耦合:若一組模塊都訪問同一個公共數(shù)據(jù)環(huán)境,則它們之間的耦合就稱為公共耦合。公共的數(shù)據(jù)環(huán)境可以是全局數(shù)據(jù)結(jié)構(gòu)、共享的通信區(qū)、內(nèi)存的公共覆蓋區(qū)等。
(3) 外部耦合:一組模塊都訪問同一全局簡單變量而不是同一全局數(shù)據(jù)結(jié)構(gòu),而且不是通過參數(shù)表傳遞該全局變量的信息,則稱之為外部耦合。
(4) 控制耦合:如果一個模塊通過傳送開關(guān)、標志、名字等控制信息,明顯地控制選擇另一模塊的功能,就是控制耦合
(5) 標記耦合:一組模塊通過參數(shù)表傳遞記錄信息,就是標記耦合。這個記錄是某一數(shù)據(jù)結(jié)構(gòu)的子結(jié)構(gòu),而不是簡單變量。其實傳遞的是這個數(shù)據(jù)結(jié)構(gòu)的地址;也就是地址傳遞。
(6) 數(shù)據(jù)耦合:指兩個模塊之間有調(diào)用關(guān)系,傳遞的是簡單的數(shù)據(jù)值,一個模塊訪問另一個模塊時,彼此之間是通過簡單數(shù)據(jù)參數(shù) (不是控制參數(shù)、公共數(shù)據(jù)結(jié)構(gòu)或外部變量) 來交換輸入、輸出信息的,相當于高級語言的值傳遞。
(7) 非直接耦合:兩個模塊之間沒有直接關(guān)系,它們之間的聯(lián)系完全是通過主模塊的控制和調(diào)用來實現(xiàn)的。
耦合強度,依賴于以下幾個因素:
(1)一個模塊對另一個模塊的調(diào)用;
(2)一個模塊向另一個模塊傳遞的數(shù)據(jù)量;
(3)一個模塊施加到另一個模塊的控制的多少;
(4)模塊之間接口的復(fù)雜程度。
參考資料:
http://baike.baidu.com/view/156245.html
http://www.cnblogs.com/dqshll/articles/1116799.html
http://blog.zol.com.cn/858/article_857495.html
posted @
2009-08-03 23:06 xiaoxinchen 閱讀(1602) |
評論 (0) |
編輯 收藏
用 jQuery 的都知道,jQuery 的 get 和 post 方法有三個參數(shù):地址,數(shù)據(jù)
和回調(diào)函數(shù),但我們知道地址也可以跟隨數(shù)據(jù)的(形如:get_data.php?v1=1&v2=2),而且第二個參數(shù)可以省略,即第二個參數(shù)可
以直接寫回調(diào)函數(shù),那么數(shù)據(jù)寫在地址后面和寫在 data 參數(shù)里有什么區(qū)別呢?
剛剛做了幾個實驗,看看下面的代碼就清楚了:
以下內(nèi)容需要回復(fù)才能看到
jquery_data.php
echo "post: ";
print_r($_POST);
echo "get: ";
print_r($_GET);
?>
jquery_test.html
實驗1:
$(function()
{
// post 方法,兩處都有數(shù)據(jù)
$.post('jquery_data.php?v1=1', {v2: 2}, function(data)
{
$('
').append(data).appendTo('body');
});
});
返回結(jié)果:
post: Array
(
[v2] => 2
)
get: Array
(
[v1] => 1
)
實驗2:
$(function()
{
// post 方法,數(shù)據(jù)在地址后面, 第二個參數(shù)為回調(diào)函數(shù)
$.post('jquery_data.php?v1=1', function(data)
{
$('<pre/>').append(data).appendTo('body');
});
});
返回結(jié)果,數(shù)據(jù)在 get 中:
post: Array
(
)
get: Array
(
[v1] => 1
)
實驗3:
$(function()
{
// get 方法,用 data 參數(shù)傳值
$.get('jquery_data.php', {v2: 2}, function(data)
{
$('<pre/>').append(data).appendTo('body');
});
});
返回結(jié)果,數(shù)據(jù)在 get 中:
post: Array
(
)
get: Array
(
[v2] => 2
)
實驗4:
$(function()
{
// get 方法,兩處都有數(shù)據(jù)
$.get('jquery_data.php?v1=1', {v2: 2}, function(data)
{
$('<pre/>').append(data).appendTo('body');
});
});
返回結(jié)果,兩處數(shù)據(jù)被合并了,都在 get 中:
post: Array
(
)
get: Array
(
[v1] => 1
[v2] => 2
)
實驗5:
$(function()
{
// get 方法,兩處都有數(shù)據(jù),且變量名相同
$.get('jquery_data.php?v2=1', {v2: 2}, function(data)
{
$('<pre/>').append(data).appendTo('body');
});
});
返回結(jié)果,數(shù)據(jù)在 get 中,且 data 參數(shù)中的數(shù)據(jù)覆蓋了地址后面的數(shù)據(jù):
post: Array
(
)
get: Array
(
[v2] => 2
)
通過這幾個簡單的小例子不難看出,地址后面的數(shù)據(jù)永遠是以 get 形式傳遞的,無論使用的是 get 方法還是 post 方法;而 data 參數(shù)中的數(shù)據(jù)是根據(jù)方法決定傳遞方式的。
因此,為了避免混淆,建議大家盡量不要把數(shù)據(jù)寫在地址后面,而是統(tǒng)一放在 data 參數(shù)中。
當然,如果你想在用 post 方法時,同時利用 get 傳值,那么就可以把要以 get 方式傳遞的數(shù)據(jù)寫在地址后面,把要以 post 方式傳遞的數(shù)據(jù)寫在 data 參數(shù)中。
總之方法是死的,人是活的,怎么用還要看實際情況。子曾經(jīng)曰過:實踐是檢驗真理的唯一標準。沒事做做實驗,掌握知識更牢固。
posted @
2009-07-29 19:22 xiaoxinchen 閱讀(914) |
評論 (1) |
編輯 收藏
(zz from http://blog.csdn.net/heiyeshuwu)
Web
Service就是為了異構(gòu)系統(tǒng)的通信而產(chǎn)生的,它基本的思想就是使用基于XML的HTTP的遠程調(diào)用提供一種標準的機制,而省去建立一種新協(xié)議的需求。
目前進行Web
Service通信有兩種協(xié)議標準,一種是XML-RPC,另外一種是SOAP。XML-RPC比較簡單,出現(xiàn)時間比較早,SOAP比較復(fù)雜,主要是一些
需要穩(wěn)定、健壯、安全并且復(fù)雜交互的時候使用。
Web Service介紹¶
Web
Service就是為了異構(gòu)系統(tǒng)的通信而產(chǎn)生的,它基本的思想就是使用基于XML的HTTP的遠程調(diào)用提供一種標準的機制,而省去建立一種新協(xié)議的需求。
目前進行Web
Service通信有兩種協(xié)議標準,一種是XML-RPC,另外一種是SOAP。XML-RPC比較簡單,出現(xiàn)時間比較早,SOAP比較復(fù)雜,主要是一些
需要穩(wěn)定、健壯、安全并且復(fù)雜交互的時候使用。
PHP中集成了XML-RPC和SOAP兩種協(xié)議的訪問,都是集中在xmlrpc擴
展當中。另外,在PHP的PEAR中,不管是PHP 4還是PHP
5,都已經(jīng)默認集成了XML-RPC擴展,而且該擴展跟xmlrpc擴展無關(guān),能夠獨立實現(xiàn)XML-RPC的協(xié)議交互,如果沒有xmlrpc擴展,建議使
用PEAR::XML-RPC擴展。
我們這里主要是以XML-RPC來簡單描述Web Service的交互過程,部分內(nèi)容來自PHP手冊,更詳細內(nèi)容,建議參考手冊。
安裝xmlrpc擴展¶
如果你的系統(tǒng)中沒有安裝xmlrpc的php擴展,那么請正確安裝。
在
Windows平臺下,首先把PHP安裝目錄下的擴展php_xmlrpc.dll放到C:\Windows或者C:\Winnt目錄下,
(PHP4的擴展在C:\php\extensions目錄中,PHP5的擴展在C:\php\ext目錄中),同時在C:\Windows
\php.ini或者C:\Winnt\php.ini中把extension=php_xmlrpc.dll前面的分號";"去掉,然后重啟Web服務(wù)
器后查看phpinfo()有沒有XML-RPC項目就能夠確定是否已經(jīng)正確安裝xmlrpc擴展。
在Unix/Linux平臺下,如果沒有安裝xmlrpc擴展,請在重新編譯PHP,在configure的時候請加入 --with-xmlrpc 選項,然后查看phpinfo()看是否正常安裝xmlrpc。
(注意:以下操作都是建立在xmlrpc擴張正常安裝前提下,請務(wù)必正確安裝。)
XML-RPC工作原理¶
XML-RPC大致就是整個過程就是使用XML來進行通信。首先構(gòu)造一個RPC 服務(wù)器端用來出來從RPC客戶端傳遞過來的使用XML封裝的請求,并且把處理結(jié)果通過XML的形式返回給RPC客戶端,客戶端就去分析XML獲取自己需要的數(shù)據(jù)。
XML-RPC的服務(wù)器端必須有現(xiàn)成的函數(shù)提供給客戶端調(diào)用,并且客戶端提交的請求中的函數(shù)和方法必須和服務(wù)器端的一致,否則將無法獲取所需要的結(jié)果。
下面我進行簡單的代碼來描述整個過程。
XML-RPC實踐¶
服務(wù)器端使用xmlrpc_server_create函數(shù)產(chǎn)生一個服務(wù)器端,然后把需要需要暴露的RPC調(diào)用接口進行注冊,接受RPC客戶端POST過來的XML數(shù)據(jù),然后進行處理,處理結(jié)果通過XML的形式顯示給客戶端。
代碼如下: rpc_server.php
<?php
/**
* 函數(shù):提供給RPC客戶端調(diào)用的函數(shù)
* 參數(shù):
* $method 客戶端需要調(diào)用的函數(shù)
* $params 客戶端需要調(diào)用的函數(shù)的參數(shù)數(shù)組
* 返回:返回指定調(diào)用結(jié)果
*/
function rpc_server_func($method, $params) {
$parameter = $params[0];
if ($parameter == "get")
{
$return = 'This data by get method';
}
else
{
$return = 'Not specify method or params';
}
return $return;
}
//產(chǎn)生一個XML-RPC的服務(wù)器端
$xmlrpc_server = xmlrpc_server_create();
//注冊一個服務(wù)器端調(diào)用的方法rpc_server,實際指向的是rpc_server_func函數(shù)
xmlrpc_server_register_method($xmlrpc_server, "rpc_server", "rpc_server_func");
//接受客戶端POST過來的XML數(shù)據(jù)
$request = $HTTP_RAW_POST_DATA;
//執(zhí)行調(diào)用客戶端的XML請求后獲取執(zhí)行結(jié)果
$xmlrpc_response = xmlrpc_server_call_method($xmlrpc_server, $request, null);
//把函數(shù)處理后的結(jié)果XML進行輸出
header('Content-Type: text/xml');
echo $xmlrpc_response;
//銷毀XML-RPC服務(wù)器端資源
xmlrpc_server_destroy($xmlrpc_server);
?>
服務(wù)器端構(gòu)造好了,那么再構(gòu)造我們的RPC客戶端。客戶端大致通過Socket訪問XML-RPC服務(wù)器端的80端口,然后把需要調(diào)用的RPC接口封裝到XML里,通過POST請求提交給RPC服務(wù)器端,最后獲取服務(wù)器端返回結(jié)果。
代碼如下:rpc_client.php
<?php
/**
* 函數(shù):提供給客戶端進行連接XML-RPC服務(wù)器端的函數(shù)
* 參數(shù):
* $host 需要連接的主機
* $port 連接主機的端口
* $rpc_server XML-RPC服務(wù)器端文件
* $request 封裝的XML請求信息
* 返回:連接成功成功返回由服務(wù)器端返回的XML信息,失敗返回false
*/
function rpc_client_call($host, $port, $rpc_server, $request) {
//打開指定的服務(wù)器端
$fp = fsockopen($host, $port);
//構(gòu)造需要進行通信的XML-RPC服務(wù)器端的查詢POST請求信息
$query = "POST $rpc_server HTTP/1.0\nUser_Agent: XML-RPC Client\nHost: ".$host."\nContent-Type: text/xml\nContent-Length: ".strlen($request)."\n\n".$request."\n";
//把構(gòu)造好的HTTP協(xié)議發(fā)送給服務(wù)器,失敗返回false
if (!fputs($fp, $query, strlen($query)))
{
$errstr = "Write error";
return false;
}
//獲取從服務(wù)器端返回的所有信息,包括HTTP頭和XML信息
$contents = '';
while (!feof($fp))
{
$contents .= fgets($fp);
}
//關(guān)閉連接資源后返回獲取的內(nèi)容
fclose($fp);
return $contents;
}
//構(gòu)造連接RPC服務(wù)器端的信息
$host = 'localhost';
$port = 80;
$rpc_server = '/~heiyeluren/rpc_server.php';
//把需要發(fā)送的XML請求進行編碼成XML,需要調(diào)用的方法是rpc_server,參數(shù)是get
$request = xmlrpc_encode_request('rpc_server', 'get');
//調(diào)用rpc_client_call函數(shù)把所有請求發(fā)送給XML-RPC服務(wù)器端后獲取信息
$response = rpc_client_call($host, $port, $rpc_server, $request);
//分析從服務(wù)器端返回的XML,去掉HTTP頭信息,并且把XML轉(zhuǎn)為PHP能識別的字符串
$split = '<?xml version="1.0" encoding="iso-8859-1"?>';
$xml = explode($split, $response);
$xml = $split . array_pop($xml);
$response = xmlrpc_decode($xml);
//輸出從RPC服務(wù)器端獲取的信息
print_r($response);
?>
大致我們上面的例子就是提交一個叫做rpc_server的方法過去,參數(shù)是get,然后獲取服務(wù)器端的返回,服務(wù)器端返回的XML數(shù)據(jù)是:
<?xml version="1.0" encoding="iso-8859-1"?>
<methodResponse>
<params>
<param>
<value>
<string>This data by get method</string>
</value>
</param>
</params>
</methodResponse>
那么我們再通過xmlrpc_decode函數(shù)把這個XML編碼為PHP的字符串,我們就能夠隨意處理了,整個Web Service交互完成。
結(jié)束語¶
不
管是XML-RPC也好,SOAP也罷,只要能夠讓我們穩(wěn)定、安全的進行遠程過程的調(diào)用,完成我們的項目,那么就算整個Web
Service就是成功的。另外,如果可以的話,也可以嘗試使用PEAR中的XML-RPC來實現(xiàn)上面類似的操作,說不定會更簡單,更適合你使用。
http://phpxmlrpc.sourceforge.net/
posted @
2009-07-02 09:42 xiaoxinchen 閱讀(231) |
評論 (0) |
編輯 收藏
JPA常用標記說明¶
Table¶
Table用來定義entity主表的name,catalog,schema等屬性。
元數(shù)據(jù)屬性說明:
- name: 表名
- catalog: 對應(yīng)關(guān)系數(shù)據(jù)庫中的catalog
- schema:對應(yīng)關(guān)系數(shù)據(jù)庫中的schema
- UniqueConstraints:定義一個UniqueConstraint數(shù)組,指定需要建唯一約束的列
@Entity
@Table(name="CUST")
public class Customer { ... }
SecondaryTable¶
一個entity class可以映射到多表,SecondaryTable用來定義單個從表的名字,主鍵名字等屬性。
元數(shù)據(jù)屬性說明:
- name: 表名
- catalog: 對應(yīng)關(guān)系數(shù)據(jù)庫中的catalog
- schema:對應(yīng)關(guān)系數(shù)據(jù)庫中的schema
- pkJoin: 定義一個PrimaryKeyJoinColumn數(shù)組,指定從表的主鍵列
- UniqueConstraints:定義一個UniqueConstraint數(shù)組,指定需要建唯一約束的列
下面的代碼說明Customer類映射到兩個表,主表名是CUSTOMER,從表名是CUST_DETAIL,從表的主鍵列和主表的主鍵列類型相同,列名為CUST_ID。
@Entity
@Table(name="CUSTOMER")
@SecondaryTable(name="CUST_DETAIL",pkJoin=@PrimaryKeyJoinColumn(name="CUST_ID"))
public class Customer { ... }
SecondaryTables¶
當一個entity class映射到一個主表和多個從表時,用SecondaryTables來定義各個從表的屬性。
元數(shù)據(jù)屬性說明:
- value: 定義一個SecondaryTable數(shù)組,指定每個從表的屬性。
@Table(name = "CUSTOMER")
@SecondaryTables( value = {
@SecondaryTable(name = "CUST_NAME", pkJoin = { @PrimaryKeyJoinColumn(name = "STMO_ID", referencedColumnName = "id") }),
@SecondaryTable(name = "CUST_ADDRESS", pkJoin = { @PrimaryKeyJoinColumn(name = "STMO_ID", referencedColumnName = "id") }) })
public class Customer {}
UniqueConstraint¶
UniqueConstraint定義在Table或SecondaryTable元數(shù)據(jù)里,用來指定建表時需要建唯一約束的列。
元數(shù)據(jù)屬性說明:
- columnNames:定義一個字符串數(shù)組,指定要建唯一約束的列名。
@Entity
@Table(name="EMPLOYEE", uniqueConstraints={@UniqueConstraint(columnNames={"EMP_ID", "EMP_NAME"})})
public class Employee { ... }
Column¶
Column元數(shù)據(jù)定義了映射到數(shù)據(jù)庫的列的所有屬性:列名,是否唯一,是否允許為空,是否允許更新等。
元數(shù)據(jù)屬性說明:
- name:列名。
- unique: 是否唯一
- nullable: 是否允許為空
- insertable: 是否允許插入
- updatable: 是否允許更新
- columnDefinition: 定義建表時創(chuàng)建此列的DDL
- secondaryTable: 從表名。如果此列不建在主表上(默認建在主表),該屬性定義該列所在從表的名字。
public class Person {
@Column(name = "PERSONNAME", unique = true, nullable = false, updatable = true)
private String name;
@Column(name = "PHOTO", columnDefinition = "BLOB NOT NULL", secondaryTable="PER_PHOTO")
private byte[] picture;
...
JoinColumn¶
如果在entity class的field上定義了關(guān)系(one2one或one2many等),我們通過JoinColumn來定義關(guān)系的屬性。JoinColumn的大部分屬性和Column類似。
元數(shù)據(jù)屬性說明:
- name:列名。
- referencedColumnName:該列指向列的列名(建表時該列作為外鍵列指向關(guān)系另一端的指定列)
- unique: 是否唯一
- nullable: 是否允許為空
- insertable: 是否允許插入
- updatable: 是否允許更新
- columnDefinition: 定義建表時創(chuàng)建此列的DDL
- secondaryTable: 從表名。如果此列不建在主表上(默認建在主表),該屬性定義該列所在從表的名字。
下面的代碼說明Custom和Order是一對一關(guān)系。在Order對應(yīng)的映射表建一個名為CUST_ID的列,該列作為外鍵指向Custom對應(yīng)表中名為ID的列。
public class Custom {
@OneToOne
@JoinColumn(name="CUST_ID", referencedColumnName="ID", unique=true, nullable=true, updatable=true)
public Order getOrder() {
return order;
}
JoinColumns¶
如果在entity class的field上定義了關(guān)系(one2one或one2many等),并且關(guān)系存在多個JoinColumn,用JoinColumns定義多個JoinColumn的屬性。
元數(shù)據(jù)屬性說明:
- value: 定義JoinColumn數(shù)組,指定每個JoinColumn的屬性。
下面的代碼說明Custom和Order是一對一關(guān)系。在Order對應(yīng)的映射表建兩列,一列名為CUST_ID,該列作為外鍵指向Custom對應(yīng)表中名為ID的列,另一列名為CUST_NAME,該列作為外鍵指向Custom對應(yīng)表中名為NAME的列。
public class Custom {
@OneToOne
@JoinColumns({
@JoinColumn(name="CUST_ID", referencedColumnName="ID"),
@JoinColumn(name="CUST_NAME", referencedColumnName="NAME")
})
public Order getOrder() {
return order;
}
聲
明當前field為映射表中的主鍵列。id值的獲取方式有五種:TABLE, SEQUENCE, IDENTITY, AUTO,
NONE。Oracle和DB2支持SEQUENCE,SQL
Server和Sybase支持IDENTITY,mysql支持AUTO。所有的數(shù)據(jù)庫都可以指定為AUTO,我們會根據(jù)不同數(shù)據(jù)庫做轉(zhuǎn)換。 NONE需要用戶自己指定Id的值。元數(shù)據(jù)屬性說明:
- generate():主鍵值的獲取類型
- generator():TableGenerator的名字(當generate=GeneratorType.TABLE才需要指定該屬性)
下面的代碼聲明Task的主鍵列id是自動增長的。(Oracle和DB2從默認的SEQUENCE取值,SQL Server和Sybase該列建成IDENTITY,mysql該列建成auto increment。)
@Entity
@Table(name = "OTASK")
public class Task {
@Id(generate = GeneratorType.AUTO)
public Integer getId() {
return id;
}
}
IdClass¶
當entity
class使用復(fù)合主鍵時,需要定義一個類作為id class。id
class必須符合以下要求:類必須聲明為public,并提供一個聲明為public的空構(gòu)造函數(shù)。必須實現(xiàn)Serializable接,覆寫
equals()和hashCode()方法。entity class的所有id field在id class都要定義,且類型一樣。
元數(shù)據(jù)屬性說明:
public class EmployeePK implements java.io.Serializable{
String empName;
Integer empAge;
public EmployeePK(){}
public boolean equals(Object obj){ ......}
public int hashCode(){......}
}
@IdClass(value=com.acme.EmployeePK.class)
@Entity(access=FIELD)
public class Employee {
@Id String empName;
@Id Integer empAge;
}
MapKey¶
在一對多,多對多關(guān)系中,我們可以用Map來保存集合對象。默認用主鍵值做key,如果使用復(fù)合主鍵,則用id class的實例做key,如果指定了name屬性,就用指定的field的值做key。
元數(shù)據(jù)屬性說明:
下面的代碼說明Person和Book之間是一對多關(guān)系。Person的books字段是Map類型,用Book的isbn字段的值作為Map的key。
@Table(name = "PERSON")
public class Person {
@OneToMany(targetEntity = Book.class, cascade = CascadeType.ALL, mappedBy = "person")
@MapKey(name = "isbn")
private Map books = new HashMap();
}
OrderBy¶
在一對多,多對多關(guān)系中,有時我們希望從數(shù)據(jù)庫加載出來的集合對象是按一定方式排序的,這可以通過OrderBy來實現(xiàn),默認是按對象的主鍵升序排列。
元數(shù)據(jù)屬性說明:
- value: 字符串類型,指定排序方式。格式為"fieldName1 [ASC|DESC],fieldName2 [ASC|DESC],......",排序類型可以不指定,默認是ASC。
下面的代碼說明Person和Book之間是一對多關(guān)系。集合books按照Book的isbn升序,name降序排列。
@Table(name = "MAPKEY_PERSON")
public class Person {
@OneToMany(targetEntity = Book.class, cascade = CascadeType.ALL, mappedBy = "person")
@OrderBy(name = "isbn ASC, name DESC")
private List books = new ArrayList();
}
PrimaryKeyJoinColumn¶
在三種情況下會用到PrimaryKeyJoinColumn。
- 繼承。
- entity class映射到一個或多個從表。從表根據(jù)主表的主鍵列(列名為referencedColumnName值的列),建立一個類型一樣的主鍵列,列名由name屬性定義。
- one2one關(guān)系,關(guān)系維護端的主鍵作為外鍵指向關(guān)系被維護端的主鍵,不再新建一個外鍵列。
元數(shù)據(jù)屬性說明:
- name:列名。
- referencedColumnName:該列引用列的列名
- columnDefinition: 定義建表時創(chuàng)建此列的DDL
下面的代碼說明Customer映射到兩個表,主表CUSTOMER,從表CUST_DETAIL,從表需要建立主鍵列CUST_ID,該列和主表的主鍵列id除了列名不同,其他定義一樣。
@Entity
@Table(name="CUSTOMER")
@SecondaryTable(name="CUST_DETAIL",pkJoin=@PrimaryKeyJoinColumn(name="CUST_ID",referencedColumnName="id"))
public class Customer {
@Id(generate = GeneratorType.AUTO)
public Integer getId() {
return id;
}
}
下面的代碼說明Employee和EmployeeInfo是一對一關(guān)系,Employee的主鍵列id作為外鍵指向EmployeeInfo的主鍵列INFO_ID。
@Table(name = "Employee")
public class Employee {
@OneToOne
@PrimaryKeyJoinColumn(name = "id", referencedColumnName="INFO_ID")
EmployeeInfo info;
}
PrimaryKeyJoinColumns¶
如果entity class使用了復(fù)合主鍵,指定單個PrimaryKeyJoinColumn不能滿足要求時,可以用PrimaryKeyJoinColumns來定義多個PrimaryKeyJoinColumn。
元數(shù)據(jù)屬性說明:
- value: 一個PrimaryKeyJoinColumn數(shù)組,包含所有PrimaryKeyJoinColumn。
下面的代碼說明了Employee和EmployeeInfo是一對一關(guān)系。他們都使用復(fù)合主鍵,建表時需要在Employee表建立一個外鍵,從Employee的主鍵列id,name指向EmployeeInfo的主鍵列INFO_ID和INFO_NAME.
@Entity
@IdClass(EmpPK.class)
@Table(name = "EMPLOYEE")
public class Employee {
private int id;
private String name;
private String address;
@OneToOne(cascade = CascadeType.ALL)
@PrimaryKeyJoinColumns({
@PrimaryKeyJoinColumn(name="id", referencedColumnName="INFO_ID"),
@PrimaryKeyJoinColumn(name="name" , referencedColumnName="INFO_NAME")})
EmployeeInfo info;
}
@Entity
@IdClass(EmpPK.class)
@Table(name = "EMPLOYEE_INFO")
public class EmployeeInfo {
@Id
@Column(name = "INFO_ID")
private int id;
@Id
@Column(name = "INFO_NAME")
private String name;
}
Transient¶
Transient用來注釋entity的屬性,指定的這些屬性不會被持久化,也不會為這些屬性建表。
@Transient
private String name;
Version¶
Version指定實體類在樂觀事務(wù)中的version屬性。在實體類重新由EntityManager管理并且加入到樂觀事務(wù)中時,保證完整性。每一個類只能有一個屬性被指定為version,version屬性應(yīng)該映射到實體類的主表上。
下面的代碼說明versionNum屬性作為這個類的version,映射到數(shù)據(jù)庫中主表的列名是OPTLOCK。
@Version
@Column("OPTLOCK")
protected int getVersionNum() { return versionNum; }
Lob¶
Lob指定一個屬性作為數(shù)據(jù)庫支持的大對象類型在數(shù)據(jù)庫中存儲。使用LobType這個枚舉來定義Lob是二進制類型還是字符類型。
LobType枚舉類型說明:
- BLOB 二進制大對象,Byte[]或者Serializable的類型可以指定為BLOB。
- CLOB 字符型大對象,char[]、Character[]或String類型可以指定為CLOB。
元數(shù)據(jù)屬性說明:
- fetch: 定義這個字段是lazy loaded還是eagerly fetched。數(shù)據(jù)類型是FetchType枚舉,默認為LAZY,即lazy loaded.
- type: 定義這個字段在數(shù)據(jù)庫中的JDBC數(shù)據(jù)類型。數(shù)據(jù)類型是LobType枚舉,默認為BLOB。
下面的代碼定義了一個BLOB類型的屬性和一個CLOB類型的屬性。
@Lob
@Column(name="PHOTO" columnDefinition="BLOB NOT NULL")
protected JPEGImage picture;
@Lob(fetch=EAGER, type=CLOB)
@Column(name="REPORT")
protected String report;
JoinTable¶
JoinTable在many-to-many關(guān)系的所有者一邊定義。如果沒有定義JoinTable,使用JoinTable的默認值。
元數(shù)據(jù)屬性說明:
- table:這個join table的Table定義。
- joinColumns:定義指向所有者主表的外鍵列,數(shù)據(jù)類型是JoinColumn數(shù)組。
- inverseJoinColumns:定義指向非所有者主表的外鍵列,數(shù)據(jù)類型是JoinColumn數(shù)組。
下面的代碼定義了一個連接表CUST和PHONE的join table。join table的表名是CUST_PHONE,包含兩個外鍵,一個外鍵是CUST_ID,指向表CUST的主鍵ID,另一個外鍵是PHONE_ID,指向表PHONE的主鍵ID。
@JoinTable(
table=@Table(name=CUST_PHONE),
joinColumns=@JoinColumn(name="CUST_ID", referencedColumnName="ID"),
inverseJoinColumns=@JoinColumn(name="PHONE_ID", referencedColumnName="ID")
)
TableGenerator¶
TableGenerator定義一個主鍵值生成器,在Id這個元數(shù)據(jù)的generate=TABLE時,generator屬性中可以使用生成器的名字。生成器可以在類、方法或者屬性上定義。
生成器是為多個實體類提供連續(xù)的ID值的表,每一行為一個類提供ID值,ID值通常是整數(shù)。
元數(shù)據(jù)屬性說明:
- name:生成器的唯一名字,可以被Id元數(shù)據(jù)使用。
- table:生成器用來存儲id值的Table定義。
- pkColumnName:生成器表的主鍵名稱。
- valueColumnName:生成器表的ID值的列名稱。
- pkColumnValue:生成器表中的一行數(shù)據(jù)的主鍵值。
- initialValue:id值的初始值。
- allocationSize:id值的增量。
下面的代碼定義了兩個生成器empGen和addressGen,生成器的表是ID_GEN。
@Entity
public class Employee {
...
@TableGenerator(
name="empGen",
table=@Table(name="ID_GEN"),
pkColumnName="GEN_KEY",
valueColumnName="GEN_VALUE",
pkColumnValue="EMP_ID",
allocationSize=1
)
@Id(generate=TABLE, generator="empGen")
public int id;
...
}
@Entity
public class Address {
...
@TableGenerator(
name="addressGen",
table=@Table(name="ID_GEN"),
pkColumnValue="ADDR_ID"
)
@Id(generate=TABLE, generator="addressGen")
public int id;
...
}
SequenceGenerator¶
SequenceGenerator定義一個主鍵值生成器,在Id這個元數(shù)據(jù)的generator屬性中可以使用生成器的名字。生成器可以在類、方法或者屬性上定義。生成器是數(shù)據(jù)庫支持的sequence對象。
元數(shù)據(jù)屬性說明:
- name:生成器的唯一名字,可以被Id元數(shù)據(jù)使用。
- sequenceName:數(shù)據(jù)庫中,sequence對象的名稱。如果不指定,會使用提供商指定的默認名稱。
- initialValue:id值的初始值。
- allocationSize:id值的增量。
下面的代碼定義了一個使用提供商默認名稱的sequence生成器。
@SequenceGenerator(name="EMP_SEQ", allocationSize=25)
DiscriminatorColumn¶
DiscriminatorColumn定義在使用SINGLE_TABLE或JOINED繼承策略的表中區(qū)別不繼承層次的列。
元數(shù)據(jù)屬性說明:
- name:column的名字。默認值為TYPE。
- columnDefinition:生成DDL的sql片斷。
- length:String類型的column的長度,其他類型使用默認值10。
下面的代碼定義了一個列名為DISC,長度為20的String類型的區(qū)別列。
@Entity
@Table(name="CUST")
@Inheritance(strategy=SINGLE_TABLE, discriminatorType=STRING, discriminatorValue="CUSTOMER")
@DiscriminatorColumn(name="DISC", length=20)
public class Customer { ... }
posted @
2009-07-02 09:41 xiaoxinchen 閱讀(176) |
評論 (0) |
編輯 收藏
web.xml的配置¶
在web.xml中添加
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:/applicationContext-resources.xml
classpath:/applicationContext-dao.xml
classpath:/applicationContext-service.xml
classpath*:/applicationContext.xml
/WEB-INF/applicationContext*.xml
/WEB-INF/security.xml
/WEB-INF/dealer-security.xml
</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
事務(wù)的配置¶
<aop:config>
<aop:advisor id="managerTx" advice-ref="txAdvice" pointcut="execution(* *..service.*Manager.*(..))" order="0" />
</aop:config>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="find*" read-only="true"/>
<tx:method name="search*" read-only="true"/>
<tx:method name="get*" read-only="true"/>
<tx:method name="*" rollback-for="Throwable"/>
</tx:attributes>
</tx:advice>
ApplicationContext.xml的配置(spring bean 的配置)¶
<!--ProductManager-START-->
<bean id="productManager" class="com.eryiju.service.product.impl.ProductManagerImpl">
<constructor-arg ref="productDao" />
<property name="brandDao" ref="brandDao"></property>
</bean>
<!--ProductManager-END-->
<bean id="productDao" class="com.eryiju.dao.product.impl.ProductDaoHibernate">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<bean id="brandDao" class="com.eryiju.dao.product.impl.BrandDaoHibernate">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
常見問題¶
如何與hibernate整合¶
<!-- Hibernate SessionFactory -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:hibernate.cfg.xml" />
<property name="hibernateProperties">
<value>
hibernate.dialect=${hibernate.dialect}
hibernate.query.substitutions=true 'Y', false 'N'
hibernate.cache.use_second_level_cache=true
hibernate.cache.use_query_cache=true
hibernate.cache.provider_class=com.eryiju.util.cache.EhCacheProvider
hibernate.show_sql=false
hibernate.connection.release_mode=after_statement
hibernate.cglib.use_reflection_optimizer=false
hibernate.search.default.directory_provider=org.hibernate.search.store.FSDirectoryProvider
hibernate.search.default.indexBase=/opt/dev/static/index
hibernate.jdbc.batch_size=50
hibernate.jdbc.fetch_size=50
</value>
<!-- Turn batching off for better error messages under PostgreSQL -->
</property>
</bean>
h2. (1)使用spring過濾器解決中文問題
在web.xml中添加:
<filter>
<filter-name>Spring character encoding filter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>GBK</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>Spring character encoding filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
h2. (2)將applicationContext.xml分解成多個文件
applicationContext-common.xml
"dataSource"
"sessionFactory"
事務(wù)相關(guān)
applicationContext-dao.xml
UserDAO
applicationContext-biz.xml
UserManager
applicationContext-action.xml
Action
struts-config.xml中要作修改:
<plug-in className="org.springframework.web.struts.ContextLoaderPlugIn">
<set-property property="contextConfigLocation" value="/WEB-INF/classes/applicationContext-*.xml" />
</plug-in>
h2. Spring2.0中的注解實現(xiàn)事務(wù)管理
第一步:引入<tx:>命名空間 ,在spring的配置文件中修改, beans根元素里多了三行,如下
Xml代碼
<?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:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
第二步:在spring的配置文件中修改,將所有具有@Transactional 注解的bean自動配置為聲明式事務(wù)支持
Java代碼
<!--JDBC事務(wù)管理器,根據(jù)你的情況使用不同的事務(wù)管理器,如果工程中有Hibernate,就用Hibernate的事務(wù)管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource">
<ref local="dataSource"/>
</property>
</bean>
<!-- 用注解來實現(xiàn)事務(wù)管理 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
第三步: 在接口或類的聲明處 ,寫一個@Transactional. 要是只的接口上寫, 接口的實現(xiàn)類就會繼承下來.
接口的實現(xiàn)類的具體方法,還可以覆蓋類聲明處的設(shè)置.
Java代碼
@Transactional
public class TestPOAOImpl extends POAOBase implements TestPOAO
{
@Transactional(isolation = Isolation.READ_COMMITTED)
public void test1()
{
String sql = "INSERT INTO sy_test (NAME,AGE) VALUES('注解趙云',30)";
execute(sql);
sql = "INSERT INTO sy_test (NAME,AGE) VALUES('注解張飛',26)";
execute(sql);
int a = 9 / 0; //異常
sql = "INSERT INTO sy_test (NAME,AGE) VALUES('注解關(guān)羽',33)";
execute(sql);
System.out.println("走完了");
}
//execute() 方法略...
}
注意的幾點:
1 @Transactional 只能被應(yīng)用到public方法上, 對于其它非public的方法,如果標記了@Transactional也不會報錯,但方法沒有事務(wù)功能.
2 默認情況下,一個有事務(wù)方法, 遇到RuntiomeException 時會回滾 . 遇到 受檢查的異常 是不會回滾 的. 要想所有異常都回滾,要加上 @Transactional( rollbackFor={Exception.class,其它異常}) .
posted @
2009-07-02 09:40 xiaoxinchen 閱讀(185) |
評論 (0) |
編輯 收藏
摘要: 概述
Maven的目標
對Maven的錯誤理解
Maven的版本
Maven的安裝
Windows 2000/xp下的安裝
基于Unxi-based的操作系統(tǒng)(Linux,Solaris and Mac OS X)
查看依賴庫的位置
http://mvnrepository.com/
Maven主要功能
...
閱讀全文
posted @
2009-07-02 09:39 xiaoxinchen 閱讀(533) |
評論 (0) |
編輯 收藏
sudo apt-get install mysql
命令行操作¶
mysql -u用戶名 -p密碼 -h數(shù)據(jù)庫地址(ip) 數(shù)據(jù)庫名稱
注意:盡量不要在-p后直接跟密碼,否則其他人很容易通過查閱命令行歷史記錄(比如,history命令)看到你的密碼。
SQL參考¶
MySQL參考¶
常見數(shù)據(jù)類型¶
integer(11) 11位字節(jié)的整數(shù)
tinyint(1)
bigint(20)
decimal(10,2) 小數(shù)
varchar(20) 最長為20位字節(jié)的可變字符串
char(20) 最長為20位字節(jié)的定長字符串(定長指的是存儲空間定長)
text 文本,用于存大量不固定長度的文本信息
blob 二級制信息
常見函數(shù)¶
length(str) 字符串的長度
trim(str) 去掉字符串前后的空格
substring(str,1) 獲取子串
convert(str using gbk) 將字符串轉(zhuǎn)化為gbk編碼
reverse(str) 倒序
增刪改查¶
insert into product (sku,name) values ('123456','productname')
向表中添加sku=123456,name='productname' 的數(shù)據(jù)
update product set name='updated product name' where sku='123456'
將表product中的sku為'123456'的數(shù)據(jù)的name字段的值設(shè)置為'updated product name'
select sku,name from product where sku='123456'
從表product 中查詢 sku為'123456'的數(shù)據(jù)
delete from product where sku='123456'
其他操作實例¶
多表查詢¶
select p.sku,b.name from product p,brand b where p.brand_id=b.id and p.sku='123456'
子查詢¶
select p.sku,p.name from product p where p.brand_id in (select id from brand where id=123)
左連接¶
select p.sku,p.name,b.name from product p left join brand b on p.brand_id=b.id
從一個表導(dǎo)入數(shù)據(jù)到另一個表¶
insert into product1 (sku,name,brand_id) (select sku,name,brand_id from product2)
查找不同的數(shù)據(jù)¶
select distinct p.sku from product p
查詢時按照某個字段排序(asc升序,desc降序)¶
select * from product order by name desc
常見問題¶
如何創(chuàng)建表¶
CREATE TABLE product (
`sku` char(6) NOT NULL COMMENT '商品的唯一標識\n',
`brand_id` int(11) default NULL,
`name` varchar(50) default NULL,
PRIMARY KEY (`sku`),
CONSTRAINT `brand_fk_constraint` FOREIGN KEY (`brand_id`) REFERENCES `brand` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
如何創(chuàng)建外鍵¶
alter table product add CONSTRAINT `brand_fk_constraint` FOREIGN KEY (`brand_id`) REFERENCES `brand` (`id`)
如何修改表中的字段¶
alter table product modify name varchar(200)
如何向表中添加字段¶
alter table product add comment varchar(200)
如何刪除表中字段¶
alter table product drop name
存儲過程和觸發(fā)器¶
h3.mysql創(chuàng)建表
drop table if exists news;
/*==========================*/
/* Table: 消息表 */
/*==========================*/
create table news
(
NewsId bigint(20) not null unsigned primary key auto_increment,
NewsContext varchar(50) not null,
NewsType varchar(50) not null,
UsersId int(11) not null
);
alter table news add constraint FK_Id foreign key (NewsId)
references users (UsersId);
官方參考:http://dev.mysql.com/doc/
posted @
2009-07-02 09:38 xiaoxinchen 閱讀(118) |
評論 (0) |
編輯 收藏
基本語法¶
#set ($var=XXX)
左邊可以是以下的內(nèi)容:
- Variable reference
- String literal
- Property reference
- Method reference
- Number literal #set ($i=1)
- ArrayList #set ($arr=["yt1","t2"])
- 算術(shù)運算符
- References 引用的類型
單行:
## this is a comment
多行:
#* this line
and this line
and this line, are comments...*#
變量 Variables¶
以 "$" 開頭,第一個字符必須為字母。character followed by a VTL Identifier. (a .. z or A .. Z).
變量可以包含的字符有以下內(nèi)容:
- alphabetic (a .. z, A .. Z)
- numeric (0 .. 9)
- hyphen ("-")
- underscore ("_")
Properties¶
$Identifier.Identifier
$user.name
hashtable user中的的name值.類似:user.get("name")
h2、Methods
object user.getName() = $user.getName()
h2、Formal Reference Notation
用{}把變量名跟字符串分開。如
#set ($user="csy"}
${user}name
返回csyname
$與$!的區(qū)別¶
當找不到username的時候,$username返回字符串"$username",而$!username返回空字符串""
雙引號 與 引號¶
#set ($var="helo")
則 test"$var" 返回testhello,test'$var' 返回test'$var'。
可以通過設(shè)置 stringliterals.interpolate=false改變默認處理方式
條件語句¶
#if( $foo )
<strong>Velocity!</strong>
#end
#if($foo)
#elseif()
#else
#end
當$foo為null或為Boolean對象的false值執(zhí)行.
邏輯運算符:¶
== && || !
循環(huán)語句¶
#foreach($var in $arrays ) // 集合包含下面三種Vector, a Hashtable or an Array
#end
#foreach( $product in $allProducts )
<li>$product</li>
#end
#foreach( $key in $allProducts.keySet() )
<li>Key: $key -> Value: $allProducts.get($key)</li>
#end
#foreach( $customer in $customerList )
<tr><td>$velocityCount</td><td>$customer.Name</td></tr>
#end
velocityCount變量在配置文件中定義¶
- Default name of the loop counter
- variable reference.
directive.foreach.counter.name = velocityCount
- Default starting value of the loop
- counter variable reference.
directive.foreach.counter.initial.value = 1
包含文件¶
#include( "one.gif","two.txt","three.htm" )
Parse導(dǎo)入腳本¶
#parse("me.vm" )
#stop 停止執(zhí)行并返回¶
定義宏¶
Velocimacros, 相當于函數(shù)支持包含功能:
#macro( d )
<tr><td></td></tr>
#end
調(diào)用
#d()
帶參數(shù)的宏¶
#macro( tablerows $color $somelist )
#foreach( $something in $somelist )
<tr><td bgcolor=$color>$something</td></tr>
#end
#end
Range Operator¶
#foreach( $foo in [1..5] )
與struts2的整合¶
模板裝載位置(按順序依次搜索)¶
- Web application 應(yīng)用程序路徑,會覆蓋掉類路徑下的同名配置文件;
- Class path 類路徑,一般為缺省模板的配置,公共模板位置;
數(shù)據(jù)來源(按順序依次搜索)¶
- The value stack
- The action context
- Built-in variables
Struts2-Velocity集成的一些隱含變量¶
- stack - ValueStack自身。調(diào)用方式:${stack.findString('ognl expr')}
- action - 最新操作的action
- reponse
- request
- session
- applicaion - 獲得servlet的環(huán)境
- base - 請求環(huán)境的路徑
Velocity Result 輸出模板¶
模擬jsp執(zhí)行環(huán)境,使用velocity的模板直接顯示到servelet的輸出流。
location - 模板位置,沒有其它參數(shù)時的缺省配置。
parse - 默認true ,false將不解析Ognl expressions.
配置范例:
<result name="success" type="velocity">
<param name="location">foo.vm</param>
</result>
等價于以下的配置方式:
<result name="success" type="velocity">
foo.vm
</result>
Velocity語法¶
http://blog.csdn.net/alexwan/archive/2007/10/29/1853285.aspx
Struts 與 Velocity 的集成¶
http://www.ibm.com/developerworks/cn/java/j-sr1.html
posted @
2009-07-02 09:38 xiaoxinchen 閱讀(161) |
評論 (0) |
編輯 收藏
CDN
的全稱是Content Delivery Network,即內(nèi)容分發(fā)網(wǎng)絡(luò)。其目的是通過在現(xiàn)有的Internet中增加一層新的網(wǎng)絡(luò)架構(gòu),
將網(wǎng)站的內(nèi)容發(fā)布到最接近用戶的網(wǎng)絡(luò)"邊緣", 使用戶可以就近取得所需的內(nèi)容, 解決Internet網(wǎng)絡(luò)擁擠的狀況,
提高用戶訪問網(wǎng)站的響應(yīng)速度。從技術(shù)上全面解決由于網(wǎng)絡(luò)帶寬小, 用戶訪問量大, 網(wǎng)點分布不均等原因所造成的用戶訪問網(wǎng)站響應(yīng)速度慢的問題。
CDN互聯(lián)網(wǎng)內(nèi)容發(fā)布網(wǎng)絡(luò)(Content Delivery Network)¶
CDN
技術(shù)是近年來在美國首先興起并迅速發(fā)展起來的一種解決互聯(lián)網(wǎng)性能不佳問題的有效手段。其基本思路就是盡可能避開互聯(lián)網(wǎng)上有可能影響數(shù)據(jù)傳輸速度和穩(wěn)定性的
瓶頸和環(huán)節(jié),使內(nèi)容傳輸?shù)母臁⒏€(wěn)。通過在網(wǎng)絡(luò)各處放置節(jié)點服務(wù)器所構(gòu)成的在現(xiàn)有的互聯(lián)網(wǎng)基礎(chǔ)之上的一層智能虛擬網(wǎng)絡(luò),cdn系統(tǒng)能夠?qū)崟r地根據(jù)網(wǎng)絡(luò)流
量和各節(jié)點的連接、負載狀況以及到用戶的距離和響應(yīng)時間等綜合信息將用戶的請求重新導(dǎo)向離用戶最近的服務(wù)節(jié)點上。
實際上,內(nèi)容分發(fā)
布網(wǎng)絡(luò)(CDN)是一種新型的網(wǎng)絡(luò)構(gòu)建方式,它是為能在傳統(tǒng)的IP網(wǎng)發(fā)布寬帶豐富媒體而特別優(yōu)化的網(wǎng)絡(luò)覆蓋層;而從廣義的角度,CDN代表了一種基于質(zhì)量
與秩序的網(wǎng)絡(luò)服務(wù)模式。簡單地說,內(nèi)容發(fā)布網(wǎng)(CDN)是一個經(jīng)策略性部署的整體系統(tǒng),包括分布式存儲、負載均衡、網(wǎng)絡(luò)請求的重定向和內(nèi)容管理4個要件,
而內(nèi)容管理和全局的網(wǎng)絡(luò)流量管理(Traffic
Management)是CDN的核心所在。通過用戶就近性和服務(wù)器負載的判斷,CDN確保內(nèi)容以一種極為高效的方式為用戶的請求提供服務(wù)。總的來說,內(nèi)
容服務(wù)基于緩存服務(wù)器,也稱作代理緩存(Surrogate),它位于網(wǎng)絡(luò)的邊緣,距用戶僅有"一跳"(Single
Hop)之遙。同時,代理緩存是內(nèi)容提供商源服務(wù)器(通常位于CDN服務(wù)提供商的數(shù)據(jù)中心)的一個透明鏡像。這樣的架構(gòu)使得CDN服務(wù)提供商能夠代表他們
客戶,即內(nèi)容供應(yīng)商,向最終用戶提供盡可能好的體驗,而這些用戶是不能容忍請求響應(yīng)時間有任何延遲的。據(jù)統(tǒng)計,采用CDN技術(shù),能處理整個網(wǎng)站頁面的
70%~95%的內(nèi)容訪問量,減輕服務(wù)器的壓力,提升了網(wǎng)站的性能和可擴展性。
與目前現(xiàn)有的內(nèi)容發(fā)布模式相比較,CDN強調(diào)了網(wǎng)絡(luò)
在內(nèi)容發(fā)布中的重要性。通過引入主動的內(nèi)容管理層的和全局負載均衡,CDN從根本上區(qū)別于傳統(tǒng)的內(nèi)容發(fā)布模式。在傳統(tǒng)的內(nèi)容發(fā)布模式中,內(nèi)容的發(fā)布由
ICP的應(yīng)用服務(wù)器完成,而網(wǎng)絡(luò)只表現(xiàn)為一個透明的數(shù)據(jù)傳輸通道,這種透明性表現(xiàn)在網(wǎng)絡(luò)的質(zhì)量保證僅僅停留在數(shù)據(jù)包的層面,而不能根據(jù)內(nèi)容對象的不同區(qū)分
服務(wù)質(zhì)量。此外,由于IP網(wǎng)的"盡力而為"的特性使得其質(zhì)量保證是依靠在用戶和應(yīng)用服務(wù)器之間端到端地提供充分的、遠大于實際所需的帶寬通量來實現(xiàn)的。在
這樣的內(nèi)容發(fā)布模式下,不僅大量寶貴的骨干帶寬被占用,同時ICP的應(yīng)用服務(wù)器的負載也變得非常重,而且不可預(yù)計。當發(fā)生一些熱點事件和出現(xiàn)浪涌流量時,
會產(chǎn)生局部熱點效應(yīng),從而使應(yīng)用服務(wù)器過載退出服務(wù)。這種基于中心的應(yīng)用服務(wù)器的內(nèi)容發(fā)布模式的另外一個缺陷在于個性化服務(wù)的缺失和對寬帶服務(wù)價值鏈的扭
曲,內(nèi)容提供商承擔(dān)了他們不該干也干不好的內(nèi)容發(fā)布服務(wù)。
縱觀整個寬帶服務(wù)的價值鏈,內(nèi)容提供商和用戶位于整個價值鏈的兩端,中間
依靠網(wǎng)絡(luò)服務(wù)提供商將其串接起來。隨著互聯(lián)網(wǎng)工業(yè)的成熟和商業(yè)模式的變革,在這條價值鏈上的角色越來越多也越來越細分。比如內(nèi)容/應(yīng)用的運營商、托管服務(wù)
提供商、骨干網(wǎng)絡(luò)服務(wù)提供商、接入服務(wù)提供商等等。在這一條價值鏈上的每一個角色都要分工合作、各司其職才能為客戶提供良好的服務(wù),從而帶來多贏的局面。
從內(nèi)容與網(wǎng)絡(luò)的結(jié)合模式上看,內(nèi)容的發(fā)布已經(jīng)走過了ICP的內(nèi)容(應(yīng)用)服務(wù)器和IDC這兩個階段。IDC的熱潮也催生了托管服務(wù)提供商這一角色。但
是,IDC并不能解決內(nèi)容的有效發(fā)布問題。內(nèi)容位于網(wǎng)絡(luò)的中心并不能解決骨干帶寬的占用和建立IP網(wǎng)絡(luò)上的流量秩序。因此將內(nèi)容推到網(wǎng)絡(luò)的邊緣,為用戶提
供就近性的邊緣服務(wù),從而保證服務(wù)的質(zhì)量和整個網(wǎng)絡(luò)上的訪問秩序就成了一種顯而易見的選擇。而這就是內(nèi)容發(fā)布網(wǎng)(CDN)服務(wù)模式。CDN的建立解決了困
擾內(nèi)容運營商的內(nèi)容"集中與分散"的兩難選擇,無疑對于構(gòu)建良好的互聯(lián)網(wǎng)價值鏈是有價值的,也是不可或缺的最優(yōu)網(wǎng)站加速服務(wù)。
目前,國內(nèi)訪問量較高的大型網(wǎng)站如新浪、網(wǎng)易等,均使用CDN網(wǎng)絡(luò)加速技術(shù),雖然網(wǎng)站的訪問巨大,但無論在什么地方訪問都會感覺速度很快。而一般的網(wǎng)站如果服務(wù)器在網(wǎng)通,電信用戶訪問很慢,如果服務(wù)器在電信,網(wǎng)通用戶訪問又很慢。
它
采取了分布式網(wǎng)絡(luò)緩存結(jié)構(gòu)(即國際上流行的web
cache技術(shù)),通過在現(xiàn)有的Internet中增加一層新的網(wǎng)絡(luò)架構(gòu),將網(wǎng)站的內(nèi)容發(fā)布到最接近用戶的cache服務(wù)器內(nèi),通過DNS負載均衡的技
術(shù),判斷用戶來源就近訪問cache服務(wù)器取得所需的內(nèi)容,解決Internet網(wǎng)絡(luò)擁塞狀況,提高用戶訪問網(wǎng)站的響應(yīng)速度,如同提供了多個分布在各地的
加速器,以達到快速、可冗余的為多個網(wǎng)站加速的目的。
CDN的特點¶
- 本地Cache加速 提高了企業(yè)站點(尤其含有大量圖片和靜態(tài)頁面站點)的訪問速度,并大大提高以上性質(zhì)站點的穩(wěn)定性
- 鏡像服務(wù) 消除了不同運營商之間互聯(lián)的瓶頸造成的影響,實現(xiàn)了跨運營商的網(wǎng)絡(luò)加速,保證不同網(wǎng)絡(luò)中的用戶都能得到良好的訪問質(zhì)量。
- 遠程加速 遠程訪問用戶根據(jù)DNS負載均衡技術(shù) 智能自動選擇Cache服務(wù)器,選擇最快的Cache服務(wù)器,加快遠程訪問的速度
- 帶寬優(yōu)化 自動生成服務(wù)器的遠程Mirror(鏡像)cache服務(wù)器,遠程用戶訪問時從cache服務(wù)器上讀取數(shù)據(jù),減少遠程訪問的帶寬、分擔(dān)網(wǎng)絡(luò)流量、減輕原站點WEB服務(wù)器負載等功能。
- 集群抗攻擊 廣泛分布的CDN節(jié)點加上節(jié)點之間的智能冗于機制,可以有效地預(yù)防黑客入侵以及降低各種D.D.o.S攻擊對網(wǎng)站的影響,同時保證較好的服務(wù)質(zhì)量 。
關(guān)鍵技術(shù)¶
- 內(nèi)容發(fā)布:它借助于建立索引、緩存、流分裂、組播(Multicast)等技術(shù),將內(nèi)容發(fā)布或投遞到距離用戶最近的遠程服務(wù)點(POP)處;
- 內(nèi)容路由:它是整體性的網(wǎng)絡(luò)負載均衡技術(shù),通過內(nèi)容路由器中的重定向(DNS)機制,在多個遠程POP上均衡用戶的請求,以使用戶請求得到最近內(nèi)容源的響應(yīng);
- 內(nèi)容交換:它根據(jù)內(nèi)容的可用性、服務(wù)器的可用性以及用戶的背景,在POP的緩存服務(wù)器上,利用應(yīng)用層交換、流分裂、重定向(ICP、WCCP)等技術(shù),智能地平衡負載流量;
- 性能管理:它通過內(nèi)部和外部監(jiān)控系統(tǒng),獲取網(wǎng)絡(luò)部件的狀況信息,測量內(nèi)容發(fā)布的端到端性能(如包丟失、延時、平均帶寬、啟動時間、幀速率等),保證網(wǎng)絡(luò)處于最佳的運行狀態(tài)。
P4P與傳統(tǒng)CDN、P2P的對比¶
7
月30日消息:德國一個名為iPoque的研究機構(gòu)在2007年研究了一百多萬網(wǎng)民將近
3TB的匿名數(shù)據(jù)流量,調(diào)查地區(qū)包括澳大利亞、東歐、德國、中東和南歐地區(qū)。調(diào)查發(fā)現(xiàn),目前網(wǎng)絡(luò)帶寬“消費大戶”是P2P文件共享,在中東占據(jù)了49%,
東歐地區(qū)占據(jù)了84%。從全球來看,晚上時段的網(wǎng)絡(luò)帶寬有95%被P2P占據(jù)。據(jù)國內(nèi)權(quán)威部門統(tǒng)計,當前P2P流量已經(jīng)占整個互聯(lián)網(wǎng)流量的約70%,并且
正在以每年350%的速度增長。P2P流量消耗了巨大的網(wǎng)絡(luò)帶寬,尤其是國際帶寬,使網(wǎng)絡(luò)基礎(chǔ)設(shè)施不堪重負,運營商苦不堪言。
問題
的癥結(jié)不在于P2P,而在于交換的機制。P2P過于強調(diào)“對等”,每個節(jié)點之間的交換完全是無序的。一個北京的用戶,既可能和廣州的用戶進行文件片段的交
換,也可能和遠在美國的某用戶進行交換。顯然,無序的交換導(dǎo)致了無謂的跨地區(qū)甚至是跨國的 “流量旅行”,這耗費了寶貴的國內(nèi)和國際帶寬資源,代價巨大。
如
果正好用戶都在同一個地區(qū),那么,本地化的交換的成本就會大大降低。這也正是P4P的簡單原理——讓P2P也玩“同城”。
P4P全稱是“Proactive network Provider Participation for
P2P(電信運營商主動參與P2P網(wǎng)絡(luò))”。與P2P隨機挑選Peer(對等機)不同,P4P協(xié)議可以協(xié)調(diào)網(wǎng)絡(luò)拓撲數(shù)據(jù),能夠有效選擇節(jié)點,從而提高網(wǎng)絡(luò)
路由效率。仍以上述例子來說,北京的用戶就可以優(yōu)先和北京同城的用戶來實現(xiàn)文件片段的交換,再擴展至較遠的地區(qū),有十分的必要時,才會出國進行文件片段交
換。當然,P4P的運行機制,要遠遠超過“同城交換”的概念,它還會根據(jù)用戶的上行、下載帶寬進行綜合判斷,以進行最有效選擇,最大化整體交換的效率。
舉
幾個例子可以說明CDN的普遍作用。例如2008年,北京奧運會之前,關(guān)于門票網(wǎng)絡(luò)出售,很多國內(nèi)的朋友都去登陸,而大家的登錄的時刻幾乎千篇一律,導(dǎo)致
中國政府的網(wǎng)站服務(wù)器支撐不了這么大的請求,誰都進去不了,都被堵死在門外。這中現(xiàn)象在國內(nèi)許多網(wǎng)站都出現(xiàn)過,比如高考時期,學(xué)生上網(wǎng)填申請,大家都說是
自己網(wǎng)絡(luò)原因,其實不然,這都跟被請求的服務(wù)器有關(guān),來自四面八方的請求去請求他一個網(wǎng)站,他自然接受不了這么大的帶寬,給人一種印象,就是互聯(lián)網(wǎng)太慢,
落后了。這樣的例子很多,我們就不紛紛介紹了。不過互聯(lián)網(wǎng),網(wǎng)聚全球人的智慧。解決方法很多,美國作為世界的互聯(lián)網(wǎng)中心,提供了CDN技術(shù),內(nèi)容網(wǎng)絡(luò)分
發(fā)。美國很多軍方工程都是采用該技術(shù)。我國國內(nèi)的香港海洋科技集團的GTONE產(chǎn)品,專業(yè)為我國國內(nèi)大型公司提供海外服務(wù),在全球都有很廣的資源。國內(nèi)很
多很有實力的公司都和他們合作了。
新建文件
posted @
2009-07-02 09:37 xiaoxinchen 閱讀(119) |
評論 (0) |
編輯 收藏
- 基本命令
- 常見文件操作
建立目錄:mkdir 目錄名
刪除空目錄:rmdir 目錄名
無條件刪除子目錄: rm -rf 目錄名
改變當前目錄:cd 目錄名 (進入用戶home目錄:cd ~;進入上一級目錄:cd -)
查看自己所在目錄:pwd
查看當前目錄大小:du
顯示目錄文件列表:ls -l (-a:增加顯示隱含目錄)
其中:藍:目錄;綠:可執(zhí)行文件;紅:壓縮文件;淺藍:鏈接文件;灰:其他文件;紅底白字:錯誤的鏈接文件
瀏覽文件:more 文件名.txt;less 文件名.txt
復(fù)制文件: cp 源文件 目標文件 (-r:包含目錄)
查找文件:(1)find (2)locate 命令名
鏈接:(1)建立hard鏈接:ln 來源文件 鏈接文件(-d:創(chuàng)建目錄鏈接);(2)建立符號鏈接:ln -s 來源文件 鏈接文件
文本編碼轉(zhuǎn)換工具iconv:iconv -f gb2312 -t utf-8 -o new.txt old.txt
輸入/輸出格式規(guī)范
-f, --from-code=NAME 原始文本編碼,
-t,--to-code=NAME 輸出編碼,信息
-l, --list 列出所有已知編碼字符集
輸出控制:
-c 忽略輸出中的無效字符
-o, --output=FILE 輸出文件
-s, --silent suppress warnings
# 進程管理
列出當前進程ID:ps -auxw
終止進程:(1)終止單一進程:kill 進程ID號
終止該程序所有進程:killall 程序名
終止X-Window程序:xkill
查看資源占用情況:(1)top (2)free (3)dmesg
查看環(huán)境變量值:env
重啟:(1)reboot (2)Ctrl Alt Del (3)init 6
關(guān)機:(1)shutdown -h now (2)halt (3)init 0
# 網(wǎng)絡(luò)管理
顯示網(wǎng)絡(luò)接口參數(shù):ifconfig
聯(lián)機狀況:ping xxx.xxx.xxx.xxx
顯示網(wǎng)絡(luò)狀況:netstat ,其中:options:-a==所有sockets;-l==包含網(wǎng)絡(luò)設(shè)備;-n==數(shù)字IP;-o==其他信息;-r==路由表;-t==只列TCP sockets;-u==只列UDP sockets;-w==只列raw sockets;
-x==只列Unix Domain sockets
# 權(quán)限設(shè)定
(1)chmod -a|u|g|o |-|=r|w|x 文件/目錄名
其中:a--所有用戶(all);u--本用戶(user);g--用戶組(group);o--其他用戶(other users)
--增加權(quán)限;---刪除權(quán)限;=--設(shè)置權(quán)限
文件:r--只讀權(quán)限(read);w--寫權(quán)限(write);x--執(zhí)行權(quán)限(execute)
目錄:r--允許列目錄下文件和子目錄;w--允許生成和刪除目錄下文件;x--允許訪問該目錄
(2)chmod xxx 文件/目錄名
其中:execute=1;write=2;read=4
x取值:0--沒有任何權(quán)限(常用);1--只能執(zhí)行(不常見);2--只能寫(不常見);3--只能寫和執(zhí)行(不常見);4--只讀(常見);5--只讀和執(zhí)行(常見);6--讀和寫(常見);7--讀.寫和執(zhí)行
# vim 常見命令
進入后為命令模式:(1)插入i;(2)打開0;(3)修改c;(4)取代r;(5)替換s
經(jīng)(1)后進入全屏幕編輯模式。
命令模式-->編輯模式(a/i);編輯模式-->命令模式(Esc);命令模式-->末行模式(:)。
:w/w newfile保存
:q/q!退出iv;:wq保存退出
http://vimcdoc.sourceforge.net/doc/help.html
#ln命令
ln 命令
用途 : 鏈接文件。
語法
1>將某個文件鏈接到一個文件上
ln [ -f | -n] [ -s ] SourceFile [ TargetFile ]
2>將一個或多個文件鏈接到一個目錄上
ln [ -f | -n] [ -s ] SourceFile ... TargetDirectory
描述
ln 命令將在 SourceFile 參數(shù)中指定的文件鏈接到在 TargetFile 參數(shù)中指定的文件,或?qū)⑵滏溄拥皆?TargetDirectory 參數(shù)中指定的另一個目錄中的文件。
在缺省情況下,ln 命令會創(chuàng)建硬鏈接。如果需要使用 ln 命令來創(chuàng)建符號鏈接,請指明 -s 標志。
符號鏈接是指向文件的一個間接指針;它的目錄項中包含了它所鏈接的文件名。符號鏈接可能會跨越文件系統(tǒng),可能指向目錄。
如果正在將某個文件鏈接到新的名字,那么只能列出一個文件。如果鏈接到一個目錄,那么可以列出多個文件。
TargetFile 參數(shù)是可選的。
如果不指定目標文件,ln 命令會在當前的目錄中創(chuàng)建一個新的文件。新的文件繼承了指定在 SourceFile 參數(shù)中的文件名。
注意:
如果不使用 -s 標志,就不能在文件系統(tǒng)之間鏈接文件。
如果 TargetDirectory 已經(jīng)是鏈接到目錄上的一個符號鏈接,那么 ln 命令將現(xiàn)有的目標視為文件。
這意味著,類似于 ln -fs somepath/lname symdir 的命令不會遵循現(xiàn)有的 symdir 符號鏈接,作為代替,它會創(chuàng)建一個從 somepath/lname 到 symdir 的新的符號鏈接。
參數(shù)
-f 促使 ln 命令替換掉任何已經(jīng)存在的目的路徑。如果目的路徑已經(jīng)存在,而沒有指定 -f 標志,ln 命令不會創(chuàng)建新的鏈接,而是向標準錯誤寫一條診斷消息并繼續(xù)鏈接剩下的 SourceFiles。
-n 指定,如果鏈接是一個現(xiàn)有的文件,那么不要覆蓋文件的內(nèi)容。 -f 標志重設(shè)了這個標志。這是缺省的行為。
-s
促使 ln 命令創(chuàng)建符號鏈接。符號鏈接中包含了它所鏈接的文件的名字。當對鏈接執(zhí)行打開操作的時候,會使用到引用文件。對符號鏈接的 stat
調(diào)用會返回鏈接的目標文件;必須完成lstat 調(diào)用來獲取鏈接的信息。可以使用 readlink
調(diào)用來讀取符號鏈接的內(nèi)容。符號鏈接可能跨越文件系統(tǒng),指向目錄。
注意:當為 -s 標志指定 SourceFile 參數(shù)的時候,必須使用絕對路徑。如果沒有指明絕對路徑,那么當 SourceFile 和 TargetFile 參數(shù)位于不同的目錄中的時候,可能會發(fā)生意外的結(jié)果。在創(chuàng)建符號鏈接之前,不需要存在源文件。
退出狀態(tài)
此命令返回以下的退出值:
0 所有指定的文件都成功鏈接上了。
0 出現(xiàn)一次錯誤。
示例
1>為了創(chuàng)建到一個文件的另一個鏈接(別名),請輸入:
ln
-f file1 file2
這會將 file1 鏈接到新的名稱, file2。如果 file2 不存在,那么會創(chuàng)建該文件名。如果 file2
已經(jīng)存在了,那么這個文件會被替換為指向 file1的一個鏈接。然后 file1 和 file2
文件名會指向同一個文件。對其中任何一個的更改都會出現(xiàn)在另一個中。如果一個文件名被 rm
命令刪除,那么該文件并沒有完全被刪除,因為它仍然以其它的名字存在。
2>為了將文件鏈接為另一個目錄中的相同名字,請輸入:
ln index dir1
這會將 index 鏈接到新的名稱,dir1/index。
注意:在示例 1 中的 file2 是一個文件的名稱;在示例 2 中的 dir1 是一個已經(jīng)存在的目錄。
3>為了將幾個文件鏈接為另一個目錄中的名稱,請輸入:
ln file2 dir2/file3 /home/dir1
這會將 file2 鏈接到新的名稱 /home/dir1/file2;將 dir2/file3 鏈接到新的名稱 /home/dir1/file3。
4>如果想要在 ln 命令中使用模式匹配字符,請輸入:
ln dir1/* .
這會將 dir1 目錄中的所有文件鏈接到當前目錄中, . (點),給他們在 dir1 目錄中同樣的名稱。
注意: 必須在星號和句點之間輸入一個空格。
5>為了創(chuàng)建一個符號鏈接,輸入:
ln -s /tmp/test test
這會在當前的目錄中創(chuàng)建符號鏈接 test。 test 文件指向 /tmp/test 文件。如果 /tmp/test 文件已經(jīng)存在了,那么 cat test 命令可以列出其內(nèi)容。
6>如果想要在不指明 TargetFile 參數(shù)的情況下得到相同的結(jié)果,請輸入:
ln -s /tmp/test
文件
/usr/bin/ln 包含了 ln 命令。
- 常見配置文件
posted @
2009-07-02 09:37 xiaoxinchen 閱讀(126) |
評論 (0) |
編輯 收藏
不用安裝,直接解壓即可。如果進行JEE開發(fā),可以用eclipse-jee-ganymede-SR2文件,而PHP的話可以直接用PDT。Python可以用基于Apanta的PyDev。
基本操作¶
選項:Windows -> Preference。
安裝新特性/插件:Help -> SoftwareUpdates
常用快捷鍵¶
快捷鍵的設(shè)置和修改在Windows -> Preference ->General -> Keys;
我最常用的:
Ctrl+Space 代碼助手完成一些代碼的插入(但一般和輸入法有沖突,可以Alt+/來代替,在keys里找到command 為Content
Assist的,把其Binding改為Alt+/)
這個是我最喜歡的功能,你可以把變量命名的很長,下次引用時只要打首個字母,再打Alt+/,就能寫出變量。
Ctrl+Q 定位到最后編輯的地方
Ctrl+Shift+R 全局 打開資源
Ctrl+E 快速顯示當前Editer的下拉列表(如果當前頁面沒有顯示的用黑體表示)
Java編輯器 組織導(dǎo)入 Ctrl+Shift+O
Java編輯器 添加導(dǎo)入 Ctrl+Shift+M
Alt+← 前一個編輯的頁面
Alt+→ 下一個編輯的頁面(當然是針對上面那條來說了)
Ctrl+1 快速修復(fù)
Ctrl+/ 注釋當前行,再按則取消注釋
Ctrl+D: 刪除當前行
Java編輯器 格式化 Ctrl+Shift+F
Alt+↓ 當前行和下面一行交互位置(特別實用,可以省去先剪切,再粘貼了)
Alt+↑ 當前行和上面一行交互位置(同上)
Ctrl+L 定位在某行 (對于程序超過100的人就有福音了)
Alt+Shift+R 重命名 (是我自己最愛用的一個了,尤其是變量和類的Rename,比手工方法能節(jié)省很多勞動力)
Alt+Shift+Z 使用try/catch塊來包圍
下面是網(wǎng)上轉(zhuǎn)過來
Eclipse快捷鍵大全(轉(zhuǎn)載)
Ctrl+1 快速修復(fù)(最經(jīng)典的快捷鍵,就不用多說了)
Ctrl+D: 刪除當前行
Ctrl+Alt+↓ 復(fù)制當前行到下一行(復(fù)制增加)
Ctrl+Alt+↑ 復(fù)制當前行到上一行(復(fù)制增加)
Alt+↓ 當前行和下面一行交互位置(特別實用,可以省去先剪切,再粘貼了)
Alt+↑ 當前行和上面一行交互位置(同上)
Alt+← 前一個編輯的頁面
Alt+→ 下一個編輯的頁面(當然是針對上面那條來說了)
Alt+Enter 顯示當前選擇資源(工程,or 文件 or文件)的屬性
Shift+Enter 在當前行的下一行插入空行(這時鼠標可以在當前行的任一位置,不一定是最后)
Shift+Ctrl+Enter 在當前行插入空行(原理同上條)
Ctrl+Q 定位到最后編輯的地方
Ctrl+L 定位在某行 (對于程序超過100的人就有福音了)
Ctrl+M 最大化當前的Edit或View (再按則反之)
Ctrl+/ 注釋當前行,再按則取消注釋
Ctrl+O 快速顯示 OutLine
Ctrl+T 快速顯示當前類的繼承結(jié)構(gòu)
Ctrl+W 關(guān)閉當前Editer
Ctrl+K 參照選中的Word快速定位到下一個
Ctrl+E 快速顯示當前Editer的下拉列表(如果當前頁面沒有顯示的用黑體表示)
Ctrl+/(小鍵盤) 折疊當前類中的所有代碼
Ctrl+×(小鍵盤) 展開當前類中的所有代碼
Ctrl+Space 代碼助手完成一些代碼的插入(但一般和輸入法有沖突,可以修改輸入法的熱鍵,也可以暫用Alt+/來代替)
Ctrl+Shift+E 顯示管理當前打開的所有的View的管理器(可以選擇關(guān)閉,激活等操作)
Ctrl+J 正向增量查找(按下Ctrl+J后,你所輸入的每個字母編輯器都提供快速匹配定位到某個單詞,如果沒有,則在stutes line中顯示沒有找到了,查一個單詞時,特別實用,這個功能Idea兩年前就有了)
Ctrl+Shift+J 反向增量查找(和上條相同,只不過是從后往前查)
Ctrl+Shift+F4 關(guān)閉所有打開的Editer
Ctrl+Shift+X 把當前選中的文本全部變味小寫
Ctrl+Shift+Y 把當前選中的文本全部變?yōu)樾?br />
Ctrl+Shift+F 格式化當前代碼
Ctrl+Shift+P 定位到對于的匹配符(譬如{}) (從前面定位后面時,光標要在匹配符里面,后面到前面,則反之)
下面的快捷鍵是重構(gòu)里面常用的,本人就自己喜歡且常用的整理一下(注:一般重構(gòu)的快捷鍵都是Alt+Shift開頭的了)
Alt+Shift+R 重命名 (是我自己最愛用的一個了,尤其是變量和類的Rename,比手工方法能節(jié)省很多勞動力)
Alt+Shift+M 抽取方法 (這是重構(gòu)里面最常用的方法之一了,尤其是對一大堆泥團代碼有用)
Alt+Shift+C 修改函數(shù)結(jié)構(gòu)(比較實用,有N個函數(shù)調(diào)用了這個方法,修改一次搞定)
Alt+Shift+L 抽取本地變量( 可以直接把一些魔法數(shù)字和字符串抽取成一個變量,尤其是多處調(diào)用的時候)
Alt+Shift+F 把Class中的local變量變?yōu)閒ield變量 (比較實用的功能)
Alt+Shift+I 合并變量(可能這樣說有點不妥Inline)
Alt+Shift+V 移動函數(shù)和變量(不怎么常用)
Alt+Shift+Z 重構(gòu)的后悔藥(Undo)
編輯
作用域 功能 快捷鍵
全局 查找并替換 Ctrl+F
文本編輯器 查找上一個 Ctrl+Shift+K
文本編輯器 查找下一個 Ctrl+K
全局 撤銷 Ctrl+Z
全局 復(fù)制 Ctrl+C
全局 恢復(fù)上一個選擇 Alt+Shift+↓
全局 剪切 Ctrl+X
全局 快速修正 Ctrl1+1
全局 內(nèi)容輔助 Alt+/
全局 全部選中 Ctrl+A
全局 刪除 Delete
全局 上下文信息 Alt+?
Alt+Shift+?
Ctrl+Shift+Space
Java編輯器 顯示工具提示描述 F2
Java編輯器 選擇封裝元素 Alt+Shift+↑
Java編輯器 選擇上一個元素 Alt+Shift+←
Java編輯器 選擇下一個元素 Alt+Shift+→
文本編輯器 增量查找 Ctrl+J
文本編輯器 增量逆向查找 Ctrl+Shift+J
全局 粘貼 Ctrl+V
全局 重做 Ctrl+Y
查看
作用域 功能 快捷鍵
全局 放大 Ctrl+=
全局 縮小 Ctrl+-
窗口
作用域 功能 快捷鍵
全局 激活編輯器 F12
全局 切換編輯器 Ctrl+Shift+W
全局 上一個編輯器 Ctrl+Shift+F6
全局 上一個視圖 Ctrl+Shift+F7
全局 上一個透視圖 Ctrl+Shift+F8
全局 下一個編輯器 Ctrl+F6
全局 下一個視圖 Ctrl+F7
全局 下一個透視圖 Ctrl+F8
文本編輯器 顯示標尺上下文菜單 Ctrl+W
全局 顯示視圖菜單 Ctrl+F10
全局 顯示系統(tǒng)菜單 Alt+-
導(dǎo)航
作用域 功能 快捷鍵
Java編輯器 打開結(jié)構(gòu) Ctrl+F3
全局 打開類型 Ctrl+Shift+T
全局 打開類型層次結(jié)構(gòu) F4
全局 打開聲明 F3
全局 打開外部javadoc Shift+F2
全局 打開資源 Ctrl+Shift+R
全局 后退歷史記錄 Alt+←
全局 前進歷史記錄 Alt+→
全局 上一個 Ctrl+,
全局 下一個 Ctrl+.
Java編輯器 顯示大綱 Ctrl+O
全局 在層次結(jié)構(gòu)中打開類型 Ctrl+Shift+H
全局 轉(zhuǎn)至匹配的括號 Ctrl+Shift+P
全局 轉(zhuǎn)至上一個編輯位置 Ctrl+Q
Java編輯器 轉(zhuǎn)至上一個成員 Ctrl+Shift+↑
Java編輯器 轉(zhuǎn)至下一個成員 Ctrl+Shift+↓
文本編輯器 轉(zhuǎn)至行 Ctrl+L
搜索
作用域 功能 快捷鍵
全局 出現(xiàn)在文件中 Ctrl+Shift+U
全局 打開搜索對話框 Ctrl+H
全局 工作區(qū)中的聲明 Ctrl+G
全局 工作區(qū)中的引用 Ctrl+Shift+G
文本編輯
作用域 功能 快捷鍵
文本編輯器 改寫切換 Insert
文本編輯器 上滾行 Ctrl+↑
文本編輯器 下滾行 Ctrl+↓
文件
作用域 功能 快捷鍵
全局 保存 Ctrl+X
Ctrl+S
全局 打印 Ctrl+P
全局 關(guān)閉 Ctrl+F4
全局 全部保存 Ctrl+Shift+S
全局 全部關(guān)閉 Ctrl+Shift+F4
全局 屬性 Alt+Enter
全局 新建 Ctrl+N
項目
作用域 功能 快捷鍵
全局 全部構(gòu)建 Ctrl+B
源代碼
作用域 功能 快捷鍵
Java編輯器 格式化 Ctrl+Shift+F
Java編輯器 取消注釋 Ctrl+\
Java編輯器 注釋 Ctrl+/
Java編輯器 添加導(dǎo)入 Ctrl+Shift+M
Java編輯器 組織導(dǎo)入 Ctrl+Shift+O
Java編輯器 使用try/catch塊來包圍 未設(shè)置,太常用了,所以在這里列出,建議自己設(shè)置。
也可以使用Ctrl+1自動修正。
運行
作用域 功能 快捷鍵
全局 單步返回 F7
全局 單步跳過 F6
全局 單步跳入 F5
全局 單步跳入選擇 Ctrl+F5
全局 調(diào)試上次啟動 F11
全局 繼續(xù) F8
全局 使用過濾器單步執(zhí)行 Shift+F5
全局 添加/去除斷點 Ctrl+Shift+B
全局 顯示 Ctrl+D
全局 運行上次啟動 Ctrl+F11
全局 運行至行 Ctrl+R
全局 執(zhí)行 Ctrl+U
重構(gòu)
作用域 功能 快捷鍵
全局 撤銷重構(gòu) Alt+Shift+Z
全局 抽取方法 Alt+Shift+M
全局 抽取局部變量 Alt+Shift+L
全局 內(nèi)聯(lián) Alt+Shift+I
全局 移動 Alt+Shift+V
全局 重命名 Alt+Shift+R
全局 重做 Alt+Shift+Y
檢出項目¶
首先安裝SVN插件(subversive或者subclipse),然后新建Project,從SVN檢出項目。需要特別注意的是,要在general / workspace選項中正確設(shè)置字符編碼,否則可能會出現(xiàn)編譯錯誤。
用Maven構(gòu)建項目¶
安裝IAM之后,在項目的context菜單中選“Maven 2 / Use Maven Dependency Management”,然后就可以管理依賴。
查看數(shù)據(jù)庫¶
打開 Database Perspective 即可。注意要先選擇驅(qū)動程序,即mysql的java connector的jar,然后才是配置各個連接的jdbc地址。
關(guān)于插件¶
插件安裝方法(zz)¶
英文教程:http://www.venukb.com/2006/08/20/install-eclipse-plugins-the-easy-way/
有關(guān)插件安裝問題,四種常用的方法在此特別注明:
#“幫助”->“軟件更新”->“查找并安裝”->“搜索要安裝的新功能部件”->“新建遠程站點”(此種方式用于在線更新)
#“幫助”->“軟件更新”->“查找并安裝”->“搜索要安裝的新功能部件”->“新建本地站點”(如果插件已經(jīng)下載到了本地,請不要用第一種方法)
- 直接拷貝plugins和features兩個目錄下的內(nèi)容置于$Eclipse_Home$/對應(yīng)的plugins和features下面
- 用link外鏈接與外部插件關(guān)聯(lián)
最菜的,一般用第一種方法,而大部分生手一般選擇第二或者第三種方法,用得習(xí)慣的一般選擇最后一種方式。此四類方法優(yōu)劣勢對比如下:
前三種方法都會將插件文件拷貝至相$Eclipse_Home$/對應(yīng)的plugins和features目錄下,從本質(zhì)上看,沒多大區(qū)別,并且插件只能
安裝和禁用,不能卸載(當然,如果你對插件對應(yīng)的目錄和文件都很熟悉的話,可以通過直接刪除拷進去的文件來達到卸載插件的目的),但方法一和方法二在安裝
插件的時候很容易出錯或者是產(chǎn)生沖突,特別是當你用了Myeclipse插件、中文包的同時,又想安裝HibernateSynchronizer、
Jode Compiler(Class反編譯工具)、Visual Editor等插件時,及有可能導(dǎo)致Myeclipse插件和中文包失效。
所以,如果插件已經(jīng)下載到了本地,請直接拷貝至$Eclipse_Home$/對應(yīng)的plugins和features目錄下,也就是用方法三,這樣能避免沖突。
方
法四是將所有的插件用一個外部目錄存放起來,假如是D:\plug-in,將上面所示的插件目錄文件全部拷貝到該目錄下,比如Tomcat插件,此時的文
件路徑就是D:\plug-in\tomcat_plug\eclipse\plugins
\com.sysdeo.eclipse.tomcat_3.1.0.beta(請注意,方法四一定要嚴格這樣的目錄路徑放置文件)。然后
在$Eclipse_Home$下新建一個links目錄,并在links目錄下建立關(guān)聯(lián)文件,假如是tomcat.link,在建立的關(guān)聯(lián)文件中加入如
下語句:
path=D:/plug-in/tomcat_plug
還可以寫成相對路徑的形式。剩下的事情,不用我說你肯定都知道了,就是重啟Eclipse,在Dos窗口下進入Eclipse安
裝目錄,鍵入命令eclipse
-clean,回車,或者進入$Eclipse_Home$/configuration目錄,刪除org.eclipse.update后再重新啟動
Eclipse。
QA相關(guān)插件¶
參考:http://www.ibm.com/developerworks/cn/java/j-ap01117/
CheckStyle http://eclipse-cs.sourceforge.net/update/
Coverlipse http://coverlipse.sf.net/update
PMD http://pmd.sourceforge.net/eclipse/
JDepend http://andrei.gmxhome.de/eclipse/
Metrics http://metrics.sourceforge.net/update
FindBugs http://findbugs.cs.umd.edu/eclipse
參考資源¶
http://www.ibm.com/developerworks/cn/eclipse/resources.html
http://www.eclipseplugincentral.com/
posted @
2009-07-02 09:36 xiaoxinchen 閱讀(269) |
評論 (0) |
編輯 收藏
posted @
2009-07-02 09:35 xiaoxinchen 閱讀(109) |
評論 (0) |
編輯 收藏
快速入門
歡迎使用BeanShell.這是一個速成課程。我們將省去一些重要的選項和細節(jié)。要學(xué)習(xí)更多的內(nèi)容請看本User's Guide的其它部分。
下載和運行BeanShell
請到http://www.beanshell.org下載最新的JAR文件。你可以用圖形桌面模式和命令行模式起動BeanShell。
如果你只是要玩一玩BeanShell,你可以在BeanShell的jar文件上雙擊來起動BeanShell的桌面。但不管怎樣,如果你要讓BeanShell與你的類與應(yīng)用程序一起工作就必須將BeanShell的jar文件加到classpath中。
你可以將BeanShell的jar文件拖到JAVA_HOME的ext目錄也可以直接加到classpath中。
- windows用戶請將bsh.jar放在JAVA_HOME/jre/lib/ext文件夾,OSX用戶可以放在/Library/Java/Extensions.
或者增加BeanShell到你的classpath目錄,如:
unix: export CLASSPATH=$CLASSPATH:bsh-xx.jar
windows:set classpath %classpath%;bsh-xx.jar
然后你就可以運行BeanShell在GUI或命令行模式:
- java bsh.Console // run the graphical desktop
or
java bsh.Interpreter // run as text-only on the command line
or
java bsh.Interpreter filename [ args ] // run script file
可以在你的應(yīng)用程序內(nèi)部來運行,也可以作為debug及servlet的遠程服務(wù)器模式,或一個Applet內(nèi)部來運行BeanShell。請參考"BeanShell Modes of Operation"獲得更多詳情。
BeanShell GUI
用GUI模式啟動BeanShell后,將打開一個桌面視窗。用鼠標右擊在桌面的背景上,你可以打開另一個控制臺視窗及其它的工具如一個簡單的類游覽器。
每一個控制臺視窗運行一個獨立的BeanShell解釋器。這個圖形化的控制臺支持基本的歷史命令,行編輯,剪切和粘貼,甚至類和變量名的自動完成功能。從控制臺你能開啟一個簡單的編輯視窗。在它里面,你可以編寫腳本和使用‘eval’選項求表達式的值。
Java語句和表達式
BeanShell能理解標準的JAVA語句,表達式,和方法宣告。語句和表達式的內(nèi)容可以是:變量,宣告,賦值,方法調(diào)用,循環(huán),條件等。
在
Java程序中你必須嚴格的使用它們,但在BeanShell中,你可以用“寬松類型”(loosely
typed)的方式來使用它們。也就是說,你可以在寫腳本時偷懶,不進行變量類型的宣告(在原始數(shù)據(jù)類型和對象都可以)。如果你試著用錯變量類
型,BeanShell將會給出一個錯誤。
這里有一些例子:
- foo = "Foo";
four = (2 + 2)*2/2;
print( foo + " = " + four ); // print() is a BeanShell command
// Do a loop
for (i=0; i<5; i++)
print(i);
// Pop up a frame with a button in it
button = new JButton( "My Button" );
frame = new JFrame( "My Frame" );
frame.getContentPane().add( button, "Center" );
frame.pack();
frame.setVisible(true);
有用的BeanShell命令
在
剛才那個例子中我們用了一個內(nèi)建在BeanShell中的一個方便的命令print(),來顯示變量的值。print()跟ava的
System.out.println()非常的相像,除非它能保證輸出總是命令行。print()也可以顯示一些對象的類型(如數(shù)組),但比Java的
更詳細。另一個相關(guān)的命令是show(),用來開啟與關(guān)閉顯示你輸入的每一行的結(jié)果。
這兒是一些其它的BeanShell的命令:
source(), run() - 將一個bsh腳本讀到解釋器或運行在另一個解釋器。
frame() - 顯示一個Frame或JFrame的GUI組件.
load(), save() - 載入和保存一個序列化的對象到一個文件.
cd(), cat(), dir(), pwd(), etc. - 類unix的shell命令。
exec() - 運行一個本地的程序。
javap() - 打印一個對象的方法和字段,類似于Java的javap命令。
setAccessibility() - 開啟無限制的存取private 和protected的組件。
要獲得更多的信息請查看BeanShell命令的詳細清單。
提示:
BeanShell命令并不是真的"內(nèi)建"其中的,而是作為腳本方法自動從classpath載入的. 你可以擴展基本命令集并加到classpath中作為自訂義的腳本來使用。
腳本方法
你可以在bsh中宣告和使用方法,就像在java的類中一樣。
- int addTwoNumbers( int a, int b ) {
return a + b;
}
sum = addTwoNumbers( 5, 7 ); // 12
bsh的方法可以有動態(tài)的(寬松的)參數(shù)和返回類型。
- add( a, b ) {
return a + b;
}
foo = add(1, 2); // 3
foo = add("Oh", " baby"); // "Oh baby"
實現(xiàn)Interface
注意:如果要BeanShell能實現(xiàn)任意的Interface,必須有jdk1.3及以上支持。
你可以在腳本中用標準的Java內(nèi)部類的語法來實現(xiàn)Interface.例如:
- ActionListener scriptedListener = new ActionListener() {
actionPerformed( event ) { ... }
}
你
可以不用實現(xiàn)Interface的所有方法,而只用實現(xiàn)你需要的方法。如果代碼中調(diào)用了未被實現(xiàn)的方法,將丟出異常。如果你想重載大量的方法的行為--例
如為日志生成一個"啞"適配器--你可以在腳本對象中實現(xiàn)一個特殊的方法:invoke(name,args)。invoke()方法用來處理任何未被定
義的方法的調(diào)用:
- ml = new MouseListener() {
mousePressed( event ) { ... }
// handle the rest
invoke( name, args ) { print("Method: "+name+" invoked!");
}
腳本對象
在
BeanShell中,和在JavaScript與Perl中一樣,腳本對象是用封閉的方法體一構(gòu)成的。通過在方法未尾返回一個特殊值"this",你就
可以像使用方法一樣調(diào)用這個對象了。在這個方法調(diào)用時,你可以給與它任何的值。通常對象內(nèi)部需要包括方法,所以BeanShell的腳本方法在一定程度上
可再包含一些方法以構(gòu)成腳本對象。例如:
- foo() {
print("foo");
x=5;
bar() {
print("bar");
}
return this;
}
myfoo = foo(); // prints "foo"
print( myfoo.x ); // prints "5"
myfoo.bar(); // prints "bar"
如果這些代碼對你來說很陌生,別急,請用戶手冊可得到更透徹的解釋。
在
你的腳本中,BeanShell腳本對象(也就是先前例子中的"this"參照)能自動實現(xiàn)任何JAVA介面類型。當JAVA代碼調(diào)用相應(yīng)與之通訊的腳本
方法內(nèi)的方法。當你試著將腳本對象作為參數(shù)傳給Java方法時,BeanShell會自動將它造型(cast)為相應(yīng)的類型。如要傳遞BeanShell
外部的對象時,你可以在需要時顯式的進行造型(cast).請看用戶手冊中的詳細內(nèi)容。
從你的應(yīng)用程序調(diào)用BeanShell
通過建立一個BeanShell解釋器,使用eval()或source()命令,你可以在你的應(yīng)用程序中求文本表達式的值和運行腳本。如果你希望在你的腳本內(nèi)部使用一個對象,可以用set()方法傳遞對象的變量參照給BeanShell,并通過get()方法取得結(jié)果。
- import bsh.Interpreter;
Interpreter i = new Interpreter(); // Construct an interpreter
i.set("foo", 5); // Set variables
i.set("date", new Date() );
Date date = (Date)i.get("date"); // retrieve a variable
// Eval a statement and get the result
i.eval("bar = foo*10");
System.out.println( i.get("bar") );
// Source an external script file
i.source("somefile.bsh");
BeanShell將成為Java平臺上的第三種編程語言
|
2005-06-08 點擊:8 來源:CSDN 作者:CSDN |
JCP接納了一個新的技術(shù)規(guī)范進入標準化進程,這個編號為JSR-274的技術(shù)規(guī)范將把BeanShell引入為Java平臺上支持的又一種編程語言。
JSR-
274(http://jcp.org/en/jsr/detail?id=274)是由 Patrick
Niemeyer提交的技術(shù)規(guī)范,其目標是將BeanShell腳本語言(http://www.beanshell.org/)規(guī)范化為Java虛擬機
平臺上支持的第三種編程語言。除了Java之外,Java虛擬機還支持Groovy腳本語言。Doug
Lea、Apache和Google三個JCP執(zhí)委會成員對此規(guī)范表示了支持。
按照Java最初的設(shè)計思路,有很多語言都可以在JVM上
運行(詳細列表參見http://en.wikipedia.org/wiki/List_of_Java_scripting_languages),
但這些語言大多沒有流行起來。直到2004年為止,Java平臺事實上只有一種編程語言,也就是Java。2004年3月,Groovy(JSR-
241)成為了Java平臺上的第二種編程語言。
消息全文請看:http://rmh.blogs.com/weblog/2005/05/beanshell_the_3.html
|
http://www.cn-java.com/target/news.php?news_id=2450
簡介:
BeanShell是一種腳本語言,一種完全符合java語法的java腳本語言,并且又擁有自己的一些語法和方法,beanShell是一種松散類型的腳本語言(這點和JS類似)。
下載地址:http://www.beanshell.org
設(shè)置環(huán)境
l 把;bsh-xx.jar放到$JAVA_HOME/jre/lib/ext文件夾下
l unix: export CLASSPATH=$CLASSPATH:bsh-xx.jar
l windows: set classpath %classpath%;bsh-xx.jar
運行方式:
l 界面UI方式 :java bsh.Console
l 命令行方式 :java bsh.Interpreter
l 運行腳本文件:java bsh.Interpreter filename [ args ]
簡單舉例:
在classpath中設(shè)置好環(huán)境變量,打開dos窗口,鍵入:java bsh.Console命令
出現(xiàn)BeanShell圖片代表設(shè)置成功,beanshell開始運行
測試內(nèi)容:
設(shè)置變量
foo = "Foo";
four = (2 + 2)*2/2;
打印變量
print( foo + " = " + four );
循環(huán)
for (i=0; i<5; i++)
print(i);
在窗口中打印按鈕
button = new JButton( "My Button" );
frame = new JFrame( "My Frame" );
frame.getContentPane().add( button, "Center" );
frame.pack();
frame.setVisible(true);
完整代碼:
foo = "Foo";
four = (2 + 2)*2/2;
print( foo + " = " + four );
for (i=0; i<5; i++)
print(i);
button = new JButton( "My Button" );
frame = new JFrame( "My Frame" );
frame.getContentPane().add( button, "Center" );
frame.pack();
frame.setVisible(true);
在窗口中輸入上面的代碼
敲回車執(zhí)行,運行結(jié)果如圖
說明:
因為beanshell是松散類型的腳本語言因此可以直接寫
foo = "Foo";
four = (2 + 2)*2/2;
print是beanshell提供一種簡單的打印命令相當于java中的System.out.println()
其他的beanshell腳本命令
· source(), run() – 讀取,或者運行一個腳本文件
· frame() – 顯示一個窗口
· load(), save() – 讀取或者保存一個腳本對象到文件
· cd(), cat(), dir(), pwd(), etc. 使用Unix下面的命令
· exec() – 運行一個本地的方法
· javap() –使用javap命令.
· setAccessibility() – 設(shè)置可以接收private和protected類型的變量
BeanShell命令不一定都是內(nèi)置的腳本命令,腳本方法會自動從classpath中取方法使用,因此你可以添加你自己的腳本到classpath中來擴充基本的命令
腳本方法
一般的方法:
int addTwoNumbers( int a, int b ) {
return a + b;
}
sum = addTwoNumbers( 5, 7 ); // 12
也可以使用動態(tài)的變量類型(無狀態(tài))方法
add( a, b ) {
return a + b;
}
foo = add(1, 2); // 3
foo = add(1, “2”); //”12” 只要有一個為字符串全部按照字符串處理,系統(tǒng)不會根據(jù)1是數(shù)字在前把“2”轉(zhuǎn)換成數(shù)字處理(特別注意)
foo = add("Oh", " baby"); // "Oh baby"
實現(xiàn)接口
實現(xiàn)任何接口需要java1.3或者更高
可以使用缺省的java匿名類的語法實現(xiàn)一個接口類,例如:
ActionListener scriptedListener = new ActionListener() {
actionPerformed( event ) { ... }
}
不需要實現(xiàn)接口的所有的方法,只需要實現(xiàn)你使用的方法即可,如果使用你沒有實現(xiàn)的方法,beanshell將拋出一個錯誤,
ml = new MouseListener() {
mousePressed( event ) { ... }
// handle the rest
invoke( name, args ) { print("Method: "+name+" invoked!");
}
腳本對象
使用特殊的關(guān)鍵字this可以創(chuàng)建一個對象(根JS類似)
foo() {
print("foo");
x=5;
bar() {
print("bar");
}
return this;
}
myfoo = foo(); // prints "foo"
print( myfoo.x ); // prints "5"
myfoo.bar(); // prints "bar"
從應(yīng)用程序中調(diào)用BeanShell
創(chuàng)建一個BeanShell的解釋器(interpreter)用eval()和source()命令可以對一個字符串求值和運行一個腳本文件
使用set()方法可以給一個對象傳入一個變量的參考
使用get()方法可以重新得到一個變量的結(jié)果
完整代碼:
package cn.com.sparknet.util;
import bsh.*;
import java.util.*;
public class BeanShell {
public static void main(String[] args) {
try {
Interpreter interpreter = new Interpreter(); // 構(gòu)造一個解釋器
interpreter.set("foo", 5); // 設(shè)置變量
interpreter.set("date", new Date()); //設(shè)置一個時間對象
Date date = (Date) interpreter.get("date"); // 重新得到時間變量
interpreter.println(date.toString()); //打印時間變量
interpreter.eval("bar = foo*10"); // 對一段腳本求值,并得到結(jié)果
System.out.println(interpreter.get("bar")); //打印變量
interpreter.source("d:\\helloWorld.bsh"); // 導(dǎo)入并執(zhí)行一個腳本文件
}
catch (Exception e) {
//如果發(fā)生異常,寫入日志文件
Log.error(new BeanShell(), "main", FormatDate.getCurrDate(), e.getMessage());
}
}
}
BeanShell語法
BeanShell是一種最原始的java解釋器。
標準的java語法
/*
Standard Java syntax
*/
// Use a hashtable
Hashtable hashtable = new Hashtable();
Date date = new Date();
hashtable.put( "today", date );
// Print the current clock value
print( System.currentTimeMillis() );
// Loop
for (int i=0; i<5; i++)
print(i);
// Pop up a frame with a button in it
JButton button = new JButton( "My Button" );
JFrame frame = new JFrame( "My Frame" );
frame.getContentPane().add( button, "Center" );
frame.pack();
frame.setVisible(true);
松散類型的java語法
/*
Loosely Typed Java syntax
*/
// Use a hashtable
hashtable = new Hashtable();
date = new Date();
hashtable.put( "today", date );
// Print the current clock value
print( System.currentTimeMillis() );
// Loop
for (i=0; i<5; i++)
print(i);
// Pop up a frame with a button in it
button = new JButton( "My Button" );
frame = new JFrame( "My Frame" );
frame.getContentPane().add( button, "Center" );
frame.pack();
frame.setVisible(true);
異常處理
標準的java異常
try {
int i = 1/0;
} catch ( ArithmeticException e ) {
print( e );
}
松散的異常處理(類似JS)
try {
...
} catch ( e ) {
...
}
松散類型變量的作用范圍
標
準的java程序的變量作用范圍是在一個模塊中的(在模塊中聲明的變量),而在松散類型的語言中如果在一個模塊中沒有指定一個變量的類型,則認為是一個全
局變量(只有它以后的代碼可以使用該變量,系統(tǒng)在調(diào)用該變量的時候自動生成一個全局變量,也就為什么在調(diào)用模塊之前不能使用該變量的原因)
// Arbitrary code block
{
y = 2; // Untyped variable assigned
int x = 1; // Typed variable assigned
}
print( y ); // 2
print( x ); // Error! x is undefined.
// Same with any block statement: if, while, try/catch, etc.
if ( true ) {
y = 2; // Untyped variable assigned
int x = 1; // Typed variable assigned
}
print( y ); // 2
print( x ); // Error! x is undefined.
同樣也使用于for-loop, if-else等循環(huán)語句
for( int i=0; i<10; i++ ) { // typed for-init variable
j=42;
}
print( i ); // Error! 'i' is undefined.
print( j ); // 42
for( z=0; z<10; z++ ) { } // untyped for-init variable
print( z ); // 10
方便靈活的語法
標準的java語法
java.awt.Button button = new java.awt.Button();
button.setLabel(“javaButton”);
松散的語法
button = new java.awt.Button();
button.label = "my button";
你也可以使用{}來對一個對象設(shè)置屬性
b = new java.awt.Button();
b{"label"} = "my button"; // Equivalent to: b.setLabel("my button");
h = new Hashtable();
h{"foo"} = "bar"; // Equivalent to: h.put("foo", "bar");
包裝和未包裝(box和unbox)
BeanShell自動轉(zhuǎn)為簡單類型
i=5;
iw=new Integer(5);
print( i * iw ); // 25
導(dǎo)入類和包
import javax.xml.parsers.*;
import mypackage.MyClass;
超級導(dǎo)入法:
import *;
BeanShell默認導(dǎo)入下面的包
· java.lang
· java.io
· java.util
· java.net
· java.awt
· java.awt.event
· javax.swing
· javax.swing.event
友好文檔實體
BeanShell支持特殊的文檔操作類型內(nèi)容
@gt > @lt <
@lteq <= @gteq >=
@or || @and &&
@bitwise_and & @bitwise_or |
@left_shift << @right_shift >>
@right_unsigned_shift >>> @and_assign &=
@or_assign |= @left_shift_assign <<=
@right_shift_assign >>= @right_unsigned_shift_assign >>>=
腳本方法
你可以定義方法象java中的定義方法一樣
int addTwoNumbers( int a, int b ) {
return a + b;
}
你可以使用內(nèi)餡的BeanShell方法使用他們
sum = addTwoNumbers( 5, 7 );
只有BeanShell變量可以被動態(tài)定義為動態(tài)類型,方法可以有動態(tài)的參數(shù)以及返回類型
add( a, b ) {
return a + b;
}
在這個方法中,BeanShell將動態(tài)的決定類型當這個方法被調(diào)用時并且能夠準確的計算出你想要的結(jié)果
foo = add(1, 2);
print( foo ); // 3
foo = add("Oh", " baby");
print( foo ); // Oh baby
在第一個例子中BeanShell將把參數(shù)定義為數(shù)字型,并返回數(shù)字型
在第二個例子中BeanShell將把參數(shù)定義為字符型,并返回字符對象
變量和方法的可見范圍
就像您所預(yù)期的那樣,在方法內(nèi)您可以參考到上下文中上面的變量和方法
a = 42;
someMethod() { ... }
foo() {
print( a );
someMethod(); // invoke someMethod()
}
// invoke foo()
foo(); // prints 42
如果一個變量只有在方法內(nèi)使用請定義成局部變量,即加上類型,如果是全局變量請在方法外定義
var = "global";
foo() {
print(var);
String var = "local";
print(var);
}
foo();
print(var);
將打印出
global
local
global
方法內(nèi)的var(第四行)變量屬于局部變量,不會覆蓋全局變量var(第一行)的因此改變var(第四行)變量不會影響到全局變量var(第一行)
范圍參考:super
使用super關(guān)鍵字可以在局部參考到全局變量
var = "global";
foo() {
String var = "local";
print(var);
print(super.var);
}
foo();
將輸出
local
global
腳本對象
this對象
在java標準語言中可以使用this返回一個類的一個實例
// MyClass.java
MyClass {
Object getObject() {
return this; // return a reference to our object
}
}
在這個例子中g(shù)etObject() 方法是返回MyClass類的一個實例
在BeanShell中對象中的變量只是局部的變量在對象內(nèi)可以使用,在對象外是不可以使用(不同于前面for-loop,if-else中的使用);
// Define the foo() method:
foo() {
bar = 42;
print( bar );
}
// Invoke the foo() method:
foo(); // prints 42
print( bar ); // Error, bar is undefined here
可以使用this返回對象,使用對象加上“.”運算符參考屬性(類似JS)
foo() {
bar = 42;
return this;
}
fooObj = foo();
print( fooObj.bar ); // prints 42!
同樣對象中也可以定義一些方法
foo() {
bar() {
...
}
}
例如
foo() {
int a = 42;
bar() {
print("The bar is open!");
}
bar();
return this;
}
// Construct the foo object
fooObj = foo(); // prints "the bar is open!"
// Print a variable of the foo object
print ( fooObj.a ) // 42
// Invoke a method on the foo object
fooObj.bar(); // prints "the bar is open!"
也可以把bar()和foo也可以代參數(shù)
foo() {
return this;
}
bar(int a) {
print("The bar is open!" + a);
}
foo().bar(1);
也可以把bar()方法定義到對象外面
foo() {
bar(int a) {
print("The bar is open!" + a);
}
return this;
}
foo().bar(1);
BeanShell一種松散的腳本語言,有很多中聲明的方法可以使用
This super global
This是參考當前對象
Super是參考父親對象
Global是參考最上層對象
super.super.super...foo = 42; // Chain super. to reach the top
global.foo = 42;
簡單例子:
文本拖動:
dragText() {
f = new Frame("Drag in the box");
f.setFont( new Font("Serif", Font.BOLD, 24) );
f.setSize(300, 300);
f.show();
gc = f.getGraphics();
gc.setColor(Color.cyan);
mouseDragged( e ) {
gc.drawString("Drag Me!", e.getX(), e.getY());
}
mouseMoved( e ) { }
f.addMouseMotionListener( this );
}
簡單畫圖
import bsh.util.BshCanvas; // BshCanvas simply buffers graphics
graph( int width, int height ) {
canvas=new BshCanvas();
canvas.setSize( width, height );
frame=frame( canvas );
graphics=canvas.getBufferedGraphics();
// draw axis
graphics.setColor( Color.red );
graphics.drawLine( 0, height/2, width, height/2 );
graphics.drawLine( width/2, 0, width/2, height );
graphics.setColor( Color.black );
plot(int x, int y) {
graphics.fillOval( (x+width/2-1), (y+height/2-1), 3, 3);
canvas.repaint();
}
return this;
}
drawSin( graph ) {
for (int x=-100; x<100; x++ ) {
y=(int)(50*Math.sin( x/10.0 ));
graph.plot( x, y );
}
}
簡單web瀏覽器
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;
import java.awt.event.*;
import java.awt.*;
JFrame browser( startingUrl ) {
invoke( method, args ) {}
windowClosing(WindowEvent we) {
we.getWindow().setVisible(false);
}
setPage( url ) {
try {
pane.setPage( url );
} catch(Exception e) {
statusBar.setText("Error opening page: "+url);
}
}
hyperlinkUpdate( HyperlinkEvent he ) {
type = he.getEventType();
if (type == HyperlinkEvent.EventType.ENTERED) {
pane.setCursor(
Cursor.getPredefinedCursor( Cursor.HAND_CURSOR) );
statusBar.setText(he.getURL().toString());
} else
if (type == HyperlinkEvent.EventType.EXITED) {
pane.setCursor( Cursor.getDefaultCursor() );
statusBar.setText(" ");
} else {
setPage( he.getURL() );
if (urlField != null)
urlField.setText(he.getURL().toString());
}
}
frame = new JFrame("Browser");
frame.setSize(400,300);
frame.addWindowListener( this );
urlPanel = new JPanel();
urlPanel.setLayout(new BorderLayout());
urlField = new JTextField(startingUrl);
urlPanel.add(new JLabel("Site: "), BorderLayout.WEST);
urlPanel.add(urlField, BorderLayout.CENTER);
statusBar = new JLabel(" ");
pane = new JEditorPane();
pane.setEditable(false);
setPage( startingUrl );
jsp = new JScrollPane(pane);
frame.getContentPane().add(jsp, BorderLayout.CENTER);
frame.getContentPane().add(urlPanel, BorderLayout.SOUTH);
frame.getContentPane().add(statusBar, BorderLayout.NORTH);
// This is the equivalent of an inner class in bsh.
urlTextHandler() {
actionPerformed(ActionEvent ae) {
setPage( ae.getActionCommand() );
}
return this;
}
urlField.addActionListener( urlTextHandler() );
pane.addHyperlinkListener( (HyperlinkListener)this );
return frame;
}
browser = browser("http://java.sun.com/");
browser.show();
更多的文檔參考BeanShell網(wǎng)站
http://www.beanshell.org
posted @
2008-10-08 14:34 xiaoxinchen 閱讀(2471) |
評論 (0) |
編輯 收藏