Eclipse 快捷鍵大全
類別 命令 鍵序列 說(shuō)明
C/C++ Source Add Block Comment Ctrl+Shift+/ C/C++ Editor
C/C++ Source Add Include Ctrl+Shift+N C/C++ Editor
C/C++ Source Comment Ctrl+/ C/C++ Editor
C/C++ Source Find Declaration Ctrl+G C/C++ Editor
C/C++ Source Find References Ctrl+Shift+G C/C++ Editor
C/C++ Source Format Ctrl+Shift+F C/C++ Editor
C/C++ Source Go to Matching Bracket Ctrl+Shift+P C/C++ Editor
C/C++ Source Go to next C/C++ member Ctrl+Shift+向下鍵 C/C++ Editor
C/C++ Source Go to previous C/C++ member Ctrl+Shift+向上鍵 C/C++ Editor
C/C++ Source Open Declaration F3 C/C++ Editor
C/C++ Source Open Definition Ctrl+F3 C/C++ Editor
C/C++ Source Open Type Ctrl+Shift+T C/C++ Editor
C/C++ Source Remove Block Comment Ctrl+Shift+\ C/C++ Editor
C/C++ Source Show outline Ctrl+O C/C++ Editor
C/C++ Source Uncomment Ctrl+\ C/C++ Editor
Makefile Source Comment Ctrl+/ Makefile Editor
Makefile Source Open declaration F3 Makefile Editor
Makefile Source Uncomment Ctrl+\ Makefile Editor
Refactor - C/C++ Redo - Refactoring Alt+Shift+Y C/C++ Editor
Refactor - C/C++ Rename - Refactoring Alt+Shift+R C/C++ Editor
Refactor - C/C++ Undo - Refactoring Alt+Shift+Z C/C++ Editor
View Zoom In Ctrl+= 在窗口中
View Zoom Out Ctrl+- 在窗口中
搜索 工作空間中的聲明 Ctrl+G 在窗口中
搜索 工作空間中的引用 Ctrl+Shift+G 在窗口中
搜索 打開“搜索”對(duì)話框 Ctrl+H 在窗口中
搜索 顯示“文件中的出現(xiàn)位置”快速菜單 Ctrl+Shift+U 在窗口中
文件 “新建”菜單 Alt+Shift+N 在窗口中
文件 保存 Ctrl+S 在窗口中
文件 全部保存 Ctrl+Shift+S 在窗口中
文件 全部關(guān)閉 Ctrl+Shift+F4 在窗口中
文件 全部關(guān)閉 Ctrl+Shift+W 在窗口中
文件 關(guān)閉 Ctrl+F4 在窗口中
文件 關(guān)閉 Ctrl+W 在窗口中
文件 刷新 F5 在窗口中
文件 屬性 Alt+Enter 在窗口中
文件 打印 Ctrl+P 在窗口中
文件 新建 Ctrl+N 在窗口中
文件 重命名 F2 在窗口中
文本編輯 上一個(gè)詞語(yǔ) Ctrl+左箭頭 編輯文本
文本編輯 上滾行 Ctrl+向上鍵 編輯文本
文本編輯 下一個(gè)詞語(yǔ) Ctrl+右箭頭 編輯文本
文本編輯 下滾行 Ctrl+向下鍵 編輯文本
文本編輯 全部展開 Ctrl+Numpad_Multiply 編輯文本
文本編輯 切換折疊 Ctrl+Numpad_Divide 編輯文本
文本編輯 刪除上一個(gè)詞語(yǔ) Ctrl+Backspace 編輯文本
文本編輯 刪除下一個(gè)詞語(yǔ) Ctrl+Delete 編輯文本
文本編輯 刪除至行末 Ctrl+Shift+Delete 編輯文本
文本編輯 刪除行 Ctrl+D 編輯文本
文本編輯 在當(dāng)前行上面插入行 Ctrl+Shift+Enter 編輯文本
文本編輯 在當(dāng)前行下面插入行 Shift+Enter 編輯文本
文本編輯 復(fù)制行 Ctrl+Alt+向下鍵 編輯文本
文本編輯 將行上移 Alt+向上鍵 編輯文本
文本編輯 將行下移 Alt+向下鍵 編輯文本
文本編輯 展開 Ctrl+Numpad_Add 編輯文本
文本編輯 折疊 Ctrl+Numpad_Subtract 編輯文本
文本編輯 改寫切換 Insert 編輯文本
文本編輯 更改為大寫 Ctrl+Shift+X 編輯文本
文本編輯 更改為小寫 Ctrl+Shift+Y 編輯文本
文本編輯 選擇上一個(gè)詞語(yǔ) Ctrl+Shift+左箭頭 編輯文本
文本編輯 選擇下一個(gè)詞語(yǔ) Ctrl+Shift+右箭頭 編輯文本
文本編輯 重復(fù)行 Ctrl+Alt+向上鍵 編輯文本
查看 Java 包資源管理器 Alt+Shift+Q,P 在窗口中
查看 Java 聲明 Alt+Shift+Q,D 在窗口中
查看 Java 類型層次結(jié)構(gòu) Alt+Shift+Q,T 在窗口中
查看 Javadoc Alt+Shift+Q,J 在窗口中
查看 變量 Alt+Shift+Q,V 在窗口中
查看 同步 Alt+Shift+Q,Y 在窗口中
查看 備忘單 Alt+Shift+Q,H 在窗口中
查看 控制臺(tái) Alt+Shift+Q,C 在窗口中
查看 搜索 Alt+Shift+Q,S 在窗口中
查看 斷點(diǎn) Alt+Shift+Q,B 在窗口中
查看 顯示視圖 (查看: 大綱) Alt+Shift+Q,O 在窗口中
查看 顯示視圖 (查看: 問(wèn)題) Alt+Shift+Q,X 在窗口中
瀏覽 &Quick Cross References Alt+Shift+P 編輯 Java 源代碼
瀏覽 Open AspectJ Type Alt+Shift+A 在窗口中
瀏覽 Open AspectJ Type in Hierarchy Alt+Shift+H 在窗口中
瀏覽 “顯示位置”菜單 Alt+Shift+W 在窗口中
瀏覽 上一個(gè)編輯位置 Ctrl+Q 在窗口中
瀏覽 下一頁(yè) Ctrl+. 在窗口中
瀏覽 前一頁(yè) Ctrl+, 在窗口中
瀏覽 前移歷史記錄 Alt+右箭頭 在窗口中
瀏覽 后退歷史記錄 Alt+左箭頭 在窗口中
瀏覽 在層次結(jié)構(gòu)中打開類型 Ctrl+Shift+H 在窗口中
瀏覽 快速大綱 Ctrl+O 編輯 Java 源代碼
瀏覽 快速層次結(jié)構(gòu) Ctrl+T 編輯 Java 源代碼
瀏覽 打開聲明 F3 在窗口中
瀏覽 打開外部 Javadoc Shift+F2 在窗口中
瀏覽 打開類型 Ctrl+Shift+T 在窗口中
瀏覽 打開類型層次結(jié)構(gòu) F4 在窗口中
瀏覽 打開結(jié)構(gòu) Ctrl+F3 編輯 Java 源代碼
瀏覽 打開調(diào)用層次結(jié)構(gòu) Ctrl+Alt+H 在窗口中
瀏覽 打開資源 Ctrl+Shift+R 在窗口中
瀏覽 轉(zhuǎn)至上一個(gè)成員 Ctrl+Shift+向上鍵 編輯 Java 源代碼
瀏覽 轉(zhuǎn)至下一個(gè)成員 Ctrl+Shift+向下鍵 編輯 Java 源代碼
瀏覽 轉(zhuǎn)至匹配的方括號(hào) Ctrl+Shift+P 編輯 Java 源代碼
瀏覽 轉(zhuǎn)至行 Ctrl+L 編輯文本
源代碼 切換 Ant 標(biāo)記出現(xiàn) Alt+Shift+O 編輯 Ant 構(gòu)建文件
源代碼 切換標(biāo)記出現(xiàn) Alt+Shift+O 編輯 Java 源代碼
源代碼 切換注釋 Ctrl+/ 編輯 Java 源代碼
源代碼 切換注釋 Ctrl+7 編輯 Java 源代碼
源代碼 切換注釋 Ctrl+Shift+C 編輯 Java 源代碼
源代碼 在文件中重命名 Alt+Shift+R 編輯 Ant 構(gòu)建文件
源代碼 快速輔助 - 在文件中重命名 Ctrl+2,R 編輯 Java 源代碼
源代碼 快速輔助 - 指定給字段 Ctrl+2,F(xiàn) 編輯 Java 源代碼
源代碼 快速輔助 - 指定給局部變量 Ctrl+2,L 編輯 Java 源代碼
源代碼 打開外部文檔 Shift+F2 編輯 Ant 構(gòu)建文件
源代碼 顯示工具提示描述 F2 編輯 Ant 構(gòu)建文件
源代碼 顯示源代碼快速菜單 Alt+Shift+S 在窗口中
源代碼 格式 Ctrl+Shift+F 編輯 Ant 構(gòu)建文件
源代碼 格式化 Ctrl+Shift+F 編輯 Java 源代碼
源代碼 添加 Javadoc 注釋 Alt+Shift+J 在窗口中
源代碼 添加塊注釋 Ctrl+Shift+/ 編輯 Java 源代碼
源代碼 添加導(dǎo)入 Ctrl+Shift+M 編輯 Java 源代碼
源代碼 組織導(dǎo)入 Ctrl+Shift+O 在窗口中
源代碼 縮進(jìn)行 Ctrl+I 編輯 Java 源代碼
源代碼 除去出現(xiàn)注釋 Alt+Shift+U 編輯 Java 源代碼
源代碼 除去塊注釋 Ctrl+Shift+\ 編輯 Java 源代碼
窗口 上一個(gè)編輯器 Ctrl+Shift+F6 在窗口中
窗口 上一個(gè)視圖 Ctrl+Shift+F7 在窗口中
窗口 上一個(gè)透視圖 Ctrl+Shift+F8 在窗口中
窗口 下一個(gè)編輯器 Ctrl+F6 在窗口中
窗口 下一個(gè)視圖 Ctrl+F7 在窗口中
窗口 下一個(gè)透視圖 Ctrl+F8 在窗口中
窗口 切換至編輯器 Ctrl+Shift+E 在窗口中
窗口 將活動(dòng)視圖或編輯器最大化 Ctrl+M 在窗口中
窗口 打開編輯器下拉列表 Ctrl+E 在窗口中
窗口 顯示標(biāo)尺上下文菜單 Ctrl+F10 編輯文本
窗口 顯示系統(tǒng)菜單 Alt+- 在窗口中
窗口 顯示視圖菜單 Ctrl+F10 在窗口中
窗口 顯示鍵輔助 Ctrl+Shift+L 在對(duì)話框和窗口中
窗口 激活編輯器 F12 在窗口中
編輯 Add Block Comment Ctrl+Shift+/ Editing in Structured Text Editors
編輯 Format Active Elements Ctrl+I Editing in Structured Text Editors
編輯 Format Document Ctrl+Shift+F Editing in Structured Text Editors
編輯 Move Alt+Shift+V Editing JSP Source
編輯 Occurrences in File Ctrl+Shift+A Editing in Structured Text Editors
編輯 Open Selection F3 Editing in Structured Text Editors
編輯 Quick Fix Ctrl+1 Editing in Structured Text Editors
編輯 Remove Block Comment Ctrl+Shift+\ Editing in Structured Text Editors
編輯 Rename Alt+Shift+R Editing JSP Source
編輯 Rename XSD element Alt+Shift+R Editing XSD context
編輯 Restore Last Selection Alt+Shift+向下鍵 Editing in Structured Text Editors
編輯 Select Enclosing Element Alt+Shift+向上鍵 Editing in Structured Text Editors
編輯 Select Next Element Alt+Shift+右箭頭 Editing in Structured Text Editors
編輯 Select Previous Element Alt+Shift+左箭頭 Editing in Structured Text Editors
編輯 Show Tooltip Description F2 Editing in Structured Text Editors
編輯 Toggle Comment Ctrl+Shift+C Editing in Structured Text Editors
編輯 “快速差別”開關(guān) Ctrl+Shift+Q 編輯文本
編輯 上下文信息 Alt+? 在窗口中
編輯 上下文信息 Alt+Shift+? 在窗口中
編輯 內(nèi)容輔助 Alt+/ 在對(duì)話框和窗口中
編輯 切換插入方式 Ctrl+Shift+Insert 編輯文本
編輯 刪除 Delete 在窗口中
編輯 剪切 Ctrl+X 在對(duì)話框和窗口中
編輯 剪切 Shift+Delete 在對(duì)話框和窗口中
編輯 增量查找 Ctrl+J 編輯文本
編輯 增量逆向查找 Ctrl+Shift+J 編輯文本
編輯 復(fù)制 Ctrl+C 在對(duì)話框和窗口中
編輯 復(fù)制 Ctrl+Insert 在對(duì)話框和窗口中
編輯 復(fù)原上一個(gè)選擇 Alt+Shift+向下鍵 編輯 Java 源代碼
編輯 快速修正 Ctrl+1 在窗口中
編輯 撤消 Ctrl+Z 在窗口中
編輯 文字補(bǔ)全 Ctrl+Alt+/ 編輯文本
編輯 顯示工具提示描述 F2 編輯 Java 源代碼
編輯 查找上一個(gè) Ctrl+Shift+K 編輯文本
編輯 查找下一個(gè) Ctrl+K 編輯文本
編輯 查找并替換 Ctrl+F 在窗口中
編輯 粘貼 Ctrl+V 在對(duì)話框和窗口中
編輯 粘貼 Shift+Insert 在對(duì)話框和窗口中
編輯 選擇上一個(gè)元素 Alt+Shift+左箭頭 編輯 Java 源代碼
編輯 選擇下一個(gè)元素 Alt+Shift+右箭頭 編輯 Java 源代碼
編輯 選擇全部 Ctrl+A 在對(duì)話框和窗口中
編輯 選擇外層元素 Alt+Shift+向上鍵 編輯 Java 源代碼
編輯 重做 Ctrl+Y 在窗口中
運(yùn)行/調(diào)試 Debug AspectJ/Java Application Alt+Shift+D,C 在窗口中
運(yùn)行/調(diào)試 Debug on Server Alt+Shift+D,R 在窗口中
運(yùn)行/調(diào)試 EOF Ctrl+Z 在控制臺(tái)中
運(yùn)行/調(diào)試 Profile on Server Alt+Shift+P,R 在窗口中
運(yùn)行/調(diào)試 Run AspectJ/Java Application Alt+Shift+X,C 在窗口中
運(yùn)行/調(diào)試 Run on Server Alt+Shift+X,R 在窗口中
運(yùn)行/調(diào)試 切換單步執(zhí)行過(guò)濾器 Shift+F5 在窗口中
運(yùn)行/調(diào)試 切換行斷點(diǎn) Ctrl+Shift+B 在窗口中
運(yùn)行/調(diào)試 單步跳入 F5 調(diào)試
運(yùn)行/調(diào)試 單步跳入選擇的內(nèi)容 Ctrl+F5 調(diào)試
運(yùn)行/調(diào)試 單步跳過(guò) F6 調(diào)試
運(yùn)行/調(diào)試 單步返回 F7 調(diào)試
運(yùn)行/調(diào)試 執(zhí)行 Ctrl+U 在窗口中
運(yùn)行/調(diào)試 顯示 Ctrl+Shift+D 在對(duì)話框和窗口中
運(yùn)行/調(diào)試 檢查 Ctrl+Shift+I 在對(duì)話框和窗口中
運(yùn)行/調(diào)試 繼續(xù) F8 調(diào)試
運(yùn)行/調(diào)試 調(diào)試 Ant 構(gòu)建 Alt+Shift+D,Q 在窗口中
運(yùn)行/調(diào)試 調(diào)試 Eclipse 應(yīng)用程序 Alt+Shift+D,E 在窗口中
運(yùn)行/調(diào)試 調(diào)試 JUnit 插件測(cè)試 Alt+Shift+D,P 在窗口中
運(yùn)行/調(diào)試 調(diào)試 JUnit 測(cè)試 Alt+Shift+D,T 在窗口中
運(yùn)行/調(diào)試 調(diào)試 Java Applet Alt+Shift+D,A 在窗口中
運(yùn)行/調(diào)試 調(diào)試 Java 應(yīng)用程序 Alt+Shift+D,J 在窗口中
運(yùn)行/調(diào)試 調(diào)試 SWT 應(yīng)用程序 Alt+Shift+D,S 在窗口中
運(yùn)行/調(diào)試 調(diào)試上次啟動(dòng) F11 在窗口中
運(yùn)行/調(diào)試 運(yùn)行 Ant 構(gòu)建 Alt+Shift+X,Q 在窗口中
運(yùn)行/調(diào)試 運(yùn)行 Eclipse 應(yīng)用程序 Alt+Shift+X,E 在窗口中
運(yùn)行/調(diào)試 運(yùn)行 JUnit 插件測(cè)試 Alt+Shift+X,P 在窗口中
運(yùn)行/調(diào)試 運(yùn)行 JUnit 測(cè)試 Alt+Shift+X,T 在窗口中
運(yùn)行/調(diào)試 運(yùn)行 Java Applet Alt+Shift+X,A 在窗口中
運(yùn)行/調(diào)試 運(yùn)行 Java 應(yīng)用程序 Alt+Shift+X,J 在窗口中
運(yùn)行/調(diào)試 運(yùn)行 SWT 應(yīng)用程序 Alt+Shift+X,S 在窗口中
運(yùn)行/調(diào)試 運(yùn)行上次啟動(dòng) Ctrl+F11 在窗口中
運(yùn)行/調(diào)試 運(yùn)行至行 Ctrl+R 調(diào)試
重構(gòu) - Java 內(nèi)聯(lián) Alt+Shift+I 在窗口中
重構(gòu) - Java 將局部變量轉(zhuǎn)換為字段 Alt+Shift+F 編輯 Java 源代碼
重構(gòu) - Java 抽取局部變量 Alt+Shift+L 在窗口中
重構(gòu) - Java 抽取方法 Alt+Shift+M 在窗口中
重構(gòu) - Java 撤銷 - 重構(gòu) Alt+Shift+Z 在窗口中
重構(gòu) - Java 顯示重構(gòu)快速菜單 Alt+Shift+T 在窗口中
重構(gòu) - Java 更改方法特征符 Alt+Shift+C 在窗口中
重構(gòu) - Java 移動(dòng) - 重構(gòu) Alt+Shift+V 在窗口中
重構(gòu) - Java 重做 - 重構(gòu) Alt+Shift+Y 在窗口中
重構(gòu) - Java 重命名 - 重構(gòu) Alt+Shift+R 在窗口中
項(xiàng)目 全部構(gòu)建 Ctrl+B 在窗口中
Spring學(xué)習(xí)筆記(一)依賴注入
?
依賴注入——是Spring最靈魂的設(shè)計(jì)思想,有人也叫做控制反轉(zhuǎn)。
1、不管是依賴注入(DI:Dependency Injection)還是控制反轉(zhuǎn)(IoC:Inversion of Control)它的思想是:控制權(quán)由應(yīng)用代碼中轉(zhuǎn)到了外部
容器,即組件之間的依賴關(guān)系由容器在運(yùn)行期決定,形象的來(lái)說(shuō),即由容器動(dòng)態(tài)的將某種依賴關(guān)系注入到組件之中。
2、依賴注入的目標(biāo)并非為軟件系統(tǒng)帶來(lái)更多的功能,而是為了提升組件重用的概率,帶來(lái)靈活性。
3、舉個(gè)例子來(lái)說(shuō)明這個(gè)問(wèn)題。
我曾看到夏昕的《Spring 開發(fā)指南》,上面為說(shuō)明這個(gè)思想,舉了電腦、USB硬盤和U盤的例子,感覺還是不太貼切,今天想了個(gè)自認(rèn)為比較好
理解的例子:
有兩種變形金剛的玩具,
一種是固定的,我把它比作原來(lái)那種控制權(quán)由應(yīng)用代碼寫死的程序
一種可以拆開重新裝配的,我把它比作用了依賴注入設(shè)計(jì)思想的程序
變形金剛的各個(gè)部件就象程序的各個(gè)組件。
變形金剛的廠家,相對(duì)它來(lái)說(shuō)是內(nèi)部的。就象程序的代碼
變形金剛的玩家,相對(duì)它來(lái)說(shuō)是外部的。就象程序的容器
大家試想一下,固定變形金剛的控制權(quán)是不是廠家決定的,外部無(wú)能為力
而可拆卸的變形金剛的控制權(quán)轉(zhuǎn)移到了外部的玩家,玩家在玩之前可以重新決定各個(gè)組件的連接關(guān)系
而這種組件的連接圖是不是也很像依賴注入思想里的配置文件。
大家再?gòu)倪@個(gè)例子分析一下依賴注入的目標(biāo)。
Spring使用BeanFactory模式來(lái)管理Bean,但Spring中提到的Bean不是標(biāo)準(zhǔn)的意義上的JavaBean(僅包含一個(gè)默認(rèn)的構(gòu)造函數(shù),在屬性后面定義相對(duì)應(yīng)的setter和getter方法),而是任何你想讓它管理的類,比如連接池、甚至BeanFactory本身。
一)Bean的設(shè)計(jì)常用下面幾種模式
1、標(biāo)準(zhǔn)Bean:
使用默認(rèn)的構(gòu)造函數(shù)和基于setter、getter方法的依賴注射
Bean類代碼:
java代碼:?
|
public
class ExampleBean { ? ? private BeanOne beanOne; ? ? private BeanTwo beanTwo; ? ? privateint count; ? ? ? ? publicvoid setBeanOne(BeanOne beanOne){ ? ? ? ? this.beanOne = beanOne; ? ? } ? ? ? ? publicvoid setBeanTwo(BeanTwo beanTwo){ ? ? ? ? this.beanTwo = beanTwo; ? ? } ? ? ? ? publicvoid setCount(int count){ ? ? ? ? this.count = count; ? ? }? ? }
|
在配置文件中定義:
java代碼:?
|
<bean id="exampleBean" class="examples.ExampleBean"> ? ? <property name="beanOne"><ref bean="bean1"/></property> ? ? <property name="beanTwo"><ref bean="bean2"/></property> ? ? <property name="count"><value>1</value></property> </bean>
<bean id="bean1" class="examples.BeanOne"/> <bean id="bean2" class="examples.BeanTwo"/>
|
2、構(gòu)造函數(shù)模式
自定義的構(gòu)造函數(shù),基于構(gòu)造函數(shù)參數(shù)的依賴注射
Bean類代碼:
java代碼:?
|
public
class ExampleBean { ? ? private BeanOne beanOne; ? ? private BeanTwo beanTwo; ? ? privateint count; ? ? ? ? public ExampleBean(BeanOne beanOne, BeanTwo beanTwo, int count){ ? ? ? ? this.beanOne = beanOne; ? ? ? ? this.beanTwo = beanTwo; ? ? ? ? this.count = count; ? ? } }
|
在配置文件中定義:
java代碼:?
|
<bean id="exampleBean" class="examples.ExampleBean"> ? ? <constructor-arg><ref bean="bean1"/></constructor-arg> ? ? <constructor-arg><ref bean="bean2"/></constructor-arg> ? ? <constructor-arg><value>1</value></constructor-arg> </bean>
<bean id="bean1" class="examples.BeanOne"/> <bean id="bean2" class="examples.BeanTwo"/>
|
3、靜態(tài)工廠方法模式
靜態(tài)工廠方法必須是static的,基于方法參數(shù)的依賴注射。
Bean類代碼:
java代碼:?
|
public
class ExampleBean { ? ? private BeanOne beanOne; ? ? private BeanTwo beanTwo; ? ? privateint count; ? ? //構(gòu)造函數(shù)私有 ? ? private ExampleBean(BeanOne beanOne, BeanTwo beanTwo, int count){ ? ? ? ? this.beanOne = beanOne; ? ? ? ? this.beanTwo = beanTwo; ? ? ? ? this.count = count; ? ? } ? ? //對(duì)外提供靜態(tài)的方法 ? ? publicstatic ExampleBean createInstance(BeanOne beanOne, BeanTwo beanTwo, int count){ ? ? ? ? ExampleBean eb = new ExampleBean(beanOne,beanTwo,count); ? ? ? ? return eb; ? ? } }
|
在配置文件中定義:
java代碼:?
|
<bean id="exampleBean" class="examples.ExampleBean" factory-method="createInstance"> ? ? <constructor-arg><ref bean="bean1"/></constructor-arg> ? ? <constructor-arg><ref bean="bean2"/></constructor-arg> ? ? <constructor-arg><value>1</value></constructor-arg> </bean>
<bean id="bean1" class="examples.BeanOne"/> <bean id="bean2" class="examples.BeanTwo"/>
|
3、實(shí)例工廠方法模式
調(diào)用一個(gè)已存在的bean(這個(gè)bean應(yīng)該是工廠類型)的工廠方法來(lái)創(chuàng)建新的bean,基于方法參數(shù)的依賴注射
該模式?jīng)]有Bean類;
在配置文件中定義:
java代碼:?
|
<bean id="exampleBean" ? ? ? factory-bean="myFactoryBean" ? ? ? factory-method="createInstance"/>
<bean id="myFactoryBean" class="examples.ExampleBean" factory-method="createInstance"> ? ? <constructor-arg><ref bean="bean1"/></constructor-arg> ? ? <constructor-arg><ref bean="bean2"/></constructor-arg> ? ? <constructor-arg><value>1</value></constructor-arg> </bean>
<bean id="bean1" class="examples.BeanOne"/> <bean id="bean2" class="examples.BeanTwo"/>
|
二)Bean其它參數(shù)的配置
一個(gè)常用Bean的配置參數(shù)和解釋
<bean id="" ——標(biāo)志符,用它引用唯一的Bean
class="" ——該Bean對(duì)應(yīng)的類,前面說(shuō)到實(shí)例工廠方法模式創(chuàng)建的Bean沒(méi)有類
singleton="" ——值為true或false,標(biāo)識(shí)該Bean是否為單實(shí)例模式?如果為false則對(duì)這個(gè)bean
的每次請(qǐng)求都會(huì)創(chuàng)建一個(gè)新的bean實(shí)例
init-method="" ——向應(yīng)用層返回引用前執(zhí)行的初始化方法
destroy-method="" ——該Bean的銷毀方法
depends-on=""> ——在Bean加載前,首先加載的指定資源
....
</bean>
三)property(或constructor-arg元素)的配置
1、用字符串形式指定常見類型的屬性或參數(shù)的value值,JavaBean的PropertyEditor負(fù)責(zé)類型轉(zhuǎn)化如:
java代碼:?
|
<property name="driverClassName"> ? ? <value>com.mysql.jdbc.Driver</value> </property> <property name="url"> ? ? <value>jdbc:mysql://localhost:3306/mydb</value> </property>
|
2、注意null和""(空串)的區(qū)別,如:
java代碼:?
|
<property name="email"><value></value></property> <property name="email"><null/></property>
|
3、list、set、map、以及 props 元素用來(lái)定義和設(shè)置Java對(duì)應(yīng)類型List、Set、Map、和 Properties ,如:
java代碼:?
|
<property name="school"> ? ?<props> ? ? ? <prop key="school01">The xi'an technology university</prop> ? ? ? <prop key="school02">The BeiJing university</prop> ? ?</props> </property>
<property name="someList"> ? ?<list> ? ? ? <value>a list element followed by a reference</value> ? ? ? <ref bean="myDataSource"/> ? ?</list> </property>
<property name="someMap"> ? ?<map> ? ? ? <entry key="001"> ? ? ? ? ?<value>just some string</value> ? ? ? </entry> ? ? ? <entry key="yup a ref"> ? ? ? ? ?<ref bean="myDataSource"/> ? ? ? </entry> ? ?</map> </property> ? ? ? ? <property name="someSet"> ? ? ? <set> ? ? ? ? ?<value>just some string</value> ? ? ? ? ?<ref bean="myDataSource"/> ? ? ? </set> </property>
|
4、內(nèi)部Bean和ref元素引用容器管理的其他bean
一個(gè)內(nèi)部Bean的例子:
java代碼:?
|
<bean id="dep" class="com.bean.Conpany"> ? ? <property name="manager"> ? ? ? ? <bean class="com.bean.Person"> ? ? ? ? ? ? <property name="name"><value>Tony</value></property> ? ? ? ? ? ? <property name="age"><value>51</value></property> ? ? ? ? </bean> ? ? </property> </bean>
|
ref元素引用的例子:
java代碼:?
|
<bean id="person_manger" class="com.bean.Person"> ? ? <property name="name"><value>Tony</value></property> ? ? <property name="age"><value>51</value></property> </bean>
<bean id="dep" class="com.bean.Conpany"> ? ? <property name="manager"> ? ? ? ? <idref bean="person_manager"/> ? ? </property> </bean>
|
注:元素引用可以是下面三種權(quán)限:
1)<idref bean="person_manager"/>
引用的Bean可以在同一個(gè)BeanFactory/ApplicationContext(無(wú)論是否在同一個(gè)XML文件中)中,也可以在父BeanFactory/ApplicationContext中
2)<idref local="person_manager"/>
引用的bean在同一個(gè)XML文件中
3)<idref parent="person_manager"/>
引用的bean必須在當(dāng)前BeanFactory(ApplicationContext)的父BeanFactory(ApplicationContext)中.
時(shí)間: 2006-3-23 12:58:26?? ?標(biāo)題: Spring學(xué)習(xí)筆記(二)Bean |   |
|
Spring使用BeanFactory模式來(lái)管理Bean,但Spring中提到的Bean不是標(biāo)準(zhǔn)的意義上的JavaBean(僅包含一個(gè)默認(rèn)的構(gòu)造函數(shù),在屬性后面定義相對(duì)應(yīng)的setter和getter方法),而是任何你想讓它管理的類,比如連接池、甚至BeanFactory本身。
一)Bean的設(shè)計(jì)常用下面幾種模式
1、標(biāo)準(zhǔn)Bean:
使用默認(rèn)的構(gòu)造函數(shù)和基于setter、getter方法的依賴注射
Bean類代碼:
java代碼:? | publicclass ExampleBean { ? ? private BeanOne beanOne; ? ? private BeanTwo beanTwo; ? ? privateint count; ? ? ? ? publicvoid setBeanOne(BeanOne beanOne){ ? ? ? ? this.beanOne = beanOne; ? ? } ? ? ? ? publicvoid setBeanTwo(BeanTwo beanTwo){ ? ? ? ? this.beanTwo = beanTwo; ? ? } ? ? ? ? publicvoid setCount(int count){ ? ? ? ? this.count = count; ? ? }? ? }
|
在配置文件中定義:
java代碼:? | <bean id="exampleBean" class="examples.ExampleBean"> ? ? <property name="beanOne"><ref bean="bean1"/></property> ? ? <property name="beanTwo"><ref bean="bean2"/></property> ? ? <property name="count"><value>1</value></property> </bean>
<bean id="bean1" class="examples.BeanOne"/> <bean id="bean2" class="examples.BeanTwo"/>
|
2、構(gòu)造函數(shù)模式
自定義的構(gòu)造函數(shù),基于構(gòu)造函數(shù)參數(shù)的依賴注射
Bean類代碼:
java代碼:? | publicclass ExampleBean { ? ? private BeanOne beanOne; ? ? private BeanTwo beanTwo; ? ? privateint count; ? ? ? ? public ExampleBean(BeanOne beanOne, BeanTwo beanTwo, int count){ ? ? ? ? this.beanOne = beanOne; ? ? ? ? this.beanTwo = beanTwo; ? ? ? ? this.count = count; ? ? } }
|
在配置文件中定義:
java代碼:? |
<bean id="exampleBean" class="examples.ExampleBean"> ? ? <constructor-arg><ref bean="bean1"/></constructor-arg> ? ? <constructor-arg><ref bean="bean2"/></constructor-arg> ? ? <constructor-arg><value>1</value></constructor-arg> </bean>
<bean id="bean1" class="examples.BeanOne"/> <bean id="bean2" class="examples.BeanTwo"/>
|
3、靜態(tài)工廠方法模式
靜態(tài)工廠方法必須是static的,基于方法參數(shù)的依賴注射。
Bean類代碼:
java代碼:? | publicclass ExampleBean { ? ? private BeanOne beanOne; ? ? private BeanTwo beanTwo; ? ? privateint count; ? ? //構(gòu)造函數(shù)私有 ? ? private ExampleBean(BeanOne beanOne, BeanTwo beanTwo, int count){ ? ? ? ? this.beanOne = beanOne; ? ? ? ? this.beanTwo = beanTwo; ? ? ? ? this.count = count; ? ? } ? ? //對(duì)外提供靜態(tài)的方法 ? ? publicstatic ExampleBean createInstance(BeanOne beanOne, BeanTwo beanTwo, int count){ ? ? ? ? ExampleBean eb = new ExampleBean(beanOne,beanTwo,count); ? ? ? ? return eb; ? ? } }
|
在配置文件中定義:
java代碼:? | <bean id="exampleBean" class="examples.ExampleBean" factory-method="createInstance"> ? ? <constructor-arg><ref bean="bean1"/></constructor-arg> ? ? <constructor-arg><ref bean="bean2"/></constructor-arg> ? ? <constructor-arg><value>1</value></constructor-arg> </bean>
<bean id="bean1" class="examples.BeanOne"/> <bean id="bean2" class="examples.BeanTwo"/>
|
3、實(shí)例工廠方法模式
調(diào)用一個(gè)已存在的bean(這個(gè)bean應(yīng)該是工廠類型)的工廠方法來(lái)創(chuàng)建新的bean,基于方法參數(shù)的依賴注射
該模式?jīng)]有Bean類;
在配置文件中定義:
java代碼:? | <bean id="exampleBean" ? ? ? factory-bean="myFactoryBean" ? ? ? factory-method="createInstance"/>
<bean id="myFactoryBean" class="examples.ExampleBean" factory-method="createInstance"> ? ? <constructor-arg><ref bean="bean1"/></constructor-arg> ? ? <constructor-arg><ref bean="bean2"/></constructor-arg> ? ? <constructor-arg><value>1</value></constructor-arg> </bean>
<bean id="bean1" class="examples.BeanOne"/> <bean id="bean2" class="examples.BeanTwo"/>
|
二)Bean其它參數(shù)的配置
一個(gè)常用Bean的配置參數(shù)和解釋
<bean id="" ——標(biāo)志符,用它引用唯一的Bean class="" ——該Bean對(duì)應(yīng)的類,前面說(shuō)到實(shí)例工廠方法模式創(chuàng)建的Bean沒(méi)有類 singleton="" ——值為true或false,標(biāo)識(shí)該Bean是否為單實(shí)例模式?如果為false則對(duì)這個(gè)bean 的每次請(qǐng)求都會(huì)創(chuàng)建一個(gè)新的bean實(shí)例 init-method="" ——向應(yīng)用層返回引用前執(zhí)行的初始化方法 destroy-method="" ——該Bean的銷毀方法 depends-on=""> ——在Bean加載前,首先加載的指定資源 .... </bean>
三)property(或constructor-arg元素)的配置
1、用字符串形式指定常見類型的屬性或參數(shù)的value值,JavaBean的PropertyEditor負(fù)責(zé)類型轉(zhuǎn)化如:
java代碼:? | <property name="driverClassName"> ? ? <value>com.mysql.jdbc.Driver</value> </property> <property name="url"> ? ? <value>jdbc:mysql://localhost:3306/mydb</value> </property>
|
2、注意null和""(空串)的區(qū)別,如:
java代碼:? |
<property name="email"><value></value></property> <property name="email"><null/></property>
|
3、list、set、map、以及 props 元素用來(lái)定義和設(shè)置Java對(duì)應(yīng)類型List、Set、Map、和 Properties ,如:
java代碼:? |
<property name="school"> ? ?<props> ? ? ? <prop key="school01">The xi'an technology university</prop> ? ? ? <prop key="school02">The BeiJing university</prop> ? ?</props> </property>
<property name="someList"> ? ?<list> ? ? ? <value>a list element followed by a reference</value> ? ? ? <ref bean="myDataSource"/> ? ?</list> </property>
<property name="someMap"> ? ?<map> ? ? ? <entry key="001"> ? ? ? ? ?<value>just some string</value> ? ? ? </entry> ? ? ? <entry key="yup a ref"> ? ? ? ? ?<ref bean="myDataSource"/> ? ? ? </entry> ? ?</map> </property> ? ? ? ? <property name="someSet"> ? ? ? <set> ? ? ? ? ?<value>just some string</value> ? ? ? ? ?<ref bean="myDataSource"/> ? ? ? </set> </property>
|
|
4、內(nèi)部Bean和ref元素引用容器管理的其他bean
一個(gè)內(nèi)部Bean的例子:
java代碼:?
|
<bean id="dep" class="com.bean.Conpany"> ? ? <property name="manager"> ? ? ? ? <bean class="com.bean.Person"> ? ? ? ? ? ? <property name="name"><value>Tony</value></property> ? ? ? ? ? ? <property name="age"><value>51</value></property> ? ? ? ? </bean> ? ? </property> </bean>
|
ref元素引用的例子:
java代碼:?
|
<bean id="person_manger" class="com.bean.Person"> ? ? <property name="name"><value>Tony</value></property> ? ? <property name="age"><value>51</value></property> </bean>
<bean id="dep" class="com.bean.Conpany"> ? ? <property name="manager"> ? ? ? ? <idref bean="person_manager"/> ? ? </property> </bean>
|
注:元素引用可以是下面三種權(quán)限:
1)<idref bean="person_manager"/>
引用的Bean可以在同一個(gè)BeanFactory/ApplicationContext(無(wú)論是否在同一個(gè)XML文件中)中,也可以在父BeanFactory/ApplicationContext中
2)<idref local="person_manager"/>
引用的bean在同一個(gè)XML文件中
3)<idref parent="person_manager"/>
引用的bean必須在當(dāng)前BeanFactory(ApplicationContext)的父BeanFactory(ApplicationContext)中
引用注明出處:http://spaces.msn.com/pococoon/blog/cns!D25B6032F7AD1992!193.entry
Hibernate一共包括了23個(gè)jar包,令人眼花繚亂。本文將詳細(xì)講解Hibernate每個(gè)jar包的作用,便于你在應(yīng)用中根據(jù)自己的需要進(jìn)行取舍。
下載Hibernate,例如2.0.3穩(wěn)定版本,解壓縮,可以看到一個(gè)hibernate2.jar和lib目錄下有22個(gè)jar包:
hibernate2.jar:
Hibernate的庫(kù),沒(méi)有什么可說(shuō)的,必須使用的jar包
cglib-asm.jar:
CGLIB庫(kù),Hibernate用它來(lái)實(shí)現(xiàn)PO字節(jié)碼的動(dòng)態(tài)生成,非常核心的庫(kù),必須使用的jar包
dom4j.jar:
dom4j是一個(gè)Java的XML API,類似于jdom,用來(lái)讀寫XML文件的。dom4j是一個(gè)非常非常優(yōu)秀的Java XML API,具有性能優(yōu)異、功能強(qiáng)大和極端易用使用的特點(diǎn),同時(shí)它也是一個(gè)開放源代碼的軟件,可以在SourceForge上找到它。在IBM developerWorks上面可以找到一篇文章,對(duì)主流的Java XML API進(jìn)行的性能、功能和易用性的評(píng)測(cè),dom4j無(wú)論在那個(gè)方面都是非常出色的。我早在將近兩年之前就開始使用dom4j,直到現(xiàn)在。如今你可以看到越來(lái)越多的Java軟件都在使用dom4j來(lái)讀寫XML,特別值得一提的是連Sun的JAXM也在用dom4j。這是必須使用的jar包,Hibernate用它來(lái)讀寫配置文件。
odmg.jar:
ODMG是一個(gè)ORM的規(guī)范,Hibernate實(shí)現(xiàn)了ODMG規(guī)范,這是一個(gè)核心的庫(kù),必須使用的jar包。
commons-collections.jar:
Apache Commons包中的一個(gè),包含了一些Apache開發(fā)的集合類,功能比java.util.*強(qiáng)大。必須使用的jar包。
commons-beanutils.jar:
Apache Commons包中的一個(gè),包含了一些Bean工具類類。必須使用的jar包。
commons-lang.jar:
Apache Commons包中的一個(gè),包含了一些數(shù)據(jù)類型工具類,是java.lang.*的擴(kuò)展。必須使用的jar包。
commons-logging.jar:
Apache Commons包中的一個(gè),包含了日志功能,必須使用的jar包。這個(gè)包本身包含了一個(gè)Simple Logger,但是功能很弱。在運(yùn)行的時(shí)候它會(huì)先在CLASSPATH找log4j,如果有,就使用log4j,如果沒(méi)有,就找JDK1.4帶的java.util.logging,如果也找不到就用Simple Logger。commons-logging.jar的出現(xiàn)是一個(gè)歷史的的遺留的遺憾,當(dāng)初Apache極力游說(shuō)Sun把log4j加入JDK1.4,然而JDK1.4項(xiàng)目小組已經(jīng)接近發(fā)布JDK1.4產(chǎn)品的時(shí)間了,因此拒絕了Apache的要求,使用自己的java.util.logging,這個(gè)包的功能比log4j差的很遠(yuǎn),性能也一般。后來(lái)Apache就開發(fā)出來(lái)了commons-logging.jar用來(lái)兼容兩個(gè)logger。因此用commons-logging.jar寫的log程序,底層的Logger是可以切換的,你可以選擇log4j,java.util.logging或者它自帶的Simple Logger。不過(guò)我仍然強(qiáng)烈建議使用log4j,因?yàn)閘og4j性能很高,log輸出信息時(shí)間幾乎等于System.out,而處理一條log平均只需要5us。你可以在Hibernate的src目錄下找到Hibernate已經(jīng)為你準(zhǔn)備好了的log4j的配置文件,你只需要到Apache 網(wǎng)站去下載log4j就可以了。commons-logging.jar也是必須的jar包。
使用Hibernate必須的jar包就是以上的這幾個(gè),剩下的都是可選的。
ant.jar:
Ant編譯工具的jar包,用來(lái)編譯Hibernate源代碼的。如果你不準(zhǔn)備修改和編譯Hibernate源代碼,那么就沒(méi)有什么用,可選的jar包
optional.jar:
Ant的一個(gè)輔助包。
c3p0.jar:
C3PO是一個(gè)數(shù)據(jù)庫(kù)連接池,Hibernate可以配置為使用C3PO連接池。如果你準(zhǔn)備用這個(gè)連接池,就需要這個(gè)jar包。
proxool.jar:
也是一個(gè)連接池,同上。
commons-pool.jar, commons-dbcp.jar:
DBCP數(shù)據(jù)庫(kù)連接池,Apache的Jakarta組織開發(fā)的,Tomcat4的連接池也是DBCP。
實(shí)際上Hibernate自己也實(shí)現(xiàn)了一個(gè)非常非常簡(jiǎn)單的數(shù)據(jù)庫(kù)連接池,加上上面3個(gè),你實(shí)際上可以在Hibernate上選擇4種不同的數(shù)據(jù)庫(kù)連接池,選擇哪一個(gè)看個(gè)人的偏好,不過(guò)DBCP可能更通用一些。另外強(qiáng)調(diào)一點(diǎn),如果在EJB中使用Hibernate,一定要用App Server的連接池,不要用以上4種連接池,否則容器管理事務(wù)不起作用。
connector.jar:
JCA 規(guī)范,如果你在App Server上把Hibernate配置為Connector的話,就需要這個(gè)jar。不過(guò)實(shí)際上一般App Server肯定會(huì)帶上這個(gè)包,所以實(shí)際上是多余的包。
jaas.jar:
JAAS是用來(lái)進(jìn)行權(quán)限驗(yàn)證的,已經(jīng)包含在JDK1.4里面了。所以實(shí)際上是多余的包。
jcs.jar:
如果你準(zhǔn)備在Hibernate中使用JCS的話,那么必須包括它,否則就不用。
jdbc2_0-stdext.jar:
JDBC2.0的擴(kuò)展包,一般來(lái)說(shuō)數(shù)據(jù)庫(kù)連接池會(huì)用上它。不過(guò)App Server都會(huì)帶上,所以也是多余的。
jta.jar:
JTA規(guī)范,當(dāng)Hibernate使用JTA的時(shí)候需要,不過(guò)App Server都會(huì)帶上,所以也是多余的。
junit.jar:
Junit包,當(dāng)你運(yùn)行Hibernate自帶的測(cè)試代碼的時(shí)候需要,否則就不用。
xalan.jar, xerces.jar, xml-apis.jar:
Xerces是XML解析器,Xalan是格式化器,xml-apis實(shí)際上是JAXP。一般App Server都會(huì)帶上,JDK1.4也包含了解析器,不過(guò)不是Xerces,是Crimson,效率比較差,不過(guò)Hibernate用XML只不過(guò)是讀取配置文件,性能沒(méi)什么緊要的,所以也是多余的。
這是好友面試的一道題,其實(shí)我知道使用的區(qū)別,StringBuffer必須new出來(lái),StringBuffer的append的效率比string的+=的效率高,
其實(shí)發(fā)現(xiàn)還有很大的區(qū)別,看了看以前scjp的考題
public class Test {
?? public static void stringReplace (String text) {
?? text = text.replace('j' , 'i');
?? }
??
?? public static void bufferReplace (StringBuffer text) {
?? text = text.append("C");
?? }
??
??? public static void main (String args[]) {
??? String textString = new String ("java");
??? StringBuffer textBuffer = new StringBuffer ("java");
???
??? stringReplace (textString);
??? bufferReplace (textBuffer);
???
??? System.out.println (textString + textBuffer);
??? }
??? }
答案是 javajavaC
這是Java參數(shù)傳遞(by value)造成的,是不可變的(immutable).,例如 基本類型,String傳值,復(fù)制了值傳遞過(guò)去;可變的(Object)傳值,復(fù)制了引用傳遞過(guò)去。
而題目中第七行text = text.append (“C”),append方法會(huì)改變text中的值
而這個(gè)text與main中的textBuffer是指向同一個(gè)對(duì)象,所以對(duì)應(yīng)的輸出是javac。
string的值永遠(yuǎn)不會(huì)改變!
String a = "a";//假設(shè)a指向地址0x0001,
a = "b";//重新負(fù)值后a指向地址0x0002,但0x0001地址中保存的"a"依舊存在,但已經(jīng)不再是a所指向的。
從表面上看String類型的對(duì)象改變了值,但事實(shí)是他不能改變值,只能改變指向的地址
StringBuffer則不同,直接改變指向的地址中保留的值
還有
StringBuffer s1 = new StringBuffer("a");
StringBuffer s2 = new StringBuffer("a");
s1.equals(s2)//為什么是false
String s1 = new String("a");
String s2 = new String("a");
s1.equals(s2)//為什么是true
StringBuffer類中沒(méi)有重新定義equals這個(gè)方法,因此這個(gè)方法就來(lái)自O(shè)bject類,
而Object類中的equals方法是用來(lái)比較地址的,所以等于false.
String類中重新定義了equals這個(gè)方法,而且比較的是值,而不是地址。所以會(huì)是
true。
對(duì)于這樣能不能面試出真正的水平,感到懷疑。
這是在網(wǎng)上發(fā)現(xiàn)的一篇關(guān)于Spring AOP編程的教程,讀完這篇文章后,Spring AOP不再難以理解,因此我把它譯成中文,推薦給Spring AOP的初學(xué)者。這是譯文的
鏈接
。
AOP正在成為軟件開發(fā)的下一個(gè)圣杯。使用AOP,你可以將處理aspect的代碼注入主程序,通常主程序的主要目的并不在于處理這些aspect。AOP可以防止代碼混亂。
為了理解AOP如何做到這點(diǎn),考慮一下記日志的工作。日志本身不太可能是你開發(fā)的主程序的主要任務(wù)。如果能將“不可見的”、通用的日志代碼注入主程序中,那該多好啊。AOP可以幫助你做到。
Spring framework是很有前途的AOP技術(shù)。作為一種非侵略性的,輕型的AOP framework,你無(wú)需使用預(yù)編譯器或其他的元標(biāo)簽,便可以在Java程序中使用它。這意味著開發(fā)團(tuán)隊(duì)里只需一人要對(duì)付AOP framework,其他人還是象往常一樣編程。
AOP是很多直覺難以理解的術(shù)語(yǔ)的根源。幸運(yùn)的是,你只要理解三個(gè)概念,就可以編寫AOP模塊。這三個(gè)概念是:advice,pointcut和advisor。advice是你想向別的程序內(nèi)部不同的地方注入的代碼。pointcut定義了需要注入advice的位置,通常是某個(gè)特定的類的一個(gè)public方法。advisor是pointcut和advice的裝配器,是將advice注入主程序中預(yù)定義位置的代碼。
既然我們知道了需要使用advisor向主要代碼中注入“不可見的”advice,讓我們實(shí)現(xiàn)一個(gè)Spring AOP的例子。在這個(gè)例子中,我們將實(shí)現(xiàn)一個(gè)before advice,這意味著advice的代碼在被調(diào)用的public方法開始前被執(zhí)行。以下是這個(gè)before advice的實(shí)現(xiàn)代碼:
代碼:
|
package com.company.springaop.test;
import java.lang.reflect.Method; import org.springframework.aop.MethodBeforeAdvice;
public class TestBeforeAdvice implements MethodBeforeAdvice {
? public void before(Method m, Object[] args, Object target) ? throws Throwable { ? ? System.out.println("Hello world! (by " ? ? ? ? + this.getClass().getName() ? ? ? ? + ")"); ? } } ? |
接口MethodBeforeAdvice只有一個(gè)方法before需要實(shí)現(xiàn),它定義了advice的實(shí)現(xiàn)。before方法共用三個(gè)參數(shù),它們提供了相當(dāng)豐富的信息。參數(shù)Method m是advice開始后執(zhí)行的方法。方法名稱可以用作判斷是否執(zhí)行代碼的條件。Object[] args是傳給被調(diào)用的public方法的參數(shù)數(shù)組。當(dāng)需要記日志時(shí),參數(shù)args和被執(zhí)行方法的名稱,都是非常有用的信息。你也可以改變傳給m的參數(shù),但要小心使用這個(gè)功能;編寫最初主程序的程序員并不知道主程序可能會(huì)和傳入?yún)?shù)的發(fā)生沖突。Object target是執(zhí)行方法m對(duì)象的引用。
在下面的BeanImpl類中,每個(gè)public方法調(diào)用前,都會(huì)執(zhí)行advice:
代碼:
|
package com.company.springaop.test;
public class BeanImpl implements Bean {
? public void theMethod() { ? ? System.out.println(this.getClass().getName() ? ? ? ? + "." + new Exception().getStackTrace()[0].getMethodName() ? ? ? ? + "()" ? ? ? ? + " says HELLO!"); ? } } |
類BeanImpl實(shí)現(xiàn)了下面的接口Bean:
代碼:
|
package com.company.springaop.test;
public interface Bean { ? public void theMethod(); } |
雖然不是必須使用接口,但面向接口而不是面向?qū)崿F(xiàn)編程是良好的編程實(shí)踐,Spring也鼓勵(lì)這樣做。
pointcut和advice通過(guò)配置文件來(lái)實(shí)現(xiàn),因此,接下來(lái)你只需編寫主方法的Java代碼:
代碼:
|
package com.company.springaop.test;
import org.springframework.context.ApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext;
public class Main {
? public static void main(String[] args) { ? ? //Read the configuration file ? ? ApplicationContext ctx ? ? ? ? = new FileSystemXmlApplicationContext("springconfig.xml");
? ? //Instantiate an object ? ? Bean x = (Bean) ctx.getBean("bean");
? ? //Execute the public method of the bean (the test) ? ? x.theMethod(); ? } } |
我們從讀入和處理配置文件開始,接下來(lái)馬上要?jiǎng)?chuàng)建它。這個(gè)配置文件將作為粘合程序不同部分的“膠水”。讀入和處理配置文件后,我們會(huì)得到一個(gè)創(chuàng)建工廠ctx。任何一個(gè)Spring管理的對(duì)象都必須通過(guò)這個(gè)工廠來(lái)創(chuàng)建。對(duì)象通過(guò)工廠創(chuàng)建后便可正常使用。
僅僅用配置文件便可把程序的每一部分組裝起來(lái)。
代碼:
|
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC? "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans> ? <!--CONFIG--> ? <bean id="bean" class="org.springframework.aop.framework.ProxyFactoryBean"> ? ? <property name="proxyInterfaces"> ? ? ? <value>com.company.springaop.test.Bean</value> ? ? </property> ? ? <property name="target"> ? ? ? <ref local="beanTarget"/> ? ? </property> ? ? <property name="interceptorNames"> ? ? ? <list> ? ? ? ? <value>theAdvisor</value> ? ? ? </list> ? ? </property> ? </bean>
? <!--CLASS--> ? <bean id="beanTarget" class="com.company.springaop.test.BeanImpl"/>
? <!--ADVISOR--> ? <!--Note: An advisor assembles pointcut and advice--> ? <bean id="theAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> ? ? <property name="advice"> ? ? ? <ref local="theBeforeAdvice"/> ? ? </property> ? ? <property name="pattern"> ? ? ? <value>com\.company\.springaop\.test\.Bean\.theMethod</value> ? ? </property> ? </bean>
? <!--ADVICE--> ? <bean id="theBeforeAdvice" class="com.company.springaop.test.TestBeforeAdvice"/> </beans> ? |
四個(gè)bean定義的次序并不重要。我們現(xiàn)在有了一個(gè)advice,一個(gè)包含了正則表達(dá)式pointcut的advisor,一個(gè)主程序類和一個(gè)配置好的接口,通過(guò)工廠ctx,這個(gè)接口返回自己本身實(shí)現(xiàn)的一個(gè)引用。
BeanImpl和TestBeforeAdvice都是直接配置。我們用一個(gè)唯一的ID創(chuàng)建一個(gè)bean元素,并指定了一個(gè)實(shí)現(xiàn)類。這就是全部的工作。
advisor通過(guò)Spring framework提供的一個(gè)RegexMethodPointcutAdvisor類來(lái)實(shí)現(xiàn)。我們用advisor的一個(gè)屬性來(lái)指定它所需的advice-bean。第二個(gè)屬性則用正則表達(dá)式定義了pointcut,確保良好的性能和易讀性。
最后配置的是bean,它可以通過(guò)一個(gè)工廠來(lái)創(chuàng)建。bean的定義看起來(lái)比實(shí)際上要復(fù)雜。bean是ProxyFactoryBean的一個(gè)實(shí)現(xiàn),它是Spring framework的一部分。這個(gè)bean的行為通過(guò)一下的三個(gè)屬性來(lái)定義:
- 屬性proxyInterface定義了接口類。
- 屬性target指向本地配置的一個(gè)bean,這個(gè)bean返回一個(gè)接口的實(shí)現(xiàn)。
- 屬性interceptorNames是唯一允許定義一個(gè)值列表的屬性。這個(gè)列表包含所有需要在beanTarget上執(zhí)行的advisor。注意,advisor列表的次序是非常重要的。
Spring工具
雖然你可以手工修改Ant構(gòu)建腳本,但使用SpringUI(譯注:SpringUI現(xiàn)在是Spring framework的一部分,并改名為spring-ide),使用Spring AOP變得很簡(jiǎn)單,只要點(diǎn)點(diǎn)鼠標(biāo)即可。你可以把SpringUI安裝成Eclipse的一個(gè)plug-in。然后,你只需在你的project上右擊鼠標(biāo),并選擇“add Spring Project Nature”。在project屬性中,你可以在“Spring Project”下添加Spring配置文件。在編譯前把下面的類庫(kù)加入project:aopalliance.jar,commons-logging.jar,jakarta-oro-2.0.7.jar和spring.jar。運(yùn)行程序時(shí)你會(huì)看到下面的信息:
... (logging information)
Hello world! (by com.company.springaop.test.TestBeforeAdvice)
com.company.springaop.test.BeanImpl.theMethod() says HELLO!
優(yōu)點(diǎn)和缺點(diǎn)
Spring比起其他的framework更有優(yōu)勢(shì),因?yàn)槌薃OP以外,它提供了更多別的功能。作為一個(gè)輕型framework,它在J2EE不同的部分都可以發(fā)揮作用。因此,即使不想使用Spring AOP,你可能還是想使用Spring。另一個(gè)優(yōu)點(diǎn)是,Spring并不要求開發(fā)團(tuán)隊(duì)所有的人員都會(huì)用它。學(xué)習(xí)Spring應(yīng)該從Spring reference的第一頁(yè)開始。讀了本文后,你應(yīng)該可以更好地理解Spring reference了。Spring唯一的缺點(diǎn)是缺乏更多的文檔,但它的mailing list是個(gè)很好的補(bǔ)充,而且會(huì)不斷地出現(xiàn)更多的文檔。
很多人對(duì)二級(jí)緩存都不太了解,或者是有錯(cuò)誤的認(rèn)識(shí),我一直想寫一篇文章介紹一下hibernate的二級(jí)緩存的,今天終于忍不住了。
我的經(jīng)驗(yàn)主要來(lái)自hibernate2.1版本,基本原理和3.0、3.1是一樣的,請(qǐng)?jiān)徫业念B固不化。
hibernate的session提供了一級(jí)緩存,每個(gè)session,對(duì)同一個(gè)id進(jìn)行兩次load,不會(huì)發(fā)送兩條sql給數(shù)據(jù)庫(kù),但是session關(guān)閉的時(shí)候,一級(jí)緩存就失效了。
二級(jí)緩存是SessionFactory級(jí)別的全局緩存,它底下可以使用不同的緩存類庫(kù),比如ehcache、oscache等,需要設(shè)置hibernate.cache.provider_class,我們這里用ehcache,在2.1中就是
hibernate.cache.provider_class=net.sf.hibernate.cache.EhCacheProvider
如果使用查詢緩存,加上
hibernate.cache.use_query_cache=true
緩存可以簡(jiǎn)單的看成一個(gè)Map,通過(guò)key在緩存里面找value。
Class的緩存
對(duì)于一條記錄,也就是一個(gè)PO來(lái)說(shuō),是根據(jù)ID來(lái)找的,緩存的key就是ID,value是POJO。無(wú)論list,load還是iterate,只要讀出一個(gè)對(duì)象,都會(huì)填充緩存。但是list不會(huì)使用緩存,而iterate會(huì)先取數(shù)據(jù)庫(kù)select id出來(lái),然后一個(gè)id一個(gè)id的load,如果在緩存里面有,就從緩存取,沒(méi)有的話就去數(shù)據(jù)庫(kù)load。假設(shè)是讀寫緩存,需要設(shè)置:
<cache usage="read-write"/>
如果你使用的二級(jí)緩存實(shí)現(xiàn)是ehcache的話,需要配置ehcache.xml
<cache name="com.xxx.pojo.Foo" maxElementsInMemory="500" eternal="false" timeToLiveSeconds="7200" timeToIdleSeconds="3600" overflowToDisk="true" />
其中eternal表示緩存是不是永遠(yuǎn)不超時(shí),timeToLiveSeconds是緩存中每個(gè)元素(這里也就是一個(gè)POJO)的超時(shí)時(shí)間,如果eternal="false",超過(guò)指定的時(shí)間,這個(gè)元素就被移走了。timeToIdleSeconds是發(fā)呆時(shí)間,是可選的。當(dāng)往緩存里面put的元素超過(guò)500個(gè)時(shí),如果overflowToDisk="true",就會(huì)把緩存中的部分?jǐn)?shù)據(jù)保存在硬盤上的臨時(shí)文件里面。
每個(gè)需要緩存的class都要這樣配置。如果你沒(méi)有配置,hibernate會(huì)在啟動(dòng)的時(shí)候警告你,然后使用defaultCache的配置,這樣多個(gè)class會(huì)共享一個(gè)配置。
當(dāng)某個(gè)ID通過(guò)hibernate修改時(shí),hibernate會(huì)知道,于是移除緩存。
這樣大家可能會(huì)想,同樣的查詢條件,第一次先list,第二次再iterate,就可以使用到緩存了。實(shí)際上這是很難的,因?yàn)槟銦o(wú)法判斷什么時(shí)候是第一次,而且每次查詢的條件通常是不一樣的,假如數(shù)據(jù)庫(kù)里面有100條記錄,id從1到100,第一次list的時(shí)候出了前50個(gè)id,第二次iterate的時(shí)候卻查詢到30至70號(hào)id,那么30-50是從緩存里面取的,51到70是從數(shù)據(jù)庫(kù)取的,共發(fā)送1+20條sql。所以我一直認(rèn)為iterate沒(méi)有什么用,總是會(huì)有1+N的問(wèn)題。
(題外話:有說(shuō)法說(shuō)大型查詢用list會(huì)把整個(gè)結(jié)果集裝入內(nèi)存,很慢,而iterate只select id比較好,但是大型查詢總是要分頁(yè)查的,誰(shuí)也不會(huì)真的把整個(gè)結(jié)果集裝進(jìn)來(lái),假如一頁(yè)20條的話,iterate共需要執(zhí)行21條語(yǔ)句,list雖然選擇若干字段,比iterate第一條select id語(yǔ)句慢一些,但只有一條語(yǔ)句,不裝入整個(gè)結(jié)果集hibernate還會(huì)根據(jù)數(shù)據(jù)庫(kù)方言做優(yōu)化,比如使用mysql的limit,整體看來(lái)應(yīng)該還是list快。)
如果想要對(duì)list或者iterate查詢的結(jié)果緩存,就要用到查詢緩存了
查詢緩存
首先需要配置hibernate.cache.use_query_cache=true
如果用ehcache,配置ehcache.xml,注意hibernate3.0以后不是net.sf的包名了
<cache name="net.sf.hibernate.cache.StandardQueryCache"
maxElementsInMemory="50" eternal="false" timeToIdleSeconds="3600"
timeToLiveSeconds="7200" overflowToDisk="true"/>
<cache name="net.sf.hibernate.cache.UpdateTimestampsCache"
maxElementsInMemory="5000" eternal="true" overflowToDisk="true"/>
然后
query.setCacheable(true);//激活查詢緩存
query.setCacheRegion("myCacheRegion");//指定要使用的cacheRegion,可選
第二行指定要使用的cacheRegion是myCacheRegion,即你可以給每個(gè)查詢緩存做一個(gè)單獨(dú)的配置,使用setCacheRegion來(lái)做這個(gè)指定,需要在ehcache.xml里面配置它:
<cache name="myCacheRegion" maxElementsInMemory="10" eternal="false" timeToIdleSeconds="3600" timeToLiveSeconds="7200" overflowToDisk="true" />
如果省略第二行,不設(shè)置cacheRegion的話,那么會(huì)使用上面提到的標(biāo)準(zhǔn)查詢緩存的配置,也就是net.sf.hibernate.cache.StandardQueryCache
對(duì)于查詢緩存來(lái)說(shuō),緩存的key是根據(jù)hql生成的sql,再加上參數(shù),分頁(yè)等信息(可以通過(guò)日志輸出看到,不過(guò)它的輸出不是很可讀,最好改一下它的代碼)。
比如hql:
from Cat c where c.name like ?
生成大致如下的sql:
select * from cat c where c.name like ?
參數(shù)是"tiger%",那么查詢緩存的key*大約*是這樣的字符串(我是憑記憶寫的,并不精確,不過(guò)看了也該明白了):
select * from cat c where c.name like ? , parameter:tiger%
這樣,保證了同樣的查詢、同樣的參數(shù)等條件下具有一樣的key。
現(xiàn)在說(shuō)說(shuō)緩存的value,如果是list方式的話,value在這里并不是整個(gè)結(jié)果集,而是查詢出來(lái)的這一串ID。也就是說(shuō),不管是list方法還是iterate方法,第一次查詢的時(shí)候,它們的查詢方式很它們平時(shí)的方式是一樣的,list執(zhí)行一條sql,iterate執(zhí)行1+N條,多出來(lái)的行為是它們填充了緩存。但是到同樣條件第二次查詢的時(shí)候,就都和iterate的行為一樣了,根據(jù)緩存的key去緩存里面查到了value,value是一串id,然后在到class的緩存里面去一個(gè)一個(gè)的load出來(lái)。這樣做是為了節(jié)約內(nèi)存。
可以看出來(lái),查詢緩存需要打開相關(guān)類的class緩存。list和iterate方法第一次執(zhí)行的時(shí)候,都是既填充查詢緩存又填充class緩存的。
這里還有一個(gè)很容易被忽視的重要問(wèn)題,即打開查詢緩存以后,即使是list方法也可能遇到1+N的問(wèn)題!相同條件第一次list的時(shí)候,因?yàn)椴樵兙彺嬷姓也坏剑还躢lass緩存是否存在數(shù)據(jù),總是發(fā)送一條sql語(yǔ)句到數(shù)據(jù)庫(kù)獲取全部數(shù)據(jù),然后填充查詢緩存和class緩存。但是第二次執(zhí)行的時(shí)候,問(wèn)題就來(lái)了,如果你的class緩存的超時(shí)時(shí)間比較短,現(xiàn)在class緩存都超時(shí)了,但是查詢緩存還在,那么list方法在獲取id串以后,將會(huì)一個(gè)一個(gè)去數(shù)據(jù)庫(kù)load!因此,class緩存的超時(shí)時(shí)間一定不能短于查詢緩存設(shè)置的超時(shí)時(shí)間!如果還設(shè)置了發(fā)呆時(shí)間的話,保證class緩存的發(fā)呆時(shí)間也大于查詢的緩存的生存時(shí)間。這里還有其他情況,比如class緩存被程序強(qiáng)制evict了,這種情況就請(qǐng)自己注意了。
另外,如果hql查詢包含select字句,那么查詢緩存里面的value就是整個(gè)結(jié)果集了。
當(dāng)hibernate更新數(shù)據(jù)庫(kù)的時(shí)候,它怎么知道更新哪些查詢緩存呢?
hibernate在一個(gè)地方維護(hù)每個(gè)表的最后更新時(shí)間,其實(shí)也就是放在上面net.sf.hibernate.cache.UpdateTimestampsCache所指定的緩存配置里面。
當(dāng)通過(guò)hibernate更新的時(shí)候,hibernate會(huì)知道這次更新影響了哪些表。然后它更新這些表的最后更新時(shí)間。每個(gè)緩存都有一個(gè)生成時(shí)間和這個(gè)緩存所查詢的表,當(dāng)hibernate查詢一個(gè)緩存是否存在的時(shí)候,如果緩存存在,它還要取出緩存的生成時(shí)間和這個(gè)緩存所查詢的表,然后去查找這些表的最后更新時(shí)間,如果有一個(gè)表在生成時(shí)間后更新過(guò)了,那么這個(gè)緩存是無(wú)效的。
可以看出,只要更新過(guò)一個(gè)表,那么凡是涉及到這個(gè)表的查詢緩存就失效了,因此查詢緩存的命中率可能會(huì)比較低。
Collection緩存
需要在hbm的collection里面設(shè)置
<cache usage="read-write"/>
假如class是Cat,collection叫children,那么ehcache里面配置
<cache name="com.xxx.pojo.Cat.children"
maxElementsInMemory="20" eternal="false" timeToIdleSeconds="3600" timeToLiveSeconds="7200"
overflowToDisk="true" />
Collection的緩存和前面查詢緩存的list一樣,也是只保持一串id,但它不會(huì)因?yàn)檫@個(gè)表更新過(guò)就失效,一個(gè)collection緩存僅在這個(gè)collection里面的元素有增刪時(shí)才失效。
這樣有一個(gè)問(wèn)題,如果你的collection是根據(jù)某個(gè)字段排序的,當(dāng)其中一個(gè)元素更新了該字段時(shí),導(dǎo)致順序改變時(shí),collection緩存里面的順序沒(méi)有做更新。
緩存策略
只讀緩存(read-only):沒(méi)有什么好說(shuō)的
讀/寫緩存(read-write):程序可能要的更新數(shù)據(jù)
不嚴(yán)格的讀/寫緩存(nonstrict-read-write):需要更新數(shù)據(jù),但是兩個(gè)事務(wù)更新同一條記錄的可能性很小,性能比讀寫緩存好
事務(wù)緩存(transactional):緩存支持事務(wù),發(fā)生異常的時(shí)候,緩存也能夠回滾,只支持jta環(huán)境,這個(gè)我沒(méi)有怎么研究過(guò)
讀寫緩存和不嚴(yán)格讀寫緩存在實(shí)現(xiàn)上的區(qū)別在于,讀寫緩存更新緩存的時(shí)候會(huì)把緩存里面的數(shù)據(jù)換成一個(gè)鎖,其他事務(wù)如果去取相應(yīng)的緩存數(shù)據(jù),發(fā)現(xiàn)被鎖住了,然后就直接取數(shù)據(jù)庫(kù)查詢。
在hibernate2.1的ehcache實(shí)現(xiàn)中,如果鎖住部分緩存的事務(wù)發(fā)生了異常,那么緩存會(huì)一直被鎖住,直到60秒后超時(shí)。
不嚴(yán)格讀寫緩存不鎖定緩存中的數(shù)據(jù)。
使用二級(jí)緩存的前置條件
你的hibernate程序?qū)?shù)據(jù)庫(kù)有獨(dú)占的寫訪問(wèn)權(quán),其他的進(jìn)程更新了數(shù)據(jù)庫(kù),hibernate是不可能知道的。你操作數(shù)據(jù)庫(kù)必需直接通過(guò)hibernate,如果你調(diào)用存儲(chǔ)過(guò)程,或者自己使用jdbc更新數(shù)據(jù)庫(kù),hibernate也是不知道的。hibernate3.0的大批量更新和刪除是不更新二級(jí)緩存的,但是據(jù)說(shuō)3.1已經(jīng)解決了這個(gè)問(wèn)題。
這個(gè)限制相當(dāng)?shù)募郑袝r(shí)候hibernate做批量更新、刪除很慢,但是你卻不能自己寫jdbc來(lái)優(yōu)化,很郁悶吧。
SessionFactory也提供了移除緩存的方法,你一定要自己寫一些JDBC的話,可以調(diào)用這些方法移除緩存,這些方法是:
void evict(Class persistentClass)
Evict all entries from the second-level cache.
void evict(Class persistentClass, Serializable id)
Evict an entry from the second-level cache.
void evictCollection(String roleName)
Evict all entries from the second-level cache.
void evictCollection(String roleName, Serializable id)
Evict an entry from the second-level cache.
void evictQueries()
Evict any query result sets cached in the default query cache region.
void evictQueries(String cacheRegion)
Evict any query result sets cached in the named query cache region.
不過(guò)我不建議這樣做,因?yàn)檫@樣很難維護(hù)。比如你現(xiàn)在用JDBC批量更新了某個(gè)表,有3個(gè)查詢緩存會(huì)用到這個(gè)表,用evictQueries(String cacheRegion)移除了3個(gè)查詢緩存,然后用evict(Class persistentClass)移除了class緩存,看上去好像完整了。不過(guò)哪天你添加了一個(gè)相關(guān)查詢緩存,可能會(huì)忘記更新這里的移除代碼。如果你的jdbc代碼到處都是,在你添加一個(gè)查詢緩存的時(shí)候,還知道其他什么地方也要做相應(yīng)的改動(dòng)嗎?
----------------------------------------------------
總結(jié):
不要想當(dāng)然的以為緩存一定能提高性能,僅僅在你能夠駕馭它并且條件合適的情況下才是這樣的。hibernate的二級(jí)緩存限制還是比較多的,不方便用jdbc可能會(huì)大大的降低更新性能。在不了解原理的情況下亂用,可能會(huì)有1+N的問(wèn)題。不當(dāng)?shù)氖褂眠€可能導(dǎo)致讀出臟數(shù)據(jù)。
如果受不了hibernate的諸多限制,那么還是自己在應(yīng)用程序的層面上做緩存吧。
在越高的層面上做緩存,效果就會(huì)越好。就好像盡管磁盤有緩存,數(shù)據(jù)庫(kù)還是要實(shí)現(xiàn)自己的緩存,盡管數(shù)據(jù)庫(kù)有緩存,咱們的應(yīng)用程序還是要做緩存。因?yàn)榈讓拥木彺嫠⒉恢栏邔右眠@些數(shù)據(jù)干什么,只能做的比較通用,而高層可以有針對(duì)性的實(shí)現(xiàn)緩存,所以在更高的級(jí)別上做緩存,效果也要好些吧。
前幾天接到了新的任務(wù),開始了公司項(xiàng)目管理軟件的開發(fā)(復(fù)雜其中一部分).
這段時(shí)間開始由項(xiàng)目經(jīng)理帶著我們開發(fā),這幾天跟他學(xué)到的東西還是不少的.如果每天都能這樣
該多好呀.?? 而且意識(shí)到嚴(yán)謹(jǐn)?shù)?font color="#ff0000">學(xué)習(xí)態(tài)度的重要性,不能囫圇吞棗.要學(xué)就要學(xué)通.
轉(zhuǎn)自:Potain 的BLOG
OpenSessionInView
Created by
potian. Last edited by
admin 61 days ago. Viewed 181 times.
[edit]
[attach]
Hibernate的Lazy初始化1:n關(guān)系時(shí),你必須保證是在同一個(gè)Session內(nèi)部使用這個(gè)關(guān)系集合,不然Hiernate將拋出例外。
另外,你不愿意你的DAO測(cè)試代碼每次都打開關(guān)系Session,因此,我們一般會(huì)采用OpenSessionInView模式。
OpenSessionInViewFilter解決Web應(yīng)用程序的問(wèn)題
如果程序是在正常的Web程序中運(yùn)行,那么Spring的
OpenSessionInViewFilter能夠解決問(wèn)題,它:
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
?????? FilterChain filterChain) throws ServletException, IOException {
??????SessionFactory sessionFactory = lookupSessionFactory();
??????logger.debug("Opening Hibernate Session in OpenSessionInViewFilter");
??????Session session = getSession(sessionFactory);
??????TransactionSynchronizationManager.bindResource(sessionFactory,
new SessionHolder(session));
??????try {
????????????filterChain.doFilter(request, response);
??????}
??????finally {
????????????TransactionSynchronizationManager.unbindResource(sessionFactory);
????????????logger.debug("Closing Hibernate Session in OpenSessionInViewFilter");
????????????closeSession(session, sessionFactory);
??????}
}
可以看到,這個(gè)Filter在request開始之前,把sessionFactory綁定到TransactionSynchronizationManager,和這個(gè)SessionHolder相關(guān)。這個(gè)意味著所有request執(zhí)行過(guò)程中將使用這個(gè)session。而在請(qǐng)求結(jié)束后,將和這個(gè)sessionFactory對(duì)應(yīng)的session解綁,并且關(guān)閉Session。
為什么綁定以后,就可以防止每次不會(huì)新開一個(gè)Session呢?看看HibernateDaoSupport的情況:
publicfinal void setSessionFactory(SessionFactory sessionFactory) {
this.hibernateTemplate = new HibernateTemplate(sessionFactory);
}
protectedfinal HibernateTemplate getHibernateTemplate() {
return hibernateTemplate;
}
我們的DAO將使用這個(gè)template進(jìn)行操作:
publicabstract class BaseHibernateObjectDao
??????extends HibernateDaoSupport
??????implements BaseObjectDao {
??????protected BaseEntityObject getByClassId(finallong id) {
????????????BaseEntityObject obj =
??????????????????(BaseEntityObject) getHibernateTemplate()
????????????????????????.execute(new HibernateCallback() {
??????????????????publicObject doInHibernate(Session session)
????????????????????????throws HibernateException {
????????????????????????return session.get(getPersistentClass(),
newLong(id));
??????????????????}
????????????});
????????????return obj;
??????}
??????public void save(BaseEntityObject entity) {
????????????getHibernateTemplate().saveOrUpdate(entity);
??????}
??????public void remove(BaseEntityObject entity) {
????????????try {
??????????????????getHibernateTemplate().delete(entity);
????????????} catch (Exception e) {
??????????????????thrownew FlexEnterpriseDataAccessException(e);
????????????}
??????}
??????public void refresh(final BaseEntityObject entity) {
????????????getHibernateTemplate().execute(new HibernateCallback() {
??????????????????publicObject doInHibernate(Session session)
????????????????????????throws HibernateException {
????????????????????????session.refresh(entity);
????????????????????????returnnull;
??????????????????}
????????????});
??????}
??????public void replicate(finalObject entity) {
????????????getHibernateTemplate().execute(new HibernateCallback() {
??????????????????publicObject doInHibernate(Session session)
????????????????????????throws HibernateException {
????????????????????????session.replicate(entity,
ReplicationMode.OVERWRITE);
????????????????????????returnnull;
??????????????????}
????????????});
??????}
而HibernateTemplate試圖每次在execute之前去獲得Session,執(zhí)行完就力爭(zhēng)關(guān)閉Session
publicObject execute(HibernateCallback action) throws DataAccessException {
??????Session session = (!this.allowCreate ?
????????????SessionFactoryUtils.getSession(getSessionFactory(),
false) :
????????????SessionFactoryUtils.getSession(getSessionFactory(),
getEntityInterceptor(),
getJdbcExceptionTranslator()));
??????boolean existingTransaction =
TransactionSynchronizationManager.hasResource(getSessionFactory());
??????if (!existingTransaction && getFlushMode() == FLUSH_NEVER) {
????????????session.setFlushMode(FlushMode.NEVER);
??????}
??????try {
????????????Object result = action.doInHibernate(session);
????????????flushIfNecessary(session, existingTransaction);
????????????return result;
??????}
??????catch (HibernateException ex) {
????????????throw convertHibernateAccessException(ex);
??????}
??????catch (SQLException ex) {
????????????throw convertJdbcAccessException(ex);
??????}
??????catch (RuntimeException ex) {
????????????// callback code threw application exception
????????????throw ex;
??????}
??????finally {
????????????SessionFactoryUtils.closeSessionIfNecessary(
session, getSessionFactory());
??????}
}
而這個(gè)SessionFactoryUtils能否得到當(dāng)前的session以及closeSessionIfNecessary是否真正關(guān)閉session,端取決于這個(gè)session是否用sessionHolder和這個(gè)sessionFactory在我們最開始提到的TransactionSynchronizationManager綁定。
publicstatic void closeSessionIfNecessary(Session session,
SessionFactory sessionFactory)
throws CleanupFailureDataAccessException {
??????if (session == null ||
?????? TransactionSynchronizationManager.hasResource(sessionFactory)) {
????????????return;
??????}
??????logger.debug("Closing Hibernate session");
??????try {
????????????session.close();
??????}
??????catch (JDBCException ex) {
????????????// SQLException underneath
????????????thrownew CleanupFailureDataAccessException(
????????????"Cannot close Hibernate session", ex.getSQLException());
??????}
??????catch (HibernateException ex) {
????????????thrownew CleanupFailureDataAccessException(
????????????"Cannot close Hibernate session", ex);
??????}
}
HibernateInterceptor和OpenSessionInViewInterceptor的問(wèn)題
使用同樣的方法,這兩個(gè)Interceptor可以用來(lái)解決問(wèn)題。但是關(guān)鍵的不同之處在于,它們的力度只能定義在DAO或業(yè)務(wù)方法上,而不是在我們的Test方法上,除非我們把它們應(yīng)用到TestCase的方法上,但你不大可能為TestCase去定義一個(gè)接口,然后把Interceptor應(yīng)用到這個(gè)接口的某些方法上。直接使用HibernateTransactionManager也是一樣的。因此,如果我們有這樣的測(cè)試:
Category parentCategory = new Category ();
??????parentCategory.setName("parent");
??????dao.save(parentCategory);
??????Category childCategory = new Category();
childCategory.setName("child");
??????parentCategory.addChild(childCategory);
??????dao.save(childCategory);
??????Category savedParent = dao.getCategory("parent");
??????Category savedChild = (Category ) savedParent.getChildren().get(0);
??????assertEquals(savedChild, childCategory);
將意味著兩件事情:
- 每次DAO執(zhí)行都會(huì)啟動(dòng)一個(gè)session和關(guān)閉一個(gè)session
- 如果我們定義了一個(gè)lazy的關(guān)系,那么最后的Category savedChild = (Category ) savedParent.getChildren().get(0);將會(huì)讓hibernate報(bào)錯(cuò)。
解決方案
一種方法是對(duì)TestCase應(yīng)用Interceptor或者TransactionManager,但這個(gè)恐怕會(huì)造成很多麻煩。除非是使用增強(qiáng)方式的AOP.我前期采用這種方法(Aspectwerkz),在Eclipse里面也跑得含好。
另一種方法是在TestCase的setup和teardown里面實(shí)現(xiàn)和Filter完全一樣的處理,其他的TestCase都從這個(gè)TestCase繼承,這種方法是我目前所使用的。
轉(zhuǎn)自:Karl Baum's Weblog
Karl Baum's Weblog
All | General | Java
Thursday July 08, 2004
Lazy Initialization and the DAO pattern with Hibernate and Spring
Hibernate and Lazy Initialization
Hibernate object relational mapping offers both lazy and non-lazy modes of object initialization. Non-lazy initialization retrieves an object and all of its related objects at load time. This can result in hundreds if not thousands of select statements when retrieving one entity. The problem is compounded when bi-directional relationships are used, often causing entire databases to be loaded during the initial request. Of course one could tediously examine each object relationship and manually remove those most costly, but in the end, we may be losing the ease of use benefit sought in using the ORM tool.
The obvious solution is to employ the lazy loading mechanism provided by hibernate. This initialization strategy only loads an object's one-to-many and many-to-many relationships when these fields are accessed. The scenario is practically transparent to the developer and a minimum amount of database requests are made, resulting in major performance gains. One drawback to this technique is that lazy loading requires the Hibernate session to remain open while the data object is in use. This causes a major problem when trying to abstract the persistence layer via the Data Access Object pattern. In order to fully abstract the persistence mechanism, all database logic, including opening and closing sessions, must not be performed in the application layer. Most often, this logic is concealed behind the DAO implementation classes which implement interface stubs. The quick and dirty solution is to forget the DAO pattern and include database connection logic in the application layer. This works for small applications but in large systems this can prove to be a major design flaw, hindering application extensibility.
Being Lazy in the Web Layer
Fortunately for us, the Spring Framework has developed an out of box web solution for using the DAO pattern in combination with Hibernate lazy loading. For anyone not familiar with using the Spring Framework in combination with Hibernate, I will not go into the details here, but I encourage you to read Hibernate Data Access with the Spring Framework. In the case of a web application, Spring comes with both the OpenSessionInViewFilter and the OpenSessionInViewInterceptor. One can use either one interchangeably as both serve the same function. The only difference between the two is the interceptor runs within the Spring container and is configured within the web application context while the Filter runs in front of Spring and is configured within the web.xml. Regardless of which one is used, they both open the hibernate session during the request binding this session to the current thread. Once bound to the thread, the open hibernate session can transparently be used within the DAO implementation classes. The session will remain open for the view allowing lazy access the database value objects. Once the view logic is complete, the hibernate session is closed either in the Filter doFilter method or the Interceptor postHandle method. Below is an example of the configuration of each component:
Interceptor Configuration
<beans>
<bean id="urlMapping"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="interceptors">
<list>
<ref bean="openSessionInViewInterceptor"/>
</list>
</property>
<property name="mappings">
...
</bean>
...
<bean name="openSessionInViewInterceptor"
class="org.springframework.orm.hibernate.support.OpenSessionInViewInterceptor">
<property name="sessionFactory"><ref bean="sessionFactory"/></property>
</bean>
</beans>
Filter Configuration
<web-app>
...
<filter>
<filter-name>hibernateFilter</filter-name>
<filter-class>
org.springframework.orm.hibernate.support.OpenSessionInViewFilter
</filter-class>
</filter>
...
<filter-mapping>
<filter-name>hibernateFilter</filter-name>
<url-pattern>*.spring</url-pattern>
</filter-mapping>
...
</web-app>
Implementing the Hibernate DAO's to use the open session is simple. In fact, if you are already using the Spring Framework to implement your Hibernate DAO's, most likely you will not have to change a thing. The DAO's must access Hibernate through the convenient HibernateTemplate utility, which makes database access a piece of cake. Below is an example DAO.
Example DAO
public class HibernateProductDAO extends HibernateDaoSupport implements ProductDAO {
public Product getProduct(Integer productId) {
return (Product)getHibernateTemplate().load(Product.class, productId);
}
public Integer saveProduct(Product product) {
return (Integer) getHibernateTemplate().save(product);
}
public void updateProduct(Product product) {
getHibernateTemplate().update(product);
}
}
Being Lazy in the Business Layer
Even outside the view, the Spring Framework makes it easy to use lazy load initialization, through the AOP interceptor HibernateInterceptor. The hibernate interceptor transparently intercepts calls to any business object configured in the Spring application context, opening a hibernate session before the call, and closing the session afterward. Let's run through a quick example. Suppose we have an interface BusinessObject:
public interface BusinessObject {
public void doSomethingThatInvolvesDaos();
}
The class BusinessObjectImpl implements BusinessObject:
public class BusinessObjectImpl implements BusinessObject {
public void doSomethingThatInvolvesDaos() {
// lots of logic that calls
// DAO classes Which access
// data objects lazily
}
}
Through some configurations in the Spring application context, we can instruct the HibernateInterceptor to intercept calls to the BusinessObjectImpl allowing it's methods to lazily access data objects. Take a look at the fragment below:
<beans>
<bean id="hibernateInterceptor" class="org.springframework.orm.hibernate.HibernateInterceptor">
<property name="sessionFactory">
<ref bean="sessionFactory"/>
</property>
</bean>
<bean id="businessObjectTarget" class="com.acompany.BusinessObjectImpl">
<property name="someDAO"><ref bean="someDAO"/></property>
</bean>
<bean id="businessObject" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target"><ref bean="businessObjectTarget"/></property>
<property name="proxyInterfaces">
<value>com.acompany.BusinessObject</value>
</property>
<property name="interceptorNames">
<list>
<value>hibernateInterceptor</value>
</list>
</property>
</bean>
</beans>
When the businessObject bean is referenced, the HibernateInterceptor opens a hibernate session and passes the call onto the BusinessObjectImpl. When the BusinessObjectImpl has finished executing, the HibernateInterceptor transparently closes the session. The application code has no knowledge of any persistence logic, yet it is still able to lazily access data objects.
Being Lazy in your Unit Tests
Last but not least, we'll need the ability to test our lazy application from J-Unit. This is easily done by overriding the setUp and tearDown methods of the TestCase class. I prefer to keep this code in a convenient abstract TestCase class for all of my tests to extend.
public abstract class MyLazyTestCase extends TestCase {
private SessionFactory sessionFactory;
private Session session;
public void setUp() throws Exception {
super.setUp();
SessionFactory sessionFactory = (SessionFactory) getBean("sessionFactory");
session = SessionFactoryUtils.getSession(sessionFactory, true);
Session s = sessionFactory.openSession();
TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(s));
}
protected Object getBean(String beanName) {
//Code to get objects from Spring application context
}
public void tearDown() throws Exception {
super.tearDown();
SessionHolder holder = (SessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
Session s = holder.getSession();
s.flush();
TransactionSynchronizationManager.unbindResource(sessionFactory);
SessionFactoryUtils.closeSessionIfNecessary(s, sessionFactory);
}
}
( Jul 08 2004, 09:39:55 AM EDT ) Permalink Comments [2]
Trackback URL: http://jroller.com/trackback/kbaum/Weblog/orm_lazy_initialization_with_dao
Comments:
A few things to keep in the back of your mind if you take this approach; 1. If any errors occur while attempting to lazy load relationships in the view (JSP) it would be hard to present a nice error to the user. 2. This would result in at least 2 hibernate sessions (db connections being open for any one request), so you might want to up the number of connections available. Cheers, Dan
Posted by Dan Washusen on July 08, 2004 at 09:02 PM EDT #
I am a little confused on why it would be difficult to show a nice error jsp. Couldn't we just use the provided servlet container error page mechanisms? In regards to the 2 hibernate sessions being opened. Are you saying that the OpenSessionInViewInterceptor would be run twice if an exception was thrown? Thanks for your feedback!
1、我認(rèn)為最關(guān)鍵的,是要讓mm看到你的上進(jìn)心。??
男人的最大魅力在于事業(yè)有成,年輕人工作時(shí)間不長(zhǎng)談不上“有成”,這時(shí)候你就要讓mm覺得你是
????個(gè)有上進(jìn)心的人。??
別的可以胡說(shuō)八道,但這個(gè)問(wèn)題不能含糊,你一定要告訴mm,你對(duì)未來(lái)充滿信心,你不滿足于現(xiàn)狀,并且你已經(jīng)有了長(zhǎng)遠(yuǎn)的計(jì)劃,總之你的未來(lái)不是夢(mèng)。??
??
2、要顯得有信心、有責(zé)任心??
不要像個(gè)小孩子,女孩子都很懶希望能找個(gè)依*,你要拿出自己的信心和責(zé)任心來(lái)。??
有一個(gè)錯(cuò)的選擇總比沒(méi)有選擇要好的多。??
??
3、不要太正經(jīng),但也不要太隨?nbsp;?
該正經(jīng)的地方就正經(jīng),該調(diào)侃的的時(shí)候就調(diào)侃。??
女孩子都喜歡有點(diǎn)玩世不恭的男人,所以別顯得對(duì)什么都特別在意,那樣太呆板。??
??
4、顯得成熟一點(diǎn)??
遇事鎮(zhèn)定、從容不迫的男人對(duì)mm有致命的吸引力。??
??
二、如何與mm展開進(jìn)一步接觸(時(shí)間:開始追的階段)??
??
1、這個(gè)階段最關(guān)鍵的是不能著急,不要把事情弄的那么清楚,讓人家一眼就能看出你在追人家。??
想一想,一般人都不會(huì)一眼就看上你,但也不會(huì)看一眼就討厭你,都是老百姓家的孩子(除非你長(zhǎng)得象周潤(rùn)發(fā)劉德華或者凱文科斯特納),好感是需要隨著了解的不斷增加而實(shí)現(xiàn)的,所以問(wèn)題的關(guān)鍵是你要得的進(jìn)一步發(fā)展的機(jī)會(huì)。??
站在女孩子的角度替人家想一想:你這么直接了當(dāng)?shù)臎_過(guò)來(lái)要搞對(duì)象,女孩子肯定有心理壓力。這要是接觸一陣后發(fā)現(xiàn)不喜歡你,那不就成了耍你了么?所以如果你開始就擺出志在必得的姿勢(shì)出來(lái),基本上會(huì)被立刻悶回去。??
??
2、要低姿態(tài)起步??
首先要把關(guān)系定位成“朋友”,本來(lái)是“普通朋友”,你希望成為“好朋友”,有品位的還可以要求對(duì)方成為“紅顏知己”什么的,總之千萬(wàn)不要說(shuō)“追你”。??
你想想,你如果根本不提“追”,那么女孩子也就更沒(méi)機(jī)會(huì)“拒絕”你——你沒(méi)追她怎么拒絕你?!??
這樣可以減輕女孩子的心理壓力,使你們能順利的交往下去。不要幻想認(rèn)識(shí)三天就答應(yīng)嫁給你,要充分的交往、了解,感情不是憑空產(chǎn)生的。??
??
3、交往的過(guò)程中不要太急躁??
要有張有弛,不要整天纏著人家,誰(shuí)這樣對(duì)你,你也會(huì)膩。我有個(gè)好朋友對(duì)我說(shuō),追女孩子的關(guān)鍵是八個(gè)字—— “忽冷忽熱、欲擒故縱”(這是我同學(xué)多少年心血的結(jié)晶)。??
??
你整天纏著人家自然不覺得你好,你適當(dāng)?shù)睦鋫€(gè)一兩天,女孩子就會(huì)想起你在的好處了。??
??
還有就是不要拿出“非你不娶”的志氣來(lái),太掉價(jià)了不好,有時(shí)候可以耍點(diǎn)花招。??
??
4、要適當(dāng)?shù)膭?chuàng)造機(jī)會(huì)??
前面說(shuō)了,不要使事情立刻變成“你在追別人”,而你又需要得到接近女孩子的機(jī)會(huì),這時(shí)就要看你的創(chuàng)造力了。??
你可以搜集情報(bào),想辦法把守株待兔變成一場(chǎng)邂逅;也可以裝做漫不經(jīng)心的找出最最充足的理由邀請(qǐng)對(duì)方和你一起做什么事。??
總之這個(gè)是最有技術(shù)含量的地方,實(shí)在不行可以找前輩請(qǐng)教。??
5、切忌切忌:隨便送人家禮物是不禮貌的??
有些人追女孩子心切,喜歡經(jīng)常買東西送人家,殊不知追女孩子最忌諱這個(gè)。??
俗話說(shuō)“無(wú)功不受祿”,你這樣送人家東西就是在施加壓力,人家會(huì)覺得欠你的,所以會(huì)想辦法還給你,如果沒(méi)辦法還給你就會(huì)想辦法不和你交往,免得總是欠你人情。??
如果你想顯示自己的誠(chéng)意,倒不妨請(qǐng)女孩子一起消費(fèi),比如說(shuō)找好的餐廳吃飯,或者找貴的地方一起玩什么的,女孩子自然能看出你花了很多錢,但錢終究是兩個(gè)人一起花了而不是變成東西帶回家。??
??
三、“女朋友”到底是什么???
??
1、“女朋友”是一種事
實(shí),而不是一份承諾??
你和女孩子開始交往,從“普通朋友”變成“好朋友”,再到“非常非常好、無(wú)話不談的朋友”,某一個(gè)陽(yáng)光燦爛的午后,你“不小心”拉了她的手;“月上柳梢頭”,你突然襲擊吻了她。這時(shí)她就已經(jīng)是你的女朋友了,無(wú)論她是否承認(rèn),她心理已經(jīng)認(rèn)為你是他男友了。??
我知道最高明的,直到上床了都沒(méi)問(wèn)過(guò)“你是否愿意做我女朋友”,最后還是女孩子急了:“你怎么還不求我做你女朋友啊!”??
所以說(shuō),千萬(wàn)不要急于把窗戶紙捅破,情況越朦朧對(duì)你越有利。??
??
2、“表白”是什么???
前面說(shuō)了,表白實(shí)際上就是一個(gè)形式而已,正確的順序應(yīng)該是:事實(shí)上已經(jīng)成為你女朋友了,你才能向人家表白,水到渠成。 很多人弄不明白這個(gè)問(wèn)題,總以為人家先答應(yīng)做自己女朋友,然后再如何如何,我只能說(shuō)他非常非常“單純”,也非常非常“愚蠢”。??
??
3、有沒(méi)有“迫不得已非表白不可”的時(shí)候???
??
有,比如說(shuō)出現(xiàn)第三者,或者你和女孩子關(guān)系沒(méi)有成熟但兩個(gè)人可能分開一段時(shí)間。??
這時(shí)候的表白就是條件不成熟的表白,風(fēng)險(xiǎn)非常大,類似于下圍棋的時(shí)候形勢(shì)嚴(yán)峻,落後的一方迫于無(wú)奈放出“勝負(fù)手”,贏了就贏了,輸了也只能說(shuō)“倒霉都是天生的”。??
??
4、“愛”字不要輕易出口??
經(jīng)常看見論壇出現(xiàn)“大膽的表白”,說(shuō)實(shí)話我真的認(rèn)為這是非常不成熟的一種表現(xiàn)。“愛”是一個(gè)神圣的字,意味著追求,也意味著承諾,甚至體現(xiàn)出一種責(zé)任。??
隨便說(shuō)“愛”的男人是不負(fù)責(zé)任的。??
??
四、文明戀愛,不可強(qiáng)求??
??
1、不是每個(gè)mm都能追到手的??
好女孩總會(huì)有很多人追,不可能遂了每個(gè)人的心愿,總會(huì)有失敗者。舉個(gè)例子,就算你刻苦鉆研掌握了最搞超的追mm原理,你一樣追不上twins里的任何一個(gè)。??
換個(gè)角度考慮問(wèn)題,一個(gè)小學(xué)沒(méi)畢業(yè)的農(nóng)村小保姆,即使對(duì)你再好,每個(gè)月賺600給你買700的禮物(透支),愿意為你“當(dāng)牛做馬”,你也不會(huì)愛上她。如果她每天哭哭啼啼的纏著,你肯定覺得煩。??
所以說(shuō)愛情是需要物質(zhì)基礎(chǔ)的,至少需要平衡。??
??
2、追mm做是一種嚴(yán)肅的社會(huì)活動(dòng)??
千萬(wàn)不要把人家搞煩了,要給自己留后路。大丈夫何患無(wú)妻?有些mm確實(shí)勢(shì)利眼(少數(shù)),如果不服氣,你可以發(fā)憤圖強(qiáng),用事實(shí)證明“她當(dāng)時(shí)瞎了眼”,絕對(duì)不要誤人誤己。 ??
??
最后補(bǔ)充一點(diǎn)千萬(wàn)不要在mm面前顯得憤世嫉俗,憤世嫉俗有時(shí)候意味著“你很失敗”