
2013年8月8日
- <%
- WebApplicationContext context = (WebApplicationContext)this.getServletContext().getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
- TestService service = (TestService)context.getBean("testService");
- %>
posted @
2013-08-08 14:48 邦 閱讀(203) |
評論 (0) |
編輯 收藏

2012年9月24日
一、模版修改
在導出表時,powerdesigner默認為我們提供了很多的模版,在工具欄中選擇【Report--->Report Template】即可看到所有的默認模版。如圖一:

圖一 模版列表
這里我們為了導出powerdesigner中創建的表,在工具欄中選擇【Report--->Reports】(快捷鍵Ctrl+E),然后創建一個New Report
,如下圖二所以,選擇Standard Physical Report,這里選擇的標準的模版,點擊OK確定。

圖二 創建新的Report
從工具欄【Report--->Print Preview】或者點擊圖標
同樣可以預覽導出到word的效果了。效果如圖三所示:

圖三 導出到word中預覽效果
從【圖三】中的工具欄里面HTM和RTF兩種導出格式。如果導出到word選擇RTF格式即可,但是我們從【圖三】中看出紅色標出的部分,1 是word的頁眉,同樣還有頁腳信息 ,2 是一些列的屬性清單。

圖四 設置頁眉、頁腳
在【圖四】中,選擇Header/Footer后,刪掉Header里面的%MODULE% %MODELNAME%,刪掉Footer里面的%APPNAME% %DATE% 頁數 %PAGE%,User_defined footer就會自動勾上,這樣就去掉了頁眉、頁腳了
還有其他的比如表前面的自增序號,第一頁中的創建者、版本、日期信息也不需要,我們可以進行配置去掉,如下【圖五】【圖六】

圖四



圖五 Properties配置
在圖五中,在默認配置中(General)勾上No paragraph numbering即可取消表前面的自增序號,在Title Page中選擇 No Title Page就不會生成第一頁中關于創建者、版本、日期等信息。
在【圖三】中,預覽看到的內容太多,就必須刪除一些我們不需要的內容,經過刪減之后,如下【圖六】所示

圖六
在【圖六】中右鍵單擊【List of Table Columns - 表<%PARENT%>列說明】,從菜單中選擇 Edit Title...,就可以編輯成現在我們已看到的。然后,在右鍵菜單中選擇Layout...,選擇我們所需要顯示的內容,Code表示列名稱,一般用英文單詞或拼音字母表示,Name一般用中文描述,Data Type 表示數據類型,Comment表示字段備注或字段說明等。
到此基本上已經完成了powerdesigner模版的修改
二、導出表
然后點擊[Report---->Generate RTF]導出到word中,最后我們看看導出到word的效果,如下圖:

圖七 導出到word效果
三、保存模版
通過上文,我們完成了自己所需的模版,然后就保存,以后可以直接使用即可,在工具欄中[Report--->Create Template From Section],然后Ctrl+S就會要求保存,取名保存即可。
posted @
2012-09-24 18:12 邦 閱讀(584) |
評論 (0) |
編輯 收藏

2012年8月2日
用jTDS通過JDBC連:
<dependency>
<groupId>net.sourceforge.jtds</groupId>
<artifactId>jtds</artifactId>
<version>1.2.4</version>
</dependency>
public static List<String> executeQuery(String sql,String columns) {
try {
Connection conn = getConnection();
Statement st = conn.createStatement();
ResultSet set = st.executeQuery(sql);
List<String> result = new ArrayList<String>();
while (set.next()) {
String[] columnList = columns.split(",");
for(String str:columnList){
result.add(set.getString(str));
}
}
set.close();
st.close();
conn.close();
return result;
} catch (SQLException e) {
throw new IllegalArgumentException(e);
}
}
public static Connection getConnection() {
try {
Class.forName("net.sourceforge.jtds.jdbc.Driver");
String url = "jdbc:jtds:sqlserver://localhost:1433;DatabaseName=jdl";
String username = "sa";
String password = "";
Connection conn = DriverManager.getConnection(url, username,
password);
return conn;
} catch (Exception e) {
throw new IllegalArgumentException(e);
}
}
posted @
2012-08-02 15:55 邦 閱讀(5146) |
評論 (0) |
編輯 收藏

2012年7月11日
在js中的url進行encodeURIComponent編碼:dd_ddtj_sub.jsp?shrxm="+encodeURIComponent(shrxm1)+"&lxdh="+encodeURIComponent(lxdh1)
后臺service進行encode編碼和decode解碼:shrxm= java.net.URLEncoder.encode(shrxm, "UTF-8");
shrxm= java.net.URLDecoder.decode(shrxm,"UTF-8");
posted @
2012-07-11 17:01 邦 閱讀(276) |
評論 (0) |
編輯 收藏

2012年7月10日
1)、下載MongoDB
http://downloads.mongodb.org/win32/mongodb-win32-i386-2.0.4.zip
2)、設置MongoDB目錄
將其解壓到 d:\,再重命名為mongodb,路徑為d:\mongodb
3)、設置數據文件路徑
在d:盤建一個data文件夾,在data文件夾中新建db文件夾,路徑d:\data\db
4)、啟動MongoDB服務
進入 cmd 提示符控制臺,
D:\mongodb\bin\mongod.exe --dbpath=d:\data\db
<!--[if !supportLists]-->1. <!--[endif]-->Mon Apr 16 08:50:54
<!--[if !supportLists]-->2. <!--[endif]-->Mon Apr 16 08:50:54 warning: 32-bit servers don't have journaling enabled by def
<!--[if !supportLists]-->3. <!--[endif]-->ault. Please use --journal if you want durability.
<!--[if !supportLists]-->4. <!--[endif]-->Mon Apr 16 08:50:54
<!--[if !supportLists]-->5. <!--[endif]-->Mon Apr 16 08:50:54 [initandlisten] MongoDB starting : pid=5084 port=27017 dbpat
<!--[if !supportLists]-->6. <!--[endif]-->h=d:\data\db 32-bit host=PC-201012302214
<!--[if !supportLists]-->7. <!--[endif]-->Mon Apr 16 08:50:54 [initandlisten]
<!--[if !supportLists]-->8. <!--[endif]-->Mon Apr 16 08:50:54 [initandlisten] ** NOTE: when using MongoDB 32 bit, you are
<!--[if !supportLists]-->9. <!--[endif]-->limited to about 2 gigabytes of data
<!--[if !supportLists]-->10. <!--[endif]-->Mon Apr 16 08:50:54 [initandlisten] ** see http://blog.mongodb.org/post/13
<!--[if !supportLists]-->11. <!--[endif]-->7788967/32-bit-limitations
<!--[if !supportLists]-->12. <!--[endif]-->Mon Apr 16 08:50:54 [initandlisten] ** with --journal, the limit is lower
<!--[if !supportLists]-->13. <!--[endif]-->Mon Apr 16 08:50:54 [initandlisten]
<!--[if !supportLists]-->14. <!--[endif]-->Mon Apr 16 08:50:54 [initandlisten] db version v2.0.4, pdfile version 4.5
<!--[if !supportLists]-->15. <!--[endif]-->Mon Apr 16 08:50:54 [initandlisten] git version: 329f3c47fe8136c03392c8f0e548506
<!--[if !supportLists]-->16. <!--[endif]-->cb21f8ebf
<!--[if !supportLists]-->17. <!--[endif]-->Mon Apr 16 08:50:54 [initandlisten] build info: windows sys.getwindowsversion(ma
<!--[if !supportLists]-->18. <!--[endif]-->jor=6, minor=0, build=6002, platform=2, service_pack='Service Pack 2') BOOST_LIB
<!--[if !supportLists]-->19. <!--[endif]-->_VERSION=1_42
<!--[if !supportLists]-->20. <!--[endif]-->Mon Apr 16 08:50:54 [initandlisten] options: { dbpath: "d:\data\db" }
<!--[if !supportLists]-->21. <!--[endif]-->Mon Apr 16 08:50:54 [websvr] admin web console waiting for connections on port 2
<!--[if !supportLists]-->22. <!--[endif]-->8017
<!--[if !supportLists]-->23. <!--[endif]-->Mon Apr 16 08:50:54 [initandlisten] waiting for connections on port 27017
MongoDB服務端的默認連接端口:27017
5)、將MongoDB作為 Windows 服務隨機啟動
先創建D:\mongodb\logs\mongodb.log文件,用于存儲MongoDB的日志文件, 再安裝系統服務:
D:\mongodb\bin\mongod --dbpath=d:\data\db --logpath=d:\mongodb\log
s\mongodb.log --install
all output going to: d:\mongodb\logs\mongodb.log
Creating service MongoDB.
Service creation successful.
Service can be started from the command line via 'net start "MongoDB"'.
D:\>net start mongodb
Mongo DB 服務已經啟動成功。
D:>
注意:如果需要卸載服務,執行命令:sc delete MongoDB
6)、客戶端連接驗證
新打開一個CMD輸入:d:\mongodb\bin\mongo,如果出現下面提示,那么您就可以開始MongoDB之旅了:
<!--[if !supportLists]-->1. <!--[endif]-->d:\mongodb\bin\mongo
<!--[if !supportLists]-->2. <!--[endif]-->MongoDB shell version: 2.0.4
<!--[if !supportLists]-->3. <!--[endif]-->connecting to: test
<!--[if !supportLists]-->4. <!--[endif]-->>
7)、查看MongoDB日志
查看D:\mongodb\logs\mongodb.log文件,即可對MongoDB的運行情況進行查看或排錯。
posted @
2012-07-10 13:24 邦 閱讀(638) |
評論 (0) |
編輯 收藏

2012年5月3日
方案一:
希望實現 當鼠標離開一個DIV的時候觸發一個事件處理函數 于是用onmouseout 結果卻發現它的觸發是不是也太敏感了 原因現在也沒有弄清楚 IE下好像是因為區分mouseout時的fromElement還是toElement ,IE 5.5以上的onmouseleave事件就比較好用 偏FF又不支持這個事件 只有自己想辦法手工判斷了。
<SCRIPT> /*** * 參數e 是對象傳遞的觸發事件 FF下想訪問event對象必須傳遞event參數 * 參數o 是目標DIV對象 */ function fun(e,o) { /* FF 下判斷鼠標是否離開DIV */ if(window.navigator.userAgent.indexOf("Firefox")>=1) { var x = e.clientX + document.body.scrollLeft; var y = e.clientY + document.body.scrollTop ; var left = o.offsetLeft; var top = o.offsetTop; var w = o.offsetWidth; var h = o.offsetHeight; if(y < top || y > (h + top) || x > left + w || x<left ) { alert("mouseout"); } }
/* IE */ if(o.contains(event.toElement ) == false ) alert("mouseout"); } </SCRIPT>
<DIV onmouseout=fun(event,this)>content</DIV> |
需要注意 在取鼠標的值的時候 一定要加上滾動條已經拖動過的內容e.clientY + document.body.scrollTop 如果只是e.clientY得到是個錯誤的值 當然如果高寬都很小 是看不出來問題。 取一個對象的高和寬 也可以使用 clientHeight clientWidth 屬性 以后遇到FF IE不兼容的時候要多看看FF的開發手冊 http://developer.mozilla.org/en/docs/DOM:element.offsetLeft
方案二:(與一相似)
js的onmouseout有很奇怪的一個問題。例如
<div onmouseout="alert(123)">
<a href="#">test</a>
</div>
我們預期只有當鼠標從div中移開的時候才會觸發onmouseout事件,可是,事實上,當我們移到div中的元素時,例如本例中的a標簽時,就會觸發onmousout事件。也就是說,移到對象的子對象上,也算onmouseout了。這往往會讓我們預期的效果達不到。今天的工作就遇到了這個問題。在blueidea上搜了一下,找了解決辦法。兼容IE和FF。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>阿當制作</title>
</head>
<body>
<script type="text/javascript">
function test(obj, e) {
if (e.currentTarget) {
if (e.relatedTarget != obj) {
if (obj != e.relatedTarget.parentNode) {
alert(1);
}
}
} else {
if (e.toElement != obj) {
if (obj != e.toElement.parentNode) {
alert(1);
}
}
}
}
</script>
<div onmouseout="test(this, event)" style="width:100px;height:100px;border:1px #666 solid">
<span style="margin:5px;width:100%;height:100%;border:1px #ff0000 solid">faddsf</span>
</div>
</body>
</html>
今天發現JQ中關于這個問題,已經有了一個好的解決辦法了.呵呵,jquery中定義了一種事件叫做"mouseleave",用這個事件做事件句柄的話,就可以解決這個問題了.越來越發現jquery是個好東西了.
方案三:
,jQuery V1.2.2推薦用bind("mouseleave",function(){})來代替以前的mouseover方法
用bind("mouseenter",function(){})來代替mouseout,同樣也針對以前的hover方法,要看詳細的說明點這個地址:http://docs.jquery.com/Release:jQuery_1.2.2
$(document).ready(function() {
$("#a1").bind("mouseleave", function(){
$('<div style="color:red;">out</div>')
.insertAfter($(this));
});
});
posted @
2012-05-03 16:08 邦 閱讀(235) |
評論 (0) |
編輯 收藏

2012年3月19日
1. 為查詢緩存優化你的查詢
大多數的MySQL服務器都開啟了查詢緩存。這是提高性最有效的方法之一,而且這是被MySQL的數據庫引擎處理的。當有很多相同的查詢被執行了多次的時候,這些查詢結果會被放到一個緩存中,這樣,后續的相同的查詢就不用操作表而直接訪問緩存結果了。
這里最主要的問題是,對于程序員來說,這個事情是很容易被忽略的。因為,我們某些查詢語句會讓MySQL不使用緩存。請看下面的示例:
1 // 查詢緩存不開啟
2 $r = mysql_query("SELECT username FROM user WHERE signup_date >= CURDATE()");
3
4 // 開啟查詢緩存
5 $today = date("Y-m-d");
6 $r = mysql_query("SELECT username FROM user WHERE signup_date >= '$today'");
上面兩條SQL語句的差別就是 CURDATE() ,MySQL的查詢緩存對這個函數不起作用。所以,像 NOW() 和 RAND() 或是其它的諸如此類的SQL函數都不會開啟查詢緩存,因為這些函數的返回是會不定的易變的。所以,你所需要的就是用一個變量來代替MySQL的函數,從而開啟緩存。
2. 當只要一行數據時使用 LIMIT 1
當你查詢表的有些時候,你已經知道結果只會有一條結果,但因為你可能需要去fetch游標,或是你也許會去檢查返回的記錄數。
在這種情況下,加上 LIMIT 1 可以增加性能。這樣一樣,MySQL數據庫引擎會在找到一條數據后停止搜索,而不是繼續往后查少下一條符合記錄的數據。
下面的示例,只是為了找一下是否有“中國”的用戶,很明顯,后面的會比前面的更有效率。(請注意,第一條中是Select *,第二條是Select 1)
01 // 沒有效率的:
02 $r = mysql_query("SELECT * FROM user WHERE country = 'China'");
03 if (mysql_num_rows($r) > 0) {
04 // ...
05 }
06
07 // 有效率的:
08 $r = mysql_query("SELECT 1 FROM user WHERE country = 'China' LIMIT 1");
09 if (mysql_num_rows($r) > 0) {
10 // ...
11 }
3. 千萬不要 ORDER BY RAND()
想打亂返回的數據行?隨機挑一個數據?真不知道誰發明了這種用法,但很多新手很喜歡這樣用。但你確不了解這樣做有多么可怕的性能問題。
如果你真的想把返回的數據行打亂了,你有N種方法可以達到這個目的。這樣使用只讓你的數據庫的性能呈指數級的下降。這里的問題是:MySQL會不得不去執行RAND()函數(很耗CPU時間),而且這是為了每一行記錄去記行,然后再對其排序。就算是你用了Limit 1也無濟于事(因為要排序)
下面的示例是隨機挑一條記錄
1 // 千萬不要這樣做:
2 $r = mysql_query("SELECT username FROM user ORDER BY RAND() LIMIT 1");
3
4 // 這要會更好:
5 $r = mysql_query("SELECT count(*) FROM user");
6 $d = mysql_fetch_row($r);
7 $rand = mt_rand(0,$d[0] - 1);
8
9 $r = mysql_query("SELECT username FROM user LIMIT $rand, 1");
4. 避免 SELECT *
從數據庫里讀出越多的數據,那么查詢就會變得越慢。并且,如果你的數據庫服務器和WEB服務器是兩臺獨立的服務器的話,這還會增加網絡傳輸的負載。
所以,你應該養成一個需要什么就取什么的好的習慣。
1 // 不推薦
2 $r = mysql_query("SELECT * FROM user WHERE user_id = 1");
3 $d = mysql_fetch_assoc($r);
4 echo "Welcome {$d['username']}";
5
6 // 推薦
7 $r = mysql_query("SELECT username FROM user WHERE user_id = 1");
8 $d = mysql_fetch_assoc($r);
9 echo "Welcome {$d['username']}";
5. 永遠為每張表設置一個ID
我們應該為數據庫里的每張表都設置一個ID做為其主鍵,而且最好的是一個INT型的(推薦使用UNSIGNED),并設置上自動增加的AUTO_INCREMENT標志。
就算是你 users 表有一個主鍵叫 “email”的字段,你也別讓它成為主鍵。使用 VARCHAR 類型來當主鍵會使用得性能下降。另外,在你的程序中,你應該使用表的ID來構造你的數據結構。
而且,在MySQL數據引擎下,還有一些操作需要使用主鍵,在這些情況下,主鍵的性能和設置變得非常重要,比如,集群,分區……
在這里,只有一個情況是例外,那就是“關聯表”的“外鍵”,也就是說,這個表的主鍵,通過若干個別的表的主鍵構成。我們把這個情況叫做“外鍵”。比如:有一個“學生表”有學生的ID,有一個“課程表”有課程ID,那么,“成績表”就是“關聯表”了,其關聯了學生表和課程表,在成績表中,學生ID和課程ID叫“外鍵”其共同組成主鍵。
6. 使用 ENUM 而不是 VARCHAR
ENUM 類型是非常快和緊湊的。在實際上,其保存的是 TINYINT,但其外表上顯示為字符串。這樣一來,用這個字段來做一些選項列表變得相當的完美。
如果你有一個字段,比如“性別”,“國家”,“民族”,“狀態”或“部門”,你知道這些字段的取值是有限而且固定的,那么,你應該使用 ENUM 而不是 VARCHAR。
MySQL也有一個“建議”(見第十條)告訴你怎么去重新組織你的表結構。當你有一個 VARCHAR 字段時,這個建議會告訴你把其改成 ENUM 類型。使用 PROCEDURE ANALYSE() 你可以得到相關的建議。
7. 盡可能的使用 NOT NULL
除非你有一個很特別的原因去使用 NULL 值,你應該總是讓你的字段保持 NOT NULL。這看起來好像有點爭議,請往下看。
首先,問問你自己“Empty”和“NULL”有多大的區別(如果是INT,那就是0和NULL)?如果你覺得它們之間沒有什么區別,那么你就不要使用NULL。(你知道嗎?在 Oracle 里,NULL 和 Empty 的字符串是一樣的!)
不要以為 NULL 不需要空間,其需要額外的空間,并且,在你進行比較的時候,你的程序會更復雜。當然,這里并不是說你就不能使用NULL了,現實情況是很復雜的,依然會有些情況下,你需要使用NULL值。
8. 把IP地址存成 UNSIGNED INT
很多程序員都會創建一個 VARCHAR(15) 字段來存放字符串形式的IP而不是整形的IP。如果你用整形來存放,只需要4個字節,并且你可以有定長的字段。而且,這會為你帶來查詢上的優勢,尤其是當你需要使用這樣的WHERE條件:IP between ip1 and ip2。
我們必需要使用UNSIGNED INT,因為 IP地址會使用整個32位的無符號整形。
而你的查詢,你可以使用 INET_ATON() 來把一個字符串IP轉成一個整形,并使用 INET_NTOA() 把一個整形轉成一個字符串IP
9. 拆分大的 DELETE 或 INSERT 語句
如果你需要在一個在線的網站上去執行一個大的 DELETE 或 INSERT 查詢,你需要非常小心,要避免你的操作讓你的整個網站停止相應。因為這兩個操作是會鎖表的,表一鎖住了,別的操作都進不來了。
如果你把你的表鎖上一段時間,比如30秒鐘,那么對于一個有很高訪問量的站點來說,這30秒所積累的訪問進程/線程,數據庫鏈接,打開的文件數,可能不僅僅會讓你泊WEB服務Crash,還可能會讓你的整臺服務器馬上宕機。
所以,如果你有一個大的處理,你定你一定把其拆分,使用 LIMIT 條件是一個好的方法。下面是一個示例:
01 while (1) {
02 //每次只做1000條
03 mysql_query("DELETE FROM logs WHERE log_date <= '2009-11-01' LIMIT 1000");
04 if (mysql_affected_rows() == 0) {
05 // 沒得可刪了,退出!
06 break;
07 }
08 // 每次都要休息一會兒
09 usleep(50000);
10 }
10. 越小的列會越快
對于大多數的數據庫引擎來說,硬盤操作可能是最重大的瓶頸。所以,把你的數據變得緊湊會對這種情況非常有幫助,因為這減少了對硬盤的訪問。
如果一個表只會有幾列罷了(比如說字典表,配置表),那么,我們就沒有理由使用 INT 來做主鍵,使用 MEDIUMINT, SMALLINT 或是更小的 TINYINT 會更經濟一些。
posted @
2012-03-19 14:50 邦 閱讀(503) |
評論 (0) |
編輯 收藏
首先看一下分頁的基本原理:
mysql> explain SELECT * FROM message ORDER BY id DESC LIMIT 10000, 20\G
***************** 1. row **************
id: 1
select_type: SIMPLE
table: message
type: index
possible_keys: NULL
key: PRIMARY
key_len: 4
ref: NULL
rows: 10020
Extra:
1 row in set (0.00 sec)
limit 10000,20的意思掃描滿足條件的10020行,扔掉前面的10000行,返回最后的20行,問題就在這里,如果是limit 100000,100,需要掃描100100行,在一個高并發的應用里,每次查詢需要掃描超過10W行,性能肯定大打折扣。
一種”clue”的做法,給翻頁提供一些”線索”,比如還是SELECT * FROM message ORDER BY id DESC,按id降序分頁,每頁20條,當前是第10頁,當前頁條目id最大的是9527,最小的是9500,如果我們只提供”上一頁”、”下一頁”這樣的跳轉(不提供到第N頁的跳轉),那么在處理”上一頁”的時候SQL語句可以是:
SELECT * FROM message WHERE id > 9527 ORDER BY id ASC LIMIT 20;
處理”下一頁”的時候SQL語句可以是:
SELECT * FROM message WHERE id < 9500 ORDER BY id DESC LIMIT 20;
不管翻多少頁,每次查詢只掃描20行。
缺點是只能提供”上一頁”、”下一頁”的鏈接形式,但是有些人非常喜歡”<上一頁 1 2 3 4 5 6 7 8 9 下一頁>”這樣的鏈接方式,怎么辦呢?
如果LIMIT m,n不可避免的話,要優化效率,只有盡可能的讓m小一下,我們擴展前面的”clue”做法,還是SELECT * FROM message ORDER BY id DESC,按id降序分頁,每頁20條,當前是第10頁,當前頁條目id最大的是9527,最小的是9500,比如要跳到第8頁,我看的SQL語句可以這樣寫:
SELECT * FROM message WHERE id > 9527 ORDER BY id ASC LIMIT 20,20;
跳轉到第13頁:
SELECT * FROM message WHERE id < 9500 ORDER BY id DESC LIMIT 40,20;
原理還是一樣,記錄住當前頁id的最大值和最小值,計算跳轉頁面和當前頁相對偏移,由于頁面相近,這個偏移量不會很大,這樣的話m值相對較小,大大減少掃描的行數。其實傳統的limit m,n,相對的偏移一直是第一頁,這樣的話越翻到后面,效率越差,而上面給出的方法就沒有這樣的問題。
posted @
2012-03-19 14:46 邦 閱讀(855) |
評論 (0) |
編輯 收藏

2012年3月6日
1.1何為maven坐標
Mavne的一大功能是管理項目依賴,為了能自動化的解析任何一個Java構件,maven就必須將它們唯一標識,這就依賴管理的底層基礎—-坐標。
Maven的世界中擁有數量非常巨大的構件,也就是平時用的一些jar、war等文件,在Maven為這些構件引入坐標概念之前,我們無法使用任何一種方式來唯一標識所有這些構件。因此maven定義了這樣組規則:世界上任何一個構件都可以使用Maven坐標唯一標識,maven坐標元素包括groupId、artifactId、version、packaging、classifier。現在,只要我們提供正確的坐標元素,maven就能找到對應的組件。比如說,當需要使用Java5平臺上TestNG的5.8版本時,就告訴Maven:“groupId=org.testng;artifactId=testng;version=5.9;classifier=jdk15
”,maven就會從倉庫中尋找相應的構件供我們使用。Maven是從哪里下載構件的呢?maven內置了一個中央倉庫的地址(http://repol.maven.org/maven2),該中央倉庫包含了世界上大部分流行的開源項目組件,maven會在需要的時候去那里下載。
1.2坐標詳解
先看一組坐標定義,如下:
<groupId>org.sonatype.nexus</groupId>
<artifactId>nexus-indexer</artifactId>
<version>2.0.0</version>
<packaging>jar</packaging>
這是nexus-indexer的坐標定義,nexus-indexer是一個對maven倉庫編纂索引并提供搜索功能的類庫,它是Nexus項目的一個子模塊。下面解釋一下各個坐標元素:
- groupId:定義當前maven項目隸屬的實際目錄。首先,maven項目和實際項目不一定是一對一的關系。其次,groupId不應該對應項目隸屬的組織或公司。最后,groupId的表示方式與Java包名的表示方式類似,通常與域名反向一一對應。
- artifactId:該元素定義實際項目中的一個Maven項目(模塊),推薦的做法是使用實際項目名稱作為前綴。
- version:該元素定義Maven項目當前所處版本。
- packaging:該元素定義Maven項目的打包方式。首先。打包方式通常與所生成構件的文件擴展名對應。其次,打包方式會影響到構件的生命周期,比如jar打包和war打包會使用不同的命令。最后,當不定義packaging的時候,Maven會使用默認值jar。
- classifier:該元素用來幫助定義構建輸出的一些附屬構件。附屬構件與主構件對應,如上例中的主構件是nexus-indexer-2.0.0.jar,該項目可能會通過使用一些插件生成如nexus-indexer-2.0.0-javadoc.jar、nexus-indexer-2.0.0-sources.jar這樣一些附屬構件。
上述五個元素中,groupId、artifactId、version是必須定義的,packaging是可選的(默認為jar),而classifier是不能直接定義的。
理解清楚Maven坐標之后,我們就能開始討論Maven的依賴管理了。
1.3依賴的配置
一個依賴聲明可以包含如下的一些元素:
<project>
…
<dependencies>
<dependency>
<groupId>…</groupId>
<artifactId>…</artifactId>
<version>…</version>
<type>…</type>
<scope>…</scope>
<optional>…</optional>
<exclusions>
<exclusion>
…
</exclusion>
…
</exclusions>
</dependency>
…
</dependencies>
…
</project>
Dependencies可以包含一個或者多個dependency元素,以聲明一個或者多個項目依賴。每個依賴可以包含的元素有:
- groupId、artifactId和version:依賴的基本坐標,對于任何一個依賴來說,基本坐標是最重要的,Maven根據坐標才能找到需要的依賴。
- type:依賴的類型,對應于項目坐標定義的packaging。大部分情況下,該元素不必聲明,其默認值為jar。
- scope:依賴的范圍。
- optional:標記依賴是否可選。
- exclusions:用來排除傳遞性依賴。
1.4依賴范圍
首先需要知道,Maven在編譯項目主代碼的時候需要使用一套classpath。其次,Maven在編譯和執行測試的時候會使用另外一套classpath。最后,實際運行Maven項目的時候,又會使用一套classpath。
依賴范圍就是用來控制依賴與這三種classpath(編譯classpaht、測試classpath、運行classpath)的關系,Maven有以下幾種依賴范圍:
- compile:編譯依賴范圍。如果沒有指定,就會默認使用該依賴范圍。使用此依賴范圍的Maven依賴,對于編譯、測試、運行三種classpath都有效。
- test:測試依賴范圍。使用此范圍依賴的Maven依賴,只對測試classpath有效,在編譯主代碼或者運行項目的時候將無法使用此類依賴
- provided:已提供依賴范圍。使用此依賴范圍的Maven依賴,對于編譯和測試classpath都有效,但是在運行時無效
- runtime:運行時依賴范圍。使用此依賴范圍的Maven依賴,對于測試和運行classpath有效,但在編譯主代碼時無效。
- system:系統依賴范圍。該依賴與三種classpath的關系,和provided依賴范圍完全一致。但是,使用system范圍的依賴時必須通過systemPath元素顯式地指定依賴文件路徑。由于此類依賴不是通過Maven倉庫解析的,而且往往與本機系統綁定,可能造成構件的不可移植,因此應該謹慎使用。SystemPath元素可以引用環境變量,如:
<dependency>
<groupId>javax.sql</groupId>
<artifactId>jdbc-stdext</artifactId>
<version>2.0</version>
<scope>system</scope>
<systemPath>${java.home}/lib/tr.jar</systemPath>
</dependency>
- import:導入依賴范圍。該依賴范圍不會對三種classpath產生實際的影響。
1.5傳遞性依賴
何為傳遞性依賴?現在舉一個例子:項目中有一個compile范圍的spring-core依賴,spring-core有一個compile范圍的commons-logging依賴,那么commons-logging就會成為該項目的compile范圍依賴,commons-logging是該項目的一個傳遞依賴。
Maven會解析各個直接依賴的POM,將那些必要的間接依賴以傳遞性依賴的形式引入到當前項目中。
依賴范圍不僅可以控制依賴與三種classpath的關系,還對傳遞性依賴產生影響。假設A依賴于B,B依賴于C,我們說A對于B是第一直接依賴,B對于C是第二直接依賴,A對于C是傳遞性依賴。第一直接依賴的范圍和第二直接依賴的范圍決定了傳遞性依賴的范圍,如表,最左邊一列表示第一直接依賴范圍,最上面一行表示第二直接依賴范圍,中間的交叉單元格則表示傳遞性依賴范圍
|
compile |
test |
provided |
runtime |
compile |
compile |
—— |
—— |
runtime |
test |
test |
—— |
—— |
test |
provided |
provided |
—— |
provided |
provided |
runtime |
runtime |
—— |
—— |
runtime |
仔細觀察一下表可以發現這樣的規律:當第二直接依賴的范圍是compile的時候,傳遞性依賴的范圍與第一直接依賴的范圍一致;當第二直接依賴的范圍是test的時候,依賴不會得以傳遞;當第二直接依賴的范圍為provided的時候,只傳遞第一直接依賴的范圍為provided的依賴,且傳遞性依賴的范圍同樣為provided;當第二直接依賴的范圍是runtime的時候,傳遞性依賴的范圍與第一直接依賴的范圍一致,但compile除外,此時傳遞性依賴的范圍為runtime。
1.6依賴調解
Maven引入的傳遞性依賴機制,一方面大大簡化和方便了依賴聲明,另一方面,大部分情況下我們只需要關心項目的直接依賴是什么,而不用考慮這些直接依賴會引入什么傳遞依賴。但是有時候,當傳遞性依賴造成問題的時候,我們就需要清楚的知道該傳遞性依賴是從哪條依賴路徑引入的。
例如A->B->C->X(1.0)、A->D->X(2.0),X是A的傳遞性依賴,但是兩條依賴路徑上有兩個版本的X,那個X會被Maven解析使用呢?Maven依賴調解的第一原則是:路徑最近者優先。依賴調解第一原則不能解決所有問題,比如:A->B->Y(1.0)、A->C->Y(2.0),Y(1.0)和Y(2.0)的依賴路徑長度是一樣的,到底誰會解析?在Maven2.0.9開始,Maven定義了依賴調解的第二原則:第一聲明者優先。
posted @
2012-03-06 09:24 邦 閱讀(1320) |
評論 (0) |
編輯 收藏