2007年4月9日
#
---轉自網絡
運行cmd regedit msconfig提示找不到文件
作者: 笑嘻嘻
今天我同學的電腦,開始運行“cmd regedit msconfig” 這三個命令都不行 ,提示"找不到文件"。可是文件明明在阿。
直接運行system32目錄下的cmd也不行,照樣提示"找不到文件"。把cmd改名為cmd1就能夠運行
我的處理過程:
開始我懷疑是中病毒了,是病毒程序在監聽哪個程序標題為“cmd”,發現就結束。
首先用卡巴掃了一下啟動項,沒發現病毒。又用冰刃查了一下啟動項,進程,都沒問題。
想了想會不會中了rootkit 級的馬兒,可用冰刃仔細看了看內核,沒有顯示紅色的阿,剛才殺毒軟件也沒報,是的可能性就不怎么大了。
那會不會是這個文件遭病毒感染了,我最討厭感染型的蠕蟲病毒了。我從我自己的電腦上把才cmd.exe拷貝過來了,用軟件比較了下(光看大小不行的),一樣的。
Hash.zip (28.61 KB , 下載:6次)
----------------------------------------------------------------------------
文件: C:WINDOWSsystem32cmd.exe
大小: 470528 字節
文件版本: 5.1.2600.2180 (xpsp_sp2_rtm.040803-2158)
修改時間: 2005年12月15日, 8:00:00
MD5: 722A247ACB86960A708528120759266D
SHA1: A859B7173FB0B1786BE07B01F779725EDF9043E7
CRC32: 15544920
-----------------------------------------------------------------------------
后來經過詢問前幾天中過病毒,會不會是上次病毒修改了注冊表什么地方,雖然病毒是被殺了,但是修改的地方仍然沒有改過來的呢。結果證明,這個判斷是正確的。
具體處理方法:
用冰刃的修改注冊表,或者將windeows目錄下的regedit.exe修改一下名字,比如叫regedit1.exe,修改注冊表。
將HKEY_LOCAL_MACHINESOFTWAREMicrosoftWindows NTCurrentVersionImage File Execution Options 里面的
cmd.exe
msconfig.exe
regedit.exe
regedit32.exe
刪除就可以了。
典型的 映像劫持。
這只是處理病毒后遺癥,具體的處理病毒的方法沒有寫,因為有很多病毒都會造成這種狀況,具體病毒具體對待。
--轉自
做共享軟件是有利可圖的,這是真的,1999年3月以前我還不信,可是經過一年多的
研究和實踐下來,我已經每月能賺4萬多美金了,比某些大公司總裁還多。但是,
我敢說,80%以上的共享軟件作者并不成功,實際上,他們遠遠沒有賺到他們本來可以
賺到的收入。
軟件共享發行銷售(先試后買)是一種市場營銷手段,和其他所有市場營銷手段一樣,
是有學問的,要想通過軟件共享發行獲得成功,就必須掌握這些學問。
今天,我來貼上第一篇技術文章,收錢的辦法
在幾年以前,Internet還沒有流行的時候,共享軟件的作者只能靠從郵件中收到用戶的支票和現金的方法來賺錢,而用戶寄出支票后,還要等上一周或更多的時間得到來自作者的注冊碼。注意,當以下幾種情況發生時,軟件作者的生意就做不成了:
1)用戶的支票本剛好用完,等他買回新支票本時,消費沖動已經沒有了
2)用戶的郵票剛好用完,他還不得不去一趟郵局買郵票,轉念一想,這軟件我也不是
非買不可,算了
3)用戶無法忍受要等好多天才能拿到注冊碼
一句話,太不方便了
現在好了,有了Internet,有了電子商務,用戶可以在最想買你的軟件的一剎那間,迅速的用他的信用卡在網上買下你的軟件,連后悔的時間都沒有,共享軟件發財的日子到來樂。
那么,如何在網上收取信用卡呢?
如果你擁有一個公司,在美國銀行有信用卡商號帳戶,又購買了銀行的GATEWAY軟件,在自己的網站上開發了信用卡收費系統當然很好,但對于廣大共享軟件作者來說,這很不現實.有簡單的辦法,就是找一家信用卡收款代理公司,讓他們替你收款,你只要每個月等他們給你寄一張總額的支票(他們會提取一定比例的傭金)就行了.
這樣的代理公司網站有:
WWW.QWERKS.COM 提成 15-20% (服務極好,是我的服務商)
WWW.Shareit.COM
WWW.REGNOW.COM
WWW.REGSOFT.COM
WWW.Kagi.com
對于咱們國內的共享軟件作者,還要做的一件事就是去中國銀行開個戶頭(北京中行的活期一本通就很好用),如果你打算讓信用卡公司把錢電匯給你,你還要知道銀行的英文名字,地址,帳戶名,
帳號,轉帳的SWIFT Code(可以從銀行職員那里問到)
到信用卡代理公司的網站上開戶非常簡單,通常確認它們的一個在線協議,填入一些個人信息和產品信息,幾分鐘就OK了
這里面有一個值得注意的地方,就是,當用戶付了款后,注冊碼怎么給的問題,你可以選擇由你來給(每收到一份訂單,他們會給你發一封email,包含用戶資料和email),由你生成注冊碼email給用戶,也可以把注冊碼生成代碼給信用卡公司,讓他們編到他們的系統里去,用戶來了訂單后自動發出注冊碼,也可以由你一次性生成幾百個注冊碼給他們,他們每收到一份訂單時用掉一個注冊碼。
我個人的意見是,這幾個信用卡服務商信譽都非常好,一次給他們幾百個注冊碼是最簡單的辦法,對服務商來說操作簡單,對用戶來說快,交完錢馬上就得到注冊碼了
當你完成作者和產品在信用卡服務商那里的登記后,就會得到一個URL連接,你把這個連接加到你的主頁上面,標上一個“Buy Now”,用戶點這里就可以用信用卡付款了,當然,你也可以把這個連接做到你的軟件界面里去,這樣用戶在試用你的軟件時,隨時想買都可以點擊這個連接上網購買
具體實例可以參考我的網站和軟件
http://www.zy2000.com
MP3 CD Maker
對于一些Internet軟件,如斷點續傳的下載軟件,還有另外一種賺錢方法,就是對用戶免費,而在軟件界面上登一個banner廣告賺取廣告費。最有名的廣告代理商是
www.radiate.com
他的廣告付費是每CPM 2-5美元,也就是說,如果一天里有10萬個用戶使用了你的軟件一次的話,你就得到200-500美元。這家公司聲稱,著名的下載工具軟件Gozilla!落戶Radiate后,每月從Radiate那里賺到22萬美元,我們著名的NetAnt是不是該趕快行動了?
我們也不反對用戶用支票和現金購買軟件,事實上,信用卡服務商都提供支票和現金收款業務,我們可以在網頁中提供信用卡服務商的地址和服務熱線電話,具體例子可以參考我的網頁中 FAQ 一頁的內容
1 .from
1.1單表查詢
from eg.cat as cat.其中,cat只是一個別名,為了用其他子語句的時候書寫簡單
1.2多表查詢
from eg.Cat,eg.Dog
from eg.Cat as cat,eg.Dog as dog
2 join相關
(inner) join
left (outer) join
right (outer) join
full join
HQL同樣對SQL中的這些特性支持
下面插播一個小話題,關于上邊的那些特性,我一直都沒怎么用,今天既然說到這里,就想
把上邊的幾個特性的用法說一下,也算對自己的一個補充:
假設有兩個表:部門、員工,下面列舉一些數據:
員工(Employee):
ID Name DepNo
001 Jplateau 01
002 Jony 01
003 Camel 02
部門(Department):
ID Name
01 研發部
02 營銷部
在Hibernate中我們操縱的都是對象,所以我們操縱的是部門類和員工類
1).(inner) join
select employee.ID as id1,employee.Name as name1,department.ID as id2,department.Name
as name2 from Employee as employee join Department as department on employee.DepNo=
department.ID (注意到條件語句我用on 沒有用where)
那么執行結果是什么呢?
id1 name1 id2 name2
++++++++++++++++++++++++++++++++++++++
001 Jplateau 01 研發部
002 Jony 01 研發部
2).left (outer) join
select employee.ID as id1,employee.Name as name1,department.ID as id2,department.Name
as name2 from Employee as employee left join Department as department on employee.DepNo=
department.ID
那么執行結果又該是什么呢?
id1 name1 id2 name2
++++++++++++++++++++++++++++++++++++++
001 Jplateau 01 研發部
002 Jony 01 研發部
003 Camel null null
{就是說此時我要已第一個表的記錄多少為準,第二個表中沒有相應紀錄的時候填充null}
3). right (outer) join
select employee.ID as id1,employee.Name as name1,department.ID as id2,department.Name
as name2 from Employee as employee right join Department as department on employee.DepNo=
department.ID
那么執行結果又該是什么呢?
id1 name1 id2 name2
++++++++++++++++++++++++++++++++++++++
001 Jplateau 01 研發部
002 Jony 01 研發部
null null 02 營銷部
{就是說此時我要已第二個表的記錄多少為準,第一個表中沒有相應紀錄的時候填充null}
3 大小寫敏感
4。select語句
就是要確定你要從查詢中返回哪些對象或者哪些對象的屬性。寫幾個例子吧:
select employee form Employee as employee
select employee form Employee as employee where employee.Name like 'J%'
select employee.Name form Employee as employee where employee.Name like 'J%'
select employee.ID as id1,employee.Name as name1,department.ID as id2,department.Name
as name2 from Employee as employee right join Department as department on employee.DepNo=
department.ID
select elements(employee.Name) from Employee as employee
(不明白elements到底是做什么用的?望給于說明)
等等
5。數學函數
JDO目前好像還不支持此類特性。
avg(...), sum(...), min(...), max(...)
count(*)
count(...), count(distinct ...), count(all...)
其用法和SQL基本相同
select distinct employee.name from Employee as employee
select count(distinct employee.name),count(employee) from Employee as employee
6。polymorphism (暫時不知道如何解釋?)
from com.test.Animal as animal
不光得到所有Animal得實例,而且可以得到所有Animal的子類(如果我們定義了一個子類Cat)
一個比較極端的例子
from java.lang.Object as o
可以得到所有持久類的實例
7。where語句
定義查詢語句的條件,舉幾個例子吧:
from Employee as employee where employee.Name='Jplateau'
from Employee as employee where employee.Name like 'J%'
from Employee as employee where employee.Name like '%u'
在where語句中“=”不光可以比較對象的屬性,也可以比較對象,如:
select animal from com.test.Animal as animal where animal.name=dog
8。表達式
在SQL語句中大部分的表達式在HQL中都可以使用:
mathematical operators +, -, *, /
binary comparison operators =, >=, <=, <>, !=, like
logical operations and, or, not
string concatenation ||
SQL scalar functions like upper() and lower()
Parentheses ( ) indicate grouping
in, between, is null
JDBC IN parameters ?
named parameters :name, :start_date, :x1 (這種應該是另一種"?"的變通解決方法)
SQL literals 'foo', 69, '1970-01-01 10:00:01.0'
Java public static final constants eg.Color.TABBY
其他不必解釋了,在這里我只想對查詢中的參數問題說明一下:
大家知道在SQL中進行傳遞參數進行查詢的時候,我們通常用PreparedStatement,在語句中寫一大堆的“?”,
在hql中也可以用這種方法,如:
List mates = sess.find(
"select employee.name from Employee as employee " +
"where employee.Name=? ",
name,
Hibernate.STRING
);
(說明:上面利用Session里的find方法,在hibernate的api Session中重載了很多find方法,它可以滿足你多種形式的查詢)
上邊是一個參數的情形,這種情況下緊接著引入參數和定義參數的類型,當為多個參數,調用另一個find方法,它的后兩個
參數都是數組的形式。
還有另外一種方法來解決上邊的問題,JDO也有這樣的方法,不過和hibernate的表現形式上有差別,但他們兩個骨子里卻是
一樣的,如:
Query q = sess.createQuery("select employee.name from Employee as employee where employee.Name=:name");
q.setString("name", "Jplateau");
//當有多個參數的時候在此逐一定義
Iterator employees = q.iterate();
9。order 語句
和sql語句沒什么差別,如:
select employee.name from Employee as employee where employee.Name like 'J%' order by employee.ID desc (或者asc)
10。group by 語句
同樣和sql語句沒什么差別,如:
select employee.name,employee.DepNo from Employee as employee group by employee.DepNo
select foo.id, avg( elements(foo.names) ), max( indices(foo.names) ) from eg.Foo foo group by foo.id
{Note: You may use the elements and indices constructs inside a select clause, even on databases with no subselects.}
誰幫我解釋一下上邊兩句,謝過!
11。子查詢
hibernate同樣支持子查詢,寫幾個例子:
from eg.Cat as fatcat where fatcat.weight > ( select avg(cat.weight) from eg.DomesticCat cat )
-轉載
下面是作者對設計模式的理解并自以為所對應的實例
一 : 單例模式(Singleton)
賬本類:1 單一實例 2 給多個對象共享 3 自己創建。網頁計數器
二:策略模式(Strategy)
使用QQ泡MM時使用外掛 客戶端 :ME 抽象類: 外掛 具體:策略(圖片,笑話,名人名言)
圖書銷售算法(不同書本折扣的算法)
三:原型模式(Prototype)
復印技術: 1 不是同一個對象 2 屬同類
短消息(轉發) 1-n個MM
四:門面模式(Façade)
Facade典型應用就是數據庫JDBC的應用和Session的應用
ME---àMM---à(father,mum,sister,brother)
五:備忘錄模式(Memento)
備份系統時使用
GHOST
六 : 命令模式(Command)
MM(客戶端)--àME(請求者)--à命令角色--à(具體命令)-à代理處(接收者)--àMM
上網 IE 輸入 http地址 發送命令
七: 解釋器(Interpreter)
編譯原理之編譯器
文言文注釋:一段文言文,將它翻譯成白話文
八:調停者模式(Mediator)
法院和原告,被告的關系
九:責任鏈模式(CHAIN OF RESPONSIBLEITY)
喝酒時通過成語接龍決定誰喝酒(馬到成功-功不可沒-沒完沒了)
十:工廠模式(Factory)
水果園—〉(葡萄園,蘋果園)--〉(葡萄,蘋果)(各自生產)
十一:抽象工廠模式(Abstract Factory)
女媧造人---〉(陰,陽)--〉(人,獸)----〉(男人,女人,公獸,母獸)(人和獸屬于不同的產品類)
十二:建造模式(Builder)
汽車制造
十三:合成模式(Composite)
windows的目錄樹(文件系統)
十四:裝飾模式(DECORATOR)
在visio中文件可以使用背景進行裝飾
變廢為寶
十五:設計模式之Adapter(適配器)
充電器(手機和220V電壓)
jdbc-odbc橋
十六:橋梁模式(Bridge)
jdbc驅動程序
十七:代理模式(Proxy)
用代理服務器連接出網
銷售代理(廠商)律師代理(客戶)
foxmail
槍手
十八:享元模式(Flyweight)
字體的26個字母和各自的斜體等
十九:狀態模式(State)
人心情不同時表現不同有不同的行為
編鐘
登錄login logout
二十:觀察者模式(Observer)
公司郵件系統everyone@sina.com的應用。當公司員工向這個郵箱發郵件時會發給公司的每一個員工。如果設置了Outlook則會及時收到通知。
接收到短消息
二十一:模板方法模式(Template)
使用網頁設計時使用的模板架構網頁(骨架) 算法的各個邏輯系統
二十二:訪問者模式(Visitor)
電腦銷售系統: 訪問者(自己)---〉電腦配置系統(主板,CPU,內存。。。。。。)
二十三:迭代子模式(Iterator)
查詢數據庫,返回結果集(map, list, set)
下面的參考文獻是讀書筆記的全部參考文獻。這里不一定用到的。
參考文獻:
http://blog.csdn.net/airhand/
http://blog.csdn.net/bloom121/
http://blog.csdn.net/laurecn/
http://blog.csdn.net/legendinfo/
http://www-128.ibm.com/developerworks/cn/java/l-struts1-1/
《Design Patterns》
《Java與模式》
《設計模式:可復用面向對象軟件的基礎》
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1227902
摘要: 事件源對象 event.srcElement.tagName event.srcElement.type
捕獲釋放 event.srcElement.setCapture(); event.srcElement.releaseCapture();
事件按鍵 event.keyCode event.shiftKey event.altKey event....
閱讀全文
轉自 IBM 劉武東,謝謝作者的辛勤勞動!
前言
Jive是一個開放的Java源代碼項目。其目標是建設一個開放結構的,強壯的,易于擴展的基于JSP的論壇。在其設計目標的指導下,其結構設計得非常得好,融合了很多新的觀念,比如Design Pattern,可更換的Skin,可插入Plug等等。詳細解讀其源代碼對于理解這些新的設計上的概念是很有裨益的。如果你對Design Pattern和Java語言有一定的了解,但是還是會時常迷惑于其中的話,不妨研究研究Jive源代碼,一定會對其中的很多概念有更深入的理解。這篇文章源于我的Jive源代碼研究筆記,希望能夠提綱挈領,帶領大家進入到這個美好的世界。當然,如果沒有時間仔細地看源代碼的話,看看這篇文章,我想也是會有一些幫助的。
再開始之前,需要指出的是,Jive中對Design Pattern的應用,并沒有拘禮與GOF書中所給出的實現方法,而是有許多變通的地方。一方面,我想是由于具體的實際需要,另一方面,我想這也是設計觀念進化的結果吧。因而,這些變通的地方,將是我講解的重點。
整體結構概敘
基于一個OO的設計原則:面向接口編程,而不是針對實現編程。Jive在設計的時候,把其大部分的基本對象都設計為接口或者抽象類。在Jive中,基本的接口有Forum,ForumMessage,ForumThread,Group,User,Authorization和Query。我們可以很容易的從這些接口的名字來知道他們的功用,下面的類圖給出了這些類之間的一些靜態關系:
圖1:Jive整體關系
你可能會有疑問,為什么會都是接口呢?這是基于擴展性考慮的。在Jive給出的實現中,所有的這些接口,Forum,ForumMessage,User等等,都使用數據庫來實現的,一條消息,或者一個用戶對應于數據庫中的一條消息Jive使用了DbForum,DbForumMessage,DbUser等類來實現這些接口,通過JDBC來操作數據庫,使之作為論壇的底層支撐。
然而,有時候,或許我們并不想使用數據庫,比如我們想只是使用文件系統來作為論壇的底層支撐,這時候,我們需要做的只是編碼實現了Forum等等接口的諸如FileFroum,FileForumMessage等對象,然后嵌入Jive中即可,原有的任何代碼都可以不用改變!!!這就是面向接口編程的威力了!
下面來看看具體的設計和編碼。
AbstractFactory模式和可擴展性
如果要實現較好的可擴展性,AbstractFactory模式確實是一件利器。如上面所說,如果要創建的Forum接口的不同實現,而又不想更改代碼的話,就需要用到抽象工廠了。再Jive中,AuthorizationFactory類是一個抽象類,用來創建Authorization對象。這是一個抽象工廠,可以通過不同的子類來創建不同的Authorization對象。這個工廠的實現方法是:
在AuthorizationFactory中使用一個private static變量factory,用來引用具體的抽象工廠的實例:
private static AuthorizationFactory factory = null;
用一個private static的String,來指明具體的抽象工廠的子類類名:
private static String className ="com.coolservlets.forum.database.DbAuthorizationFactory";
然后是用一個private static的loadAuthorizationFactory方法來給這個factory變量賦值,生成具體的抽象工廠類:
private static void loadAuthorizationFactory() {
if (factory == null) {
synchronized(className) {
if (factory == null) {
String classNameProp = PropertyManager.getProperty(
"AuthorizationFactory.className"
);
if (classNameProp != null) {
className = classNameProp;
}
try {
Class c = Class.forName(className);
factory = (AuthorizationFactory)c.newInstance();
}
catch (Exception e) {
System.err.println("Exception loading class: " + e);
e.printStackTrace();
}
}
}
}
}
|
在static的getAuthorization方法返回一個Authorization的過程中,先初始化工廠類factory變量,然后用factory的createAuthorization方法來創建:
public static Authorization getAuthorization(String username,
String password) throws UnauthorizedException
{
loadAuthorizationFactory();
return factory.createAuthorization(username, password);
}
|
不同的子類有不同的createAuthorization方法的實現。比如在DbAuthorizationFactory這個AuthorizationFactory的數據庫實現子類中,createAuthorization方法是這樣實現的:
public Authorization createAuthorization(String username, String password)
throws UnauthorizedException
{
if (username == null || password == null) {
throw new UnauthorizedException();
}
password = StringUtils.hash(password);
int userID = 0;
Connection con = null;
PreparedStatement pstmt = null;
try {
con = DbConnectionManager.getConnection();
pstmt = con.prepareStatement(AUTHORIZE);
pstmt.setString(1, username);
pstmt.setString(2, password);
ResultSet rs = pstmt.executeQuery();
if (!rs.next()) {
throw new UnauthorizedException();
}
userID = rs.getInt(1);
}
catch( SQLException sqle ) {
System.err.println("Exception in DbAuthorizationFactory:" + sqle);
sqle.printStackTrace();
throw new UnauthorizedException();
}
finally {
try { pstmt.close(); }
catch (Exception e) { e.printStackTrace(); }
try { con.close(); }
catch (Exception e) { e.printStackTrace(); }
}
return new DbAuthorization(userID);
}
|
在這個類中,可以看到抽象類和具體的子類之間的關系,它們是如何協作的,又是如何劃分抽象方法和非抽象方法的,這都是值得注意的地方。一般的,抽象方法需要子類來實現,而抽象類中的非抽象方法應該所有子類所能夠共享的,或者可是說,是定義在抽象方法之上的較高層的方法。這確實是一個抽象工廠的好例子!雖然實現的方法已經和GOF中給出的實現相差較遠了,但思想沒變,這兒的實現,也確實是要巧妙的些。
還有就是靜態方法的使用,使得這個類看起來有些Singleton的意味。這使得對于AbstractFactory的創建變得簡單。
下面的類圖給出了這個AbstractFactory的實現的總體情況:
圖2:AbstractFactory模式的實現類圖
在AuthorizationFactory中定義的其它方法,涉及到具體的如何創建Authorization,都是作為abstract方法出現,具體實現留給子類來完成。
這樣,在需要生成一個Authorization的時候,只需要調用AuthorizationFactory的靜態方法getAuthorization就可以了,由子類實現了具體的細節。
其它的,如同上面講到的,在創建Forum的時候用的ForumFactory,具有同上面一樣的實現,這就是模式之所以稱為模式的所在了。
Proxy模式和權限控制
Proxy模式的功能有很多,比如遠程代理,用來給遠程對象提供一個本地代表;虛代理,用來為創建開大開銷的對象提供緩沖,等等。在Jive中使用的是保護代理,為被保護的對象提供權限控制。
我們都知道在一個論壇中,權限的控制是必須的,否則論壇就很可能會被搞得一團糟。Jive中引入Proxy對象,Authorization接口以及權限描敘屬類來提供對論壇的保護。
以ForumFactory為例,一個額外的ForumFactoryProxy來處理權限認證的工作,它為某一個ForumFactory提供了一個代理,保證只有授權的用戶才能夠存取ForumFactory的某些操作。實際上ForumFactory在這兒不僅僅只是一個生成Forum的類的,它更像是一個Forum的管理類。提供了添加,刪除,枚舉等等一系列的功能,而有些功能不是什么樣的人都可以使用的,因而引入了另外的一個代理類來處理權限的問題。
當然,代理類需要繼承ForumFactory,以使方法簽名一致: ForumFactoryProxy extends ForumFactory
在它的構造方法中,就提供了一個ForumFactory對象,這是需要被代理的對象;一個Authorization對象,提供用戶信息;還有一個ForumPermissions,提供認證信息:
public ForumFactoryProxy(ForumFactory factory, Authorization authorization,
ForumPermissions permissions)
{
this.factory = factory;
this.authorization = authorization;
this.permissions = permissions;
}
|
一般的代理過程都是這樣的,在訪問某個方法之前,必須接受權限的檢查,以createForum為例:
public Forum createForum(String name, String description)
throws UnauthorizedException, ForumAlreadyExistsException
{
if (permissions.get(ForumPermissions.SYSTEM_ADMIN)) {
Forum newForum = factory.createForum(name, description);
return new ForumProxy(newForum, authorization, permissions);
}
else {
throw new UnauthorizedException();
}
}
|
下面給出這個模式的類圖:
圖3:Proxy模式的類圖
這個模式的實現基本上和GOF中所給出的實現一致。在Jive中,幾乎所有的接口,Forum,ForumMessage,ForumThread等等,都會有一個相應的Proxy對象來進行權限控制。而在創建具體的對象的時候,都是用相應的Proxy對象來代替原有的對象返回的。例如在ForumFactory的getInstance()方法中需要返回一個Forum的時候,Jive是這樣做的:
public static ForumFactory getInstance(Authorization authorization) {
......
ForumFactoryProxy proxy = new ForumFactoryProxy(factory,authorization, factory.getPermissions(authorization));
return proxy;
}
|
因而,所有被創建的對象實際上都是Proxy對象,抽象工廠保證了沒有權限驗證的對象根本不會客戶所得到,它們只會在Proxy的內部扮演角色,而永遠不會被外部對象所存取,這樣,就從根本上保證了論壇的安全。
Decorator模式和過濾器
一般的在OO設計中,而外功能的添加是通過繼承來實現的,但是繼承有的時候不夠靈活,而且當功能的組合很多的時候,繼承的子類就會成幾何級數增長,使得類多的難以控制。正是基于這樣的考慮,Decorator模式得以誕生。
Decorator模式相當于封裝了某個特定的操作,當某個對象需要這個操作的時候,加上這個Decorator即可。并且,多個Decorator還可以組合,以提供更多的功能。
在Jive中,Decorator模式應用在一些過濾器(Filter)中。Filter提供對ForumMessage對象內容的重新構造。比如,當一個ForumMessage對象流過一個名為FilterCodeHighlight的過濾器后,存在于消息中的所有Java源代碼文本,會被重新構造為具有語法高亮顯示的消息。在比如,當經過了語法高亮修飾的消息再流過一個名為FilterHtml的過濾器后,消息中的HTML片斷會被注釋可以在HTML內部顯示文本,這樣就防止了用戶輸入了HTML控制標簽后,使得頁面顯示不正常的問題。
Jive中,所有的過濾器繼承于一個抽象類ForumMessageFilter,而ForumMessageFilter又實現了ForumMessage接口。也就是說,每一個過濾器實際上也是一個ForumMessage對象。
ForumMessageFilter中還封裝一個ForumMessage對象。進行過濾的方法很簡單,使用的是getBody(),比如在FilterCodeHighlight這個類中:
public String getBody() {
return highlightCode(message.getBody());
}
|
highlightCode是一個private方法,實施具體的過濾的細節。getBody()方法實際上是定義在ForumMessage接口中的,當調用過濾器的getBody()方法時,就能夠得到結構重整后的ForumMessage對象了。這個對象可以被其他客戶引用,也可以在傳遞給另外的過濾器,實施進一步的操作。
在實現一個具體的消息的過濾的時候,在Forum中有addForumMessageFilter(),applyFilters()方法,用來實現對過濾器的應用。
對一個Forum,使用addForumMessageFilter()方法添加一個Filter的時候,并沒有指定一個具體的Message,而只是一個規則(Filter中封裝了過濾規則),然后applyFilter()方法中,實施這些規則:
public ForumMessage applyFilters(ForumMessage message) {
//Loop through filters and apply them
for (int i=0; i < filters.length; i++) {
message = filters[i].clone(message);
}
return message;
}
|
過濾器的clone()方法,為過濾器復制消息體。這個方法的使用,分離了在過濾器中對于消息體和過濾規則的初始化過程,這也是一個值得借鑒的技巧!
下面給出Decorator模式的類圖:
圖4:Decorator模式的類圖
我們可以看到Decorator模式實際上和Proxy模式是很相近的,但是它們代表兩個不同的功能含義。Proxy模式提供一個對象的控制,而Decorator模式則是為對象提供額外的功能。
Iterator模式和論壇的瀏覽erator模式用來分離數據結構和遍歷算法,降低兩者之間的耦合度,以使得同一個數據結構用不同的算法遍歷時,仍能夠具有相同的接口,另一方面,Iterator模式使得當改換遍歷算法后,不需要更改程序的代碼。
在Java的JDK中本身就定義有一個Iterator接口,在Iterator接口中僅僅定義了三個方法,hasNext()判斷是否遍歷完最后一個元素,next()方法返回要遍歷的數據結構中一個對象,remove()則刪除當前對象。Jive中使用IteratorProxy抽象類繼承了這一接口。這兒Proxy的含義和上面一樣,也就是說,這個IteratorProxy出了會實現Iterator的遍歷功能外,還會有代理權限控制的功能。
對于論壇中的基本對象Forum,ForumThread,ForumMessage,Group,User都有相應的遍歷器。比如對應于Forum接口有ForumIteratorProxy對象。這個ForumIteratorProxy遍歷器就相當于一個封裝了一系列Forum對象的集合類,通過定義好的接口hasNext()和next()可以方便的遍歷這個集合,而并不需要知道是如何遍歷這個集合的。遍歷的算法可能很簡單,也可能很復雜,但是對于外部的客戶而言,這并沒有任何的區別。
而對于論壇中具體的遍歷方法,這取決于具體的實現,在Jive中給出的是數據庫的實現。
我們就以MessageIteratorProxy為例,來講解Iterator模式的用法。
DbThreadIterator對象實現了Iterator接口,是對于一個Thread中所有Message的遍歷器,我們來看看它是如何實現的。
hasNext()判斷在這個Thread中是不是還有下一條Message:
public boolean hasNext() {
if (currentIndex+1 >= messages.length) {
return false;
}
return true;
}
|
next()方法從數據庫中取出與在這個Thread中的下一條Message:
public Object next() throws java.util.NoSuchElementException {
ForumMessage message = null;
if (nextMessage != null) {
message = nextMessage;
nextMessage = null;
}
else {
message = getNextMessage();
if (message == null) {
throw new java.util.NoSuchElementException();
}
}
return message;
}
|
這樣,通過對數據庫的操作,DbThreadIterator實現了對一個Thread中所有Message遍歷的方法。
再ForumThread接口中有messages()方法,返回在這個Thread中的所有Message的一個遍歷器(Iterator),實際上也就是返回了一個Message的集合:
public Iterator messages();
在DbForumThread中實現了這個方法:
public Iterator messages() {return new DbThreadIterator(this);}
從DbForumThread的messages()方法中所返回的就是這個Thread中所有Message的一個遍歷器,通過這個遍歷器,我們就可以訪問Thread中的所有的Message了。當然,事情還沒有完,由于權限的問題,我們還需要構造這個遍歷器的Proxy對象,然后通過這個Proxy對象來訪問遍歷器。
下面的類圖給出了在Jive中Iterator模式的實現方法:
圖5:Jive中Iterator模式的實現
在Jive中,因為在一個Thread之下,Message是按樹形結構組織的,因而,當需要層級表示一個Thread中的Message之間的關系的時候,僅僅用上面講到的線性的Iterator是不夠的。這時候,對Iterator的概念進行推廣,就引入了TreeWalker接口。
顧名思義,TreeWalker提供了遍歷一個樹和存取樹上節點的方法:
public interface TreeWalker {
public ForumMessage getRoot();
public ForumMessage getChild(ForumMessage parent, int index);
public int getChildCount(ForumMessage parent);
public int getRecursiveChildCount(ForumMessage parent);
public int getIndexOfChild(ForumMessage parent, ForumMessage child);
public boolean isLeaf(ForumMessage node);
|
TreeWalker只是Iterator的簡單推廣,并沒有Iterator應用的那么廣泛,而且,也可以很容易的在TreeWalker上面在套一層Iterator的借口,讓它在某些情況下行使Iterator的職責。這兒就不再多討論了。
再此,Jive設計中所有涉及到的設計模式的地方,基本上都講完了,看完了之后,是不是對設計模式有了更進一步的了解了呢?
下一部分的內容,將會涉及到具體的編碼,深入到JSP的內部,我們將會看到Jive中是如何實現可更換的Skin的,還會涉及Tag Library的一些內容。好了,這次就到這兒了。下次再見。
轉載自http://hi.baidu.com/ahunspun/blog/item/0069084e9882a0cbd0c86a66.html
一. Input和Output
1. stream代表的是任何有能力產出數據的數據源,或是任何有能力接收數據的接收源。在Java的IO中,所有的stream(包括Input和Out
stream)都包括兩種類型:
1.1 以字節為導向的stream
以字節為導向的stream,表示以字節為單位從stream中讀取或往stream中寫入信息。以字節為導向的stream包括下面幾種類型:
1) inputstream:
1) ByteArrayInputStream:把內存中的一個緩沖區作為InputStream使用
2) StringBufferInputStream:把一個String對象作為InputStream
3) FileInputStream:把一個文件作為InputStream,實現對文件的讀取操作
4) PipedInputStream:實現了pipe的概念,主要在線程中使用
5) SequenceInputStream:把多個InputStream合并為一個InputStream
2) Outputstream
1) ByteArrayOutputStream:把信息存入內存中的一個緩沖區中
2) FileOutputStream:把信息存入文件中
3) PipedOutputStream:實現了pipe的概念,主要在線程中使用
4) SequenceOutputStream:把多個OutStream合并為一個OutStream
1.2 以Unicode字符為導向的stream
以Unicode字符為導向的stream,表示以Unicode字符為單位從stream中讀取或往stream中寫入信息。以Unicode字符為導向的stream包括下面幾
種類型:
1) Input Stream
1) CharArrayReader:與ByteArrayInputStream對應
2) StringReader:與StringBufferInputStream對應
3) FileReader:與FileInputStream對應
4) PipedReader:與PipedInputStream對應
2) Out Stream
1) CharArrayWrite:與ByteArrayOutputStream對應
2) StringWrite:無與之對應的以字節為導向的stream
3) FileWrite:與FileOutputStream對應
4) PipedWrite:與PipedOutputStream對應
以字符為導向的stream基本上對有與之相對應的以字節為導向的stream。兩個對應類實現的功能相同,字是在操作時的導向不同。如
CharArrayReader:和ByteArrayInputStream的作用都是把內存中的一個緩沖區作為InputStream使用,所不同的是前者每次從內存中讀取一個
字節的信息,而后者每次從內存中讀取一個字符。
1.3 兩種不現導向的stream之間的轉換
InputStreamReader和OutputStreamReader:把一個以字節為導向的stream轉換成一個以字符為導向的stream。
2. stream添加屬性
2.1 “為stream添加屬性”的作用
運用上面介紹的Java中操作IO的API,我們就可完成我們想完成的任何操作了。但通過FilterInputStream和FilterOutStream的子類,我們可以
為stream添加屬性。下面以一個例子來說明這種功能的作用。
如果我們要往一個文件中寫入數據,我們可以這樣操作:
FileOutStream fs = new FileOutStream(“test.txt”);
然后就可以通過產生的fs對象調用write()函數來往test.txt文件中寫入數據了。但是,如果我們想實現“先把要寫入文件的數據先緩存到內存
中,再把緩存中的數據寫入文件中”的功能時,上面的API就沒有一個能滿足我們的需求了。但是通過FilterInputStream和FilterOutStream的
子類,為FileOutStream添加我們所需要的功能。
2.2 FilterInputStream的各種類型
2.2.1 用于封裝以字節為導向的InputStream
1) DataInputStream:從stream中讀取基本類型(int、char等)數據。
2) BufferedInputStream:使用緩沖區
3) LineNumberInputStream:會記錄input stream內的行數,然后可以調用getLineNumber()和setLineNumber(int)
4) PushbackInputStream:很少用到,一般用于編譯器開發
2.2.2 用于封裝以字符為導向的InputStream
1) 沒有與DataInputStream對應的類。除非在要使用readLine()時改用BufferedReader,否則使用DataInputStream
2) BufferedReader:與BufferedInputStream對應
3) LineNumberReader:與LineNumberInputStream對應
4) PushBackReader:與PushbackInputStream對應
2.3 FilterOutStream的各種類型
2.2.3 用于封裝以字節為導向的OutputStream
1) DataIOutStream:往stream中輸出基本類型(int、char等)數據。
2) BufferedOutStream:使用緩沖區
3) PrintStream:產生格式化輸出
2.2.4 用于封裝以字符為導向的OutputStream
1) BufferedWrite:與對應
2) PrintWrite:與對應
3. RandomAccessFile
1) 可通過RandomAccessFile對象完成對文件的讀寫操作
2) 在產生一個對象時,可指明要打開的文件的性質:r,只讀;w,只寫;rw可讀寫
3) 可以直接跳到文件中指定的位置
4. I/O應用的一個例子
import java.io.*;
public class TestIO{
public static void main(String[] args)
throws IOException{
//1.以行為單位從一個文件讀取數據
BufferedReader in =
new BufferedReader(
new FileReader("F:\\nepalon\\TestIO.java"));
String s, s2 = new String();
while((s = in.readLine()) != null)
s2 += s + "\n";
in.close();
//1b. 接收鍵盤的輸入
BufferedReader stdin =
new BufferedReader(
new InputStreamReader(System.in));
System.out.println("Enter a line:");
System.out.println(stdin.readLine());
//2. 從一個String對象中讀取數據
StringReader in2 = new StringReader(s2);
int c;
while((c = in2.read()) != -1)
System.out.println((char)c);
in2.close();
//3. 從內存取出格式化輸入
try{
DataInputStream in3 =
new DataInputStream(
new ByteArrayInputStream(s2.getBytes()));
while(true)
System.out.println((char)in3.readByte());
}
catch(EOFException e){
System.out.println("End of stream");
}
//4. 輸出到文件
try{
BufferedReader in4 =
new BufferedReader(
new StringReader(s2));
PrintWriter out1 =
new PrintWriter(
new BufferedWriter(
new FileWriter("F:\\nepalon\\ TestIO.out")));
int lineCount = 1;
while((s = in4.readLine()) != null)
out1.println(lineCount++ + ":" + s);
out1.close();
in4.close();
}
catch(EOFException ex){
System.out.println("End of stream");
}
//5. 數據的存儲和恢復
try{
DataOutputStream out2 =
new DataOutputStream(
new BufferedOutputStream(
new FileOutputStream("F:\\nepalon\\ Data.txt")));
out2.writeDouble(3.1415926);
out2.writeChars("\nThas was pi:writeChars\n");
out2.writeBytes("Thas was pi:writeByte\n");
out2.close();
DataInputStream in5 =
new DataInputStream(
new BufferedInputStream(
new FileInputStream("F:\\nepalon\\ Data.txt")));
BufferedReader in5br =
new BufferedReader(
new InputStreamReader(in5));
System.out.println(in5.readDouble());
System.out.println(in5br.readLine());
System.out.println(in5br.readLine());
}
catch(EOFException e){
System.out.println("End of stream");
}
//6. 通過RandomAccessFile操作文件
RandomAccessFile rf =
new RandomAccessFile("F:\\nepalon\\ rtest.dat", "rw");
for(int i=0; i<10; i++)
rf.writeDouble(i*1.414);
rf.close();
rf = new RandomAccessFile("F:\\nepalon\\ rtest.dat", "r");
for(int i=0; i<10; i++)
System.out.println("Value " + i + ":" + rf.readDouble());
rf.close();
rf = new RandomAccessFile("F:\\nepalon\\ rtest.dat", "rw");
rf.seek(5*8);
rf.writeDouble(47.0001);
rf.close();
rf = new RandomAccessFile("F:\\nepalon\\ rtest.dat", "r");
for(int i=0; i<10; i++)
System.out.println("Value " + i + ":" + rf.readDouble());
rf.close();
}
}
關于代碼的解釋(以區為單位):
1區中,當讀取文件時,先把文件內容讀到緩存中,當調用in.readLine()時,再從緩存中以字符的方式讀取數據(以下簡稱“緩存字節讀取方
式”)。
1b區中,由于想以緩存字節讀取方式從標準IO(鍵盤)中讀取數據,所以要先把標準IO(System.in)轉換成字符導向的stream,再進行
BufferedReader封裝。
2區中,要以字符的形式從一個String對象中讀取數據,所以要產生一個StringReader類型的stream。
4區中,對String對象s2讀取數據時,先把對象中的數據存入緩存中,再從緩沖中進行讀取;對TestIO.out文件進行操作時,先把格式化后的信
息輸出到緩存中,再把緩存中的信息輸出到文件中。
5區中,對Data.txt文件進行輸出時,是先把基本類型的數據輸出屋緩存中,再把緩存中的數據輸出到文件中;對文件進行讀取操作時,先把文
件中的數據讀取到緩存中,再從緩存中以基本類型的形式進行讀取。注意in5.readDouble()這一行。因為寫入第一個writeDouble(),所以為了
正確顯示。也要以基本類型的形式進行讀取。
6區是通過RandomAccessFile類對文件進行操作。
摘要: 感謝ryang的勞動!
Java實現通用線程池
線程池通俗的描述就是預先創建若干空閑線程,等到需要用多線程去處理事務的時候去喚醒某些空閑線程執行處理任務,這樣就省去了頻繁創建線程的時間,因為頻繁創建線程是要耗費大量的CPU資源的。如果一個應用程序需要頻繁地處理大量并發事務,不斷的創建銷毀線程往往會大大地降低系統的效率,這時候線程池就派上用場了。 &...
閱讀全文
虛擬機加載類的途徑:
1、Dog dog = new Dog();
這個動作會導致常量池的解析,Dog類被隱式裝載。
如果當前ClassLoader無法找到Dog,則拋出NoClassDefFoundError。
2、Class clazz = Class.forName(“Dog”);
Object dog =clazz.newInstance();
通過反射加載類型,并創建對象實例
如果無法找到Dog,則拋出ClassNotFoundException。
3、Class clazz = classLoader.loadClass(“Dog”);
Object dog =clazz.newInstance();
通過反射加載類型,并創建對象實例
如果無法找到Dog,則拋出ClassNotFoundException。
那么,1和2和3究竟有什么區別呢?分別用于什么情況呢?
1和2使用的類加載器是相同的,都是當前類加載器。(即:this.getClass.getClassLoader)。
3由用戶指定類加載器。
如果需要在當前類路徑以外尋找類,則只能采用第3種方式。第3種方式加載的類與當前類分屬不同的命名空間。
當前類加載器命名空間對其不可見。當然,如果被加載類的超類對于當前類命名空間可見的話,則可以進行強制轉型。
第1和第2種情況區別不大。如果,Dog類在編譯時無法得到,則使用第2種方式。
另外,第1種和第2種都會導致類被初始化,即:執行類的靜態初始化語句,而第3種情況不會。
另外注意,第1種拋出Error,第2、3種拋出Exception,它們分屬于不同的異常/錯誤分支。
JAVA 方面
1 面向對象的特征有哪些方面
2 String 是最基本的數據類型嗎?
3 int 和 Integer 有什么區別
4 String 和StringBuffer 的區別
5 運行時異常與一般異常有何異同?
異常表示程序運行過程中可能出現的非正常狀態,運行時異常表示虛擬機的通常
操作中可能遇到的異常,是一種常見運行錯誤。java 編譯器要求方法必須聲明拋
出可能發生的非運行時異常,但是并不要求必須聲明拋出未被捕獲的運行時異
常。
6 說出一些常用的類,包,接口,請各舉5 個
7 說出ArrayList,Vector, LinkedList 的存儲性能和特性
ArrayList 和Vector 都是使用數組方式存儲數據,此數組元素數大于實際存儲的
數據以便增加和插入元素,它們都允許直接按序號索引元素,但是插入元素要涉
及數組元素移動等內存操作,所以索引數據快而插入數據慢,Vector 由于使用了
synchronized 方法(線程安全),通常性能上較ArrayList 差,而LinkedList 使用
雙向鏈表實現存儲,按序號索引數據需要進行前向或后向遍歷,但是插入數據時
只需要記錄本項的前后項即可,所以插入速度較快。
8 設計4 個線程,其中兩個線程每次對j 增加1,另外兩個線程對j 每次減少1。
寫出程序。
以下程序使用內部類實現線程,對j 增減的時候沒有考慮順序問題。
public class ThreadTest1{
private int j;
public static void main(String args[]){
ThreadTest1 tt=new ThreadTest1();
Inc inc=tt.new Inc();
Dec dec=tt.new Dec();
for(int i=0;i<2;i++){
Thread t=new Thread(inc);
t.start();
t=new Thread(dec);
t.start();
}
}
private synchronized void inc(){
j++;
System.out.println(Thread.currentThread().getName()+"-inc:"+j);
}
private synchronized void dec(){
j--;
System.out.println(Thread.currentThread().getName()+"-dec:"+j);
}
class Inc implements Runnable{
public void run(){
for(int i=0;i<100;i++){
inc();
}
}
}
class Dec implements Runnable{
public void run(){
for(int i=0;i<100;i++){
dec();
}
}
}
}
9.JSP 的內置對象及方法。
request request 表示HttpServletRequest 對象。它包含了有關瀏覽器請求的信息,并且提
供了幾個用于獲取cookie, header, 和session 數據的有用的方法。
response response 表示HttpServletResponse 對象,并提供了幾個用于設置送回 瀏覽器的
響應的方法(如cookies,頭信息等)
out out 對象是javax.jsp.JspWriter 的一個實例,并提供了幾個方法使你能用于向瀏覽器回
送輸出結果。
pageContext pageContext 表示一個javax.servlet.jsp.PageContext 對象。它是用于方便存
取各種范圍的名字空間、servlet 相關的對象的API,并且包裝了通用的servlet 相關功能的
方法。
session session 表示一個請求的javax.servlet.http.HttpSession 對象。Session 可以存貯用
戶的狀態信息
application applicaton 表示一個javax.servle.ServletContext 對象。這有助于查找有關
servlet 引擎和servlet 環境的信息
config config 表示一個javax.servlet.ServletConfig 對象。該對象用于存取servlet 實例的初
始化參數。
page page 表示從該頁面產生的一個servlet 實例
10.用socket 通訊寫出客戶端和服務器端的通訊,要求客戶發送數據后能夠回顯
相同的數據。
參見課程中socket 通訊例子。
11 說出Servlet 的生命周期,并說出Servlet 和CGI 的區別。
Servlet 被服務器實例化后,容器運行其init 方法,請求到達時運行其service 方
法,service 方法自動派遣運行與請求對應的doXXX 方法(doGet,doPost)等,
當服務器決定將實例銷毀的時候調用其destroy 方法。
與cgi 的區別在于servlet 處于服務器進程中,它通過多線程方式運行其service
方法,一個實例可以服務于多個請求,并且其實例一般不會銷毀,而CGI 對每
個請求都產生新的進程,服務完成后就銷毀,所以效率上低于servlet。
12.EJB 是基于哪些技術實現的?并說出SessionBean 和EntityBean 的區別,
StatefulBean 和StatelessBean 的區別。
13.EJB 包括(SessionBean,EntityBean)說出他們的生命周期,及如何管理事務
的?
14.說出數據連接池的工作機制是什么?
15 同步和異步有和異同,在什么情況下分別使用他們?舉例說明。
16 應用服務器有那些?
17 你所知道的集合類都有哪些?主要方法?
18 給你一個:驅動程序A,數據源名稱為B,用戶名稱為C,密碼為D,數據庫表為T,
請用JDBC 檢索出表T 的所有數據。
19.說出在JSP 頁面里是怎么分頁的?
頁面需要保存以下參數:
總行數:根據sql 語句得到總行數
每頁顯示行數:設定值
當前頁數:請求參數
頁面根據當前頁數和每頁行數計算出當前頁第一行行數,定位結果集到此行,對
結果集取出每頁顯示行數的行即可。
數據庫方面:
1. 存儲過程和函數的區別
存儲過程是用戶定義的一系列sql 語句的集合,涉及特定表或其它對象
的任務,用戶可以調用存儲過程,而函數通常是數據庫已定義的方法,
它接收參數并返回某種類型的值并且不涉及特定用戶表。
2. 事務是什么?
事務是作為一個邏輯單元執行的一系列操作,一個邏輯工作單元必須有四個
屬性,稱為 ACID(原子性、一致性、隔離性和持久性)屬性,只有這樣才能成
為一個事務:
原子性
事務必須是原子工作單元;對于其數據修改,要么全都執行,要么全都不執行。
一致性
事務在完成時,必須使所有的數據都保持一致狀態。在相關數據庫中,所有規則
都必須應用于事務的修改,以保持所有數據的完整性。事務結束時,所有的內部
數據結構(如 B 樹索引或雙向鏈表)都必須是正確的。
隔離性
由并發事務所作的修改必須與任何其它并發事務所作的修改隔離。事務查看數據
時數據所處的狀態,要么是另一并發事務修改它之前的狀態,要么是另一事務修
改它之后的狀態,事務不會查看中間狀態的數據。這稱為可串行性,因為它能夠
重新裝載起始數據,并且重播一系列事務,以使數據結束時的狀態與原始事務執
行的狀態相同。
持久性
事務完成之后,它對于系統的影響是永久性的。該修改即使出現系統故障也將一
直保持。
3. 游標的作用?如何知道游標已經到了最后?
游標用于定位結果集的行,通過判斷全局變量@@FETCH_STATUS 可以判
斷是否到了最后,通常此變量不等于0 表示出錯或到了最后。
4. 觸發器分為事前觸發和事后觸發,這兩種觸發有和區別。語句級觸發和
行級觸發有何區別。
事前觸發器運行于觸發事件發生之前,而事后觸發器運行于觸發事件發
生之后。通常事前觸發器可以獲取事件之前和新的字段值。
語句級觸發器可以在語句執行前或后執行,而行級觸發在觸發器所影響
的每一行觸發一次。
http://www.zhuicha.com/zhuicha/onlineHB/linuxcmd/ 此網站列舉了常用命令
文件傳輸
備份壓縮
文件管理
磁盤管理
磁盤維護
系統設置
系統管理
文檔編輯
網絡通訊
電子郵件與新聞組
X WINDOWS SYSTEM
對于這個系列里的問題,每個學Java的人都應該搞懂。當然,如果只是學Java玩玩就無所謂了。如果你認為自己已經超越初學者了,卻不很懂這些問題,請將你自己重歸初學者行列。內容均來自于CSDN的經典老貼。
問題一:我聲明了什么!
String s = "Hello world!";
許多人都做過這樣的事情,但是,我們到底聲明了什么?回答通常是:一個String,內容是“Hello world!”。這樣模糊的回答通常是概念不清的根源。如果要準確的回答,一半的人大概會回答錯誤。
這個語句聲明的是一個指向對象的引用,名為“s”,可以指向類型為String的任何對象,目前指向"Hello world!"這個String類型的對象。這就是真正發生的事情。我們并沒有聲明一個String對象,我們只是聲明了一個只能指向String對象的引用變量。所以,如果在剛才那句語句后面,如果再運行一句:
String string = s;
我們是聲明了另外一個只能指向String對象的引用,名為string,并沒有第二個對象產生,string還是指向原來那個對象,也就是,和s指向同一個對象。
問題二:"=="和equals方法究竟有什么區別?
==操作符專門用來比較變量的值是否相等。比較好理解的一點是:
int a=10;
int b=10;
則a==b將是true。
但不好理解的地方是:
String a=new String("foo");
String b=new String("foo");
則a==b將返回false。
根據前一帖說過,對象變量其實是一個引用,它們的值是指向對象所在的內存地址,而不是對象本身。a和b都使用了new操作符,意味著將在內存中產生兩個內容為"foo"的字符串,既然是“兩個”,它們自然位于不同的內存地址。a和b的值其實是兩個不同的內存地址的值,所以使用"=="操作符,結果會是false。誠然,a和b所指的對象,它們的內容都是"foo",應該是“相等”,但是==操作符并不涉及到對象內容的比較。
對象內容的比較,正是equals方法做的事。
看一下Object對象的equals方法是如何實現的:
boolean equals(Object o){
return this==o;
}
Object對象默認使用了==操作符。所以如果你自創的類沒有覆蓋equals方法,那你的類使用equals和使用==會得到同樣的結果。同樣也可以看出,Object的equals方法沒有達到equals方法應該達到的目標:比較兩個對象內容是否相等。因為答案應該由類的創建者決定,所以Object把這個任務留給了類的創建者。
看一下一個極端的類:
Class Monster{
private String content;
...
boolean equals(Object another){ return true;}
}
我覆蓋了equals方法。這個實現會導致無論Monster實例內容如何,它們之間的比較永遠返回true。
所以當你是用equals方法判斷對象的內容是否相等,請不要想當然。因為可能你認為相等,而這個類的作者不這樣認為,而類的equals方法的實現是由他掌握的。如果你需要使用equals方法,或者使用任何基于散列碼的集合(HashSet,HashMap,HashTable),請察看一下java doc以確認這個類的equals邏輯是如何實現的。
繼續深入討論一下“==”和equals,看如下例子
public class TestStringIntern {
public static void main(String[] args) {
testIt();
}
private static void testIt() {
String s1 = "sean_gao";
String s2 = "sean"+"_"+"gao";
String s3 = new String("sean_gao");
String s4 = new String("sean_gao").intern();
System.out.println("s1==s2? "+(s1==s2));
System.out.println("s1==s3? "+(s1==s3));
System.out.println("s1==s4? "+(s1==s4));
System.out.println("s1.equals(s2)? "+(s1.equals(s2)));
System.out.println("s1.equals(s3)? "+(s1.equals(s3)));
System.out.println("s1.equals(s4)? "+(s1.equals(s4)));
}
}
以下是結果:
s1==s2? true // 引用的是同一個對象,因為內容一致
s1==s3? false // 引用的是不同的對象,因為用了new關鍵字
s1==s4? true // 引用的是同一個對象,因為用了intern方法
s1.equals(s2)? true // 內容一致
s1.equals(s3)? true // 內容一致
s1.equals(s4)? true // 內容一致
再次解釋:對于String對象,如果是按照String s = "some string";這樣的形式聲明的,如果同一個JVM中恰好有相同內容的String對象,那么這個s指向的就是那個已有的對象。但如果使用String s = new String("some string");這樣的語法,那么不管JVM中有沒有可以重用的String對象,都將新建一個對象。
==操作符判斷的是對象引用是否指向同一個對象,而equals方法在String類中的實現是判斷String對象的內容是否一致。
問題三:String到底變了沒有?
沒有。因為String被設計成不可變(immutable)類,所以它的所有對象都是不可變對象。請看下列代碼:
String s = "Hello";
s = s + " world!";
s所指向的對象是否改變了呢?從本系列第一篇的結論很容易導出這個結論。我們來看看發生了什么事情。在這段代碼中,s原先指向一個String對象,內容是"Hello",然后我們對s進行了+操作,那么s所指向的那個對象是否發生了改變呢?答案是沒有。這時,s不指向原來那個對象了,而指向了另一個String對象,內容為"Hello world!",原來那個對象還存在于內存之中,只是s這個引用變量不再指向它了。
通過上面的說明,我們很容易導出另一個結論,如果經常對字符串進行各種各樣的修改,或者說,不可預見的修改,那么使用String來代表字符串的話會引起很大的內存開銷。因為String對象建立之后不能再改變,所以對于每一個不同的字符串,都需要一個String對象來表示。這時,應該考慮使用StringBuffer類,它允許修改,而不是每個不同的字符串都要生成一個新的對象。并且,這兩種類的對象轉換十分容易。
同時,我們還可以知道,如果要使用內容相同的字符串,不必每次都new一個String。例如我們要在構造器中對一個名叫s的String引用變量進行初始化,把它設置為初始值,應當這樣做:
public class Demo {
private String s;
...
public Demo {
s = "Initial Value";
}
...
}
而非
s = new String("Initial Value");
后者每次都會調用構造器,生成新對象,性能低下且內存開銷大,并且沒有意義,因為String對象不可改變,所以對于內容相同的字符串,只要一個String對象來表示就可以了。也就說,多次調用上面的構造器創建多個對象,他們的String類型屬性s都指向同一個對象。
上面的結論還基于這樣一個事實:對于字符串常量,如果內容相同,Java認為它們代表同一個String對象。而用關鍵字new調用構造器,總是會創建一個新的對象,無論內容是否相同。
至于為什么要把String類設計成不可變類,是它的用途決定的。其實不只String,很多Java標準類庫中的類都是不可變的。在開發一個系統的時候,我們有時候也需要設計不可變類,來傳遞一組相關的值,這也是面向對象思想的體現。不可變類有一些優點,比如因為它的對象是只讀的,所以多線程并發訪問也不會有任何問題。當然也有一些缺點,比如每個不同的狀態都要一個對象來代表,可能會造成性能上的問題。所以Java標準類庫還提供了一個可變版本,即StringBuffer。
問題四:final關鍵字到底修飾了什么?
final使得被修飾的變量"不變",但是由于對象型變量的本質是“引用”,使得“不變”也有了兩種含義:引用本身的不變,和引用指向的對象不變。
引用本身的不變:
final StringBuffer a=new StringBuffer("immutable");
final StringBuffer b=new StringBuffer("not immutable");
a=b;//編譯期錯誤
引用指向的對象不變:
final StringBuffer a=new StringBuffer("immutable");
a.append(" broken!"); //編譯通過
可見,final只對引用的“值”(也即它所指向的那個對象的內存地址)有效,它迫使引用只能指向初始指向的那個對象,改變它的指向會導致編譯期錯誤。至于它所指向的對象的變化,final是不負責的。這很類似==操作符:==操作符只負責引用的“值”相等,至于這個地址所指向的對象內容是否相等,==操作符是不管的。
理解final問題有很重要的含義。許多程序漏洞都基于此----final只能保證引用永遠指向固定對象,不能保證那個對象的狀態不變。在多線程的操作中,一個對象會被多個線程共享或修改,一個線程對對象無意識的修改可能會導致另一個使用此對象的線程崩潰。一個錯誤的解決方法就是在此對象新建的時候把它聲明為final,意圖使得它“永遠不變”。其實那是徒勞的。
問題五:到底要怎么樣初始化!
本問題討論變量的初始化,所以先來看一下Java中有哪些種類的變量。
1. 類的屬性,或者叫值域
2. 方法里的局部變量
3. 方法的參數
對于第一種變量,Java虛擬機會自動進行初始化。如果給出了初始值,則初始化為該初始值。如果沒有給出,則把它初始化為該類型變量的默認初始值。
int類型變量默認初始值為0
float類型變量默認初始值為0.0f
double類型變量默認初始值為0.0
boolean類型變量默認初始值為false
char類型變量默認初始值為0(ASCII碼)
long類型變量默認初始值為0
所有對象引用類型變量默認初始值為null,即不指向任何對象。注意數組本身也是對象,所以沒有初始化的數組引用在自動初始化后其值也是null。
對于兩種不同的類屬性,static屬性與instance屬性,初始化的時機是不同的。instance屬性在創建實例的時候初始化,static屬性在類加載,也就是第一次用到這個類的時候初始化,對于后來的實例的創建,不再次進行初始化。這個問題會在以后的系列中進行詳細討論。
對于第二種變量,必須明確地進行初始化。如果再沒有初始化之前就試圖使用它,編譯器會抗議。如果初始化的語句在try塊中或if塊中,也必須要讓它在第一次使用前一定能夠得到賦值。也就是說,把初始化語句放在只有if塊的條件判斷語句中編譯器也會抗議,因為執行的時候可能不符合if后面的判斷條件,如此一來初始化語句就不會被執行了,這就違反了局部變量使用前必須初始化的規定。但如果在else塊中也有初始化語句,就可以通過編譯,因為無論如何,總有至少一條初始化語句會被執行,不會發生使用前未被初始化的事情。對于try-catch也是一樣,如果只有在try塊里才有初始化語句,編譯部通過。如果在catch或finally里也有,則可以通過編譯。總之,要保證局部變量在使用之前一定被初始化了。所以,一個好的做法是在聲明他們的時候就初始化他們,如果不知道要出事化成什么值好,就用上面的默認值吧!
其實第三種變量和第二種本質上是一樣的,都是方法中的局部變量。只不過作為參數,肯定是被初始化過的,傳入的值就是初始值,所以不需要初始化。
問題六:instanceof是什么東東?
instanceof是Java的一個二元操作符,和==,>,<是同一類東東。由于它是由字母組成的,所以也是Java的保留關鍵字。它的作用是測試它左邊的對象是否是它右邊的類的實例,返回boolean類型的數據。舉個例子:
String s = "I AM an Object!";
boolean isObject = s instanceof Object;
我們聲明了一個String對象引用,指向一個String對象,然后用instancof來測試它所指向的對象是否是Object類的一個實例,顯然,這是真的,所以返回true,也就是isObject的值為True。
instanceof有一些用處。比如我們寫了一個處理賬單的系統,其中有這樣三個類:
public class Bill {//省略細節}
public class PhoneBill extends Bill {//省略細節}
public class GasBill extends Bill {//省略細節}
在處理程序里有一個方法,接受一個Bill類型的對象,計算金額。假設兩種賬單計算方法不同,而傳入的Bill對象可能是兩種中的任何一種,所以要用instanceof來判斷:
public double calculate(Bill bill) {
if (bill instanceof PhoneBill) {
//計算電話賬單
}
if (bill instanceof GasBill) {
//計算燃氣賬單
}
...
}
這樣就可以用一個方法處理兩種子類。
然而,這種做法通常被認為是沒有好好利用面向對象中的多態性。其實上面的功能要求用方法重載完全可以實現,這是面向對象變成應有的做法,避免回到結構化編程模式。只要提供兩個名字和返回值都相同,接受參數類型不同的方法就可以了:
public double calculate(PhoneBill bill) {
//計算電話賬單
}
public double calculate(GasBill bill) {
//計算燃氣賬單
}
所以,使用instanceof在絕大多數情況下并不是推薦的做法,應當好好利用多態。
主要就我所了解的J2EE開發的框架或開源項目做個介紹,可以根據需求選用適當的開源組件進行開發.主要還是以Spring為核心,也總結了一些以前web開發常用的開源工具和開源類庫
1持久層:
1)Hibernate
這個不用介紹了,用的很頻繁,用的比較多的是映射,包括繼承映射和父子表映射
對于DAO在這里介紹個在它基礎上開發的包bba96,目前最新版本是bba96 2.0它對Hibernate進行了封裝, 查詢功能包括執行hsql或者sql查詢/更新的方法,如果你要多層次邏輯的條件查詢可以自己組裝QueryObject.可以參考它做HibernateDAO.也可以直接利用它
2) iBATIS
另一個ORM工具,Apache的,沒有Hibernate那么集成,自由度比較大
2:SpringMVC
原理說明和快速入門:
配置文件為:
Spring的配置文件默認為WEB-INF/xxxx-servelet.xm其中xxx為web.xml中org.springframework.web.servlet.DispatcherServlet的servlet-name。
Action分發:
Spring將按照配置文件定義的URL,Mapping到具體Controller類,再根據URL里的action= xxx或其他參數,利用反射調用Controller里對應的Action方法。
輸入數據綁定:
Spring提供Binder 通過名字的一一對應反射綁定Pojo,也可以直接從request.getParameter()取數據。
輸入數據驗證
Sping 提供了Validator接口當然還可以使用開源的Commons-Validaor支持最好
Interceptor(攔截器)
Spring的攔截器提供接口需要自己編寫,在這點不如WebWork做的好.全面
(這里提一下WebWork和Struts的區別最主要的區別在于WebWork在建立一個Action時是新New一個對象而Struts是SingleMoule所有的都繼承它的一個Action,所以根據項目需要合適的選擇.)
3:View層
1) 標簽庫:JSP2.0/JSTL
由于Webwork或Spring的標簽確實很有限,一般view層用JSTL標簽,而且據說JSTL設計很好速度是所有標簽中最快的使用起來也很簡單
2) 富客戶端:DOJO Widgets, YUI(YahooUI),FCKEditor, Coolest日歷控件
Dojo主要提供Tree, Tab等富客戶端控件,可以用其進行輔助客戶端開發
YahooUI和DOJO一樣它有自己的一套javascript調試控制臺,主要支持ajax開發也有很多Tree,Table,Menu等富客戶端控件
FCKEditor 最流行的文本編輯器
Coolest日歷控件 目前很多日歷控件可用,集成在項目中也比較簡單,這個只是其中的一個,界面不錯的說..
3) JavaScript:Prototype.js
Prototype.js作為javascript的成功的開源框架,封裝了很多好用的功能,通過它很容易編寫AJAX應用,現在AJAX技術逐漸成熟,框架資源比較豐富,比如YUI,DWR等等,也是因為JavaScript沒有合適的調試工具,所以沒有必要從零開始編寫AJAX應用,個人認為多用一些成熟的Ajax框架實現無刷新更新頁面是不錯的選擇.
4)表格控件:Display Tag ,Extreme Table
這兩個的功能差不多,都是View層表格的生成,界面也比較相向,可以導出Excel,Pdf,對Spring支持很容易.
相比較而言比較推薦ExtremeTable,它的設計很好功能上比DisplayTag多一些,支持Ajax,封裝了一些攔截器,而且最方面的是在主頁wiki中有詳細的中文使用文檔.
5):OSCache
OSCache是OpenSymphony組織提供的一個J2EE架構中Web應用層的緩存技術實現組件,Cache是一種用于提高系統響應速度、改善系統運行性能的技術。尤其是在Web應用中,通過緩存頁面的輸出結果,可以很顯著的改善系統的穩定性和運行性能。
它主要用在處理短時間或一定時間內一些數據或頁面不會發生變化,或將一些不變的統計報表,緩沖在內存,可以充分的減輕服務器的壓力,防治負載平衡,快速重啟服務器(通過硬盤緩存).
6)SiteMesh
sitemesh應用Decorator模式主要用于提高頁面的可維護性和復用性,其原理是用Filter截取request和response,把頁面組件head,content,banner結合為一個完整的視圖。通常我們都是用include標簽在每個jsp頁面中來不斷的包含各種header, stylesheet, scripts and footer,現在,在sitemesh的幫助下,我們刪掉他們輕松達到復合視圖模式.
Sitemesh也是 OpenSymphony的一個項目現在最近的版本是2.2,目前OpenSymphony自從04年就沒有更新的版本了..感覺它還是比較有創新的一種頁面組裝方式, OpenSymphony開源組織的代碼一般寫的比較漂亮,可以改其源代碼對自己的項目進行適配.
測試發現Sitemesh還存在一些問題,比如中文問題,它的默認編碼是iso-8859-1在使用時候需要做一些改動.
7)CSS,XHTML
這個不用說了,遵循W3C標準的web頁面開發.
8)分頁標簽: pager-taglib組件
Pager-taglib 是一套分頁標簽庫,可以靈活地實現多種不同風格的分頁導航頁面,并且可以很好的與服務器分頁邏輯分離.使用起來也比較簡單.
9)Form: Jodd Form taglib
Jodd Form taglib使用比較簡單,只要把<form>的頭尾以<jodd:form bean= "mybean">包住
就會自動綁定mybean, 自動綁定mybean的所有同名屬性到普通html標記input, selectbox, checkbox,radiobox.....在這些input框里不用再寫任何代碼…
10)Ajax:DWR
J2EE應用最常用的ajax框架
11)報表 圖表
Eclipse BIRT功能比較強大,也很龐大..好幾十M,一般沒有特別需求或別的圖表設計軟件可以解決的不用它
JasperReports+ iReport是一個基于Java的開源報表工具,它可以在Java環境下像其它IDE報表工具一樣來制作報表。JasperReports支持PDF、HTML、XLS、CSV和XML文件輸出格式。JasperReports是當前Java開發者最常用的報表工具。
JFreeChart主要是用來制作各種各樣的圖表,這些圖表包括:餅圖、柱狀圖(普通柱狀圖以及堆棧柱狀圖)、線圖、區域圖、分布圖、混合圖、甘特圖以及一些儀表盤等等。
琴棋報表,國產的..重點推薦,適合中國的情況,開放源代碼,使用完全免費。純JAVA開發,適用多種系統平臺。特別適合B/S結構的系統。官方網站有其優點介紹,看來用它還是不錯的選擇,最重要的是支持國產呵呵
4:權限控制: Acegi
Acegi是Spring Framework 下最成熟的安全系統,它提供了強大靈活的企業級安全服務,如完善的認證和授權機制,Http資源訪問控制,Method 調用訪問控制等等,支持CAS
(耶魯大學的單點登陸技術,這個單點登陸方案比較出名.我也進行過配置使用,可以根據項目需要,如果用戶分布在不同的地方不同的系統通用一套登陸口令可以用它進行解決,一般注冊機登陸機就是這樣解決的)
Acegi只是于Spring結合最好的安全框架,功能比較強大,當然還有一些其他的安全框架,這里列舉一些比較流行的是我從網上找到的,使用方法看其官方文檔把…
JAAS, Seraph, jSai - Servlet Security, Gabriel, JOSSO, Kasai, jPAM, OpenSAML都是些安全控制的框架..真夠多的呵呵
5:全文檢索
1) Lucene
Lucene是一套全文索引接口,可以通過它將數據進行倒排文件處理加入索引文件,它的索引速度和查詢速度是相當快的,查詢百萬級數據毫秒級出結果,現在最火的Apache開源項目,版本更新速度很快現在已經到了2.0,每個版本更新的都比較大,目前用的最多的版本應該是1.4.3,但它有個不太方面的地方單個索引文件有2G文件限制,現在2.0版本沒有這個限制,我研究的比較多,它的擴展性比較好,可以很方面的擴充其分詞接口和查詢接口.
基于它的開發的系統很多,比如最常用的Eclipse的搜索功能,還有一些開源的軟件比如Compass,Nutch,Lius,還有我最近做的InSearch(企業級FTP文件網頁搜索)
6:公共Util類
主要是Jakarta-Commons類庫,其中最常用得是以下幾個類庫
1) Jakarta-Commons-Language
最常用得類是StringUtils類,提供了使用的字符串處理的常用方法效率比較高
2) Jakarta-Commons-Beantuils
主要用Beantuils能夠獲得反射函數封裝及對嵌套屬性,map,array型屬性的讀取。
3) Jakarta-Commons-Collections
里面有很多Utils方法
7 日志管理
Log4J
任務是日志記錄,分為Info,Warn,error幾個層次可以更好的調試程序
8 開源的J2EE框架
1) Appfuse
Appfuse是Matt Raible 開發的一個指導性的入門級J2EE框架, 它對如何集成流行的Spring、Hibernate、iBatis、Struts、Xdcolet、JUnit等基礎框架給出了示范. 在持久層,AppFuse采用了Hibernate O/R映射工具;在容器方面,它采用了Spring,用戶可以自由選擇Struts、Spring/MVC,Webwork,JSF這幾個Web框架。
2) SpringSide
.SpringSide較完整的演示了企業應用的各個方面,是一個電子商務網站的應用 SpringSide也大量參考了Appfuse中的優秀經驗。最重要的是它是國內的一個開源項目,可以了解到國內現在的一些實際技術動態和方向很有指導意義…
9:模版 Template
主要有Veloctiy和Freemarker
模板用Servlet提供的數據動態地生成 HTML。編譯器速度快,輸出接近靜態HTML 頁面的速度。
10:工作流
我所知道比較出名的主要有JBpm Shark Osworkflow,由于對它沒有過多的研究所以還不是很清楚之間有什么區別.
項目管理軟件
dotProject:是一個基于LAMP的開源項目管理軟件。最出名的項目管理軟件
JIRA: 項目計劃,任務安排,錯誤管理
Bugzilla:提交和管理bug,和eclipse集成,可以通過安裝MyEclipse配置一下即可使用
BugFree借鑒微軟公司軟件研發理念、免費開放源代碼、基于Web的精簡版Bug管理
CVS:這個就不介紹了都在用.
SVN: SubVersion已逐漸超越CVS,更適應于JavaEE的項目。Apache用了它很久后,Sourceforge剛剛推出SVN的支持。
測試用例:主要JUnit單元測試,編寫TestCase,Spring也對Junit做了很好的支持
后記:
以Spring為主的應用開發可選用的組件中間件真是眼花繚亂,所以針對不同的項目需求可以利用不同的開源產品解決,比如用Spring+Hibernate/ iBATIS或Spring+WebWork+Hibernate/ iBATIS或Spring+Struts+Hibernate/ iBATIS,合理的框架設計和代碼復用設計對項目開發效率和程序性能有很大的提高,也有利于后期的維護.
現在主流的Java集成開發工具(IDE)Eclipse越來越流行,自從3.0版本以后Eclipse也逐漸穩定,現在Eclipse開發社區的人員越來越多版本更新速度也越來越快,目前最近的版本是3.2,Eclipse相比較一些其他的IDE如NetBeans/SunOne Studio,Jbuilder,IntelliJ IDEA主要的優點在于它是免費的、開放源代碼的、質量很好,而且非常容易定制。Eclipse的最大的優勢在于它的插件機制,除了很小的運行時內核外,Eclipse的所有的東西都是插件.現在插件超多眼花繚亂…正確有效的使用一些插件對開發速度很有提高.Eclipse的插件開發機制也比較簡單運用Eclipse的基礎庫SWT,JFace,和插件開發環境PDE可以定制開發一些符合自己框架標準的代碼生成框架,避免重復性代碼的編寫,把更多的精力放在如何構造合理的架構提高項目開發的速度方面.
首先介紹插件的配置方法一般來講對于非安裝文件,將插件的文件夾features, plugins放在Eclipse目錄下的features,plugins下即可.如果不能使用可能是因為插件版本和當前Eclipse版本不匹配所致,下載合適的插件版本即可.目前3.1版本比較穩定在此平臺應用的插件也比較多
下面主要介紹開發J2EE常用的插件的功能和簡單快速的使用方法:
1:MyEclipse
很強大的Eclipse增強插件集合,集成了很多的J2EE的功能,比如Spring,Hibernate,Struts,EJB,JSTL等插件,也支持Tomcat,Jboss,Weblogic,WebSphere等主流容器的配置,還有DatabaseExplorer數據庫管理插件,也集成了主流的bug管理系統bugzilla的錯誤提交,也提供了UML圖的繪制插件,是一個比較全面的插件集合,官方更新速度很快,現在最新的版本是MyEclipse 5.0 GA支持Eclipse3.2不過是收費的,可以在網上找到破解碼:-)
對于Tomcat開發為主流的常用的功能它的Reload機制,在window->Preferences->MyEclipse下Application Servers里Tomcat設置TomcatHome,然后通過快捷工具欄中的Run/Stop/Restart MyEclipse Application Servers啟動服務,這樣你的項目修改Java類不需要在重啟Tomcat就可以實現改過的功能.
如果初時工程設為Web Projects可以通過Myeclipse為其添加Spring,Struts,Jsf,Jstl,Hibernate的庫,設置方法為右鍵你的工程然后在菜單中選擇Myeclipse在彈出菜單中Add相應的Capabilities.也可以選擇為工程添加J2EE容器,如果上一步配置了Myeclipse Application Servers可以Add Tomcat Server,然后它會自動部署到Tomcat的webapps下.
DatabaseExplorer配置比較簡單也比較好用,配置方法為:New一個Driver選擇相應SqlServer2000,或Oracle的驅動jar,按照提示配置好數據庫連接字符串就可以操作數據庫了,也可以執行sql查詢.
Myeclipse為開發便利為Eclipse開發了它的幾個視圖,可以通過菜單window->Open Perspective選擇適當的視圖,如MyEclipse Database Explorer,MyEclipse J2EE Development,MyEclipseHibernate和MyEclipse Web2.0
MyEclipse的缺點就在于對系統要求太高,開文件過多會死掉有時,所以一般1G內存跑起來比較爽,可以通過-Xmx屬性對Eclipse使用的內存進行擴充.
對于UML方面說一下一般MyEclipse這個功能是個花瓶中看不中用,小的功能比較簡單的UML圖還可以夠用,對于UML的正向或者逆向工程不支持,所以一般不用它.建議使用”Eclipse UML”插件
2. Lomboz
Lomboz也是一個功能強大的基于J2EE的集成插件其功能主要是JSP高亮顯示,語法檢查和編碼助手,支持部署J2EE Web應用程序,利用Wizard創建Web應用和常用的代碼生成,支持JSP的調試.
Lomboz的配置很簡單將其放在Eclipse相應的文件夾即可.
Lomboz的優勢在于可以調試Jsp, Lomboz的調試原理在于對要調試的jsp頁面所產生的java代碼進行調試,和java工程的調試過程一樣,調試方法是打開Lomboz J2EE View 選擇服務器,單擊右鍵debug server,打開jsp所生成的java文件設置斷點,在IE打開jsp就可以激活調試進行jsp調試,其實我感覺最好的調試方法是System.out.println,比較快捷.
3.SWT-Designer
看名字就知道是開發Java圖形用戶界面的插件,可以用于開發PDE插件或基于SWT的應用程序,非常強大的開發工具收費的不過比VE穩定很多,可以畫界面,使用方法比較簡單
在官方下載找個注冊碼激活就可以.
4.JSEclipse
這個對于WEB開發很有用可以對javascript進行高亮顯示,語法檢查和編碼助手,特別是在Myeclipse或Lomboz下js開發時有時候沒有編碼助手,錯誤也沒有提示,很不方面,JSEclipse可以進行編碼提示和錯誤提示很實用!對于以后的ajax編碼和富客戶端開發調試效率會有很大的提高!
5.Properties Editor
編輯java的屬性文件,并可以自動存盤為Unicode格式
6.XMLBuddy
XMLBuddy 主要用于編輯XML文件
7.Log4E
Log4E Log4j插件,提供各種和Log4j相關的任務,如為方法、類添加一個logger等,主要優點在于代碼生成免去了每個類都要logger一下的麻煩
.使用方法比較簡單..選中某個.java文件右鍵選擇Log4J.
8.FreeMarker Eclipse Plugin / FreeMarker IDE
FreeMarker沒有語法高亮看起來確實很不爽…調試起來比較痛苦這個插件用來在Eclipse中支持FreeMarker模板語言.它包括語法高亮顯示,語法錯誤提示、視圖等.
9.Veloedit
Velocity模版開發插件與FreeMarker類似
以上幾個都是最常用的J2EE的插件,我都測試過很方便,在網上都有新版本下載,如果你的內存比較大可以用MyEclipse作為主要開發工具,輔助其他幾個實用的插件,如果你機子配置不是很高.采用Lomboz加上其他幾個插件也可.當然還有很多實用的插件這里沒有介紹比如Profiler(性能跟蹤、測量工具,能跟蹤、測量BS程序) VSS Plugin for Eclipse (Microsoft Visual SourceSafe (VSS)),大家可以發掘介紹…
常用Eclipse快捷鍵介紹
主要總結了最最常用的Eclipse快捷鍵的功能
F3: 打開申明(Open declaration)。
Control-Shift-G: 在workspace中搜索引用(reference)。這個熱鍵的作用和F3恰好相反.
Control-Shift-F: 根據代碼風格設定重新格式化代碼.
Control-Shift-O: 快速引入要import的類說明.
Control-O: 快速概要。通過這個快捷鍵,你可以迅速的跳到一個方法或者屬性.
Control-/: 對一行注釋或取消注釋。對于多行也同樣適用。
Spirng的InitializingBean為bean提供了定義初始化方法的方式。InitializingBean是一個接口,它僅僅包含一個方法:afterPropertiesSet()。

Bean實現這個接口,在afterPropertiesSet()中編寫初始化代碼:
package research.spring.beanfactory.ch4;
import org.springframework.beans.factory.InitializingBean;
public class LifeCycleBean implements InitializingBean{
public void afterPropertiesSet() throws Exception {
System.out.println("LifeCycleBean initializing...");
}
}
在xml配置文件中并不需要對bean進行特殊的配置:
xml version="1.0" encoding="UTF-8"?>
DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean name="lifeBean" class="research.spring.beanfactory.ch4.LifeCycleBean">
bean>
beans>
編寫測試程序進行測試:
package research.spring.beanfactory.ch4;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
public class LifeCycleTest {
public static void main(String[] args) {
XmlBeanFactory factory=new XmlBeanFactory(new ClassPathResource(
"research/spring/beanfactory/ch4/context.xml"));
factory.getBean("lifeBean");
}
}
運行上面的程序我們會看到:“LifeCycleBean initializing...”,這說明bean的afterPropertiesSet已經被Spring調用了。
Spring在設置完一個bean所有的合作者后,會檢查bean是否實現了InitializingBean接口,如果實現就調用bean的afterPropertiesSet方法。
SHAPE \* MERGEFORMAT
查看bean是否實現InitializingBean接口
|
Spring雖然可以通過InitializingBean完成一個bean初始化后對這個bean的回調,但是這種方式要求bean實現 InitializingBean接口。一但bean實現了InitializingBean接口,那么這個bean的代碼就和Spring耦合到一起了。通常情況下我不鼓勵bean直接實現InitializingBean,可以使用Spring提供的init-method的功能來執行一個bean 子定義的初始化方法。
寫一個java class,這個類不實現任何Spring的接口。定義一個沒有參數的方法init()。
package research.spring.beanfactory.ch4;
public class LifeCycleBean{
public void init(){
System.out.println("LifeCycleBean.init...");
}
}
在Spring中配置這個bean:
xml version="1.0" encoding="UTF-8"?>
DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean name="lifeBean" class="research.spring.beanfactory.ch4.LifeCycleBean"
init-method="init">
bean>
beans>
當Spring實例化lifeBean時,你會在控制臺上看到” LifeCycleBean.init...”。
Spring要求init-method是一個無參數的方法,如果init-method指定的方法中有參數,那么Spring將會拋出java.lang.NoSuchMethodException
init-method指定的方法可以是public、protected以及private的,并且方法也可以是final的。
init-method指定的方法可以是聲明為拋出異常的,就像這樣:
final protected void init() throws Exception{
System.out.println("init method...");
if(true) throw new Exception("init exception");
}
如果在init-method方法中拋出了異常,那么Spring將中止這個Bean的后續處理,并且拋出一個org.springframework.beans.factory.BeanCreationException異常。
InitializingBean和init-method可以一起使用,Spring會先處理InitializingBean再處理init-method。
org.springframework.beans.factory.support. AbstractAutowireCapableBeanFactory完成一個Bean初始化方法的調用工作。 AbstractAutowireCapableBeanFactory是XmlBeanFactory的超類,再 AbstractAutowireCapableBeanFactory的invokeInitMethods方法中實現調用一個Bean初始化方法:
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.java:
//……
//在一個bean的合作者設備完成后,執行一個bean的初始化方法。
protected void invokeInitMethods(String beanName, Object bean, RootBeanDefinition mergedBeanDefinition)
throws Throwable {
//判斷bean是否實現了InitializingBean接口
if (bean instanceof InitializingBean) {
if (logger.isDebugEnabled()) {
logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
}
//調用afterPropertiesSet方法
((InitializingBean) bean).afterPropertiesSet();
}
//判斷bean是否定義了init-method
if(mergedBeanDefinition!=null&&mergedBeanDefinition.getInitMethodName() != null) {
//調用invokeCustomInitMethod方法來執行init-method定義的方法
invokeCustomInitMethod(beanName, bean, mergedBeanDefinition.getInitMethodName());
}
}
//執行一個bean定義的init-method方法
protected void invokeCustomInitMethod(String beanName, Object bean, String initMethodName)
throws Throwable {
if (logger.isDebugEnabled()) {
logger.debug("Invoking custom init method '" + initMethodName +
"' on bean with name '" + beanName + "'");
}
//使用方法名,反射Method對象
Method initMethod = BeanUtils.findMethod(bean.getClass(), initMethodName, null);
if (initMethod == null) {
throw new NoSuchMethodException(
"Couldn't find an init method named '" + initMethodName + "' on bean with name '" + beanName + "'");
}
//判斷方法是否是public
if (!Modifier.isPublic(initMethod.getModifiers())) {
//設置accessible為true,可以訪問private方法。
initMethod.setAccessible(true);
}
try {
//反射執行這個方法
initMethod.invoke(bean, (Object[]) null);
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}
//………..
通過分析上面的源代碼我們可以看到,init-method是通過反射執行的,而afterPropertiesSet是直接執行的。所以 afterPropertiesSet的執行效率比init-method要高,不過init-method消除了bean對Spring依賴。在實際使用時我推薦使用init-method。
需要注意的是Spring總是先處理bean定義的InitializingBean,然后才處理init-method。如果在Spirng處理InitializingBean時出錯,那么Spring將直接拋出異常,不會再繼續處理init-method。
如果一個bean被定義為非單例的,那么afterPropertiesSet和init-method在bean的每一個實例被創建時都會執行。單例 bean的afterPropertiesSet和init-method只在bean第一次被實例時調用一次。大多數情況下 afterPropertiesSet和init-method都應用在單例的bean上。
Spring BeanFactory提供了類似pico container中自動裝配組件依賴的對象的功能。自動裝配能應用在每個組件上,可以為一些組件定義自動裝配,而另一些組件則不使用。
使用”autowire”屬性可以設置自動裝配,autowire有五種模式:
默認屬性,不進行自動裝配。
通過bean的屬性名稱自動裝配合作者。
SHAPE \* MERGEFORMAT
Spring用bean 中set方法名和BeanFactory中定義的合作者的名稱做匹配,一但2者匹配,Sping就會把合作者進行注入。
可以使用id屬性也可以使用name屬性定義合作者的名稱,這2個屬性在Spring進行自動裝配時沒有區別。
當有多個名稱相同的合作者在Spring中定義時,Srping在自動裝配時選擇最后一個定義的合作者注入。
SHAPE \* MERGEFORMAT
在多個合作者名稱相同進行自動裝配時,合作者的id屬性并不會比name屬性優先處理。無論怎樣定義Spring總會把最后一個定義的合作者注入。
通過bean set方法中參數的類型和BeanFactory中定義合作者的類型做匹配,Spring會找到匹配的合作者進行注入。
SHAPE \* MERGEFORMAT
在byType自動裝配模式中,Spring不關心合作者的名稱,只關心合作者的類型是否滿足條件。
類似上面介紹的byName的方式,在byType方式中,當具有相同名稱并且有相同類型的多個合作者被找到時,Spring會注入最后一個定義的合作者。
SHAPE \* MERGEFORMAT
在byType裝配時,如果有2個不同名稱但是類型相同的合作者被找到,那么Spring會拋出一個依賴異常。
SHAPE \* MERGEFORMAT
拋出依賴異常,通知用戶在byType方式中同樣類型的Bean只能定義一個。
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'dao' defined in class path resource [research/spring/beanfactory/ch3/context.xml]: Unsatisfied dependency expressed through bean property 'database': There are 2 beans of type [class research.spring.beanfactory.ch3.Database] for autowire by type. There should have been 1 to be able to autowire property 'database' of bean 'dao'...
constructor
constructor其實時按byType的方式進行構造函數的注入。
SHAPE \* MERGEFORMAT
constructor裝配方式不關心構造參數的順序,無論構造函數參數的順序如何Spring都會按類型匹配到正確的合作者進行注入。
在byType方式中,當沒有找到類型相同的合作者時Spring什么都不會去做。但是在constructor方式中,當沒有找到和Bean構造函數中參數類型相匹配的合作者時,Spring會拋出異常。
Spring在進行constructor方式的自動裝配時,強制要求所有的構造函數中所有的合作者都必須存在。
在autodetect的方式中,Spring檢查一個Bean內部是否有默認的構造函數。如果有默認的參數Spring就使用byType的方式進行自動裝配。如果沒有默認的構造函數Spring則使用constructor的方式進行自動裝配。
如果一個Bean同時定義了默認構造函數和帶參數的構造函數,Spring仍會使用byType的方式進行裝配。
不管使用上述哪種裝配方式,都可以在Bean中顯示的定義合作者。顯示定義的依賴關系優先級比自動裝配高。
自動裝配的功能可以和自動依賴檢查一起使用。Spring會首先進行自動裝配,然后在進行依賴檢查。
自動裝配提供了簡化配置的可能性,但是我并不建議在項目中大量的使用自動裝配,特別時byType方式。因為自動裝配,尤其時byType方式,破壞了Bean和合作者之間顯示的依賴關系,所有的依賴關系都時不明顯的。在使用自動裝配后我們的依賴關系需要到源代碼中才能看到,這使得維護或文檔化Bean的依賴關系變得很困難。
適當的使用自動裝配比如byName方式的裝配,是有一些好處的。比如我們在一些特定的范圍里可以借助byName自動裝配的功能來實現“
以慣例來代替配置”的框架。
自動依賴檢查可以保證所有java bean中的屬性(set方法)都在Spring中正確的配置。如果在一個java bean中定義了一個name屬性,并且也setName方法。那么在開啟自動依賴檢查功能后,就必須在Spring中定義這個屬性,否則Spring將拋出異常。
請看下面的例子:
Dao.java
包含一個setName方法。
package research.spring.beanfactory.ch3;
public class Dao {
private String name;
public void setName(String name) {
this.name = name;
}
}
context.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean name="dao" class="research.spring.beanfactory.ch3.Dao"> </bean>
<bean id="database" class="research.spring.beanfactory.ch3.Database">
</bean>
</beans>
我們在context.xml沒有定義Dao的name屬性。上面的配置,Spring可以正常的實例化Dao對象。
下面我們修改context.xml:
我們通過dependency-check=all,在Dao上增加了自動依賴檢查的功能。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean name="dao" class="research.spring.beanfactory.ch3.Dao" dependency-check="all" > </bean>
<bean id="database" class="research.spring.beanfactory.ch3.Database">
</bean>
</beans>
當配置依賴檢查時,Spring實例化Dao時會拋出一個異常:

Spring定義了4種依賴檢查的策略:
不進行依賴檢查。
只對簡單屬性和集合中的簡單屬性進行檢查。不對依賴的對象檢查。
只對為對象類型的屬性進行檢查。
對所有類型進行檢查。
如果把上面例子里的context.xml改成這樣:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean name="dao" class="research.spring.beanfactory.ch3.Dao" dependency-check="objects" > </bean>
<bean id="database" class="research.spring.beanfactory.ch3.Database">
</bean>
</beans>
Spring將不會拋出異常,因為objects只對依賴的對象進行檢查。
dependency-check在Spring中又以下的限制:
- 不能對構造函數中的參數進行檢查。
- 即使屬性中有默認值,只要包含了set方法,那么dependency-check仍然需要檢查Spring中是否配置了這個屬性。
package research.spring.beanfactory.ch3;
public class Dao {
private Database database;
private String name="chenjie";
//dependency-check仍然會檢查這個屬性是否配置注入
public void setName(String name) {
this.name = name;
}
public void setDatabase(Database database) {
this.database = database;
}
}
即使Dao設置里name得默認值,但是只要有setName方法,dependency-check仍然會判斷是否在配置文件中設置了setName對應的注入。
depend-on用來表示一個Bean的實例化依靠另一個Bean先實例化。如果在一個bean A上定義了depend-on B那么就表示:A 實例化前先實例化 B。
這種情況下,A可能根本不需要持有一個B對象。
比如說,你的DAO Bean實例化之前你必須要先實例化Database Bean,DAO Bean并不需要持有一個Database Bean的實例。因為DAO的使用是依賴Database啟動的,如果Database Bean不啟動,那么DAO即使實例化也是不可用的。這種情況DAO對Database的依賴是不直接的。
除了在DAO上使用構造函數注入Database Bean以外,Spring沒有任何依賴注入的關系能夠滿足上面的情況。但是DAO也許根本不需要Database的實例被注入,因為DAO是通過JDBC訪問數據庫的,它不需要調用Database 上的任何方法和屬性。
在這種情況下你可以使用depends-on來定義在DAO被實例化之前先去實例化Database。你可這樣定義:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean name="dao" class="research.spring.beanfactory.ch3.Dao" depends-on="database">
</bean>
<bean id="database" class="research.spring.beanfactory.ch3.Database">
</bean>
</beans>
通過定義depends-on=”database”可以控制Sping實例化dao的順序。在任何時候Spring總會保證實例化DAO之前先實例Database。
通常depends-on常常應用在上面的場景中。如果DAO depend-on Database的同時需要得到Database的實例,那么使用構造函數注入是一個比較好的解決辦法。因為構造函數注入的方式是要先實例化目標對象依賴的對象然后在實例化目標對象。關于構造函數的輸入請參考另一篇文章
《Spring內核研究-set方法注入和構造函數注入》
DAO depend-on Database時,也可以在DAO上定義setDatabase方法來接收一個Database的實例。這樣Sping會保證DAO創建前先創建Database實例,然后在把實例化DAO后調用DAO的setDatabase方法把剛才創建的Database的實例注入給DAO。前提條件時Database必須定義成單例的。否則Spring在DAO depend-on Database時會創建一個Database的實例,在DAO.setDatabase時又會創建Database另外的一個實例。這種情況可能不是你想要的,而且很可能會造成比較隱蔽的錯誤。
使用set方法注入depend-on的對象:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean name="dao" class="research.spring.beanfactory.ch3.Dao" depends-on="database ">
<property name="database">
<ref bean="database"></ref>
</property>
</bean>
<bean id="database" class="research.spring.beanfactory.ch3.Database">
</bean>
</beans>
一般在depends-on一個對象并且又需要這個對象實例的情況下,我都建議你使用構造函數的注入方式替換depend-on。只有不能構造函數中添加依賴對象參數的情況下才使用上面例子里的方式。
可以同時使用depends-on和構造函數注入,如A depends-on B 并且 new A(B b)。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean name="dao" class="research.spring.beanfactory.ch3.Dao" depends-on="database">
<constructor-arg>
<ref bean="database"></ref>
</constructor-arg>
</bean>
<bean id="database" class="research.spring.beanfactory.ch3.Database">
</bean>
</beans>
然而這種做法是不合適的,因為在構在函數中注入依賴對象的方式可以包含depends-on的情況。也就時說new A(B b)包含了A depends-on B的所有情況。既然已經定義了new A(B b)就沒有必要在定義A depends-on B。所以,new A(B b)可以替代A depends-on B。在A創建前必須創建B,而且A不需要使用B實例的情況下只能使用A depends-on B。
Spring允許Bean和Bean依賴的Bean(合作者)上同時定義depends-on。比如A depends-on B && B depends-on C && C depends-on D。下面這樣定義是合法的。Sping實例化他們的順序是D->C->B->A。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean name="a" class="research.spring.beanfactory.ch3.A" depends-on="b" />
<bean name="b" class="research.spring.beanfactory.ch3.B" depends-on="c" />
<bean name="c" class="research.spring.beanfactory.ch3.C" depends-on="D" />
<bean name="d" class="research.spring.beanfactory.ch3.D" />
</beans>
但是Spring不允許A depends-on B && B depends-on A的情況。看下面的例子,由于D又依賴回A,這種在依賴關系中形成了一個閉環,Spring將無法處理這種依賴關系。所以下面的這種定義是不合法的。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean name="a" class="research.spring.beanfactory.ch3.A" depends-on="b" />
<bean name="b" class="research.spring.beanfactory.ch3.B" depends-on="c" />
<bean name="c" class="research.spring.beanfactory.ch3.C" depends-on="D" />
<bean name="d" class="research.spring.beanfactory.ch3.D" depends-on="A"/>
</beans>
一個Bean可以同時depends-on多個對象如,A depends-on D,C,B。可以使用“,”或“;”定義多個depends-on的對象。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean name="a" class="research.spring.beanfactory.ch3.A" depends-on="d,c,b" />
<bean name="b" class="research.spring.beanfactory.ch3.B" />
<bean name="c" class="research.spring.beanfactory.ch3.C" />
<bean name="d" class="research.spring.beanfactory.ch3.D" />
</beans>
上面的例子中A的實例化需要先實例化D,C,B。Spring會按照depend-on中定義的順序來處理Bean。在這個例子里Spring實例化對象的順利是D->C->B->A。雖然實例化對象的順序和前面“A depends-on B && B depends-on C && C depends-on D”的情況一下,但是這里的意義是完全不同的。不能用“A depends-on D,C,B”代替“A depends-on B && B depends-on C && C depends-on D”。
depends-on是一個非常又用的功能,借助depends-on我們可以管理那些依賴關系不明顯或者沒有直接依賴關系的對象。
Spring專門設計了對工廠模式支持,你可以使用靜態工廠方法來創建一個Bean,也可以使用實例工廠的方法來創建Bean。下面分別介紹這2種方法。
定義一個Bean使用自己類上的靜態工廠方法來創建自己。
context.xml
factory-menthod定義了userDao Bean使用UserDao類的getInstance方法來創建自己的實例。userManager仍然通過lookup方法獲得userDao。Lookup方法并不關心一個Bean的實例時怎樣創建的,所以可以混合使用lookup方法和factory-menthod方法。
xml version="1.0" encoding="UTF-8"?>
DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean name="userManager"
class="research.spring.beanfactory.ch2.UserManager">
<lookup-method name="getUserDao" bean="userDao" />
bean>
<bean name="userDao" class="research.spring.beanfactory.ch2.UserDao"
factory-method="getInstance" / >
beans>
UserDao.java
增加一個getInstance方法來創建自己的實例。
package research.spring.beanfactory.ch2;
public class UserDao {
public static UserDao getInstance() {
return new UserDao("static factory method");
}
private String name = "";
public UserDao(String name) {
this.name = name;
}
public void create() {
System.out.println("create user from - " + name);
}
}
Test.java
package research.spring.beanfactory.ch2;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
public class Test {
public static void main(String[] args) {
XmlBeanFactory factory=new XmlBeanFactory(new ClassPathResource(
"research/spring/beanfactory/ch2/context.xml"));
UserManager manager=(UserManager) factory.getBean("userManager");
manager.createUser();
}
}
運行Test.java,你會看到:
create user from - static factory method
這說明userDao使用它自己得靜態工廠創建得。
靜態工廠方法存在一些限制:
- 靜態工廠方法上不能有參數,也不能在Spring種定義靜態工廠方法的參數。
- 靜態工廠方法只能是public的,不能是private或protected的。
- 靜態工廠方法不能和構造函數注入一起使用。下面的定義時不能正常工作的:
package research.spring.beanfactory.ch2;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
public class Test {
public static void main(String[] args) {
XmlBeanFactory factory=new XmlBeanFactory(new ClassPathResource(
"research/spring/beanfactory/ch2/context.xml"));
UserManager manager=(UserManager) factory.getBean("userManager");
manager.createUser();
}
}
定義一個Bean使用這個Bean的工廠對象上的工廠方法來創建自己。
我們定義一個UserDao的Factory來創建UserDao。
UserDaoFactory.java
package research.spring.beanfactory.ch2;
public class UserDaoFactory{
public UserDao getUserDao(){
return new UserDao("UserDaoFactory");
}
}
修改context.xml:
xml version="1.0" encoding="UTF-8"?>
DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean name="userManager"
class="research.spring.beanfactory.ch2.UserManager">
<lookup-method name="getUserDao" bean="userDao" />
bean>
<bean name="userDao" class="research.spring.beanfactory.ch2.UserDao"
factory-bean="userDaoFactory" factory-method="getUserDao" >
bean>
<bean name="userDaoFactory" class="research.spring.beanfactory.ch2.UserDaoFactory">
bean>
beans>
再次運行Test.java你會看到:
create user from – UserDaoFactory
通過上面的配置Spring已經使用userDaoFactory實例的工廠方法來創建userDao了。
- factory-bean定義了工廠Bean
- factory-method定義了工廠方法
實例工廠和靜態工廠一樣都存在相同的限制:
- 靜態工廠方法上不能有參數,也不能在Spring種定義靜態工廠方法的參數。
- 靜態工廠方法只能是public的,不能是private或protected的。
- 靜態工廠方法不能和構造函數注入一起使用。
和靜態工廠不同的是:
- 實例工廠方法不能是靜態的,而靜態工廠方法必須是靜態的。
通過上面的例子我們看到Spring對工廠模式對了完整的支持。但是這里還是需要說明,如果使用IoC模式設計的系統一般情況下不需要為任何Bean做工廠類。在我的觀點里,工廠模式僅僅是遺留系統,使用依賴注入模式可以取代工廠模式。Spring對工廠的支持僅僅是為了可以很好的集成遺留系統。
“Lookup方法”可以使Spring替換一個bean原有的,獲取其它對象具體的方法,并自動返回在容器中的查找結果。
我們來看這個例子:
UserDao.java
在UserDao的構造函數中接受一個name參數,創建UserDao的對象會把自己的名字傳遞給userDao,這樣userDao的create方法中就會把userDao的創建者打印出來。
package research.spring.beanfactory.ch2;
public class UserDao {
private String name="";
public UserDao(String name){
this.name=name;
}
public void create(){
System.out.println("create user from - "+name);
}
}
UserManager.java
在這段代碼中UserManager依靠getUserDao方法來獲取UserDao對象。由于在getUserDao方法里顯示的聲明了如何去實例一個UserDao,所以上面的代碼不符合IoC模式的風格。雖然使用GetUserDao封裝了UserDao的創建過程,但是UserManager和UserDao的關系仍然非常緊密。
package research.spring.beanfactory.ch2;
public class UserManager {
public UserDao getUserDao() {
return new UserDao("UserManager.getUserDao()");
}
public void createUser() {
UserDao dao = getUserDao(); //通過getUserDao獲得userDao
dao.create();
}
}
LookupMethodTest.java
通過BeanFactory獲得UserManager,并調用createUser方法。
package research.spring.beanfactory.ch2;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
public class LookupMethodTest {
public static void main(String[] args) {
XmlBeanFactory factory=new
XmlBeanFactory(new ClassPathResource("research/spring/beanfactory/ch2/context.xml"));
UserManager manager=(UserManager) factory.getBean("userManager");
manager.createUser(); //create a User
}
}
context.xml
xml version="1.0" encoding="UTF-8"?>
DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean name="userManager"
class="research.spring.beanfactory.ch2.UserManager">
bean>
<bean name="userDao class="research.spring.beanfactory.ch2.UserDao" >
bean>
beans>
運行LookupMethodTest你會看到屏幕輸入” create user from - UserManager.getUserDao()”。
由于是遺留系統,所以我們不能修改UserManager。現在我希望讓這個UserManager依賴的Dao對象由spring管理,而不修改原有的代碼。
在這個場景中我們就可以利用Spring提供的“Lookup方法”來替換原有的getUserDao方法,實現自動獲取userDao的功能。修改context.xml:
xml version="1.0" encoding="UTF-8"?>
DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean name="userManager"
class="research.spring.beanfactory.ch2.UserManager">
<lookup-method name="getUserDao" bean="userDao" />
bean>
<bean name="userDao" class="research.spring.beanfactory.ch2.UserDao" >
<constructor-arg>
<value>lookup methodvalue>
constructor-arg>
bean>
<bean name="userDaoFactory" class="research.spring.beanfactory.ch2.UserDaoFactory">
bean>
beans>
再次運行LookupMethodTest你會看到不同的輸出結果“create user from - lookup method”。字符串“lookup method”是通過構造函數注入給userDao的。原來的userManager.java并沒有作任何修改,仍然是通過UserDao dao = getUserDao();來獲得userDao的。這說明Spring已經替換了原有的getUserDao方法的實現,當執行getUserDao時Spring會在容器中尋找指定的Bean,并返回這個Bean。
通過這種機制我們可以在不修改原系統代碼的情況下,可以輕易的把UserDao換成別的類型相容的對象而不會影響原系統。Spring是使用CGLIB在字節碼級別動態實現出userManager的子類,并重寫getUserDao方法的方式來實現這個神奇的功能的。
修改LookupMethodTest.java:
package research.spring.beanfactory.ch2;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
public class LookupMethodTest {
public static void main(String[] args) {
XmlBeanFactory factory=new XmlBeanFactory(new ClassPathResource(
"research/spring/beanfactory/ch2/context.xml"));
UserManager manager=(UserManager) factory.getBean("userManager");
System.out.println(manager.toString()); //打印userManager的信息
manager.createUser(); //create a User
}
}
我們在獲取UserManager的實例后打印出這個實例的信息,再次運行LookupMethodTest你會看到:

注意manager.toString()打印出了:
這個是CGLIG動態生成的類,而不是原來的UserManager的實例。所以請記住在任何時候只要定義了一個Bean的Lookup方法,那么這個Bean的實例將是一個CGLIB動態生成的實例而不是原來類的實例。
Spring允許在一個Bean中定義多個Lookup方法。
<bean name="userManager"
class="research.spring.beanfactory.ch2.UserManager">
<lookup-method name="getUserDao" bean="userDao" />
<lookup-method name="getOtherDao" bean="otherDao" />
bean>
上面的做法是合法的,并且可以正常工作。
雖然Spring會用CGLIB動態生成一個帶有Lookup方法bean的子類,但是這并不影響Spring完成其它的功能。Sping還是允許Lookup方法和setXXX、構造函數注入以及后面我們將介紹的自動依賴檢查和自動裝配的功能同時使用。
Spring還允許Lookup方法中定義的方法帶有參數,但是Sping不會處理這些參數。
修改UserManager:
package research.spring.beanfactory.ch2;
public class UserManager {
private UserDao dao;
public void setDao(UserDao dao) {
this.dao = dao;
}
public UserDao getUserDao(String daoName) {
return new UserDao("UserManager.getUserDao()");
}
public void createUser() {
UserDao dao = getUserDao(“userDao”); //通過getUserDao獲得userDao
dao.create();
}
}
雖然方法上由參數,但是上面的代碼可以正常工作。Spring不會處理這些參數。
Spring對Lookup方法也存在一些限制:
- 方法不能是private的,但可以是protected的。
- 方法不能是靜態的。
有一個比較有趣的用法,就是在抽象類上定義Lookup方法。你一定記得經典的工廠模式吧。定義一個抽象工廠,然后為每一類具體產品實現一個具體產品的工廠。
一個抽象工廠:
package research.spring.beanfactory.ch2;
public abstract class Factory {
public abstract UserDao getProduct();
}
具體一類產品的工廠:
package research.spring.beanfactory.ch2;
public class UserDaoFactory extends Factory{
public UserDao getProduct(){
return new UserDao("UserDaoFactory");
}
}
用戶可以通過:
new UserDaoFactory().getProduce();
來獲取具體的UserDao產品。
但是如果有很多產品就需要做出實現出很多工廠如,DocumentDaoFactory、GroupDaoFactory等等,這樣系統中會出現大量的工廠。工廠的泛濫并不能說明系統的設計是合理的。
既然Spring可以在抽象類上使用Lookup方法,那么我們就可以不同實現真的去實現那么多的子類了。我們可以在抽象類上直接定義Lookup方法和目標對象。用戶直接通過抽象類來獲得需要的產品對象。看下面這個例子:
Factory.ava
package research.spring.beanfactory.ch2;
public abstract class Factory {
public abstract Object getProduct();
}
context.xml
如果指定userDaoFactory的類為一個抽象類,并且再這個bean里定義了Lookup方法,那么Spring會自動生成這個抽象類的子類實現。
xml version="1.0" encoding="UTF-8"?>
DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean name="userManager"
class="research.spring.beanfactory.ch2.UserManager">
<lookup-method name="getUserDao" bean="userDao" />
bean>
<bean name="userDao" class="research.spring.beanfactory.ch2.UserDao" >
<constructor-arg>
<value>lookup methodvalue>
constructor-arg>
bean>
<bean name="userDaoFactory" class="research.spring.beanfactory.ch2.Factory" singleton="false">
<lookup-method name="getProduct" bean="userDao" />
bean>
beans>
Test.java
package research.spring.beanfactory.ch2;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
public class LookupMethodTest {
public static void main(String[] args) {
XmlBeanFactory factory=new XmlBeanFactory(new ClassPathResource(
"research/spring/beanfactory/ch2/context.xml"));
//獲得抽象工廠
Factory abstractFactory=(Factory) factory.getBean("userDaoFactory");
UserDao userDao=(UserDao) abstractFactory.getProduct();
System.out.println(userDao.toString());
userDao.create();
}
}
運行Test你會看到:
research.spring.beanfactory.ch2.UserManager$$EnhancerByCGLIB$$cc2f8f1c@12c7568
create user from - lookup method
對,這個結果和上面的例子是完全一樣的。UserDao并沒有改變,我們通過抽象的Factory獲得了具體的UserDao的實例。這樣即使系統中很多的具體產品我們也不需要實現每類產品的工廠類了。只需要在系統中配置多個抽象工廠,并且配置每個工廠的singlton為false,在用戶使用時使用不同抽象工廠的實例就可以了。
<bean name="userDaoFactory" class="research.spring.beanfactory.ch2.Factory" singleton="false">
<lookup-method name="getProduct" bean="userDao" />
bean>
<bean name="documentDaoFactory" class="research.spring.beanfactory.ch2.Factory" singleton="false">
<lookup-method name="getProduct" bean="documentDao" />
bean>
Spring不關心抽象類中的定義的lookup方法是否時抽象的,Spring都會重寫這個方法。
既然Sping可以動態實現抽象類的子類那么,它能不能動態創建出實現一個接口的類呢。答案時肯定的。上面的例子可以直接把Factory變成一個接口,仍然可以正常工作。
這里需要注意的是,只要在一個Bean上明確的定義了Lookup方法,Spring才會使用CGLIB來做原對象的字節碼代理。如果一個沒有定義Lookup方法的抽象類或接口是不能直接被Spring實例的。
本文介紹了Lookup方法的使用和工作原理,希望讀者能夠對Lookup方法有了比較深入的了解。雖然我的例子可以簡化工廠模式,但是我并不鼓勵大家在實際系統中這樣做。因為我始終認為“工廠模式”只要在遺留系統中才會碰到。使用IoC模式基本上可以替代所有的對象創建模式。本章的例子只是為了說明Lookup方法如何使用,和Lookup方法的一些特殊情況。Lookup方法一般只在處理遺留代碼時使用。
Spring種提供了2種常用的注入方式,set方法注入和構造函數注入。由于這2種注入方式很相似,都可以滿足我們的需求,所以在大多數情況下我們忽視了這2種注入方式的區別。下面讓我們看看這2種注入方式的特點。
我們先看看Spring在使用set方法注入時,是怎樣實例化一個Bean和Bean的合作者的:
在A中有一個setB方法用來接收B對象的實例。那么Spring實例化A對象的過程如下:
在不考慮Bean的初始化方法和一些Spring回調的情況下,Spring首先去調用A對象的構造函數實例化A,然后查找A依賴的對象本例子中是B(合作者)。一但找到合作者,Spring就會調用合作者(B)的構造函數實例化B。如果B還有依賴的對象Spring會把B上依賴的所有對象都按照相同的機制實例化然后調用A對象的setB(B b)把b對象注入給A。
因為Spring調用一個對象的set方法注入前,這個對象必須先被實例化。所以在"使用set方法注入"的情況下Spring會首先調用對象的構造函數。
我們在來看通過構造函數注入的過程:
如果發現配置了對象的構造注入,那么Spring會在調用構造函數前把構造函數需要的依賴對象都實例化好,然后再把這些實例化后的對象作為參數去調用構造函數。
在使用構造函數和set方法依賴注入時,Spring處理對象和對象依賴的對象的順序時不一樣的。一般把一個Bean設計為構造函數接收依賴對象時,其實是表達了這樣一種關系:他們(依賴對象)不存在時我也不存在,即“沒有他們就沒有我”。
通過構造函數的注入方式其實表達了2個對象間的一種強的聚合關系:組合關系。就比如一輛車如果沒有輪子、引擎等部件那么車也就不存在了。而且車是由若干重要部件組成的,在這些部件沒有的情況下車也不可能存在。這里車和他的重要部件就時組合的關系。如果你的應用中有這樣類似的場景那么你應該使用“構造函數注入”的方式管理他們的關系。“構造函數注入”可以保證合作者先創建,在后在創建自己。
通過set方法注入的方式表達了2個對象間較弱的依賴關系:聚合關系。就像一輛車,如果沒有車內音像車也時可以工作的。當你不要求合作者于自己被創建時,“set方法注入”注入比較合適。
雖然在理論上“構造函數注入”和“set方法注入”代表2種不同的依賴強度,但是在spring中,spring并不會把無效的合作者傳遞給一個bean。如果合作者無效或不存在spring會拋出異常,這樣spring保證一個對象的合作者都是可用的。所以在spring中,“構造函數注入”和“set方法注入”唯一的區別在于2種方式創建合作者的順序不同。
使用構造函數依賴注入時,Spring保證所有一個對象所有依賴的對象先實例化后,才實例化這個對象。(沒有他們就沒有我原則)
使用set方法依賴注入時,Spring首先實例化對象,然后才實例化所有依賴的對象。