公司的項目中用到了這個新消息提示的效果,主要用于提示用戶有新消息。具體實現(xiàn)代碼如下:
01 |
var newMessageRemind={ |
02 |
_step: 0, |
03 |
_title: document.title, |
04 |
_timer: null , |
05 |
//顯示新消息提示 |
06 |
show: function (){ |
07 |
var temps = newMessageRemind._title.replace( "【 】" , "" ).replace( "【新消息】" , "" ); |
08 |
newMessageRemind._timer = setTimeout( function () { |
09 |
newMessageRemind.show(); |
10 |
//這里寫Cookie操作 |
11 |
newMessageRemind._step++; |
12 |
if (newMessageRemind._step == 3) { newMessageRemind._step = 1 }; |
13 |
if (newMessageRemind._step == 1) { document.title = "【 】" + temps }; |
14 |
if (newMessageRemind._step == 2) { document.title = "【新消息】" + temps }; |
15 |
}, 800); |
16 |
return [newMessageRemind._timer, newMessageRemind._title]; |
17 |
}, |
18 |
//取消新消息提示 |
19 |
clear: function (){ |
20 |
clearTimeout(newMessageRemind._timer ); |
21 |
document.title = newMessageRemind._title; |
22 |
//這里寫Cookie操作 |
23 |
} |
24 |
|
25 |
}; |
調(diào)用顯示新消息提示:newMessageRemind.show();
調(diào)用取消新消息提示:newMessageRemind.clear();
查看demo:http://www.css88.com/demo/newMessageRemind/
另:單純的這個代碼會出現(xiàn)這么一個問題:
就是當你打開一個站點很多張頁面的時候,如過有新消息,那么所有頁面都會不停的閃,當你查看消息后其他頁面仍會提示。
我們公司是通過使用Cookie的方式解決的,當查看新消息后所有標題閃動的頁面將全部取消提示。
聲明: 本文采用 BY-NC-SA 協(xié)議進行授權(quán) | WEB前端開發(fā)
轉(zhuǎn)載請注明轉(zhuǎn)自《標題欄新消息提示效果》
今天一個剛學js的朋友給了我一段代碼問為什么方法不執(zhí)行,代碼如下:
1 |
function makefunc(x) { |
2 |
return function (){ |
3 |
return x; |
4 |
} |
5 |
} |
6 |
alert(makefunc(0)); |
其實不是不執(zhí)行,只是朋友的意思這里alert出來的應(yīng)該是“0”,而不是function (){return x;}。
不是腳本寫錯了,只是沒搞懂return,從當前函數(shù)退出,并從那個函數(shù)返回一個值。如果返回的是一個函數(shù),那么返回的也是函數(shù)本身。
可以這樣修改上面的代碼,就是alert(makefunc(0)()):
1 |
function makefunc(x) { |
2 |
return ( function (){ |
3 |
return x; |
4 |
})(); |
5 |
} |
6 |
alert(makefunc(0)()); |
如果要返回函數(shù)執(zhí)行的結(jié)果那么首先要讓這個函數(shù)執(zhí)行,例如:
1 |
function makefunc(x) { |
2 |
return ( function (){ |
3 |
return x; |
4 |
})(); |
5 |
} |
6 |
alert(makefunc(0)); |
這里有一個匿名函數(shù),
1 |
( function (){ |
2 |
return x; |
3 |
})(); |
在第一個括號內(nèi)是匿名函數(shù),第二個括號用于調(diào)用該匿名函數(shù),您可以在第二個括號中傳入所需的參數(shù)。例如:
1 |
( function ( x , y){ |
2 |
alert( x + y); |
3 |
})(2 ,3 ); |
聲明: 本文采用 BY-NC-SA 協(xié)議進行授權(quán) | WEB前端開發(fā)
轉(zhuǎn)載請注明轉(zhuǎn)自《return閉包函數(shù)》
哇,居然頁面倒過來了,頁頭和頁腳翻了一個根頭,其實我以前還在想,背景圖片是不是可以這樣呢,今天至少在頁面上看到了。
真好奇,立即啟動火狐看了一下,哈哈,找到原因了,馬上在試了一下IE,搞定,也OK,哈哈,如果不知道的同學們,我想你們也想知道這是怎么回事吧。
其實就是這個東東在做怪。。。
-moz-transform: rotate(180deg);
-webkit-transform: rotate(180deg);
filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2);
三行代碼,分別用來支持moz,webkit和IE的內(nèi)核的瀏覽器。
我們看看克軍是怎么做的呢,他使用了JS,讓代碼立即執(zhí)行的方式。。給頁面增加一個<sytle>和相應(yīng)的樣式,并且為body增加相應(yīng)的class.下面我將他的js代碼貼出來。嘿嘿!
;(function(){
var d = document, n = d.createElement('style'), r='.flip { -moz-transform: rotate(180deg);-webkit-transform: rotate(180deg);filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2); }';
n.type = 'text/css';
if(n.styleSheet)
{
n.styleSheet.cssText = r;
}
else
{
n.appendChild(d.createTextNode(r))
}
d.getElementsByTagName('head')[0].appendChild(n);
d.body.className += ' flip';
}
為了使用document方便,他把document傳給了變量d,建立了一個style標簽為變量n,把樣式的內(nèi)容傳給
了變量r,克軍的命名都很簡潔。
在為這個n其實就是style標簽了type這個屬性。
下面是進行判斷頁面中style有不有屬性,如果沒有,直接將r,也就是樣式的內(nèi)容放入n中。
如果有,就得使用建立文本并且追加的方式。
我不知道我的解釋對不對哈,反正大概意思就是這樣的,歡迎指正。
一切準備就緒以后,就將n添加到head中去,在將class增加到body上,這樣頁面一加載。。。。你的頁面就會被旋轉(zhuǎn)180度,當然你可以旋轉(zhuǎn)90度,10度,數(shù)字是可以調(diào)整的喲。
2010.09.30 今天在使用過程中,最后發(fā)現(xiàn),原來ie只支持4個值,分別旋轉(zhuǎn)值可以是1,2,3或4。這些數(shù)字分別代表90,180,270,或360度旋轉(zhuǎn)。
通過許多實際項目,個人認為一個完備的前端產(chǎn)品開發(fā)團隊,必須擁有如下的人才配備,也希望大家補充:
具體技能描述:
也許表面上看,這跟Server端技能無關(guān),但我覺得有好的Server端的意識,一定會有所幫助(當然不可能解決所有的問題)。畢竟信息結(jié)構(gòu)和數(shù)據(jù)庫是密切相關(guān)的,而Server是連接數(shù)據(jù)庫的唯一渠道(至少大多數(shù)B/S應(yīng)用是如此)。掌握Server端的基本技能,對于同邏輯層開發(fā)人員設(shè)計接口是非常重要的。而且HTML表現(xiàn)層在開發(fā)時與數(shù)據(jù)的分離,也與Server端的各種模板技術(shù)有關(guān)。例如PHP中的Smarty模板(我曾經(jīng)用的)、jsp的model2概念等等。HTML結(jié)構(gòu)如何設(shè)計,如何讓HTML重用,甚至在HTML層進行OOP的開發(fā)(我現(xiàn)在在新產(chǎn)品線中設(shè)計的前端開發(fā)流程),都需要Server端的支持。最起碼,你要告訴php程序員你需要什么。如果你完全對PHP一無所知的話,那也無從談起了。
此外,對于創(chuàng)業(yè)團隊,往往人手非常有限。為了讓運營成本降到最低,所有的技術(shù)人員都有義務(wù)對Server端技術(shù)有所了解。如果為了修改一個網(wǎng)頁的標題還要跑去喊PHP程序員連接Remote Server的話,那實在是增加了整個公司的運營成本。
總結(jié):我認為,可以不了解技術(shù)細節(jié),但應(yīng)該知道原理,最好能掌握一兩套設(shè)計思想(畢竟數(shù)據(jù)邏輯都在這里走,光看HTML和JavaScript,對人的見識還是有局限的,這種局限限制了我自己很久的時間),那將是一比寶貴的財富。
看到很多朋友留言說前端工程師沒前途,我在想,同時掌握移動設(shè)備的技能是否也是拓展前途的一個必要性?這里再多說幾句,關(guān)于技術(shù)人員的前途,目前在國內(nèi)確實得用”慘淡”來形容。浮躁的氛圍讓技術(shù)人才往往過早放棄了自己的技術(shù)生涯,而爾虞我詐的整體道德水平也讓單純的技術(shù)人員痛不欲生(我身邊太多了,恩,不說具體細節(jié)了,呵呵)。
作為一個技術(shù)人員,開發(fā)人員,在保持純粹地敬業(yè)心態(tài)(這是前提,這么沒有,啥也別談)外,更要學會如何保護自己,如何壯大自身,社會不會同情你,只有你自己才能保護你自己。
本文來自:http://www.awflasher.com/blog/archives/906
ThickBox運行需要的文件
官方下載:
Download thickbox.js or thickbox-compressed.js, ThickBox.css, and the loading graphic (loadingAnimation.gif) to your local machine (or cut and paste the code from the tabs). Along with these three files, a copy of the jQuery JavaScript library is needed. For this site, and ThickBox, I am using the compressed version of jQuery.
首先在 HTML 文件的 head中導入jquery.js 和thickbox.js文件,導入 thickbox.css 文件;并且jquery.js 文件放在前面:
<script src="../Scripts/jquery-latest.pack.js" mce_src="Scripts/jquery-latest.pack.js" type="text/javascript"></script> <script src="../Scripts/thickbox.js" mce_src="Scripts/thickbox.js" type="text/javascript"></script> <link href="../Styles/thickbox.css" mce_href="Styles/thickbox.css" rel="stylesheet" type="text/css" />
最后你只要給元素添加 class=”thickbox” 屬性就可以開始用 thickbox
實現(xiàn)了一張圖片的彈出展示功能:
<a href="”bg.jpg”" mce_href="”bg.jpg”" class=”thickbox” ><img src="”bg.jpg”" mce_src="”bg.jpg”" alt=”圖片”/></a> //只需要指定圖片的class為thickbox彈出框使用方法:
<a href="Default.aspx?keepThis=true&TB_iframe=true&height=400&width=500" title="主頁" class="thickbox" </a> <input onclick="<web.path:path/>/bannedUserList!unBannedUserList?height=400&width=800&inlineId=myOnPageContent" title="彈出層" class="thickbox" type="button" value="Ban Another" /> //內(nèi)嵌內(nèi)容 <input alt="#TB_inline?height=300&width=400&inlineId=myOnPageContent" title="標題" class="thickbox" type="button" value="Show" /> <a href="#TB_inline?height=155&width=300&inlineId=hiddenModalContent&modal=true" class="thickbox">顯示隱藏內(nèi)容a> //遮罩層 URL后面加?KeepThis=true&TB_iframe=true&height=400&width=600 參數(shù)字符串中加 modal=true ?KeepThis=true&TB_iframe=true&height=400&width=600&modal=true 這樣當關(guān)閉ThickBox時會調(diào)用ThickBox iframe (self.parent.tb_remove())內(nèi)部的一個tb_remove()函數(shù) 所有其他參數(shù)字符都必須在TB_iframe 參數(shù)之前。URL中所有"TB" 之后的將被移除。 <a href="index.html?keepThis=true&TB_iframe=true&height=250&width=400" title="標題" class="thickbox">打開一個頁面</a> <a href="index.html?keepThis=true&TB_iframe=true&height=300&width=500" title="標題" class="thickbox">打開一個頁面</a> <a href="index.html?placeValuesBeforeTB_=savedValues&TB_iframe=true&height=200&width=300&modal=true" title="標題" class="thickbox">打開一個頁面</a>
自定義設(shè)置:
1、彈出窗口(div)右上角的關(guān)閉按鈕為顯示為"close or esc key",而不是中文的; 如果想把它變成[X]或"關(guān)閉"應(yīng)該怎么來辦呢?
將thickbox.js文件打開,查找關(guān)鍵字"or esc key",將其刪除,并將前面的close更改為[X]或"關(guān)閉",然后把文件另存為UTF-8格式,如果不保存為UTF-8的話,將會出現(xiàn)亂碼。2、thickbox 彈出層的遮住層透明度修改
打開thickbox.css查找.TB_overlayBG 進行更改
.TB_overlayBG { background-color:#000; filter:alpha(opacity=75); -moz-opacity: 0.75; opacity: 0.75; }
3、關(guān)閉層:如果我們需要自己添加一個關(guān)閉按鈕或者圖片可以使用:
onclick="self.parent.tb_remove();"
4、關(guān)閉層刷新父頁面,修改關(guān)閉方法 :
function tb_remove() { $("#TB_imageOff").unbind("click"); $("#TB_closeWindowButton").unbind("click"); $("#TB_window").fadeOut("fast",function(){$('#TB_window,#TB_overlay,#TB_HideSelect').trigger("unload").unbind().remove();}); $("#TB_load").remove(); if (typeof document.body.style.maxHeight == "undefined") {//if IE 6 $("body","html").css({height: "auto", width: "auto"}); $("html").css("overflow",""); } document.onkeydown = ""; document.onkeyup = ""; //刷新父頁面,未指定 window.location.reload(); return false; }
5、thickbox插件默認情況是點擊灰色的遮罩層就會關(guān)閉取消
把兩個$("#TB_overlay").click(tb_remove);去掉就可以取消掉
6、updatepanel回發(fā)后thickbox失效的解決方法
只需把以下代碼粘貼至頁面中就OK了。 <script type="text/javascript" language="javascript"> function pageLoad() { var isAsyncPostback = Sys.WebForms.PageRequestManager.getInstance().get_isInAsyncPostBack(); if (isAsyncPostback) { tb_init('a.thickbox, area.thickbox, input.thickbox'); } } </script>
<a >OECSPACE</a>
<a href="boxs.html?keepThis=true&TB_iframe=true&height=100&width=220&modal=true" title="ThickBox 3.1:modal=true表示禁用title,去掉即可顯示title及可自動關(guān)閉" class="thickbox">Open iFrame Modal</a>
<a href="box.html?height=350&width=350&modal=true" title="ThickBox 3.1:Ajax載入,頁面無法查看源代碼" class="thickbox">Example</a>
<a href="#TB_inline?height=200&width=300&inlineId=hiddenModalContent&modal=true" title="ThickBox 3.1:鏈接顯示隱藏層" class="thickbox">Show hidden modal content</a>
<div id="hiddenModalContent" style="display:none">
<p>ThickBox hidden modal content. Click to hide.</p>
<p style="text-align:center"><input type="submit" value=" O K " onclick="tb_remove()" /></p>
</div>
<input alt="#TB_inline?height=150&width=400&inlineId=myOnPageContent " title="ThickBox 3.1:按鈕顯示隱藏層" class="thickbox" type="button" value="Show" />
<div id="myOnPageContent" style="display:none">
<p>ThickBox hidden modal content.Auto Hide.</p>
</div>
<a href="images/plant1.jpg" title="plant" class="thickbox"><img src="images/plant1_t.jpg" alt="ThickBox 3.1" /></a>
<a href="images/plant1.jpg" title="plant1" class="thickbox" rel="gallery-plants"><img src="images/plant1_t.jpg" alt="ThickBox 3.1 1" /></a>
<a href="images/plant2.jpg" title="plant2" class="thickbox" rel="gallery-plants"><img src="images/plant2_t.jpg" alt="ThickBox 3.1 2" /></a>
<a href="images/plant3.jpg" title="plant3" class="thickbox" rel="gallery-plants"><img src="images/plant3_t.jpg" alt="ThickBox 3.1 3" /></a>
$("#TB_window").append("<a href='' id='TB_ImageOff' title='Close'><img id='TB_Image' src='"+url+"' width='"+imageWidth+"' height='"+imageHeight+"' alt='"+caption+"'/></a>" + "<div id='TB_caption'>"+caption+"<div id='TB_secondLine'>" + TB_imageCount + TB_PrevHTML + TB_NextHTML + "</div></div><div id='TB_closeWindow'><a href='#' id='TB_closeWindowButton' title='Close'>close</a> or Esc Key</div>");
$("#TB_window").append("<img id='TB_Image' src='"+url+"' width='"+imageWidth+"' height='"+imageHeight+"' alt='"+caption+"'/>" + "<div id='TB_caption'>"+caption+"<div id='TB_secondLine'>" + TB_imageCount + TB_PrevHTML + TB_NextHTML + "</div></div><div id='TB_closeWindow'><a href='#' id='TB_closeWindowButton' title='Close'>close</a> or Esc Key</div>");
//最基本的做法
private int prime1(int num) {
int i = 0, s = 0;
label1: for (int n = 2; n <= num; n++) {
for (int m = 2; m * m <= n; m++) {
if (n % m == 0)
continue label1;
}
s++;
i++;
//System.out.println("第" + i + "個素數(shù)是:" + n);
}
return s;
}
//6N±1法
private int prime2(int num){
int i = 0, s = 0;
for(int n = 2; n <=3; n ++){
s++;
i++;
//System.out.println("第" + i + "個素數(shù)是:" + n);
}
label1: for(int n = 1; ; n++) {
label2: for (int m = 0; m <= 1; m++) {
int tmp = 2 * (3 * n + m) - 1;
if (tmp > num)
break label1;
for(int k = 2; k * k <= tmp; k++)
if (tmp % k == 0)
if (m == 0)
continue label2;
else
continue label1;
s++;
i++;
//System.out.println("第" + i + "個素數(shù)是:" + tmp);
}
}
return s;
}
public static void main(String args[]) {
Scanner in = new Scanner(System.in);
int num = in.nextInt();
long start = System.currentTimeMillis();
int sum = new Prime().prime1(num);
long end = System.currentTimeMillis();
System.out.println("方法一共" + sum + "個素數(shù)");
System.out.println("用時:" + (end - start));
start = System.currentTimeMillis();
sum = new Prime().prime2(num);
end = System.currentTimeMillis();
System.out.println("方法二共" + sum + "個素數(shù)");
System.out.println("用時:" + (end - start));
}
}
輸入:1000000
運行結(jié)果:
方法一共78498個素數(shù)
用時:3434
方法二共78498個素數(shù)
用時:3453
(看來基本方法比6N±1法還要更快些,奇怪了,我的程序?qū)懙臎]什么問題阿)
【1】求10000以內(nèi)的所有素數(shù)。
素數(shù)是除了1和它本身之外再不能被其他數(shù)整除的自然數(shù)。由于找不到一個通項公式來表示所有的素數(shù),所以對于數(shù)學家來說,
素數(shù)一直是一個未解之謎。像著名的
哥德巴赫猜想、孿生素數(shù)猜想,幾百年來不知吸引了世界上多少優(yōu)秀的數(shù)學家。盡管他們苦心鉆研,嘔心瀝血,但至今仍然未見分曉。
自從有了計算機之后,人們借助于計算機的威力,已經(jīng)找到了2216091以內(nèi)的所有素數(shù)。
求素數(shù)的方法有很多種,最簡單的方法是根據(jù)素數(shù)的定義來求。對于一個自然數(shù)N,用大于1小于N的各個自然數(shù)都去除一下N,如果都除不盡,則N為素數(shù),否則N為合數(shù)。
但是,如果用素數(shù)定義的方法來編制計算機程序,它的效率一定是非常低的,其中有許多地方都值得改進。
第一,對于一個自然數(shù)N,只要能被一個非1非自身的數(shù)整除,它就肯定不是素數(shù),所以不
必再用其他的數(shù)去除。
第二,對于N來說,只需用小于N的素數(shù)去除就可以了。例如,如果N能被15整除,實際
上就能被3和5整除,如果N不能被3和5整除,那么N也決不會被15整除。
第三,對于N來說,不必用從2到N一1的所有素數(shù)去除,只需用小于等于√N(根號N)的所有素數(shù)去除就可以了。這一點可以用反證法來證明:
如果N是合數(shù),則一定存在大于1小于N的整數(shù)d1和d2,使得N=d1×d2。
如果d1和d2均大于√N,則有:N=d1×d2>√N×√N=N。
而這是不可能的,所以,d1和d2中必有一個小于或等于√N。
基于上述分析,設(shè)計算法如下:
(1)用2,3,5,7逐個試除N的方法求出100以內(nèi)的所有素數(shù)。
(2)用100以內(nèi)的所有素數(shù)逐個試除的方法求出10000以內(nèi)的素數(shù)。
首先,將2,3,5,7分別存放在a[1]、a[2]、a[3]、a[4]中,以后每求出一個素數(shù),只要不大于100,就依次存放在A數(shù)組中的一個單元
中。當我們求100—10000之間的素數(shù)時,可依次用a[1]-a[2]的素數(shù)去試除N,這個范圍內(nèi)的素數(shù)可以不保存,直接打印。
【2】用篩法求素數(shù)。
簡單介紹一下厄拉多塞篩法。厄拉多塞是一位古希臘數(shù)學家,他在尋找素數(shù)時,采用了一種與眾不同的方法:先將2-N的各數(shù)寫在紙上:
在2的上面畫一個圓圈,然后劃去2的其他倍數(shù);第一個既未畫圈又沒有被劃去的數(shù)是3,將它畫圈,再劃去3的其他倍數(shù);現(xiàn)在既未畫圈又沒有被劃去的第一個數(shù)
是5,將它畫圈,并劃去5的其他倍數(shù)……依次類推,一直到所有小于或等于N的各數(shù)都畫了圈或劃去為止。這時,表中畫了圈的以及未劃去的那些數(shù)正好就是小于
N的素數(shù)。
這很像一面篩子,把滿足條件的數(shù)留下來,把不滿足條件的數(shù)篩掉。由于這種方法是厄拉多塞首先發(fā)明的,所以,后人就把這種方法稱作厄拉多塞篩法。
在計算機中,篩法可以用給數(shù)組單元置零的方法來實現(xiàn)。具體來說就是:首先開一個數(shù)組:a[i],i=1,2,3,…,同時,令所有的數(shù)組元素都等于下標
值,即a[i]=i,當i不是素數(shù)時,令a[i]=0
。當輸出結(jié)果時,只要判斷a[i]是否等于零即可,如果a[i]=0,則令i=i+1,檢查下一個a[i]。
篩法是計算機程序設(shè)計中常用的算法之一。
【3】用6N±1法求素數(shù)。
任何一個自然數(shù),總可以表示成為如下的形式之一:
6N,6N+1,6N+2,6N+3,6N+4,6N+5 (N=0,1,2,…)
顯然,當N≥1時,6N,6N+2,6N+3,6N+4都不是素數(shù),只有形如6N+1和6N+5的自然數(shù)有可能是素數(shù)。所以,除了2和3之外,所有的素數(shù)都可以表示成6N±1的形式(N為自然數(shù))。
根據(jù)上述分析,我們可以構(gòu)造另一面篩子,只對形如6 N±1的自然數(shù)進行篩選,這樣就可以大大減少篩選的次數(shù),從而進一步提高程序的運行效率和速度。
在程序上,我們可以用一個二重循環(huán)實現(xiàn)這一點,外循環(huán)i按3的倍數(shù)遞增,內(nèi)循環(huán)j為0-1的循環(huán),則2(i+j)-1恰好就是形如6N±1的自然數(shù)。
http://www.tkk7.com/renyangok/archive/2006/11/20/82278.html
第三,Static Nested Class 和 Inner Class的不同,說得越多越好(面試題有的很籠統(tǒng))。
第四,&和&&的區(qū)別。
第五,HashMap和Hashtable的區(qū)別。
第六,Collection 和 Collections的區(qū)別。
第七,什么時候用assert。
第八,GC是什么? 為什么要有GC?
第九,String s = new String("xyz");創(chuàng)建了幾個String Object?
第十,Math.round(11.5)等於多少? Math.round(-11.5)等於多少?
第十一,short s1 = 1; s1 = s1 + 1;有什么錯? short s1 = 1; s1 += 1;有什么錯?
第十二,sleep() 和 wait() 有什么區(qū)別?
第十三,Java有沒有g(shù)oto?
第十四,數(shù)組有沒有l(wèi)ength()這個方法? String有沒有l(wèi)ength()這個方法?
第十五,Overload和Override的區(qū)別。Overloaded的方法是否可以改變返回值的類型?
第十六,Set里的元素是不能重復的,那么用什么方法來區(qū)分重復與否呢? 是用==還是equals()? 它們有何區(qū)別?
第十七,給我一個你最常見到的runtime exception。
第十八,error和exception有什么區(qū)別?
第十九,List, Set, Map是否繼承自Collection接口?
第二十,abstract class和interface有什么區(qū)別?
第二十一,abstract的method是否可同時是static,是否可同時是native,是否可同時是synchronized?
第二十二,接口是否可繼承接口? 抽象類是否可實現(xiàn)(implements)接口? 抽象類是否可繼承實體類(concrete class)?
第二十三,啟動一個線程是用run()還是start()?
第二十四,構(gòu)造器Constructor是否可被override?
第二十五,是否可以繼承String類?
第二十六,當一個線程進入一個對象的一個synchronized方法后,其它線程是否可進入此對象的其它方法?
第二十七,try {}里有一個return語句,那么緊跟在這個try后的finally {}里的code會不會被執(zhí)行,什么時候被執(zhí)行,在return前還是后?
第二十八,編程題: 用最有效率的方法算出2乘以8等於幾?
第二十九,兩個對象值相同(x.equals(y) == true),但卻可有不同的hash code,這句話對不對?
第三十,當一個對象被當作參數(shù)傳遞到一個方法后,此方法可改變這個對象的屬性,并可返回變化后的結(jié)果,那么這里到底是值傳遞還是引用傳遞?
第三十一,swtich是否能作用在byte上,是否能作用在long上,是否能作用在String上?
第三十二,編程題: 寫一個Singleton出來。
以下是答案
第一,談?wù)刦inal, finally, finalize的區(qū)別。
final—修飾符(關(guān)鍵字)如果一個類被聲明為final,意味著它不能再派生出新的子類,不能作為父類被繼承。因此一個類不能既被聲明為 abstract的,又被聲明為final的。將變量或方法聲明為final,可以保證它們在使用中不被改變。被聲明為final的變量必須在聲明時給定初值,而在以后的引用中只能讀取,不可修改。被聲明為final的方法也同樣只能使用,不能重載
finally—再異常處理時提供 finally 塊來執(zhí)行任何清除操作。如果拋出一個異常,那么相匹配的 catch 子句就會執(zhí)行,然后控制就會進入 finally 塊(如果有的話)。
finalize—方法名。Java 技術(shù)允許使用 finalize() 方法在垃圾收集器將對象從內(nèi)存中清除出去之前做必要的清理工作。這個方法是由垃圾收集器在確定這個對象沒有被引用時對這個對象調(diào)用的。它是在 Object 類中定義的,因此所有的類都繼承了它。子類覆蓋 finalize() 方法以整理系統(tǒng)資源或者執(zhí)行其他清理工作。finalize() 方法是在垃圾收集器刪除對象之前對這個對象調(diào)用的。
第二,Anonymous Inner Class (匿名內(nèi)部類) 是否可以extends(繼承)其它類,是否可以implements(實現(xiàn))interface(接口)?
匿名的內(nèi)部類是沒有名字的內(nèi)部類。不能extends(繼承) 其它類,但一個內(nèi)部類可以作為一個接口,由另一個內(nèi)部類實現(xiàn)。
第三,Static Nested Class 和 Inner Class的不同,說得越多越好(面試題有的很籠統(tǒng))。
Nested Class (一般是C++的說法),Inner Class (一般是JAVA的說法)。Java內(nèi)部類與C++嵌套類最大的不同就在于是否有指向外部的引用上。具體可見http: //www.frontfree.net/articles/services/view.asp?id=704&page=1
注: 靜態(tài)內(nèi)部類(Inner Class)意味著1創(chuàng)建一個static內(nèi)部類的對象,不需要一個外部類對象,2不能從一個static內(nèi)部類的一個對象訪問一個外部類對象
第四,&和&&的區(qū)別。
&是位運算符。&&是布爾邏輯運算符。
第五,HashMap和Hashtable的區(qū)別。
都屬于Map接口的類,實現(xiàn)了將惟一鍵映射到特定的值上。
HashMap 類沒有分類或者排序。它允許一個 null 鍵和多個 null 值。
Hashtable 類似于 HashMap,但是不允許 null 鍵和 null 值。它也比 HashMap 慢,因為它是同步的。
第六,Collection 和 Collections的區(qū)別。
Collections是個java.util下的類,它包含有各種有關(guān)集合操作的靜態(tài)方法。
Collection是個java.util下的接口,它是各種集合結(jié)構(gòu)的父接口。
第七,什么時候用assert。
斷言是一個包含布爾表達式的語句,在執(zhí)行這個語句時假定該表達式為 true。如果表達式計算為 false,那么系統(tǒng)會報告一個 AssertionError。它用于調(diào)試目的:
assert(a > 0); // throws an AssertionError if a < = 0
斷言可以有兩種形式:
assert Expression1 ;
assert Expression1 : Expression2 ;
Expression1 應(yīng)該總是產(chǎn)生一個布爾值。
Expression2 可以是得出一個值的任意表達式。這個值用于生成顯示更多調(diào)試信息的 String 消息。
斷言在默認情況下是禁用的。要在編譯時啟用斷言,需要使用 source 1.4 標記:
javac -source 1.4 Test.java
要在運行時啟用斷言,可使用 -enableassertions 或者 -ea 標記。
要在運行時選擇禁用斷言,可使用 -da 或者 -disableassertions 標記。
要系統(tǒng)類中啟用斷言,可使用 -esa 或者 -dsa 標記。還可以在包的基礎(chǔ)上啟用或者禁用斷言。
可以在預計正常情況下不會到達的任何位置上放置斷言。斷言可以用于驗證傳遞給私有方法的參數(shù)。不過,斷言不應(yīng)該用于驗證傳遞給公有方法的參數(shù),因為不管是否啟用了斷言,公有方法都必須檢查其參數(shù)。不過,既可以在公有方法中,也可以在非公有方法中利用斷言測試后置條件。另外,斷言不應(yīng)該以任何方式改變程序的狀態(tài)。
第八,GC是什么? 為什么要有GC? (基礎(chǔ))。
GC是垃圾收集器。Java 程序員不用擔心內(nèi)存管理,因為垃圾收集器會自動進行管理。要請求垃圾收集,可以調(diào)用下面的方法之一:
System.gc()
Runtime.getRuntime().gc()
第九,String s = new String("xyz");創(chuàng)建了幾個String Object?
兩個對象,一個是“xyx”,一個是指向“xyx”的引用對象s。
第十,Math.round(11.5)等於多少? Math.round(-11.5)等於多少?
Math.round(11.5)返回(long)12,Math.round(-11.5)返回(long)-11;
第十一,short s1 = 1; s1 = s1 + 1;有什么錯? short s1 = 1; s1 += 1;有什么錯?
short s1 = 1; s1 = s1 + 1;有錯,s1是short型,s1+1是int型,不能顯式轉(zhuǎn)化為short型。可修改為s1 =(short)(s1 + 1) 。short s1 = 1; s1 += 1正確。
第十二,sleep() 和 wait() 有什么區(qū)別? 搞線程的最愛
sleep()方法是使線程停止一段時間的方法。在sleep 時間間隔期滿后,線程不一定立即恢復執(zhí)行。這是因為在那個時刻,其它線程可能正在運行而且沒有被調(diào)度為放棄執(zhí)行,除非(a)“醒來”的線程具有更高的優(yōu)先級
(b)正在運行的線程因為其它原因而阻塞。
wait()是線程交互時,如果線程對一個同步對象x 發(fā)出一個wait()調(diào)用,該線程會暫停執(zhí)行,被調(diào)對象進入等待狀態(tài),直到被喚醒或等待時間到。
第十三,Java有沒有g(shù)oto?
Goto—java中的保留字,現(xiàn)在沒有在java中使用。
第十四,數(shù)組有沒有l(wèi)ength()這個方法? String有沒有l(wèi)ength()這個方法?
數(shù)組沒有l(wèi)ength()這個方法,有l(wèi)ength的屬性。
String有有l(wèi)ength()這個方法。
第十五,Overload和Override的區(qū)別。Overloaded的方法是否可以改變返回值的類型?
方法的重寫Overriding和重載Overloading是Java多態(tài)性的不同表現(xiàn)。重寫Overriding是父類與子類之間多態(tài)性的一種表現(xiàn),重載Overloading是一個類中多態(tài)性的一種表現(xiàn)。如果在子類中定義某方法與其父類有相同的名稱和參數(shù),我們說該方法被重寫 (Overriding)。子類的對象使用這個方法時,將調(diào)用子類中的定義,對它而言,父類中的定義如同被“屏蔽”了。如果在一個類中定義了多個同名的方法,它們或有不同的參數(shù)個數(shù)或有不同的參數(shù)類型,則稱為方法的重載(Overloading)。Overloaded的方法是可以改變返回值的類型。
第十六,Set里的元素是不能重復的,那么用什么方法來區(qū)分重復與否呢? 是用==還是equals()? 它們有何區(qū)別?
Set里的元素是不能重復的,那么用iterator()方法來區(qū)分重復與否。equals()是判讀兩個Set是否相等。
equals()和==方法決定引用值是否指向同一對象equals()在類中被覆蓋,為的是當兩個分離的對象的內(nèi)容和類型相配的話,返回真值。
第十七,給我一個你最常見到的runtime exception。
ArithmeticException, ArrayStoreException, BufferOverflowException, BufferUnderflowException, CannotRedoException, CannotUndoException, ClassCastException, CMMException, ConcurrentModificationException, DOMException, EmptyStackException, IllegalArgumentException, IllegalMonitorStateException, IllegalPathStateException, IllegalStateException,
ImagingOpException, IndexOutOfBoundsException, MissingResourceException, NegativeArraySizeException, NoSuchElementException, NullPointerException, ProfileDataException, ProviderException, RasterFormatException, SecurityException, SystemException, UndeclaredThrowableException, UnmodifiableSetException, UnsupportedOperationException
第十八,error和exception有什么區(qū)別?
error 表示恢復不是不可能但很困難的情況下的一種嚴重問題。比如說內(nèi)存溢出。不可能指望程序能處理這樣的情況。
exception 表示一種設(shè)計或?qū)崿F(xiàn)問題。也就是說,它表示如果程序運行正常,從不會發(fā)生的情況。
第十九,List, Set, Map是否繼承自Collection接口?
List,Set是
Map不是
第二十,abstract class和interface有什么區(qū)別?
聲明方法的存在而不去實現(xiàn)它的類被叫做抽象類(abstract class),它用于要創(chuàng)建一個體現(xiàn)某些基本行為的類,并為該類聲明方法,但不能在該類中實現(xiàn)該類的情況。不能創(chuàng)建abstract 類的實例。然而可以創(chuàng)建一個變量,其類型是一個抽象類,并讓它指向具體子類的一個實例。不能有抽象構(gòu)造函數(shù)或抽象靜態(tài)方法。Abstract 類的子類為它們父類中的所有抽象方法提供實現(xiàn),否則它們也是抽象類為。取而代之,在子類中實現(xiàn)該方法。知道其行為的其它類可以在類中實現(xiàn)這些方法。
接口(interface)是抽象類的變體。在接口中,所有方法都是抽象的。多繼承性可通過實現(xiàn)這樣的接口而獲得。接口中的所有方法都是抽象的,沒有一個有程序體。接口只可以定義static final成員變量。接口的實現(xiàn)與子類相似,除了該實現(xiàn)類不能從接口定義中繼承行為。當類實現(xiàn)特殊接口時,它定義(即將程序體給予)所有這種接口的方法。然后,它可以在實現(xiàn)了該接口的類的任何對象上調(diào)用接口的方法。由于有抽象類,它允許使用接口名作為引用變量的類型。通常的動態(tài)聯(lián)編將生效。引用可以轉(zhuǎn)換到接口類型或從接口類型轉(zhuǎn)換,instanceof 運算符可以用來決定某對象的類是否實現(xiàn)了接口。
第二十一,abstract的method是否可同時是static,是否可同時是native,是否可同時是synchronized?
都不能
第二十二,接口是否可繼承接口? 抽象類是否可實現(xiàn)(implements)接口? 抽象類是否可繼承實體類(concrete class)?
接口可以繼承接口。抽象類可以實現(xiàn)(implements)接口,抽象類是否可繼承實體類,但前提是實體類必須有明確的構(gòu)造函數(shù)。
第二十三,啟動一個線程是用run()還是start()?
啟動一個線程是調(diào)用start()方法,使線程所代表的虛擬處理機處于可運行狀態(tài),這意味著它可以由JVM調(diào)度并執(zhí)行。這并不意味著線程就會立即運行。run()方法可以產(chǎn)生必須退出的標志來停止一個線程。
第二十四,構(gòu)造器Constructor是否可被override?
構(gòu)造器Constructor不能被繼承,因此不能重寫Overriding,但可以被重載Overloading。
第二十五,是否可以繼承String類?
String類是final類故不可以繼承。
第二十六,當一個線程進入一個對象的一個synchronized方法后,其它線程是否可進入此對象的其它方法?
不能,一個對象的一個synchronized方法只能由一個線程訪問。
第二十七,try {}里有一個return語句,那么緊跟在這個try后的finally {}里的code會不會被執(zhí)行,什么時候被執(zhí)行,在return前還是后?
會執(zhí)行,在return前執(zhí)行。
第二十八,編程題: 用最有效率的方法算出2乘以8等於幾?
有C背景的程序員特別喜歡問這種問題。
2 << 3
第二十九,兩個對象值相同(x.equals(y) == true),但卻可有不同的hash code,這句話對不對?
不對,有相同的hash code。
第三十,當一個對象被當作參數(shù)傳遞到一個方法后,此方法可改變這個對象的屬性,并可返回變化后的結(jié)果,那么這里到底是值傳遞還是引用傳遞?
是值傳遞。Java 編程語言只由值傳遞參數(shù)。當一個對象實例作為一個參數(shù)被傳遞到方法中時,參數(shù)的值就是對該對象的引用。對象的內(nèi)容可以在被調(diào)用的方法中改變,但對象的引用是永遠不會改變的。
第三十一,swtich是否能作用在byte上,是否能作用在long上,是否能作用在String上?
switch(expr1)中,expr1是一個整數(shù)表達式。因此傳遞給 switch 和 case 語句的參數(shù)應(yīng)該是 int、 short、 char 或者 byte。long,string 都不能作用于swtich。
第三十二,編程題: 寫一個Singleton出來。
Singleton模式主要作用是保證在Java應(yīng)用程序中,一個類Class只有一個實例存在。
一般Singleton模式通常有幾種種形式:
第一種形式: 定義一個類,它的構(gòu)造函數(shù)為private的,它有一個static的private的該類變量,在類初始化時實例話,通過一個public的getInstance方法獲取對它的引用,繼而調(diào)用其中的方法。
public class Singleton {
private Singleton(){}
//在自己內(nèi)部定義自己一個實例,是不是很奇怪?
//注意這是private 只供內(nèi)部調(diào)用
private static Singleton instance = new Singleton();
//這里提供了一個供外部訪問本class的靜態(tài)方法,可以直接訪問
public static Singleton getInstance() {
return instance;
}
}
第二種形式:
public class Singleton {
private static Singleton instance = null;
public static synchronized Singleton getInstance() {
//這個方法比上面有所改進,不用每次都進行生成對象,只是第一次
//使用時生成實例,提高了效率!
if (instance==null)
instance=new Singleton();
return instance; }
}
其他形式:
定義一個類,它的構(gòu)造函數(shù)為private的,所有方法為static的。
一般認為第一種形式要更加安全些
第三十三 Hashtable和HashMap
Hashtable繼承自Dictionary類,而HashMap是Java1.2引進的Map interface的一個實現(xiàn)
HashMap允許將null作為一個entry的key或者value,而Hashtable不允許
還有就是,HashMap把Hashtable的contains方法去掉了,改成containsvalue和containsKey。因為contains方法容易讓人引起誤解。
最大的不同是,Hashtable的方法是Synchronize的,而HashMap不是,在
多個線程訪問Hashtable時,不需要自己為它的方法實現(xiàn)同步,而HashMap
就必須為之提供外同步。
Hashtable和HashMap采用的hash/rehash算法都大概一樣,所以性能不會有很大的差異。
它是用于進行兩個對象的比較的,是對象內(nèi)容的比 較,當然也能用于進行對象參閱值的比較。什么是對象參閱值的比較?就是兩個參閱變量的值得比較,我們 都知道參閱變量的值其實就是一個數(shù)字,這個數(shù)字可以看成是鑒別不同對象的代號。兩個對象參閱值的比較,就是兩個數(shù)字的比較,兩個代號的比較。這種比較是默 認的對象比較方式,在Object這個對象中,這種方式就已經(jīng)設(shè)計好了。所以你也不用自己來重寫,浪費不必要的時間。
對象內(nèi)容的比較才是設(shè)計equals()的真正目的,Java語言對equals()的要求如下,這些要求是必須遵循的。否則,你就不該浪費時間:
為
什么這兩個規(guī)則是這樣的,原因其實很簡單,拿HashSet來說吧,HashSet可以擁有一個或更多的箱子,在同一個箱子中可以有一個
或更多的獨特元對象(HashSet所容納的必須是獨特的元對象)。這個例子說明一個元對象可以和其他不同的元對象擁有相同的hashCode。但是一個
元對象只能和擁有同樣內(nèi)容的元對象相等。所以這兩個規(guī)則必須成立。
設(shè)計這兩個函數(shù)所要注意到的:
如果你設(shè)計的對象類型并不使用于收集性對象,那么沒有必要自己再設(shè)計這兩個函數(shù)的處理方式。這是正確的面向?qū)ο笤O(shè)計方法,任何用戶一時用不到的功能,就先不要設(shè)計,以免給日后功能擴展帶來麻煩。
如果你在設(shè)計時想別出心裁,不遵守以上的兩套規(guī)則,那么勸你還是不要做這樣想入非非的事。我還沒有遇到過哪一個開發(fā)者和我說設(shè)計這兩個函數(shù)要違背前面說的兩個規(guī)則,我碰到這些違反規(guī)則的情況時,都是作為設(shè)計錯誤處理。
當 一個對象類型作為收集型對象的元對象時,這個對象應(yīng)該擁有自己處理equals(),和/或處理hashCode()的設(shè)計,而且要遵守前面所說 的兩種原則。equals()先要查null和是否是同一類型。查同一類型是為了避免出現(xiàn)ClassCastException這樣的異常給丟出來。查 null是為了避免出現(xiàn)NullPointerException這樣的異常給丟出來。
如果你的對象里面容納的數(shù)據(jù)過多,那么這兩個函數(shù) equals()和hashCode()將會變得效率低。如果對象中擁有無法serialized的數(shù)據(jù),equals()有可能在操作中出現(xiàn)錯誤。想象 一個對象x,它的一個整型數(shù)據(jù)是transient型(不能被serialize成二進制數(shù)據(jù)流)。然而equals()和hashCode()都有依靠 這個整型數(shù)據(jù),那么,這個對象在serialization之前和之后,是否一樣?答案是不一樣。因為serialization之前的整型數(shù)據(jù)是有效的 數(shù)據(jù),在serialization之后,這個整型數(shù)據(jù)的值并沒有存儲下來,再重新由二進制數(shù)據(jù)流轉(zhuǎn)換成對象后,兩者(對象在serialization 之前和之后)的狀態(tài)已經(jīng)不同了。這也是要注意的。Java Web應(yīng)用程序框架是企業(yè)Java得以成功的重要原因之一。人們懷疑如果沒有Apache Struts框架Java EE是否能夠如此成功。雖然底層編程語言很重要,但通常是框架使編程語言成為引人注目的中心的。如果您經(jīng)常訪問討論論壇,就會注意到Ruby語言和Ruby On Rails框架之間也是這種情況。Ruby已經(jīng)出現(xiàn)十多年了,然而只是在Ruby On Rails框架流行之后,開發(fā)人員才開始注意到Ruby語言。
諸如Ruby、PHP和Python之類的腳本語言最近幾年越來越流行,因此,需要開發(fā)一個Java腳本備選語言和類似Rails的針對Java環(huán)境的框架。Groovy就是這個腳本語言,而Grails就是這個框架。
在本文中我將討論Groovy的Web開發(fā)功能,然后繼續(xù)討論Grails框架。我將開發(fā)一個示例Grails Web應(yīng)用程序,并討論此框架的各種特性。
Groovy是一種語言,其語法類似于Java,但比Java更簡單。它通常被視為腳本/靈活/動態(tài)的語言,但是我不喜歡這類形容詞,因為我認為它們只會令人困惑。如果說Java是一位明智的中年男子,那么Groovy就是他十幾歲的兒子。Groovy具有父親的許多特點,但是更為狂野且更為有趣。他們也可以很好地合作。
Groovy的規(guī)則比Java少得多。例如,要在Java中獲得標準的"Hello World"輸出,您需要編寫一個類、一個具有合適參數(shù)的主方法,等等。但是在Groovy中,如果不想編寫所有樣板代碼,您可以拋開類定義和主方法,僅編寫一行代碼即可打印出"Hello World"。
以下是打印Hello World的文件 Hello.groovy 的內(nèi)容:
println "Hello World"
Java平臺僅關(guān)心使字節(jié)碼得到執(zhí)行。同樣,此平臺不強迫您使用Java語言。只要提供了字節(jié)碼,工作就會進行。Groovy代碼會被編譯為字節(jié)碼,而對于Java平臺來說,字節(jié)碼是從Java代碼還是Groovy代碼生成的并沒有任何區(qū)別。
以下是一個Groovy例子,它顯示了Groovy對清單、映射和范圍的內(nèi)置支持,并證明了Groovy的簡單性及其利用Java的強大功能的能力:
// Print Date def mydate = new java.util.Date() println mydate //Iterate through a map def numbersMAP = ['1':'ONE', '2':'TWO'] for (entry in numbersMAP) { println "${entry.key} = ${entry.value}" } //Introducing the range def range = 'a'..'d' //Lists def numberlist = [1, 2, 3, 4, 5, 6, 7, 8] println numberlist; println "Maximum value: ${numberlist.max()}"
請注意以上代碼直接使用java.util.Date ,對收集的內(nèi)置支持減少了使用清單、映射和范圍所需的代碼。還有許多其他有趣的Groovy特性,例如閉包和簡化的XML處理。您可以在groovy.codehaus.org上找到詳細清單。
現(xiàn)在讓我們來討論如何將Groovy用于Web開發(fā)。
大多數(shù)Java EE教程都從一個基本servlet例子開始。對于Groovy Web開發(fā)來說,您將從groovlet(在groovy中servlet的對應(yīng)概念)開始。如果您在servlet中擺脫了類和doXX() 方法聲明,那么剩下的內(nèi)容就與groovlet很像了。以下是一個名為 Login.groovy 的groovlet例子,您需要將它置于Web應(yīng)用程序的最高級目錄:
def username= request.getParameter("username") def password= request.getParameter("password") if (username == "java" && password == "developer") { response.sendRedirect("home.jsp") session = request.getSession(true); session.setAttribute("name", username) } else { println """ <h1>Login Invalid</h1> <p>Your IP has been logged > ${request.remoteHost}</p> """ paramMap = request.getParameterMap() println "<p>You Submitted:</p>" for (entry in paramMap) { println "${entry.key} = ${entry.value}<br/>" } }
您可以僅創(chuàng)建一個簡單的HTML表單,然后將此表單的行為屬性發(fā)送到 action="Login.groovy"。然后將以下標簽添加到web.xml:
<servlet> <servlet-name>Groovy</servlet-name> <servlet-class>groovy.servlet.GroovyServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>Groovy</servlet-name> <url-pattern>*.groovy</url-pattern> </servlet-mapping>
現(xiàn)在只需將要求的Groovy jar 文件添加到WEB-INF/lib 目錄,您的Groovy Web應(yīng)用程序就準備好在任意Java EE應(yīng)用服務(wù)器上運行了。
您應(yīng)該已經(jīng)注意到代碼中沒有分號,而且使用了隱式變量如request和response。其他隱式變量有context、application、session、out、sout和 html。
GSP是JSP在groovy中的對應(yīng)概念。您無需使用println生成HTML;只需將Groovy代碼嵌入HTML頁面。本文中的例子將在提到Grails時使用GSP。
請注意,因為所有代碼最終都要轉(zhuǎn)換為字節(jié)碼,所以groovlet和GSP能夠與servlet和JSP輕松協(xié)作。因此您無需區(qū)分groovlet和GSP或者servlet和JSP。
現(xiàn)在讓我們討論前途無量的Grails框架。如果成功的話,Grails能夠極大地改變開發(fā)Java Web應(yīng)用程序的方式。Ruby on Rails對Ruby的影響,Grails也能夠?qū)roovy實現(xiàn)。
Grails試圖使用Ruby On Rails的“規(guī)約編程”(coding by convention)范例來降低對配置文件和其他樣板代碼的需求。使用“規(guī)約編程” ,如果文件的名稱本身就能說明此文件的用途,那么您就不需要在配置文件中再次聲明這些內(nèi)容了。此框架會查看文件名,并自己弄清文件用途。通過使用“規(guī)約編程” ,Grails還將自動生成Web應(yīng)用程序中需要的許多內(nèi)容。通過使用Grails,您將能夠在很短的時間內(nèi)、以最小的復雜性使Web應(yīng)用程序就緒。請看以下例子。
Grails基于開源技術(shù),例如Spring、Hibernate和SiteMesh。如果您已經(jīng)擅長這些技術(shù),那么這是件好事;但是如果您由于某種原因不喜歡這些技術(shù),或者您認為不僅需要學習Grails,還需要學習其他三種框架,那么這就不是件好事了。雖然這些技術(shù)能夠幫助Grails執(zhí)行得更好,但是學習四種框架對于大多數(shù)人來說是一個很高的門檻。Grails文檔目前主要關(guān)注它與Spring、Hibernate和其他程序的集成,然而我認為它需要采用相反的方法,將Grails推行為一個簡單快速的Web應(yīng)用程序開發(fā)框架。開發(fā)人員無需擔心或考慮底層發(fā)生了什么。
幸運的是,一旦您開始使用Grails,您將發(fā)現(xiàn)Grails隱藏了這些框架的大多數(shù)底層復雜性。如果您忘掉在底層運行的是Spring、Hibernate和其他程序,那么事情就會變得簡單。
大多數(shù)框架都有數(shù)十種特性,其中只有很少幾種得到了廣泛使用。對于Grails來說,這種關(guān)鍵特性是指“規(guī)則編程”(coding by convention)范例和構(gòu)件的自動生成。
Grails的其他特性包括對Ajax、驗證、單元測試和功能測試的內(nèi)置支持。它使用免費的開源Canoo WebTest項目來實現(xiàn)Web應(yīng)用程序的功能測試。Grails還提供與Quartz Scheduler的集成。
現(xiàn)在是時候安裝Grails框架并且編寫您的第一個應(yīng)用程序了。
安裝過程非常簡單。以下是Grails下載頁面:http://grails.org/Download。您可以從http://dist.codehaus.org/grails/grails-bin-0.2.1.zip下載version 0.2.1。請注意Grails源代碼和文檔作為單獨的下載提供。下載zip文件之后,只需將其內(nèi)容解壓縮到一個目錄即可,在我的案例中此目錄是 C:\groovy\grails-0.2.1\。
創(chuàng)建一個名為GRAILS_HOME 的新環(huán)境變量,并將其值設(shè)為 C:\groovy\grails-0.2.1\。接下來將GRAILS_HOME\bin 添加到PATH 環(huán)境變量。這樣安裝就完成了。通過在命令提示符界面中運行g(shù)rails 命令您可以檢查安裝是否成功。您應(yīng)該獲得此命令的使用信息。
既然您有了一個運行中的Grails安裝,那么您已經(jīng)為創(chuàng)建Grails Web應(yīng)用程序做好了準備。
多年來我一直計劃開發(fā)一個可以幫助我管理衣服的應(yīng)用程序——這個應(yīng)用程序應(yīng)該能夠告訴我我最喜歡的T恤衫放在哪里、是否洗過、是否熨過,等等。總有一天我會靠銷售這個應(yīng)用程序掙上幾百萬,但是現(xiàn)在我將把它用作Grails例子。
第一步是創(chuàng)建一個Grails項目目錄結(jié)構(gòu)。在這一步我將在C:\groovy\grailsapps 創(chuàng)建一個新目錄,并在此級別打開一個命令提示符窗口。在此窗口中,執(zhí)行命令grails create-app。要求您輸入應(yīng)用程序名稱。輸入 ClothesMgt。Grails將顯示它為您創(chuàng)建的全部目錄和文件。圖1顯示了最后得到的命令結(jié)構(gòu)。
圖1:Grails項目目錄結(jié)構(gòu)
此命令將創(chuàng)建約800 KB大小的文件和目錄。這里的想法是此框架遵循已經(jīng)建立的Web應(yīng)用程序開發(fā)慣例,因此它創(chuàng)建的文件和目錄在大多數(shù)Web應(yīng)用程序中是有用的。雖然有些人可能不喜歡這種強制使用某種結(jié)構(gòu)的想法,但是這種基于慣例的自動生成正是Grails的RAD特性的基礎(chǔ)。
如果更仔細地看一下這些目錄,您就會發(fā)現(xiàn)存在用于諸如控制器、視圖、測試、配置文件和標簽庫之類東西的目錄。您還會發(fā)現(xiàn)存在一些基本JavaScript和CSS文件。那么現(xiàn)在應(yīng)用程序的基本結(jié)構(gòu)已經(jīng)有了。您只需做些填空,應(yīng)用程序即可就緒。
請注意自動生成目錄和文件的命令是可選的。您可以手動創(chuàng)建全部文件和目錄。如果熟悉Apache Ant,那么您甚至可以打開GRAILS_HOME 目錄中的\src\grails\build.xml 文件,來仔細查看每個Grails命令的用途。
在此例中我將使用一個 運行于localhost的名為Clothes_Grails的MySQL數(shù)據(jù)庫。Grails內(nèi)置一個HSQL數(shù)據(jù)庫,這對測試簡單的應(yīng)用程序或僅試用Grails非常有用。如果您使用HSQL DB,那么無需執(zhí)行以下幾步。我將使用MySQL來證明您能夠非常輕松地使用HSQL之外的數(shù)據(jù)庫。
從http://www.mysql.com/products/connector/j/ 下載MySQL驅(qū)動器,并將mysql-connector-java-<version number>-stable-bin.jar 文件放置在ClothesMgt\lib 目錄中。接下來您需要編輯 ClothesMgt\grails-app\conf\ApplicationDataSource.groovy文件。
現(xiàn)在此文件的內(nèi)容應(yīng)該類似以下內(nèi)容:
class ApplicationDataSource { boolean pooling = true String dbCreate = "create-drop" String url = "jdbc:mysql://localhost/Clothes_Grails" String driverClassName = "com.mysql.jdbc.Driver" String username = "grails" String password = "groovy" }
現(xiàn)在讓我們看一下如何使用此數(shù)據(jù)庫和對象關(guān)系映射。
Grails的對象關(guān)系映射(GORM)功能在內(nèi)部使用Hibernate 3,但是您無需了解或更改任何Hibernate設(shè)置。Grails具有稱為“域類”的東西,這些域類的對象被映射到數(shù)據(jù)庫。您可以使用關(guān)系來鏈接域類,它們也提供用于CRUD(創(chuàng)建/讀取/更新/刪除)操作的功能非常強大的動態(tài)方法。
在此例中,我們將創(chuàng)建三個域類,其名稱分別是Shirt、Trouser和Cabinet。要創(chuàng)建域類,只需運行命令 grails create-domain-class。請記住在您的項目目錄(而不是它的上級目錄)內(nèi)運行此命令。這是一個常見錯誤,雖然我已經(jīng)提醒了您,您還是會犯至少一次這樣的錯誤。
您必須提供給create-domain-class 命令的唯一輸入是類的名稱。運行此命令三次,將Shirt、Trouser和Cabinet作為三個域類的名稱。Grails現(xiàn)在將在目錄 grails-app/domain/中創(chuàng)建這些域類。它們將僅具有兩個屬性id 和 version。我將為這些類添加屬性,以便使它們更能代表襯衫、褲子和衣櫥。
清單1:Cabinet.groovy
class Cabinet { Long id Long version String name String location def relatesToMany = [ shirts : Shirt, trousers : Trouser ] Set shirts = new HashSet() Set trousers = new HashSet() String toString() { "${this.class.name} : $id" } boolean equals(other) { if(other?.is(this))return true if(!(other instanceof Cabinet)) return false if(!id || !other?.id || id!=other?.id) return false return true } int hashCode() { int hashCode = 0 hashCode = 29 * (hashCode + ( !id ? 0 : id ^ (id >>> 32)) ) } }
清單2: Trouser.groovy
class Trouser { Long id Long version String name String color Cabinet cabinet def belongsTo = Cabinet String toString() { "${this.class.name} : $id" } boolean equals(other) { if(other?.is(this))return true if(!(other instanceof Trouser)) return false if(!id || !other?.id || id!=other?.id) return false return true } int hashCode() { int hashCode = 0 hashCode = 29 * (hashCode + ( !id ? 0 : id ^ (id >>> 32) ) ) } }
清單3: Shirt.groovy
class Shirt { Long id Long version String name String color Cabinet cabinet def belongsTo = Cabinet String toString() { "${this.class.name} : $id" } boolean equals(other) { if(other?.is(this))return true if(!(other instanceof Shirt)) return false if(!id || !other?.id || id!=other?.id) return false return true } int hashCode() { int hashCode = 0 hashCode = 29 * (hashCode + ( !id ? 0 : id ^ (id >>> 32))) } }
我添加的僅有的幾行聲明了字段名稱和顏色,然后聲明了Cabinet、Shirt和Trouser之間的關(guān)系。每個Shirt和Trouser都屬于Cabinet,而Cabinet具有shirt和trouser的集合。belongsTo 屬性在此案例中是可選的,因為在一對多關(guān)系中,Grails會將“一”這一方視為所有者。因此您就無需顯式聲明了。在這里我進行顯式聲明只是為了使這種關(guān)系更明顯。
接下來我們將討論Grails應(yīng)用程序的控制器和視圖部分。
既然域類已經(jīng)就緒,讓我們使用generate-all命令自動生成基本CRUD Web應(yīng)用程序。運行g(shù)rails generate-all 命令三次,當被詢問時提供域類名稱。generate-all 命令的目的是生成每個域類的控制器和視圖,但是由于bug-245,Grails 0.2.1不能生成控制器。您必須手動生成控制器,其方法是對每個域類使用generate-controller 命令。
現(xiàn)在您應(yīng)該在grails-app\controllers 目錄中看到三個控制器。這些控制器負責處理Web應(yīng)用程序中針對特定域類的請求。因此ShirtController.groovy 將處理Web應(yīng)用程序中與Shirt域類相關(guān)的CRUD請求,等等。現(xiàn)在控制器具有多個閉包,每個閉包映射到一個URI。閉包是Groovy語言很好的一個特性,然而要習慣它還是需要一些時間的。清單4顯示了Shirtcontroller.groovy的一段摘錄。
清單4:ShirtController.groovy 摘錄
class ShirtController { def index = { redirect(action:list,params:params) } def list = { [ shirtList: Shirt.list( params ) ] } def show = { [ shirt : Shirt.get( params.id ) ] } def delete = { def shirt = Shirt.get( params.id ) if(shirt) { shirt.delete() flash.message = "Shirt ${params.id} deleted." redirect(action:list) } else { flash.message = "Shirt not found with id ${params.id}" redirect(action:list) } } // ... }
在此例中,ShirtController 中的list閉包將處理URI是/shirt/list的請求,等等。您可在控制器中使用您習慣在Java Web應(yīng)用程序中使用的東西,例如請求、會話和servletContext。
請注意:閉包也將值作為顯式返回語句返回,或者作為閉包體中的最后一個語句的值返回。不要因為Grails生成的代碼中沒有return 而困惑。
一旦控制器完成了對請求的處理,它必須委托給合適的視圖。Grails使用慣例機制實現(xiàn)此操作。因此ShirtController 中的list閉包將委托給視圖 /grails-app/views/shirt/list.gsp 或 /grails-app/views/shirt/list.jsp。 盡管您在使用Grails,全部視圖可以是JSP文件而不是GSP。我?guī)缀鯖]有編寫任何代碼,但是我已經(jīng)準備好了一個Web應(yīng)用程序。
讓我們嘗試部署和運行我們的應(yīng)用程序。
Grails具有一個內(nèi)置Resin服務(wù)器,您可使用grails run-app 命令運行應(yīng)用程序。此命令會將應(yīng)用程序部署到Resin服務(wù)器并啟動服務(wù)器。因此您現(xiàn)在可以在http://localhost:8080/ClothesMgt 訪問此應(yīng)用程序。您還可以同樣輕松地將應(yīng)用程序部署到任意JavaEE服務(wù)器。我嘗試將它部署到Tomcat。要實現(xiàn)此操作,我所需要做的是運行g(shù)rails war 命令,將生成的war文件復制到Tomcat中的webapps目錄!
在此案例中生成的war文件的名稱為 ClothesMgt.war。一旦部署到Tomcat,您就應(yīng)該能夠在http://localhost:8080/ClothesMgt/ 上訪問它,并看到如圖2所示的屏幕。
圖2:Grails 應(yīng)用程序
通過此應(yīng)用程序,能夠獲得Shirt、Trouser和Cabinet的全部CRUD功能。可以顯示衣櫥的全部數(shù)據(jù)、向衣櫥添加新襯衫和褲子、編輯它們的值和刪除記錄——實現(xiàn)這些操作都無需編寫任何業(yè)務(wù)邏輯、視圖或數(shù)據(jù)訪問代碼。僅在幾分鐘內(nèi)您就在JavaEE服務(wù)器上部署好了一個合適的Web應(yīng)用程序。很酷吧?!
讓我們更進一步來定制Grails。
我現(xiàn)在將把新功能和頁面添加到Web應(yīng)用程序,同時重用已經(jīng)存在的域類。shirt/list 和 trouser/list 會分別顯示襯衫和褲子的清單,現(xiàn)在讓我們添加一個新的顯示,來同時顯示襯衫和褲子的清單。要創(chuàng)建一個新的顯示,您需要一個新的控制器和視圖。
使用 generate-controller 和 generate-views 命令,可以輕松實現(xiàn)使用域類自動生成視圖和控制器。然而,在此案例中我希望創(chuàng)建一個與域類不直接關(guān)聯(lián)的控制器。因此我將使用grails create-controller命令。當被提示輸入控制器名稱時,聲明Display。Grails將在grails-app/controllers/ 目錄創(chuàng)建一個名為DisplayController.groovy 的控制器,在grails-tests 目錄創(chuàng)建一個測試套件。如清單5所示編輯控制器。
清單5:DisplayController.groovy
class DisplayController { def index = {redirect(action:list,params:params)} def list = { params['max'] = 10 return [ shirtList: Shirt.list( params ), trouserList: Trouser.list( params )] } }
index 閉包將請求重定向到清單。在list 閉包中我將最大參數(shù)設(shè)為10,然后使用動態(tài)方法Shirt.list 和 Trouser.list。然后返回Groovy Map,它有兩個清單——襯衫清單和褲子清單。
作為Java開發(fā)人員,當看到Shirt.list()時會自然認為是在Shirt域類中的list 方法。然而,如果打開Shirt.groovy,會發(fā)現(xiàn)并沒有此方法。對于Java開發(fā)人員來說,不了解Groovy的特性就使用Grails不僅是令人困惑的,而且是死胡同。動態(tài)方法是Grails的特殊特性,它是構(gòu)建于Groovy語言的一個非常特殊的特性元對象協(xié)議 (MOP)之上的。如此證明可以使用動態(tài)方法查詢域類。因此,在控制器中,您將注意到在域類上調(diào)用的方法似乎在域類中不存在。您可以在這里閱讀關(guān)于使用動態(tài)方法查詢的更多信息。可以在這里找到對Grails控制器和域類中可用的動態(tài)方法的參考資料。
既然控制器能夠處理請求、獲取清單并轉(zhuǎn)發(fā)到視圖,我需要創(chuàng)建相應(yīng)視圖。
當創(chuàng)建控制器時,Grails還在grails-app\views 目錄創(chuàng)建了一個新的顯示目錄,并將以下映射添加到web.xml 文件中。
<servlet-mapping> <servlet-name>grails</servlet-name> <url-pattern>/display/*</url-pattern> </servlet-mapping>
目前Grails有一個generate-views 命令,此命令能夠生成基于域類的視圖,然而沒有能夠自動生成視圖的create-view 命令。請看圖3中的例子。
圖3:一個顯示Trousers的默認視圖
因為我希望創(chuàng)建一個獨立于域類的視圖,所以讓我們手動創(chuàng)建視圖文件。在目錄grails-app\views\display\中,創(chuàng)建一個名為 list.gsp的文件,如清單6所示。
清單6:list.gsp
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <title>Display Shirt And Trouser List</title> <link rel="stylesheet" href="${createLinkTo(dir:'css',file:'main.css')}"></link> </head> <body> <div class="nav"> <span class="menuButton"><a href="${createLinkTo(dir:'')}">Home</a></span> </div> <div class="body"> <h1>Shirt List</h1> <table> <tr> <th>Id</th><th>Cabinet</th> <th>Color</th><th>Name</th> </tr> <g:each in="${shirtList}"> <tr> <td>${it.id}</td> <td>${it.cabinet.name}</td> <td>${it.color}</td> <td>${it.name}</td> </tr> </g:each> </table> <h1>Trouser List</h1> <table> <tr> <th>Id</th> <th>Cabinet</th> <th>Color</th> <th>Name</th> </tr> <g:each in="${trouserList}"> <tr> <td>${it.id}</td> <td>${it.cabinet.name}</td> <td>${it.color}</td> <td>${it.name}</td> </tr> </g:each> </table> </div> </body> </html>
與我之前使用的方式類似,您現(xiàn)在也可以使用run-app 命令運行應(yīng)用程序,或者創(chuàng)建一個war文件并將其部署到Tomcat。您應(yīng)該在http://localhost:8080/ClothesMgt/display/下看到新的視圖,如圖4所示。
圖4:新創(chuàng)建的列出襯衫和褲子清單的視圖
現(xiàn)在讓我們快速討論一下Grails服務(wù)。
如果您想知道如何分開業(yè)務(wù)邏輯以及放置業(yè)務(wù)邏輯的位置,答案在Grails 服務(wù)中。服務(wù)以SomeNameService.groovy 格式命名,且被置于 /grails-app/services/目錄。服務(wù)可利用依賴注入特性,您能夠輕松地從控制器內(nèi)部調(diào)用這些服務(wù)。
讓我們來看一個使用服務(wù)的例子。首先,使用create-service 命令創(chuàng)建新服務(wù)。運行此命令并命名服務(wù)Order。Grails將創(chuàng)建兩個文件——grails-app/services/OrderService.groovy 和 grails-tests/OrderTests.groovy。
現(xiàn)在編輯OrderService.groovy,如清單7所示。當引入新的orderGoods() 方法時會自動生成serviceMethod() 。
清單7:OrderService.groovy
class OrderService { boolean transactional = true def serviceMethod() { // TODO } def orderGoods() { return "Order Placed - New shirts and trousers \ will be sent shortly." } }
現(xiàn)在編輯DisplayController,如清單8所示。引入使用OrderService的重排閉包。請注意服務(wù)將由Groovy注入。
清單8:DisplayController.groovy
class DisplayController { OrderService orderService def index = {redirect(action:list,params:params)} def list = { params['max'] = 10 return [ shirtList: Shirt.list( params ) , trouserList: Trouser.list( params )] } def reorder = { render(orderService.orderGoods()) } }
現(xiàn)在當您訪問URL http://localhost:8080/ClothesMgt/display/reorder時,重排閉包將調(diào)用 OrderService,響應(yīng)會被發(fā)回到瀏覽器。您能夠以類似方式將全部業(yè)務(wù)邏輯移入服務(wù),然后使用Grails的注入功能非常輕松地使用它們。
正如之前提到的,域類沒有能夠從數(shù)據(jù)庫獲取數(shù)據(jù)或更新/刪除現(xiàn)有數(shù)據(jù)的任何方法,例如find()、 findAll() 或 save() 。在控制器中您也沒有編寫諸如 redirect() 或 render() 之類的方法。但是域類和控制器有它們的計劃目的,且允許所有要求的操作。原因是Grails中動態(tài)方法和屬性的存在。動態(tài)方法被動態(tài)添加到類,就好像功能是在程序中編譯的一樣。
這些是可用的方法和屬性,無需編寫。這些動態(tài)方法涵蓋了大多數(shù)Web應(yīng)用程序開發(fā)中會碰到的常見情況。對于域類來說,存在諸如find()、findAll()、list()、executeQuery()、save()和 delete()之類的動態(tài)方法。控制器具有諸如session、request和response之類的動態(tài)屬性,以及諸如chain()、render()和 redirect()之類的方法。要真正利用Grails的強大功能,您需要了解所有這些動態(tài)方法和屬性的功能。
Grails的一個重要特性是能夠在開發(fā)過程中進行了更改時自動重載文件。因此只需編輯和保存gsp文件,就會自動重載新文件。然而這里創(chuàng)建的類似OrderService 的事務(wù)服務(wù)不會被重載。您會在服務(wù)器控制臺看到以下消息"[groovy] Cannot reload class [class OrderService] reloading of transactional service classes is not currently possible. Set class to non-transactional first. "。
Grails的自動重載功能會為您節(jié)省許多時間,您就無需浪費時間來重啟服務(wù)器了。我碰到過一些Grails不能自動重載的案例,例如將一個jsp文件重命名到gsp。然而,Grails的這項功能有望在未來版本中得到進一步改進。
在Groovy JSR 06 的之前版本中,您必須使用@Property 來定義Groovy中的新屬性。因此您會在線看到許多使用@Property的舊的Groovy例子。然而請注意,@Property已經(jīng)從Groovy JSR 06中移除,在Grails 0.2和之后的版本中也不會再需要它。請參閱@Property 建議來獲得更多細節(jié)。
在本文中,我介紹了Grails框架的基本特性,并使用Grails創(chuàng)建了一個應(yīng)用程序。Groovy和Grails最大的好處是一切都運行在優(yōu)秀的舊Java和Java EE上——因此您能夠使用Groovy和Grails的RAD特性快速開發(fā)應(yīng)用程序,然后將應(yīng)用程序部署到可靠的Java EE服務(wù)器上。考慮到關(guān)于Ruby和Rails的宣傳噪音,顯然需要一個Java備選方案。Groovy和Grails看起來非常適合這個角色。
下載本文中的代碼:
substr 方法
返回一個從指定位置開始的指定長度的子字符串。
stringvar.substr(start [, length ])
參數(shù)
stringvar
必選項。要提取子字符串的字符串文字或 String 對象。
start
必選項。所需的子字符串的起始位置。字符串中的第一個字符的索引為 0。
length
可選項。在返回的子字符串中應(yīng)包括的字符個數(shù)。
說明
如果 length 為 0 或負數(shù),將返回一個空字符串。如果沒有指定該參數(shù),則子字符串將延續(xù)到 stringvar 的最后。
示例
下面的示例演示了substr 方法的用法。
function SubstrDemo(){
var s, ss; // 聲明變量。
var s = "The rain in Spain falls mainly in the plain.";
ss = s.substr(12, 5); // 獲取子字符串。
return(ss); // 返回 "Spain"。
}
substring 方法
返回位于 String 對象中指定位置的子字符串。
strVariable.substring(start, end)
"String Literal".substring(start, end)
參數(shù)
start
指明子字符串的起始位置,該索引從 0 開始起算。
end
指明子字符串的結(jié)束位置,該索引從 0 開始起算。
說明
substring 方法將返回一個包含從 start 到最后(不包含 end )的子字符串的字符串。
substring 方法使用 start 和 end 兩者中的較小值作為子字符串的起始點。例如, strvar.substring(0, 3) 和 strvar.substring(3, 0) 將返回相同的子字符串。
如果 start 或 end 為 NaN 或者負數(shù),那么將其替換為0。
子字符串的長度等于 start 和 end 之差的絕對值。例如,在 strvar.substring(0, 3) 和 strvar.substring(3, 0) 返回的子字符串的的長度是 3。
示例
下面的示例演示了 substring 方法的用法。
function SubstringDemo(){
var ss; // 聲明變量。
var s = "The rain in Spain falls mainly in the plain..";
ss = s.substring(12, 17); // 取子字符串。
return(ss); // 返回子字符串。
}
這樣我們就可以通過http://myhost:8080/svn/<項目名> 來訪問存放于資源庫F:/SubversionFiles中的指定項目。當然有可能你并不希望某個項目提供這樣一種訪問方式,這時候你可以使用SVNPath為每個項目進行單獨的設(shè)置,SVNPath的使用方法如下:
DAV svn
SVNPath "F:/SubversionFiles/project1"
同樣把這段配置放在httpd.conf最后,重啟Apache HTTP服務(wù)即可通過http://myhost:8080/svn/project1 來訪問project1項目的資源庫。
下面作一個測試:
到F:\SubversionFiles下建立一個子目錄Test,然后到Aapche下的Bin目錄下執(zhí)行svnadmin create F:\SubversionFiles\Test
打開瀏覽器輸入網(wǎng)址:http://localhost:8080/svn/Test ,應(yīng)該可以正確訪問,但當前該項目下還沒有加入任何內(nèi)容,所以顯示為空的
有一點需要提示的是,現(xiàn)在的訪問是完全匿名的,任何人都可以對SVN進行操作。所以我們接下來利用Apache的權(quán)限管理功能來對SVN進行用戶驗證集成
加入用戶身份驗證:
在確定對訪問用戶的權(quán)限控制之前,你必須規(guī)劃好是對整個資源庫中的所有項目還是單獨的某一個項目進行統(tǒng)一的身份驗證, 也就是我們前面講到的是使用SVNParentPath還是SVNPath的問題。
最簡單的身份驗證方式是使用Basic HTTP Authentication機制,該方式通過用戶名和口令對訪問用戶進行身份驗證。我們可以直接通過Apache提供的支持進行設(shè)置。Apache提供一個htpasswd工具來管理用戶名和口令。接下來我們利用這個工具來添加兩個用戶。
在命令行窗口中轉(zhuǎn)到Apache所在的目錄,執(zhí)行下列命令
說明:創(chuàng)建用戶Xrinehart
輸入:htpasswd –c F:\SubversionFiles\svn_auth_passwd Xrinehart
說明:使用-c參數(shù)來創(chuàng)建一個passwd文件
輸出:
New password: *****
Re-type new password: *****
Adding password for user Xrinehart
再創(chuàng)建用戶的時候就不用-c參數(shù),而是用-m參數(shù),因為文件svn_auth_passwd 已經(jīng)創(chuàng)建。
打開svn_auth_passwd文件,密碼使用MD5加密過了,而且同樣的密碼加密出來的內(nèi)容卻不相同
接下來我們必須告訴Apache服務(wù)器如何使用這個passwd文件,打開httpd.conf找到剛才我們添加的Location配置的位置,修改如下:
#
# SVN
#
DAV svn
SVNParentPath "F:/SubversionFiles"
# how to authenticate a user
AuthType Basic
AuthName "Subversion repository"
AuthUserFile "F:/SubversionFiles/svn_auth_passwd"
# only authenticated users may access the repository
Require valid-user
重新啟動Apache HTTP服務(wù)器,使用瀏覽器打開 http://localhost:8080/svn/Test 你將會看到要求登錄的對話框,輸入你剛設(shè)置的用戶名和口令即可。
這樣Apache和SVN的集成就基本告成了