亚洲欧美一区二区三区久本道91,国产精品大全,欧美成a人片在线观看久http://www.aygfsteel.com/nogocn/轉變思維觀念:人是群體生活的.需要溝通與理解.zh-cnFri, 08 Aug 2025 02:29:51 GMTFri, 08 Aug 2025 02:29:51 GMT60保存引用JBPM4http://www.aygfsteel.com/nogocn/archive/2009/11/24/303479.htmlNGNGTue, 24 Nov 2009 07:48:00 GMThttp://www.aygfsteel.com/nogocn/archive/2009/11/24/303479.htmlhttp://www.aygfsteel.com/nogocn/comments/303479.htmlhttp://www.aygfsteel.com/nogocn/archive/2009/11/24/303479.html#Feedback0http://www.aygfsteel.com/nogocn/comments/commentRss/303479.htmlhttp://www.aygfsteel.com/nogocn/services/trackbacks/303479.html

jBPM3 vs jBPM4

JBoss Goup 目前已經發布了 jBPM4 Alpha1 版本,在版本 4 中最大的變化就是引入 PVM (流程虛擬機)的概念,而引擎內部的調度算法中重要的 Token 機制,在新版中也去掉了,縱觀整個代碼,變化可以說非常的大,筆者接下來就試著來比較一下這種變化,讓大家能有個直觀的認識。當然 Jbpm4 JBoss 的官方網站上的 Road map 中,在今年的 7 1 號才會發布第一個正式版本,因此后續可能還會有變化。

1、  流程定義對象的變化:

Jbpm3 流程定義對象關系圖:

圖一 jbpm3流程定義對象關系圖

從上圖我們可以看出這 jbpm3 中, GraphElement 是流程圖中所有流程元素的父對象,而整個流程是由 ProcessDefinition Node Transition 三個主要對象構成;

 

 圖二 PVM 實體對象關系圖      

從上圖可以看出,由于 PVM 概念的引入,所以在 jbpm3 中的 Graph 包在 jbpm4 中被移除了。在 pvm 中,在設計期,所有節點元素的父類為 ProcessElementImpl ,流程的主要組成元素 Nodelmpl TransitionImpl ProcessDefinitionImpl EventImpl 則都直接或間接繼承自 ProcessElementImpl 。在運行期: jbpm4 把流程的運行期行為定義為執行行為( ExecutionImpl )及原子操作行為( AtomicOperation ,其具體實現為 ExecuteNode ProceedToDestination TakeTranstion MoveToParentNode MoveToChildNode signal ),其中 ExecutionImpl 是流程實例、活動實例、事件監聽器的所有執行期行為的實現類。

 

圖三 jpdl 運行期活動實體對象關系圖

  上圖是 jbpm4 在運行期的活動實例對象關系圖,從圖中我們可以看出,在運行期, jbpm4 中定義了兩個活動接口 Activity ExternalActivity ,其中 ExternalActivity 繼承自 Activity Activity 是所有自動活動節點的父接口,其實現類為 JpdlActivity ,而 JpdlActivity 又衍生出了、 StartActivity JoinActivity ForkActivity EndActivity CreateTimerActivity JavaActivity EsbActivity 等實例活動對象。而 ExternalActivity 是具有等待狀態的活動( StateActivity )父接口,像人工活動 TaskActivity 就是實現了此接口。

  2、  核心引擎的調度算法

Jbpm3 的核心調度算法是基于 Token 機制的,在運行期這個 Token Node Instance 之間流轉,依靠 Token 的觸發來推進流程。具體的調度機制,可參加胡長城的文章( http://blog.csdn.net/james999/archive/2007/09/02/1769592.aspx );其實這個 Token 來自于 Pertri-net ,感興趣的讀者可以去看 Pertri-net 中的 Token Place

  

圖四 jbpm3引擎調度圖  

Jbpm4 則去掉了 Token ,那么它的核心調度機制是怎樣實現的呢?

 

 圖五 jbpm4流程啟動序列圖

 

圖六 jbpm4 流程推進序列圖

圖五是在 jbpm4 中啟動一個流程實例的執行序列圖,圖六是節點推進的執行序列圖,從上面兩個圖中我們可以看到核心的調度是依據 Execution 的轉移來實現的( ExecutionImpl 可以是 ActivityExecution ClientProcessInstance EventListenerExecution 的實例), Execution 實際上就是取代了 Jbpm3 中的 Token Execution 的轉移實際上就是根據狀態機的變遷( ActivityExecution ClientProcessInstance EventListenerExecution 實例之間的切換)加上調用相應的原子操作: ExecuteNode MoveToChildNode MoveToParentNode ProceedToDesitination Signal TakeTransition (詳見 pvm/internal/model/op 包下的相關類)來實現的。所以 Execution 實例的集合及有向圖實際上就是運行期的路徑。

3、  Event-Action 機制的變化

jbpm3 中是基于 Event-Action 機制來實現事件與動作的觸發的,但是在 jbpm4 中則采用觀察者模式來觸發事件的。所有用戶自己定義的動作,全部要實現 EventListener 接口,這些動作作為監聽者(就是事件 Event 的觀察者 Observer )注冊到相應的流程定義對象上( ProcessElement 或者 Node ),而事件 Event 則作為被觀察的對象(實際上就是 Observerable ),實際上在 jbpm4 中專門定義出了一個對象 ObservableElementImpl ,流程定義中的 NodeImpl TransitionImpl ProcessDefinitionImpl 均繼承自此對象,因此這些元素本身就可以作為 Observerable 而被觀察者來監控。

4、  客戶端接口的變化

jbpm4中對客戶端的接口統一為7個服務接口:ProcessServiceExecutionServiceCommandService TaskService ManagementServiceHistoryServiceIdentityService,這7個接口可以從ProcessEngine接口中獲得,jbpm4在啟動的過程中由JbpmConfiguration負責構建引擎。

Ø ProcessService-流程定義的服務接口,包括對流程定義的部署、查詢、刪除操作;

Ø ExecutionService-執行服務接口,包括啟動流程、實例推進、設置變量等操作;

Ø CommandService-Command模式的服務接口,實際上就是將客戶端的請求全部封裝在一個調用接口中,然后由這個接口去調用Command接口的眾多實現(StartExecutionCmdSignalCmdSetVariablesCmdGetTimersCmdDeployCmdNewTaskCmdSubmitTaskExecuteJobCmd等等,具體可參加pvm/internal/cmdtask/internal/cmd包及其它包下實現Command接口的類),這是典型的Command模式的應用,感興趣的讀者可以去了解設計模式中的Command模式;

Ø TaskService-人工活動的服務接口,包括對任務的創建、提交、查詢、保存、刪除等操作;

Ø ManagementService-web管理控制臺的服務接口,目前只有獲得消息及計時器的接口實現;

Ø HistoryService-目前有對歷史庫中的流程實例、活動實例進行查詢、某個流程定義中的所有活動的平均持續時間、某個流程定義中的某個活動實例的轉移的執行次數

Ø IdentityService-用戶、組、成員關系的相關操作方法

5、歷史庫的加入

jBPM3中數據庫設計一直是我比較詬病的地方,尤其是其實例數據庫沒有設計歷史庫的概念并按照辦結狀態將運行結束的實例數據歸入歷史庫,在這種情況下它的實例數據庫就會隨著時間而無限膨脹,這就阻礙了它的真實應用,而在jBPM4的最新代碼中(注意Alpha1還沒有出現),歷史庫的相關功能代碼竟然出現了!詳見ExecutionImpl最新代碼中的fireHistoryEvent方法及一系列的historyXXX方法。在ActivityBehaviourexecute方法中加入了historyTaskStart方法的調用、signal方法中加入了historyTaskEnd方法的調用,而以上2個方法在ExecutionImpl中都是以歷史事件(HistoryEvent4個實現子類ProcessInstanceStartProcessInstanceEndActivityStartActivityEnd分別用作流程實例的創建結束期、活動實例的創建結束期的歷史數據處理)的觸發機制來實現的,也就是在整個流程實例執行的過程中,都加入了對將運行數據存入歷史庫的歷史事件(HistoryEvent)的觸發。這樣實例列表的查詢可以只查詢歷史庫。不過這里很遺憾的是,這個事件沒有同時清除運行庫的數據,這樣還是會造成運行庫的無限膨脹問題。



NG 2009-11-24 15:48 發表評論
]]>
eclipse 快捷鍵http://www.aygfsteel.com/nogocn/archive/2009/10/09/297532.htmlNGNGFri, 09 Oct 2009 06:24:00 GMThttp://www.aygfsteel.com/nogocn/archive/2009/10/09/297532.htmlhttp://www.aygfsteel.com/nogocn/comments/297532.htmlhttp://www.aygfsteel.com/nogocn/archive/2009/10/09/297532.html#Feedback0http://www.aygfsteel.com/nogocn/comments/commentRss/297532.htmlhttp://www.aygfsteel.com/nogocn/services/trackbacks/297532.html下面的這些是我最常用的快捷鍵:
CTRL+SHIFT+T:根據名字查找類,可以使用通配符,只能查找類,可以是工程引用的包中的類
CTRL+SHIFT+R:根據名字查找資源,可以是jsp,xml,js等任何文件
F11:以DEBUG模式運行上次運行的類
F5:單步進入,進入當前調試的代碼行執行
F6:單步執行當前行
F8:執行到下一個斷點
ALT+SHIFT+R:重命名選擇的文件名,如果是類的話,將進行重構,同時修改相關的引用
ALT+上下箭頭:將當前行或者選中的行上移或者下移一行
ALT+/:代碼自動補全
CTRL+SHIFT+O:自動導入需要的類
CTRL+SHIFT+F:格式化當前文件或者選中的行
CTRL+L:定位到指定的行
CTRL+/:注釋/取消注釋
CTRL+ALT+H:打開調用層次,如果要知道一個方法的調用入口時很有用
CTRL+Z:取消上次修改(Undo)
CTRL+Y:重復上次修改(Redo)
快捷鍵大全
作用域 功能 快捷鍵
  全局 查找并替換 Ctrl+F
  文本編輯器 查找上一個 Ctrl+Shift+K
  文本編輯器 查找下一個 Ctrl+K
  全局 撤銷 Ctrl+Z
  全局 復制 Ctrl+C
  全局 恢復上一個選擇 Alt+Shift+↓
  全局 剪切 Ctrl+X
  全局 快速修正 Ctrl1+1
  全局 內容輔助 Alt+/
  全局 全部選中 Ctrl+A
  全局 刪除 Delete
  全局 上下文信息 Alt+?
  Alt+Shift+?
  Ctrl+Shift+Space
  Java編輯器 顯示工具提示描述 F2
  Java編輯器 選擇封裝元素 Alt+Shift+↑
  Java編輯器 選擇上一個元素 Alt+Shift+←
  Java編輯器 選擇下一個元素 Alt+Shift+→
  文本編輯器 增量查找 Ctrl+J
  文本編輯器 增量逆向查找 Ctrl+Shift+J
  全局 粘貼 Ctrl+V
  全局 重做 Ctrl+Y

  查看
  作用域 功能 快捷鍵
  全局 放大 Ctrl+=
  全局 縮小 Ctrl+-

  窗口
  作用域 功能 快捷鍵
  全局 激活編輯器 F12
  全局 切換編輯器 Ctrl+Shift+W
  全局 上一個編輯器 Ctrl+Shift+F6
  全局 上一個視圖 Ctrl+Shift+F7
  全局 上一個透視圖 Ctrl+Shift+F8
  全局 下一個編輯器 Ctrl+F6
  全局 下一個視圖 Ctrl+F7
  全局 下一個透視圖 Ctrl+F8
  文本編輯器 顯示標尺上下文菜單 Ctrl+W
  全局 顯示視圖菜單 Ctrl+F10
  全局 顯示系統菜單 Alt+-

  導航
  作用域 功能 快捷鍵
  Java編輯器 打開結構 Ctrl+F3
  全局 打開類型 Ctrl+Shift+T
  全局 打開類型層次結構 F4
  全局 打開聲明 F3
  全局 打開外部javadoc Shift+F2
  全局 打開資源 Ctrl+Shift+R
  全局 后退歷史記錄 Alt+←
  全局 前進歷史記錄 Alt+→
  全局 上一個 Ctrl+,
  全局 下一個 Ctrl+.
  Java編輯器 顯示大綱 Ctrl+O
  全局 在層次結構中打開類型 Ctrl+Shift+H
  全局 轉至匹配的括號 Ctrl+Shift+P
  全局 轉至上一個編輯位置 Ctrl+Q
  Java編輯器 轉至上一個成員 Ctrl+Shift+↑
  Java編輯器 轉至下一個成員 Ctrl+Shift+↓
  文本編輯器 轉至行 Ctrl+L

  搜索
  作用域 功能 快捷鍵
  全局 出現在文件中 Ctrl+Shift+U
  全局 打開搜索對話框 Ctrl+H
  全局 工作區中的聲明 Ctrl+G
  全局 工作區中的引用 Ctrl+Shift+G

  文本編輯
  作用域 功能 快捷鍵
  文本編輯器 改寫切換 Insert
  文本編輯器 上滾行 Ctrl+↑
  文本編輯器 下滾行 Ctrl+↓

  文件
  作用域 功能 快捷鍵
  全局 保存 Ctrl+X
  Ctrl+S
  全局 打印 Ctrl+P
  全局 關閉 Ctrl+F4
  全局 全部保存 Ctrl+Shift+S
  全局 全部關閉 Ctrl+Shift+F4
  全局 屬性 Alt+Enter
  全局 新建 Ctrl+N

  項目
  作用域 功能 快捷鍵
  全局 全部構建 Ctrl+B

  源代碼
  作用域 功能 快捷鍵
  Java編輯器 格式化 Ctrl+Shift+F
  Java編輯器 取消注釋 Ctrl+\
  Java編輯器 注釋 Ctrl+/
  Java編輯器 添加導入 Ctrl+Shift+M
  Java編輯器 組織導入 Ctrl+Shift+O
  Java編輯器 使用try/catch塊來包圍 未設置,太常用了,所以在這里列出,建議自己設置。
  也可以使用Ctrl+1自動修正。

  運行
  作用域 功能 快捷鍵
  全局 單步返回 F7
  全局 單步跳過 F6
  全局 單步跳入 F5
  全局 單步跳入選擇 Ctrl+F5
  全局 調試上次啟動 F11
  全局 繼續 F8
  全局 使用過濾器單步執行 Shift+F5
  全局 添加/去除斷點 Ctrl+Shift+B
  全局 顯示 Ctrl+D
  全局 運行上次啟動 Ctrl+F11
  全局 運行至行 Ctrl+R
  全局 執行 Ctrl+U

  重構
  作用域 功能 快捷鍵
  全局 撤銷重構 Alt+Shift+Z
  全局 抽取方法 Alt+Shift+M
  全局 抽取局部變量 Alt+Shift+L
  全局 內聯 Alt+Shift+I
  全局 移動 Alt+Shift+V
  全局 重命名 Alt+Shift+R
  全局 重做 Alt+Shift+Y

Eclipse啟動參數大全 - -
                         
Eclipse 運行命令行參數大全
包括英文版本和中文版本兩種的說明, 特別需要值得一提的是那個 -nl 參數, 可以指定程序啟動時所使用的語言. 例如:
eclipse -nl en_US
將啟動英文語言, 這個特性在安裝了國際化語言包以后特別有用, 可以方便的切換各個語言的版本. 注意 IBM WSAD v5.1 也支持這個功能.

運行 Eclipse
將 Eclipse 驅動程序安裝(解壓縮)到某個目錄(例如,c:\eclipse)中之后,通過運行頂級安裝目錄中的 Eclipse 可執行文件來啟動"工作臺"。在 Windows 系統上,該可執行文件稱為 eclipse.exe,而在 Linux 系統上稱為 eclipse。注意:下列討論描述 Windows 系統上的設置。Linux 上的設置是相似的。

如果您沒有另行指定,則平臺將缺省工作區目錄創建為可執行文件的兄弟目錄(例如 c:\eclipse\workspace)。此工作區目錄用作項目的缺省內容區,還用于保存任何必需的元數據。要進行共享安裝或多工作區安裝,應明確指 出工作區的位置而不是使用缺省值。有兩種控制工作區位置的方法:使用當前工作目錄或使用 -data 命令行自變量。

將工作區位置設置為在當前工作目錄內
在此方案中,工作區位置將是當前工作目錄中稱為 workspace 的目錄。

實現此目的最容易的方法可能是使用下列步驟來創建快捷方式:

導航到 Windows 資源管理器中的 eclipse.exe 并使用右鍵拖動來創建 eclipse.exe 的快捷方式。
編輯快捷方式的屬性,以使啟動位置:字段標識工作區位置的父目錄(例如,c:\users\robert)。
關閉屬性對話框并雙擊快捷方式(如果提供的目錄為 c:\users\robert,則工作區位置將為 c:\users\robert\workspace)。
當然,您也可以使用命令提示符(通過將目錄切換為工作區父目錄然后運行 eclipse.exe)來獲得同樣的效果。

使用 -data 設置工作區的特定位置
要使用 -data 命令行自變量,只要將 -data your_workspace_location(例如,-data c:\users\robert\myworkspace)添加至快捷方式屬性中的目標字段或顯式地將它包括在命令行上。

使用 -vm 設置 java VM
建議顯式指定在運行 Eclipse 時要使用哪個 Java VM。使用 -vm 命令行自變量(例如,-vm c:\jre\bin\javaw.exe)可以實現此目的。如果不使用 -vm,則 Eclipse 將使用在 O/S 路徑上找到的一個 Java VM。當安裝其它產品時,它們可更改您的路徑,導致在下一次啟動 Eclipse 時使用另一 Java VM。

運行 Eclipse 中的高級主題
Eclipse 可執行文件及平臺本身提供了人們感興趣的開發或調試 Eclipse 各部件的許多執行選項。運行 Eclipse 可執行文件的一般格式是:

eclipse [platform options] [-vmargs [Java VM arguments]]
Eclipse 啟動參數 命令 描述 原因
-arch architecture
定義 Eclipse 平臺在其上運行的處理器體系結構。Eclipse 平臺通常使用 Java os.arch 屬性的常用值來計算最佳設置。如果在此處指定該項,則這是 Eclipse 平臺使用的值。此處指定的值可作為 BootLoader.getOSArch() 用于插件。示例值有:"x86"、"sparc"、"PA-RISC"和"ppc"。 2.0
-application applicationId
要運行的應用程序。應用程序由向 org.eclipse.core.runtime.applications 擴展點提供擴展的插件來聲明。通常不需要此自變量。如果指定了此項,則該值會覆蓋配置提供的值。如果不指定此項,則會運行"Eclipse 工作臺"。 1.0
-boot bootJarURL
(建議不使用;用 -configuration 代替;支持 1.0 兼容)。Eclipse 平臺的引導插件代碼(boot.jar)的位置,表示為 URL。如果指定此項,則會用它來為裝入 Eclipse 平臺引導程序類裝入器的類裝入器設置類路徑。僅當更改 startup.jar 和 boot.jar 的相對位置時才需要它。注意,不允許使用相對 URL。 *1.0
-classloaderproperties [file]
如果指定的話,則使用給定位置處的類裝入器屬性文件來激活平臺類類裝入器增強。文件自變量可以是文件路徑或 URL。注意,不允許使用相對 URL。單擊此處以獲得更多詳細信息。 2.0.2
-configuration configurationFileURL
Eclipse 平臺配置文件的位置,表示為 URL。配置文件確定 Eclipse 平臺、可用插件集和主要功能部件的位置。注意,不允許使用相對 URL。當安裝或更新 Eclipse 平臺時配置文件被寫至此位置。 2.0
-consolelog
將 Eclipse 平臺的錯誤日志鏡像到用來運行 Eclipse 的控制臺。與 -debug 組合時很方便使用。 1.0
-data workspacePath
要運行 Eclipse 平臺的工作區的路徑。工作區位置也是項目的缺省位置。相對于從中啟動 eclipse 的目錄來解釋相對路徑。 1.0
-debug [optionsFile]
將平臺置于調試方式,并從給定位置處的文件裝入調試選項(如果指定的話)。此文件指示哪些調試點可用于插件以及是否已啟用它們。如果未給出文件位置,則平 臺在啟動 eclipse 的目錄中查找稱為".options"的文件。URL 和文件系統路徑都可作為文件位置。 1.0
-dev [classpathEntries]
將平臺置于開發方式。將可選類路徑條目(用逗號分隔的列表)添加至每個插件的運行時類路徑。例如,當工作區包含要開發的插件時,指定 -dev bin 會為每個插件項目的名為 bin 的目錄添加類路徑條目,允許在其中存儲最新生成的類文件。除去了冗余或不存在的類路徑條目。 1.0
-endsplash params
用于在 Eclipse 平臺啟動并運行時關閉閃屏的內部選項。此選項在閃屏處理鏈中不同的位置有不同的語法和語義。 2.0
-feature featureId
主要功能部件的標識。主要功能部件為 Eclipse 的已啟動實例提供了產品個性,并確定使用的產品定制信息。 2.0
-keyring keyringFilePath
磁盤上授權數據庫(或"密鑰環"文件)的位置。此自變量必須與 -password 選項配合使用。相對于從中啟動 eclipse 的目錄來解釋相對路徑。 1.0
-nl local

本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/JefferyLee/articles/1760821.aspx



NG 2009-10-09 14:24 發表評論
]]>
JAVA得到網卡物理地址(windows和Linux)http://www.aygfsteel.com/nogocn/archive/2009/09/14/294971.htmlNGNGMon, 14 Sep 2009 02:00:00 GMThttp://www.aygfsteel.com/nogocn/archive/2009/09/14/294971.htmlhttp://www.aygfsteel.com/nogocn/comments/294971.htmlhttp://www.aygfsteel.com/nogocn/archive/2009/09/14/294971.html#Feedback0http://www.aygfsteel.com/nogocn/comments/commentRss/294971.htmlhttp://www.aygfsteel.com/nogocn/services/trackbacks/294971.html   1import java.io.BufferedReader;  
   
2import java.io.IOException;  
   
3import java.io.InputStreamReader;  
   
4import java.util.Properties;  
   
5import java.util.logging.Level;  
   
6import java.util.logging.Logger;  
   
7.   
   
8public class Test {  
   
9.   
  
10.     public static String getMACAddress() {  
  
11.   
  
12.          String address = "";  
  
13.          String os = System.getProperty("os.name");  
  
14.          System.out.println(os);  
  
15.         if (os != null) {  
  
16.             if (os.startsWith("Windows")) {  
  
17.                 try {  
  
18.                      ProcessBuilder pb = new ProcessBuilder("ipconfig""/all");  
  
19.                      Process p = pb.start();  
  
20.                      BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));  
  
21.                      String line;  
  
22.                     while ((line = br.readLine()) != null) {  
  
23.                         if (line.indexOf("Physical Address"!= -1) {  
  
24.                             int index = line.indexOf(":");  
  
25.                              address = line.substring(index + 1);  
  
26.                             break;  
  
27.                          }  
  
28.                      }  
  
29.                      br.close();  
  
30.                     return address.trim();  
  
31.                  } catch (IOException e) {  
  
32.                       
  
33.                  }  
  
34.              }else if(os.startsWith("Linux")){  
  
35.                 try {  
  
36.                      ProcessBuilder pb = new ProcessBuilder("ifconfig");  
  
37.                      Process p = pb.start();  
  
38.                      BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));  
  
39.                      String line;  
  
40.                     while((line=br.readLine())!=null){  
  
41.                         int index=line.indexOf("硬件地址");  
  
42.                         if(index!=-1){  
  
43.                              address=line.substring(index+4);  
  
44.                             break;  
  
45.                          }  
  
46.                      }  
  
47.                      br.close();  
  
48.                     return address.trim();  
  
49.                  } catch (IOException ex) {  
  
50.                      Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);  
  
51.                  }  
  
52.                   
  
53.              }  
  
54.          }  
  
55.         return address;  
  
56.      }  
  
57.   
  
58.     public static void main(String[] args) {  
  
59.          System.out.println("" + Test.getMACAddress());  
  
60.      }  
  
61. } 


NG 2009-09-14 10:00 發表評論
]]>
Java獲取CPU序列號和網卡Mac地址http://www.aygfsteel.com/nogocn/archive/2009/09/14/294969.htmlNGNGMon, 14 Sep 2009 01:51:00 GMThttp://www.aygfsteel.com/nogocn/archive/2009/09/14/294969.htmlhttp://www.aygfsteel.com/nogocn/comments/294969.htmlhttp://www.aygfsteel.com/nogocn/archive/2009/09/14/294969.html#Feedback0http://www.aygfsteel.com/nogocn/comments/commentRss/294969.htmlhttp://www.aygfsteel.com/nogocn/services/trackbacks/294969.html /*
利用Runtime call操作系統的命令,具體的命令取決于不同的操作系統,注意不要調用Runtime.getRuntime().exec(String)接口,要用Runtime.getRuntime().exec(String[])這個接口,不然復雜命令的執行會有問題。例子如下(拿cpu個數,其他類似):
定義命令:
WindowsCmd ="cmd.exe /c echo %NUMBER_OF_PROCESSORS%";//windows的特殊
SolarisCmd = {"/bin/sh", "-c", "/usr/sbin/psrinfo | wc -l"};
AIXCmd = {"/bin/sh", "-c", "/usr/sbin/lsdev -Cc processor | wc -l"};
HPUXCmd = {"/bin/sh", "-c", "echo \"map\" | /usr/sbin/cstm | grep CPU | wc -l "};
LinuxCmd = {"/bin/sh", "-c", "cat /proc/cpuinfo | grep ^process | wc -l"};

然后判斷系統:
os = System.getProperty("os.name").toLowerCase();

根據不同的操作系統call不同的命令。
*/
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.LineNumberReader;

public class GetMACAddress
{
public String getMACAddress(String ipAddress)
{
String str = "",strMAC = "",macAddress = "";
try
{
Process pp = Runtime.getRuntime().exec("nbtstat -a " + ipAddress);
InputStreamReader ir = new InputStreamReader(pp.getInputStream());
LineNumberReader input = new LineNumberReader(ir);
for(int i = 1;i < 100;i++)
{
str = input.readLine();
if(str != null)
{
if(str.indexOf("MAC Address") > 1)
{
strMAC = str.substring(str.indexOf("MAC Address") + 14,str.length());
break;
}
}
}
}
catch(IOException ex)
{
return "Can't Get MAC Address!";
}
//
if(strMAC.length() < 17)
{
return "Error!";
}
macAddress = strMAC.substring(0,2) + ":"
+ strMAC.substring(3,5) + ":"
+ strMAC.substring(6,8) + ":"
+ strMAC.substring(9,11) + ":"
+ strMAC.substring(12,14) + ":"
+ strMAC.substring(15,17);
//
return macAddress;
}

public static void main(String[] args)
{
GetMACAddress getMACAddress = new GetMACAddress();
System.out.println(getMACAddress.getMACAddress("172.18.8.225"));

try
{
java.lang.Process proc = Runtime.getRuntime().exec("ipconfig /all");
InputStream istr = proc.getInputStream();
byte[] data = new byte[1024];
istr.read(data);
String netdata = new String(data);
System.out.println("Your Mac Address=" + procAll(netdata));
}
catch(IOException e)
{
System.out.println("error=" + e);
}
}

public static String procAll(String str)
{
return procStringEnd(procFirstMac(procAddress(str)));
}

public static String procAddress(String str)
{
int indexof = str.indexOf("Physical Address");
if(indexof > 0)
{
return str.substring(indexof,str.length());
}
return str;
}

public static String procFirstMac(String str)
{
int indexof = str.indexOf(":");
if(indexof > 0)
{
return str.substring(indexof + 1,str.length()).trim();
}
return str;
}

public static String procStringEnd(String str)
{
int indexof = str.indexOf("\r");
if(indexof > 0)
{
return str.substring(0,indexof).trim();
}
return str;
}
}



import java.util.Vector;

class GetNetMAC
{
//網卡物理地址長度
static private final int _physicalLength = 16;

public static void main(String[] args)
{
//output you computer phycail ip address
System.out.println("The MAC Addressis:\t" + getPhysicalAddress());
}

static public String getPhysicalAddress()
{
GetNetMACShell shell = new GetNetMACShell();
String cmd = "cmd.exe /c ipconfig/all";
Vector result;
result = shell.execute(cmd);
return parseCmd(result.toString());
}

//從字符串中解析出所需要獲得的字符串
static private String parseCmd(String s)
{
String find = "Physical Address. . . . . . . . . :";
int findIndex = s.indexOf(find);
if(findIndex == -1)
{
return "not find";
}
else
{
return s.substring(findIndex + find.length() + 1,findIndex + find.length() + 1 + _physicalLength);
}
}
}





NG 2009-09-14 09:51 發表評論
]]>
Dom4j的使用(全而好的文章)http://www.aygfsteel.com/nogocn/archive/2009/08/31/293303.htmlNGNGMon, 31 Aug 2009 05:49:00 GMThttp://www.aygfsteel.com/nogocn/archive/2009/08/31/293303.htmlhttp://www.aygfsteel.com/nogocn/comments/293303.htmlhttp://www.aygfsteel.com/nogocn/archive/2009/08/31/293303.html#Feedback0http://www.aygfsteel.com/nogocn/comments/commentRss/293303.htmlhttp://www.aygfsteel.com/nogocn/services/trackbacks/293303.html閱讀全文

NG 2009-08-31 13:49 發表評論
]]>
利用反射機制動態將XML信息設置入對象http://www.aygfsteel.com/nogocn/archive/2009/08/31/293258.htmlNGNGMon, 31 Aug 2009 01:23:00 GMThttp://www.aygfsteel.com/nogocn/archive/2009/08/31/293258.htmlhttp://www.aygfsteel.com/nogocn/comments/293258.htmlhttp://www.aygfsteel.com/nogocn/archive/2009/08/31/293258.html#Feedback0http://www.aygfsteel.com/nogocn/comments/commentRss/293258.htmlhttp://www.aygfsteel.com/nogocn/services/trackbacks/293258.html
1. 主函數
Java代碼 復制代碼
  1.     public static void main(String[] args) {   
  2.         // TODO Auto-generated method stub   
  3.         PaserXML p=new PaserXML();   
  4.         String xml="<?xml version='1.0' encoding='UTF-8'?> <root><Customer><id>1</id><name>ddd</name></Customer>"+   
  5. "<order><orderId>2</orderId><orderName>nnmnmnmn</orderName></order></root>";   
  6.   
  7.         p.parserByXpath2(xml);   
  8.     }  


2. 利用XPath獲得節點信息
Java代碼 復制代碼
  1. /**  
  2.  *  利用反射機制實現的 將xml中的相關信息付給Vo  
  3.  *  輸入:String XML  
  4.  *  返回: void  
  5.  *  執行中---將XML數據 付給對應的VO :這里需要對命名進行規范  
  6.  * */  
  7.   
  8. public void parserByXpath2(String xml){   
  9.        
  10.      try {   
  11.             Reader in=new StringReader(xml);   
  12.             Document document=builder.build(in);   
  13.             Element root=document.getRootElement();   
  14. /               System.out.println(root.getName());   
  15.             Element o=(Element)org.jdom.xpath.XPath.selectSingleNode(root, "/root/order");   
  16.             OrderVO vo=new OrderVO();   
  17.             this.ElementTOVO(o, vo);//真正執行將通過Xpath解析出來的節點下的 信息  付給 VO   
  18.             System.out.println("vo.getOrderId()"+vo.getOrderId());   
  19.             System.out.println("vo.getOrderName()"+vo.getOrderName());   
  20.         } catch (JDOMException e) {   
  21.             // TODO Auto-generated catch block   
  22.             e.printStackTrace();   
  23.         } catch (IOException e) {   
  24.             // TODO Auto-generated catch block   
  25.             e.printStackTrace();   
  26.         }   
  27.        
  28. }  


3. 反射機制實現set方法的動態調用
Java代碼 復制代碼
  1. /**  
  2.      * 輸入: Element 利用Xpath解析出來要做處理的 Docuemnt 文檔樹中的階段信息  
  3.      * 輸出: Void  
  4.      * 思路: 讀取XML文件中要做處理的值信息,將值賦給相應的VO的字段,這里需要調用其set方法實現  
  5.      *       由于不同的VO會有不同數量,不同值得字段和set方法,這里采用反射機制.  
  6.      *         
  7.      *         
  8.      * */  
  9. public void ElementTOVO(Element e,Object vo){   
  10.                
  11.         Class clazz=vo.getClass();   
  12.         Field fd=null;   
  13.         Method m=null;   
  14.         Class fType=null;   
  15.         String fName="";   
  16.         Element element=null;   
  17.         String xmlValue="";   
  18. java.lang.reflect.Method[] method=clazz.getDeclaredMethods();//獲得所有的聲明方法   
  19.                 java.lang.reflect.Field[] field=clazz.getDeclaredFields();//獲得所有的聲明字段   
  20.                 for(int i=0;i<field.length;i++){   
  21.                     fd=field[i];   
  22.                     fName=fd.getName();// 獲得字段的名稱   
  23.                     fType=fd.getType();// 獲得字段的類型   
  24.                     if("java.lang.String".equals(fType.getName())){//判斷字段類型是否為String型,由于這里需要利用反射機制調用方法而且為了統一實現   
  25.                         //將VO中的字段都定義為String 型   
  26.                         element=e.getChild(fName);//獲得e(即傳過來的Element)下的制定名稱的Element fName    
  27.                         if(element==null){   
  28.                             xmlValue=null;   
  29.                         }   
  30.                         else{   
  31.                             xmlValue=element.getValue();//獲得指定元素下的值信息   
  32.                         }   
  33.                         fName=fName.substring(0,1).toUpperCase()+fName.substring(1,fName.length());//由于屬性名第一個字母為小寫   
  34.                         //為了調用set方法,將第一個字母大寫 以便構造set方法名。 比如 orderId--->OrderId---->setOrderId   
  35.                         if(fName!=null){//如果方法名存在   
  36.                             try {   
  37.                                 m=clazz.getMethod("set"+fName, new Class[]{fType});//通過方法名獲得 指定的方法參數類型 比如String   
  38.                                 //第一個參數指定要調用方法的名稱、第二個指定調用方法的參數類型   
  39.                             } catch (SecurityException e1) {   
  40.                                 // TODO Auto-generated catch block   
  41.                                 e1.printStackTrace();   
  42.                             } catch (NoSuchMethodException e1) {   
  43.                                 // TODO Auto-generated catch block   
  44.                                 e1.printStackTrace();   
  45.                             }   
  46.                         }   
  47.                         if(xmlValue!=null){   
  48.                             try {   
  49.                                 System.out.println(xmlValue);   
  50.                                 m.invoke(vo, new Object[]{xmlValue});//調用動態生成的方法   
  51.                                 //參數一指定方法所在的類,參數二制定 參數的值   
  52.                             } catch (IllegalArgumentException e1) {   
  53.                                 // TODO Auto-generated catch block   
  54.                                 e1.printStackTrace();   
  55.                             } catch (IllegalAccessException e1) {   
  56.                                 // TODO Auto-generated catch block   
  57.                                 e1.printStackTrace();   
  58.                             } catch (InvocationTargetException e1) {   
  59.                                 // TODO Auto-generated catch block   
  60.                                 e1.printStackTrace();   
  61.                             }   
  62.                         }   
  63.                     }   
  64.                 }   
  65.             }  


NG 2009-08-31 09:23 發表評論
]]>
WebWork2.2工作原理總結http://www.aygfsteel.com/nogocn/archive/2009/07/08/285921.htmlNGNGWed, 08 Jul 2009 03:09:00 GMThttp://www.aygfsteel.com/nogocn/archive/2009/07/08/285921.htmlhttp://www.aygfsteel.com/nogocn/comments/285921.htmlhttp://www.aygfsteel.com/nogocn/archive/2009/07/08/285921.html#Feedback0http://www.aygfsteel.com/nogocn/comments/commentRss/285921.htmlhttp://www.aygfsteel.com/nogocn/services/trackbacks/285921.html一、WebWork的框架初始化過程
利用WebWork做的項目,在服務器啟動時完成WebWork的框架初始化。具體是通過
Web.xml中配置好的FilterDispatcher過濾器中的init(FilterConfig filterConfig)方法完成。
并且web.xml中配置好FilterDispatcher的映射,當用戶用映射好的結尾資源請求瀏覽器時,FillterDispather會進行請求處理.
       具體實現是通過以下步驟:
1、    通過FilterDispatcher中的
public void init(FilterConfig filterConfig) throws ServletException方法,進行框架的初始化
2、    Init方法又同過調用DispatcherUtils類的
public static void initialize(ServletContext servletContext)方法創建
DispatcherUtils實例,同時間接調用DispatcherUtils類的
protected void init(ServletContext servletContext)方法初始化Configuration
配置,創建對象創建的工廠ObjectFactoryObjectTypeDeterminer
至此完成WebWork框架的初始化。
二、WebWork的用戶請求處理過程
所有以web.xml中映射FilterDispatcher結尾的服務請求將由FilterDispatcher進行處理。 1、從用戶請求的服務名中解析出對應Action的名稱。
   具體完成是:戶按webwork規則請求時,服務器會調用FilterDispatcher的doFilter方法,完成第二步的內容。
       2、遍歷 HttpServletRequest、HttpSession、ServletContext 中的數據,并將其復制到
Webwork的Map中,為下一步創建Action事例打下基礎。
具體完成是:過調用DispatcherUtils的serviceAction方法中的
Map extraContext = createContextMap(request, response, mapping, context);完成以上信息的封裝。
       3、以上一步封裝好的信息為參數,調用 ActionProxyFactory創建對應的 ActionProxy實例。ActionProxyFactory 將根據 Xwork 配置文件(xwork.xml)中的設定,創建ActionProxy實例,ActionProxy中包含了 Action的配置信息(包括 Action名稱,對應實現類等等)。
       具體完成是:通過
ActionProxy proxy = ActionProxyFactory.getFactory().createActionProxy(namespace, name, extraContext, true, false);//創建動態代理
       DefaultActionProxyFactory實現ActionProxyFactory的createActionProxy方法,返回new DefaultActionProxy(namespace, actionName, extraContext, true, true);
       DefaultActionProxy是對ActionProxy的默認實現,
通過DefaultActionProxy類的DefaultActionProxy(namespace, actionName, extraContext, true, true)構造方法實例化DefaultActionProxy,同時得到用戶請求的actionName及namespace,
并通過
config = ConfigurationManager.getConfiguration().getRuntimeConfiguration().getActionConfig(namespace, actionName);
ConfigurationManager
publicstaticsynchronized Configuration getConfiguration() {
        if (configurationInstance == null) {
            configurationInstance = new DefaultConfiguration();
            try {
                configurationInstance.reload();
            } catch (ConfigurationException e) {
                configurationInstance = null;
                throw e;
            }
        } else {
            conditionalReload();
        }
 
        returnconfigurationInstance;
}    
1.   完成對xwork.xml(具體操作類是XmlConfigurationProvider)配置信息的讀取。獲得與此次請求相關的ActionConfig
 
       4、ActionProxy創建對應的Action實例,并根據配置進行一系列的處理程序。
       通過DefaultActionProxy類的
invocation = ActionProxyFactory.getFactory().createActionInvocation(this, extraContext);
//通過createActionInvocation方法創建動作調用類ActionInvocation,處理被Action調用的方法
privatevoid resolveMethod() {
        // if the method is set to null, use the one from the configuration
        // if the one from the configuration is also null, use "execute"
        if (!TextUtils.stringSet(this.method)) {
            this.method = config.getMethodName();
            if (!TextUtils.stringSet(this.method)) {
                this.method = "execute";
            }
        }
}
然后調用DispatcherUtilsserviceAction方法中的
if (mapping.getResult() != null) {
                Result result = mapping.getResult();
                result.execute(proxy.getInvocation());
            } else {
                proxy.execute();
}
 
完成用戶的最終要執行的action方法。
public String execute() throws Exception {
        ActionContext nestedContext = ActionContext.getContext();
        ActionContext.setContext(invocation.getInvocationContext());
 
        String retCode = null;
 
        try {
            retCode = invocation.invoke();
        } finally {
            if (cleanupContext) {
                ActionContext.setContext(nestedContext);
            }
        }
 
        return retCode;
    }
最終處理ActionContext對象
Action調用提交給ActionInvocation處理
 
三、WebWork的執行流程圖
 


NG 2009-07-08 11:09 發表評論
]]>
忘記李剛,一步一步跟我學Struts2 —— OGNL,數據運轉的催化劑 http://www.aygfsteel.com/nogocn/archive/2009/07/08/285920.htmlNGNGWed, 08 Jul 2009 03:07:00 GMThttp://www.aygfsteel.com/nogocn/archive/2009/07/08/285920.htmlhttp://www.aygfsteel.com/nogocn/comments/285920.htmlhttp://www.aygfsteel.com/nogocn/archive/2009/07/08/285920.html#Feedback0http://www.aygfsteel.com/nogocn/comments/commentRss/285920.htmlhttp://www.aygfsteel.com/nogocn/services/trackbacks/285920.html  關鍵字: struts2 ognl valuestack
專欄地址:http://www.javaeye.com/wiki/struts2/1353-ognl-catalyst-for-data-operation-in-struts2

首先讓我們花費1分鐘的時間來簡單思考一個問題,MVC這3者之間,到底是通過什么真正融合起來的?

有人說是Controller,因為它是核心控制器,沒有Controller,MVC就無從談起,失去了職責劃分的原本初衷。也有人說是View,因為所有的需求都是頁面驅動的,沒有頁面,就沒有請求,沒有請求,也談不上控制器和數據模型。

個人觀點:貫穿MVC模型之間起到粘合劑作用的是數據。數據在View層成為了展示的內容,而在Controller層,成為了操作的載體,所以數據是整個MVC的核心。

流轉的數據

無論MVC三者之間的粘合劑到底是什么,數據在各個層次之間進行流轉是一個不爭的事實。而這種流轉,也就會面臨一些困境,這些困境,是由于數據在不同世界中的表現形式不同而造成的:

1. 數據在頁面上是一個扁平的,不帶數據類型的字符串,無論你的數據結構有多復雜,數據類型有多豐富,到了展示的時候,全都一視同仁的成為字符串在頁面上展現出來。

2. 數據在Java世界中可以表現為豐富的數據結構和數據類型,你可以自行定義你喜歡的類,在類與類之間進行繼承、嵌套。我們通常會把這種模型稱之為復雜的對象樹。

此時,如果數據在頁面和Java世界中互相流轉傳遞,就會顯得不匹配。所以也就引出了幾個需要解決的問題:

1. 當數據從View層傳遞到Controller層時,我們應該保證一個扁平而分散在各處的數據集合能以一定的規則設置到Java世界中的對象樹中去。同時,能夠聰明的進行由字符串類型到Java中各個類型的轉化。

2. 當數據從Controller層傳遞到View層時,我們應該保證在View層能夠以某些簡易的規則對對象樹進行訪問。同時,在一定程度上控制對象樹中的數據的顯示格式。

如果我們稍微深入一些來思考這個問題,我們就會發現,解決數據由于表現形式的不同而發生流轉不匹配的問題對我們來說其實并不陌生。同樣的問題會發生在Java世界與數據庫世界中,面對這種對象與關系模型的不匹配,我們采用的解決方法是使用ORM框架,例如Hibernate,iBatis等等。那么現在,在Web層同樣也發生了不匹配,所以我們也需要使用一些工具來幫助我們解決問題。

在這里,我們主要討論的,是數據從View層傳遞到Controller層時的解決方案,而數據從Controller層傳遞到View層的解決方案,我們將在Struts2的Result章節重點討論。

OGNL —— 完美的催化劑

為了解決數據從View層傳遞到Controller層時的不匹配性,Struts2采納了XWork的OGNL方案。并且在OGNL的基礎上,構建了OGNLValueStack的機制,從而比較完美的解決了數據流轉中的不匹配性。

OGNL(Object Graph Navigation Language),是一種表達式語言。使用這種表達式語言,你可以通過某種表達式語法,存取Java對象樹中的任意屬性、調用Java對象樹的方法、同時能夠自動實現必要的類型轉化。如果我們把表達式看做是一個帶有語義的字符串,那么OGNL無疑成為了這個語義字符串與Java對象之間溝通的橋梁。

如何使用OGNL

讓我們先研究一下OGNL的API,他來自于Ognl的靜態方法:

Java代碼 復制代碼
  1.   
  2. /**  
  3.  * Evaluates the given OGNL expression tree to extract a value from the given root  
  4.  * object. The default context is set for the given context and root via  
  5.  * <CODE>addDefaultContext()</CODE>.  
  6.  *  
  7.  * @param tree the OGNL expression tree to evaluate, as returned by parseExpression()  
  8.  * @param context the naming context for the evaluation  
  9.  * @param root the root object for the OGNL expression  
  10.  * @return the result of evaluating the expression  
  11.  * @throws MethodFailedException if the expression called a method which failed  
  12.  * @throws NoSuchPropertyException if the expression referred to a nonexistent property  
  13.  * @throws InappropriateExpressionException if the expression can't be used in this context  
  14.  * @throws OgnlException if there is a pathological environmental problem  
  15.  */  
  16. public static Object getValue( Object tree, Map context, Object root ) throws OgnlException;   
  17.   
  18. /**  
  19.  * Evaluates the given OGNL expression tree to insert a value into the object graph  
  20.  * rooted at the given root object.  The default context is set for the given  
  21.  * context and root via <CODE>addDefaultContext()</CODE>.  
  22.  *  
  23.  * @param tree the OGNL expression tree to evaluate, as returned by parseExpression()  
  24.  * @param context the naming context for the evaluation  
  25.  * @param root the root object for the OGNL expression  
  26.  * @param value the value to insert into the object graph  
  27.  * @throws MethodFailedException if the expression called a method which failed  
  28.  * @throws NoSuchPropertyException if the expression referred to a nonexistent property  
  29.  * @throws InappropriateExpressionException if the expression can't be used in this context  
  30.  * @throws OgnlException if there is a pathological environmental problem  
  31.  */  
  32. public static void setValue( Object tree, Map context, Object root, Object value ) throws OgnlException  


我們可以看到,OGNL的API其實相當簡單,你可以通過傳遞三個參數來實現OGNL的一切操作。而這三個參數,被我稱為OGNL的三要素。

那么運用這個API,我們能干點什么呢?跑個測試看看結果:

Java代碼 復制代碼
  1.   
  2. /**  
  3.  * @author Downpour  
  4.  */  
  5. public class User {   
  6.        
  7.     private Integer id;   
  8.        
  9.     private String name;   
  10.        
  11.     private Department department = new Department();   
  12.        
  13.     public User() {   
  14.            
  15.     }   
  16.            
  17.         // setter and getters   
  18. }   
  19.   
  20. //=========================================================================   
  21.   
  22. /**  
  23.  * @author Downpour  
  24.  */  
  25. public class Department {   
  26.        
  27.     private Integer id;   
  28.        
  29.     private String name;   
  30.        
  31.     public Department() {   
  32.            
  33.     }   
  34.            
  35.         // setter and getters   
  36. }   
  37.   
  38. //=========================================================================   
  39.   
  40. /**  
  41.  * @author Downpour  
  42.  */  
  43. public class OGNLTestCase extends TestCase {   
  44.        
  45.     /**  
  46.      *   
  47.      * @throws Exception  
  48.      */  
  49.     @SuppressWarnings("unchecked")   
  50.     @Test  
  51.     public void testGetValue() throws Exception {   
  52.            
  53.         // Create root object   
  54.         User user = new User();   
  55.         user.setId(1);   
  56.         user.setName("downpour");   
  57.   
  58.         // Create context   
  59.         Map context = new HashMap();   
  60.         context.put("introduction","My name is ");   
  61.            
  62.         // Test to directly get value from root object, with no context   
  63.         Object name = Ognl.getValue(Ognl.parseExpression("name"), user);   
  64.         assertEquals("downpour",name);   
  65.            
  66.         // Test to get value(parameter) from context   
  67.         Object contextValue = Ognl.getValue(Ognl.parseExpression("#introduction"), context, user);   
  68.         assertEquals("My name is ", contextValue);   
  69.            
  70.         // Test to get value and parameter from root object and context   
  71.         Object hello = Ognl.getValue(Ognl.parseExpression("#introduction + name"), context, user);   
  72.         assertEquals("My name is downpour",hello);   
  73.                        
  74.     }   
  75.   
  76.     /**  
  77.      *   
  78.      * @throws Exception  
  79.      */  
  80.     @SuppressWarnings("unchecked")   
  81.     @Test  
  82.     public void testSetValue() throws Exception {   
  83.            
  84.         // Create root object   
  85.         User user = new User();   
  86.         user.setId(1);   
  87.         user.setName("downpour");   
  88.            
  89.                 // Set value according to the expression   
  90.         Ognl.setValue("department.name", user, "dev");   
  91.         assertEquals("dev", user.getDepartment().getName());   
  92.            
  93.     }   
  94.        
  95.   
  96. }  


我們可以看到,簡單的API,就已經能夠完成對各種對象樹的讀取和設值工作了。這也體現出OGNL的學習成本非常低。

在上面的測試用例中,需要特別強調進行區分的,是在針對不同內容進行取值或者設值時,OGNL表達式的不同。

Struts2 Reference 寫道
The framework uses a standard naming context to evaluate OGNL expressions. The top level object dealing with OGNL is a Map (usually referred as a context map or context). OGNL has a notion of there being a root (or default) object within the context. In expression, the properties of the root object can be referenced without any special "marker" notion. References to other objects are marked with a pound sign (#).


上面這段內容摘自Struts2的Reference,我把這段話總結為以下2條規則:

A) 針對根對象(Root Object)的操作,表達式是自根對象到被訪問對象的某個鏈式操作的字符串表示。
B) 針對上下文環境(Context)的操作,表達式是自上下文環境(Context)到被訪問對象的某個鏈式操作的字符串表示,但是必須在這個字符串的前面加上#符號,以表示與訪問根對象的區別。


上面的這點區別咋看起來非常容易理解,不過一旦放到特定的環境中,就會顯示出其重要性,它可以解釋很多Struts2在頁面展示上取值的各種復雜的表達式的現象。這一點在下一篇文章中會進行具體的分析。

OGNL三要素

我把傳入OGNL的API的三個參數,稱之為OGNL的三要素。OGNL的操作實際上就是圍繞著這三個參數而進行的。

1. 表達式(Expression)

表達式是整個OGNL的核心,所有的OGNL操作都是針對表達式的解析后進行的。表達式會規定此次OGNL操作到底要干什么

我們可以看到,在上面的測試中,name、department.name等都是表達式,表示取name或者department中的name的值。OGNL支持很多類型的表達式,之后我們會看到更多。

2. 根對象(Root Object)

根對象可以理解為OGNL的操作對象。在表達式規定了“干什么”以后,你還需要指定到底“對誰干”

在上面的測試代碼中,user就是根對象。這就意味著,我們需要對user這個對象去取name這個屬性的值(對user這個對象去設置其中的department中的name屬性值)。

3. 上下文環境(Context)

有了表達式和根對象,我們實際上已經可以使用OGNL的基本功能。例如,根據表達式對根對象進行取值或者設值工作。

不過實際上,在OGNL的內部,所有的操作都會在一個特定的環境中運行,這個環境就是OGNL的上下文環境(Context)。說得再明白一些,就是這個上下文環境(Context),將規定OGNL的操作“在哪里干”

OGNL的上下文環境是一個Map結構,稱之為OgnlContext。上面我們提到的根對象(Root Object),事實上也會被加入到上下文環境中去,并且這將作為一個特殊的變量進行處理,具體就表現為針對根對象(Root Object)的存取操作的表達式是不需要增加#符號進行區分的。

OgnlContext不僅提供了OGNL的運行環境。在這其中,我們還能設置一些自定義的parameter到Context中,以便我們在進行OGNL操作的時候能夠方便的使用這些parameter。不過正如我們上面反復強調的,我們在訪問這些parameter時,需要使用#作為前綴才能進行。

OGNL與模板

我們在嘗試了OGNL的基本操作并了解了OGNL的三要素之后,或許很容易把OGNL的操作與模板聯系起來進行比較。在很多方面,他們也的確有著相似之處。

對于模板,會有一些普通的輸出元素,也有一些模板語言特殊的符號構成的元素,這些元素一旦與具體的Java對象融合起來,就會得到我們需要的輸出結果。

而OGNL看起來也是非常的類似,OGNL中的表達式就雷同于模板語言的特殊符號,目的是針對某些Java對象進行存取。而OGNL與模板都將數據與展現分開,將數據放到某個特定的地方,具體來說,就是Java對象。只是OGNL與模板的語法結構不完全相同而已。

深入淺出OGNL

在了解了OGNL的API和基本操作以后,我們來深入到OGNL的內部來看看,挖掘一些更加深入的知識。

OGNL表達式

OGNL支持各種紛繁復雜的表達式。但是最最基本的表達式的原型,是將對象的引用值用點串聯起來,從左到右,每一次表達式計算返回的結果成為當前對象,后面部分接著在當前對象上進行計算,一直到全部表達式計算完成,返回最后得到的對象。OGNL則針對這條基本原則進行不斷的擴充,從而使之支持對象樹、數組、容器的訪問,甚至是類似SQL中的投影選擇等操作。

接下來我們就來看看一些常用的OGNL表達式:

1. 基本對象樹的訪問

對象樹的訪問就是通過使用點號將對象的引用串聯起來進行。

例如:name,department.name,user.department.factory.manager.name

2. 對容器變量的訪問

對容器變量的訪問,通過#符號加上表達式進行。

例如:#name,#department.name,#user.department.factory.manager.name

3. 使用操作符號

OGNL表達式中能使用的操作符基本跟Java里的操作符一樣,除了能使用 +, -, *, /, ++, --, ==, !=, = 等操作符之外,還能使用 mod, in, not in等。

4. 容器、數組、對象

OGNL支持對數組和ArrayList等容器的順序訪問:

例如:group.users[0]

同時,OGNL支持對Map的按鍵值查找:

例如:#session['mySessionPropKey']

不僅如此,OGNL還支持容器的構造的表達式:

例如:{"green", "red", "blue"}構造一個List,#{"key1" : "value1", "key2" : "value2", "key3" : "value3"}構造一個Map

你也可以通過任意類對象的構造函數進行對象新建:

例如:new java.net.URL("http://localhost/")

5. 對靜態方法或變量的訪問

要引用類的靜態方法和字段,他們的表達方式是一樣的@class@member或者@class@method(args):

例如:@com.javaeye.core.Resource@ENABLE,@com.javaeye.core.Resource@getAllResources

6. 方法調用

直接通過類似Java的方法調用方式進行,你甚至可以傳遞參數:

例如:user.getName(),group.users.size(),group.containsUser(#requestUser)

7. 投影和選擇



OGNL支持類似數據庫中的投影(projection) 和選擇(selection)。

投影就是選出集合中每個元素的相同屬性組成新的集合,類似于關系數據庫的字段操作。投影操作語法為 collection.{XXX},其中XXX 是這個集合中每個元素的公共屬性。

例如:group.userList.{username}將獲得某個group中的所有user的name的列表。

選擇就是過濾滿足selection 條件的集合元素,類似于關系數據庫的紀錄操作。選擇操作的語法為:collection.{X YYY},其中X 是一個選擇操作符,后面則是選擇用的邏輯表達式。而選擇操作符有三種:
? 選擇滿足條件的所有元素
^ 選擇滿足條件的第一個元素
$ 選擇滿足條件的最后一個元素

例如:group.userList.{? #this.name != null}將獲得某個group中user的name不為空的user的列表。

上述的所有的表達式,只是對OGNL所有表達式的大概的一個概括,除此之外,OGNL還有更多的表達式,例如lamba表達式等等。最具體的表達式的文檔,大家可以參考OGNL自帶的文檔:

http://www.ognl.org/2.6.9/Documentation/html/LanguageGuide/apa.html

在撰寫時,我也參考了potain同學的XWork教程以及一些網絡上的一些文章,特此列出:

http://www.lifevv.com/java/doc/20071018173750030.html

http://blog.csdn.net/ice_fire2008/archive/2008/05/12/2438817.aspx

OGNLContext

OGNLContext就是OGNL的運行上下文環境。OGNLContext其實是一個Map結構,如果查看一下它的源碼,就會發現,它其實實現了java.utils.Map的接口。當你在調用OGNL的取值或者設值的方法時,你可能會自己定義一個Context,并且將它傳遞給方法。事實上,你所傳遞進去的這個Context,會在OGNL內部被轉化成OGNLContext,而你傳遞進去的所有的鍵值對,也會被OGNLContext接管維護,這里有點類似一個裝飾器,向你屏蔽了一些其內部的實現機理。

在OGNLContext的內部維護的東西很多,其中,我挑選2個比較重要的提一下。一個是你在調用方法時傳入的Context,它會被維護在OGNL內部,并且作為存取變量的基礎依據。另外一個,是在Context內部維護了一個key為root的值,它將規定在OGNLContext進行計算時,哪個元素被指定為根對象。其在進行存取時,將會被特殊對待。

this指針

我們知道,OGNL表達式是以點進行串聯的一個字符串鏈式表達式。而這個表達式在進行計算的時候,從左到右,每一次表達式計算返回的結果成為當前對象,并繼續進行計算,直到得到計算結果。每次計算的中間對象都會放在一個叫做this的變量里面這個this變量就稱之為this指針。

例如:group.userList.size().(#this+1).toString()

在這個例子中,#this其實就是group.userList.size()的計算結構。

使用this指針,我們就可以在OGNL表達式中進行一些簡單的計算,從而完成我們的計算邏輯,而this指針在lamba表達式的引用中尤為廣泛,有興趣的讀者可以深入研究OGNL自帶的文檔中lamba表達式的章節。

默認行為和類型轉化

在我們所講述的所有的OGNL的操作中,實際上,全部都忽略了OGNL內部幫助你完成的很多默認行為和類型轉化方面的工作。

我們來看一下OGNL在進行操作初始化時候的一個函數簽名:

Java代碼 復制代碼
  1.   
  2. /**  
  3.  * Appends the standard naming context for evaluating an OGNL expression  
  4.  * into the context given so that cached maps can be used as a context.  
  5.  *  
  6.  * @param root the root of the object graph  
  7.  * @param context the context to which OGNL context will be added.  
  8.  * @return Context Map with the keys <CODE>root</CODE> and <CODE>context</CODE>  
  9.  *         set appropriately  
  10.  */  
  11. public static Map addDefaultContext( Object root, ClassResolver classResolver, TypeConverter converter, MemberAccess memberAccess, Map context );  


可以看到,在初始化時,OGNL還需要額外初始化一個類型轉化的接口和一些其他的信息。只不過這些默認行為,由OGNL的內部屏蔽了。

一旦需要自己定義針對某個特定類型的類型轉化方式,你就需要實現TypeConverter接口,并且在OGNL中進行注冊。

同時,如果需要對OGNL的許多默認行為做出改變,則需要通過設置OGNL的全局環境變量進行。

上述的這些內容,有些會在后面的章節涉及,有興趣的讀者,也可以參閱OGNL的源碼和OGNL的文檔尋求幫助。


NG 2009-07-08 11:07 發表評論
]]>
spring優秀工具類,文件資源操作和 Web 相關工具類(備用)http://www.aygfsteel.com/nogocn/archive/2009/07/08/285918.htmlNGNGWed, 08 Jul 2009 03:06:00 GMThttp://www.aygfsteel.com/nogocn/archive/2009/07/08/285918.htmlhttp://www.aygfsteel.com/nogocn/comments/285918.htmlhttp://www.aygfsteel.com/nogocn/archive/2009/07/08/285918.html#Feedback0http://www.aygfsteel.com/nogocn/comments/commentRss/285918.htmlhttp://www.aygfsteel.com/nogocn/services/trackbacks/285918.htmlSpring 不但提供了一個功能全面的應用開發框架,本身還擁有眾多可以在程序編寫時直接使用的工具類,您不但可以在 Spring 應用中使用這些工具類,也可以在其它的應用中使用,這些工具類中的大部分是可以在脫離 Spring 框架時使用的。了解 Spring 中有哪些好用的工具類并在程序編寫時適當使用,將有助于提高開發效率、增強代碼質量。在這個分為兩部分的文章中,我們將從眾多的 Spring 工具類中遴選出那些好用的工具類介紹給大家。第 1 部分將介紹與文件資源操作和 Web 相關的工具類。

文件資源操作

文件資源的操作是應用程序中常見的功能,如當上傳一個文件后將其保存在特定目錄下,從指定地址加載一個配置文件等等。我們一般使用 JDK 的 I/O 處理類完成這些操作,但對于一般的應用程序來說,JDK 的這些操作類所提供的方法過于底層,直接使用它們進行文件操作不但程序編寫復雜而且容易產生錯誤。相比于 JDK 的 File,Spring 的 Resource 接口(資源概念的描述接口)抽象層面更高且涵蓋面更廣,Spring 提供了許多方便易用的資源操作工具類,它們大大降低資源操作的復雜度,同時具有更強的普適性。這些工具類不依賴于 Spring 容器,這意味著您可以在程序中象一般普通類一樣使用它們。

加載文件資源

Spring 定義了一個 org.springframework.core.io.Resource 接口,Resource 接口是為了統一各種類型不同的資源而定義的,Spring 提供了若干 Resource 接口的實現類,這些實現類可以輕松地加載不同類型的底層資源,并提供了獲取文件名、URL 地址以及資源內容的操作方法。

訪問文件資源

假設有一個文件地位于 Web 應用的類路徑下,您可以通過以下方式對這個文件資源進行訪問:

  • 通過 FileSystemResource 以文件系統絕對路徑的方式進行訪問;
  • 通過 ClassPathResource 以類路徑的方式進行訪問;
  • 通過 ServletContextResource 以相對于Web應用根目錄的方式進行訪問。

相比于通過 JDK 的 File 類訪問文件資源的方式,Spring 的 Resource 實現類無疑提供了更加靈活的操作方式,您可以根據情況選擇適合的 Resource 實現類訪問資源。下面,我們分別通過 FileSystemResource 和 ClassPathResource 訪問同一個文件資源:


清單 1. FileSourceExample
package com.baobaotao.io;
            import java.io.IOException;
            import java.io.InputStream;
            import org.springframework.core.io.ClassPathResource;
            import org.springframework.core.io.FileSystemResource;
            import org.springframework.core.io.Resource;
            public class FileSourceExample {
            public static void main(String[] args) {
            try {
            String filePath =
            "D:/masterSpring/chapter23/webapp/WEB-INF/classes/conf/file1.txt";
            // ① 使用系統文件路徑方式加載文件
            Resource res1 = new FileSystemResource(filePath);
            // ② 使用類路徑方式加載文件
            Resource res2 = new ClassPathResource("conf/file1.txt");
            InputStream ins1 = res1.getInputStream();
            InputStream ins2 = res2.getInputStream();
            System.out.println("res1:"+res1.getFilename());
            System.out.println("res2:"+res2.getFilename());
            } catch (IOException e) {
            e.printStackTrace();
            }
            }
            }
            

在獲取資源后,您就可以通過 Resource 接口定義的多個方法訪問文件的數據和其它的信息:如您可以通過 getFileName() 獲取文件名,通過 getFile() 獲取資源對應的 File 對象,通過 getInputStream() 直接獲取文件的輸入流。此外,您還可以通過 createRelative(String relativePath) 在資源相對地址上創建新的資源。

在 Web 應用中,您還可以通過 ServletContextResource 以相對于 Web 應用根目錄的方式訪問文件資源,如下所示:

<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
            <jsp:directive.page import="
            org.springframework.web.context.support.ServletContextResource"/>
            <jsp:directive.page import="org.springframework.core.io.Resource"/>
            <%
            // ① 注意文件資源地址以相對于 Web 應用根路徑的方式表示
            Resource res3 = new ServletContextResource(application,
            "/WEB-INF/classes/conf/file1.txt");
            out.print(res3.getFilename());
            %>
            

對于位于遠程服務器(Web 服務器或 FTP 服務器)的文件資源,您則可以方便地通過 UrlResource 進行訪問。

為了方便訪問不同類型的資源,您必須使用相應的 Resource 實現類,是否可以在不顯式使用 Resource 實現類的情況下,僅根據帶特殊前綴的資源地址直接加載文件資源呢?Spring 提供了一個 ResourceUtils 工具類,它支持“classpath:”和“file:”的地址前綴,它能夠從指定的地址加載文件資源,請看下面的例子:


清單 2. ResourceUtilsExample
package com.baobaotao.io;
            import java.io.File;
            import org.springframework.util.ResourceUtils;
            public class ResourceUtilsExample {
            public static void main(String[] args) throws Throwable{
            File clsFile = ResourceUtils.getFile("classpath:conf/file1.txt");
            System.out.println(clsFile.isFile());
            String httpFilePath = "file:D:/masterSpring/chapter23/src/conf/file1.txt";
            File httpFile = ResourceUtils.getFile(httpFilePath);
            System.out.println(httpFile.isFile());
            }
            }
            

ResourceUtils 的 getFile(String resourceLocation) 方法支持帶特殊前綴的資源地址,這樣,我們就可以在不和 Resource 實現類打交道的情況下使用 Spring 文件資源加載的功能了。

本地化文件資源

本地化文件資源是一組通過本地化標識名進行特殊命名的文件,Spring 提供的 LocalizedResourceHelper 允許通過文件資源基名和本地化實體獲取匹配的本地化文件資源并以 Resource 對象返回。假設在類路徑的 i18n 目錄下,擁有一組基名為 message 的本地化文件資源,我們通過以下實例演示獲取對應中國大陸和美國的本地化文件資源:


清單 3. LocaleResourceTest
package com.baobaotao.io;
            import java.util.Locale;
            import org.springframework.core.io.Resource;
            import org.springframework.core.io.support.LocalizedResourceHelper;
            public class LocaleResourceTest {
            public static void main(String[] args) {
            LocalizedResourceHelper lrHalper = new LocalizedResourceHelper();
            // ① 獲取對應美國的本地化文件資源
            Resource msg_us = lrHalper.findLocalizedResource("i18n/message", ".properties",
            Locale.US);
            // ② 獲取對應中國大陸的本地化文件資源
            Resource msg_cn = lrHalper.findLocalizedResource("i18n/message", ".properties",
            Locale.CHINA);
            System.out.println("fileName(us):"+msg_us.getFilename());
            System.out.println("fileName(cn):"+msg_cn.getFilename());
            }
            }
            

雖然 JDK 的 java.util.ResourceBundle 類也可以通過相似的方式獲取本地化文件資源,但是其返回的是 ResourceBundle 類型的對象。如果您決定統一使用 Spring 的 Resource 接表征文件資源,那么 LocalizedResourceHelper 就是獲取文件資源的非常適合的幫助類了。

文件操作

在使用各種 Resource 接口的實現類加載文件資源后,經常需要對文件資源進行讀取、拷貝、轉存等不同類型的操作。您可以通過 Resource 接口所提供了方法完成這些功能,不過在大多數情況下,通過 Spring 為 Resource 所配備的工具類完成文件資源的操作將更加方便。

文件內容拷貝

第一個我們要認識的是 FileCopyUtils,它提供了許多一步式的靜態操作方法,能夠將文件內容拷貝到一個目標 byte[]、String 甚至一個輸出流或輸出文件中。下面的實例展示了 FileCopyUtils 具體使用方法:


清單 4. FileCopyUtilsExample
package com.baobaotao.io;
            import java.io.ByteArrayOutputStream;
            import java.io.File;
            import java.io.FileReader;
            import java.io.OutputStream;
            import org.springframework.core.io.ClassPathResource;
            import org.springframework.core.io.Resource;
            import org.springframework.util.FileCopyUtils;
            public class FileCopyUtilsExample {
            public static void main(String[] args) throws Throwable {
            Resource res = new ClassPathResource("conf/file1.txt");
            // ① 將文件內容拷貝到一個 byte[] 中
            byte[] fileData = FileCopyUtils.copyToByteArray(res.getFile());
            // ② 將文件內容拷貝到一個 String 中
            String fileStr = FileCopyUtils.copyToString(new FileReader(res.getFile()));
            // ③ 將文件內容拷貝到另一個目標文件
            FileCopyUtils.copy(res.getFile(),
            new File(res.getFile().getParent()+ "/file2.txt"));
            // ④ 將文件內容拷貝到一個輸出流中
            OutputStream os = new ByteArrayOutputStream();
            FileCopyUtils.copy(res.getInputStream(), os);
            }
            }
            

往往我們都通過直接操作 InputStream 讀取文件的內容,但是流操作的代碼是比較底層的,代碼的面向對象性并不強。通過 FileCopyUtils 讀取和拷貝文件內容易于操作且相當直觀。如在 ① 處,我們通過 FileCopyUtils 的 copyToByteArray(File in) 方法就可以直接將文件內容讀到一個 byte[] 中;另一個可用的方法是 copyToByteArray(InputStream in),它將輸入流讀取到一個 byte[] 中。

如果是文本文件,您可能希望將文件內容讀取到 String 中,此時您可以使用 copyToString(Reader in) 方法,如 ② 所示。使用 FileReader 對 File 進行封裝,或使用 InputStreamReader 對 InputStream 進行封裝就可以了。

FileCopyUtils 還提供了多個將文件內容拷貝到各種目標對象中的方法,這些方法包括:

方法 說明
static void copy(byte[] in, File out) 將 byte[] 拷貝到一個文件中
static void copy(byte[] in, OutputStream out) 將 byte[] 拷貝到一個輸出流中
static int copy(File in, File out) 將文件拷貝到另一個文件中
static int copy(InputStream in, OutputStream out) 將輸入流拷貝到輸出流中
static int copy(Reader in, Writer out) 將 Reader 讀取的內容拷貝到 Writer 指向目標輸出中
static void copy(String in, Writer out) 將字符串拷貝到一個 Writer 指向的目標中

在實例中,我們雖然使用 Resource 加載文件資源,但 FileCopyUtils 本身和 Resource 沒有任何關系,您完全可以在基于 JDK I/O API 的程序中使用這個工具類。

屬性文件操作

我們知道可以通過 java.util.Properties的load(InputStream inStream) 方法從一個輸入流中加載屬性資源。Spring 提供的 PropertiesLoaderUtils 允許您直接通過基于類路徑的文件地址加載屬性資源,請看下面的例子:

package com.baobaotao.io;
            import java.util.Properties;
            import org.springframework.core.io.support.PropertiesLoaderUtils;
            public class PropertiesLoaderUtilsExample {
            public static void main(String[] args) throws Throwable {
            // ① jdbc.properties 是位于類路徑下的文件
            Properties props = PropertiesLoaderUtils.loadAllProperties("jdbc.properties");
            System.out.println(props.getProperty("jdbc.driverClassName"));
            }
            }
            

一般情況下,應用程序的屬性文件都放置在類路徑下,所以 PropertiesLoaderUtils 比之于 Properties#load(InputStream inStream) 方法顯然具有更強的實用性。此外,PropertiesLoaderUtils 還可以直接從 Resource 對象中加載屬性資源:

方法 說明
static Properties loadProperties(Resource resource) 從 Resource 中加載屬性
static void fillProperties(Properties props, Resource resource) 將 Resource 中的屬性數據添加到一個已經存在的 Properties 對象中

特殊編碼的資源

當您使用 Resource 實現類加載文件資源時,它默認采用操作系統的編碼格式。如果文件資源采用了特殊的編碼格式(如 UTF-8),則在讀取資源內容時必須事先通過 EncodedResource 指定編碼格式,否則將會產生中文亂碼的問題。


清單 5. EncodedResourceExample
package com.baobaotao.io;
            import org.springframework.core.io.ClassPathResource;
            import org.springframework.core.io.Resource;
            import org.springframework.core.io.support.EncodedResource;
            import org.springframework.util.FileCopyUtils;
            public class EncodedResourceExample {
            public static void main(String[] args) throws Throwable  {
            Resource res = new ClassPathResource("conf/file1.txt");
            // ① 指定文件資源對應的編碼格式(UTF-8)
            EncodedResource encRes = new EncodedResource(res,"UTF-8");
            // ② 這樣才能正確讀取文件的內容,而不會出現亂碼
            String content  = FileCopyUtils.copyToString(encRes.getReader());
            System.out.println(content);
            }
            }
            

EncodedResource 擁有一個 getResource() 方法獲取 Resource,但該方法返回的是通過構造函數傳入的原 Resource 對象,所以必須通過 EncodedResource#getReader() 獲取應用編碼后的 Reader 對象,然后再通過該 Reader 讀取文件的內容。





Web 相關工具類

您幾乎總是使用 Spring 框架開發 Web 的應用,Spring 為 Web 應用提供了很多有用的工具類,這些工具類可以給您的程序開發帶來很多便利。在這節里,我們將逐一介紹這些工具類的使用方法。

操作 Servlet API 的工具類

當您在控制器、JSP 頁面中想直接訪問 Spring 容器時,您必須事先獲取 WebApplicationContext 對象。Spring 容器在啟動時將 WebApplicationContext 保存在 ServletContext的屬性列表中,通過 WebApplicationContextUtils 工具類可以方便地獲取 WebApplicationContext 對象。

WebApplicationContextUtils

當 Web 應用集成 Spring 容器后,代表 Spring 容器的 WebApplicationContext 對象將以 WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE 為鍵存放在 ServletContext 屬性列表中。您當然可以直接通過以下語句獲取 WebApplicationContext:

WebApplicationContext wac = (WebApplicationContext)servletContext.
            getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
            

但通過位于 org.springframework.web.context.support 包中的 WebApplicationContextUtils 工具類獲取 WebApplicationContext 更方便:

WebApplicationContext wac =WebApplicationContextUtils.
            getWebApplicationContext(servletContext);
            

當 ServletContext 屬性列表中不存在 WebApplicationContext 時,getWebApplicationContext() 方法不會拋出異常,它簡單地返回 null。如果后續代碼直接訪問返回的結果將引發一個 NullPointerException 異常,而 WebApplicationContextUtils 另一個 getRequiredWebApplicationContext(ServletContext sc) 方法要求 ServletContext 屬性列表中一定要包含一個有效的 WebApplicationContext 對象,否則馬上拋出一個 IllegalStateException 異常。我們推薦使用后者,因為它能提前發現錯誤的時間,強制開發者搭建好必備的基礎設施。

WebUtils

位于 org.springframework.web.util 包中的 WebUtils 是一個非常好用的工具類,它對很多 Servlet API 提供了易用的代理方法,降低了訪問 Servlet API 的復雜度,可以將其看成是常用 Servlet API 方法的門面類。

下面這些方法為訪問 HttpServletRequest 和 HttpSession 中的對象和屬性帶來了方便:

方法 說明
Cookie getCookie(HttpServletRequest request, String name) 獲取 HttpServletRequest 中特定名字的 Cookie 對象。如果您需要創建 Cookie, Spring 也提供了一個方便的 CookieGenerator 工具類;
Object getSessionAttribute(HttpServletRequest request, String name) 獲取 HttpSession 特定屬性名的對象,否則您必須通過request.getHttpSession.getAttribute(name) 完成相同的操作;
Object getRequiredSessionAttribute(HttpServletRequest request, String name) 和上一個方法類似,只不過強制要求 HttpSession 中擁有指定的屬性,否則拋出異常;
String getSessionId(HttpServletRequest request) 獲取 Session ID 的值;
void exposeRequestAttributes(ServletRequest request, Map attributes) 將 Map 元素添加到 ServletRequest 的屬性列表中,當請求被導向(forward)到下一個處理程序時,這些請求屬性就可以被訪問到了;

此外,WebUtils還提供了一些和ServletContext相關的方便方法:

方法 說明
String getRealPath(ServletContext servletContext, String path) 獲取相對路徑對應文件系統的真實文件路徑;
File getTempDir(ServletContext servletContext) 獲取 ServletContex 對應的臨時文件地址,它以 File 對象的形式返回。

下面的片斷演示了使用 WebUtils 從 HttpSession 中獲取屬性對象的操作:

protected Object formBackingObject(HttpServletRequest request) throws Exception {
            UserSession userSession = (UserSession) WebUtils.getSessionAttribute(request,
            "userSession");
            if (userSession != null) {
            return new AccountForm(this.petStore.getAccount(
            userSession.getAccount().getUsername()));
            } else {
            return new AccountForm();
            }
            }
            

Spring 所提供的過濾器和監聽器

Spring 為 Web 應用提供了幾個過濾器和監聽器,在適合的時間使用它們,可以解決一些常見的 Web 應用問題。

延遲加載過濾器

Hibernate 允許對關聯對象、屬性進行延遲加載,但是必須保證延遲加載的操作限于同一個 Hibernate Session 范圍之內進行。如果 Service 層返回一個啟用了延遲加載功能的領域對象給 Web 層,當 Web 層訪問到那些需要延遲加載的數據時,由于加載領域對象的 Hibernate Session 已經關閉,這些導致延遲加載數據的訪問異常。

Spring 為此專門提供了一個 OpenSessionInViewFilter 過濾器,它的主要功能是使每個請求過程綁定一個 Hibernate Session,即使最初的事務已經完成了,也可以在 Web 層進行延遲加載的操作。

OpenSessionInViewFilter 過濾器將 Hibernate Session 綁定到請求線程中,它將自動被 Spring 的事務管理器探測到。所以 OpenSessionInViewFilter 適用于 Service 層使用HibernateTransactionManager 或 JtaTransactionManager 進行事務管理的環境,也可以用于非事務只讀的數據操作中。

要啟用這個過濾器,必須在 web.xml 中對此進行配置:

…
            <filter>
            <filter-name>hibernateFilter</filter-name>
            <filter-class>
            org.springframework.orm.hibernate3.support.OpenSessionInViewFilter
            </filter-class>
            </filter>
            <filter-mapping>
            <filter-name>hibernateFilter</filter-name>
            <url-pattern>*.html</url-pattern>
            </filter-mapping>
            …
            

上面的配置,我們假設使用 .html 的后綴作為 Web 框架的 URL 匹配模式,如果您使用 Struts 等 Web 框架,可以將其改為對應的“*.do”模型。

中文亂碼過濾器

在您通過表單向服務器提交數據時,一個經典的問題就是中文亂碼問題。雖然我們所有的 JSP 文件和頁面編碼格式都采用 UTF-8,但這個問題還是會出現。解決的辦法很簡單,我們只需要在 web.xml 中配置一個 Spring 的編碼轉換過濾器就可以了:

<web-app>
            <!---listener的配置-->
            <filter>
            <filter-name>encodingFilter</filter-name>
            <filter-class>
            org.springframework.web.filter.CharacterEncodingFilter ① Spring 編輯過濾器
            </filter-class>
            <init-param> ② 編碼方式
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
            </init-param>
            <init-param> ③ 強制進行編碼轉換
            <param-name>forceEncoding</param-name>
            <param-value>true</param-value>
            </init-param>
            </filter>
            <filter-mapping> ② 過濾器的匹配 URL
            <filter-name>encodingFilter</filter-name>
            <url-pattern>*.html</url-pattern>
            </filter-mapping>
            <!---servlet的配置-->
            </web-app>
            

這樣所有以 .html 為后綴的 URL 請求的數據都會被轉碼為 UTF-8 編碼格式,表單中文亂碼的問題就可以解決了。

請求跟蹤日志過濾器

除了以上兩個常用的過濾器外,還有兩個在程序調試時可能會用到的請求日志跟蹤過濾器,它們會將請求的一些重要信息記錄到日志中,方便程序的調試。這兩個日志過濾器只有在日志級別為 DEBUG 時才會起作用:

方法 說明
org.springframework.web.filter.ServletContextRequestLoggingFilter 該過濾器將請求的 URI 記錄到 Common 日志中(如通過 Log4J 指定的日志文件);
org.springframework.web.filter.ServletContextRequestLoggingFilter 該過濾器將請求的 URI 記錄到 ServletContext 日志中。

以下是日志過濾器記錄的請求跟蹤日志的片斷:

(JspServlet.java:224) -     JspEngine --> /htmlTest.jsp
            (JspServlet.java:225) - 	     ServletPath: /htmlTest.jsp
            (JspServlet.java:226) - 	        PathInfo: null
            (JspServlet.java:227) - 	        RealPath: D:\masterSpring\chapter23\webapp\htmlTest.jsp
            (JspServlet.java:228) - 	      RequestURI: /baobaotao/htmlTest.jsp
            …
            

通過這個請求跟蹤日志,程度調試者可以詳細地查看到有哪些請求被調用,請求的參數是什么,請求是否正確返回等信息。雖然這兩個請求跟蹤日志過濾器一般在程序調試時使用,但是即使程序部署不將其從 web.xml 中移除也不會有大礙,因為只要將日志級別設置為 DEBUG 以上級別,它們就不會輸出請求跟蹤日志信息了。

轉存 Web 應用根目錄監聽器和 Log4J 監聽器

Spring 在 org.springframework.web.util 包中提供了幾個特殊用途的 Servlet 監聽器,正確地使用它們可以完成一些特定需求的功能。比如某些第三方工具支持通過 ${key} 的方式引用系統參數(即可以通過 System.getProperty() 獲取的屬性),WebAppRootListener 可以將 Web 應用根目錄添加到系統參數中,對應的屬性名可以通過名為“webAppRootKey”的 Servlet 上下文參數指定,默認為“webapp.root”。下面是該監聽器的具體的配置:


清單 6. WebAppRootListener 監聽器配置
…
            <context-param>
            <param-name>webAppRootKey</param-name>
            <param-value>baobaotao.root</param-value> ① Web 應用根目錄以該屬性名添加到系統參數中
            </context-param>
            …
            ② 負責將 Web 應用根目錄以 webAppRootKey 上下文參數指定的屬性名添加到系統參數中
            <listener>
            <listener-class>
            org.springframework.web.util.WebAppRootListener
            </listener-class>
            </listener>
            …
            

這樣,您就可以在程序中通過 System.getProperty("baobaotao.root") 獲取 Web 應用的根目錄了。不過更常見的使用場景是在第三方工具的配置文件中通過${baobaotao.root} 引用 Web 應用的根目錄。比如以下的 log4j.properties 配置文件就通過 ${baobaotao.root} 設置了日志文件的地址:

log4j.rootLogger=INFO,R
            log4j.appender.R=org.apache.log4j.RollingFileAppender
            log4j.appender.R.File=${baobaotao.root}/WEB-INF/logs/log4j.log ① 指定日志文件的地址
            log4j.appender.R.MaxFileSize=100KB
            log4j.appender.R.MaxBackupIndex=1
            log4j.appender.R.layout.ConversionPattern=%d %5p [%t] (%F:%L) - %m%n
            

另一個專門用于 Log4J 的監聽器是 Log4jConfigListener。一般情況下,您必須將 Log4J 日志配置文件以 log4j.properties 為文件名并保存在類路徑下。Log4jConfigListener 允許您通過 log4jConfigLocation Servlet 上下文參數顯式指定 Log4J 配置文件的地址,如下所示:

① 指定 Log4J 配置文件的地址
            <context-param>
            <param-name>log4jConfigLocation</param-name>
            <param-value>/WEB-INF/log4j.properties</param-value>
            </context-param>
            …
            ② 使用該監聽器初始化 Log4J 日志引擎
            <listener>
            <listener-class>
            org.springframework.web.util.Log4jConfigListener
            </listener-class>
            </listener>
            …
            

提示

一些Web應用服務器(如 Tomcat)不會為不同的Web應用使用獨立的系統參數,也就是說,應用服務器上所有的 Web 應用都共享同一個系統參數對象。這時,您必須通過webAppRootKey 上下文參數為不同Web應用指定不同的屬性名:如第一個 Web 應用使用 webapp1.root 而第二個 Web 應用使用 webapp2.root 等,這樣才不會發生后者覆蓋前者的問題。此外,WebAppRootListener 和 Log4jConfigListener 都只能應用在 Web 應用部署后 WAR 文件會解包的 Web 應用服務器上。一些 Web 應用服務器不會將Web 應用的 WAR 文件解包,整個 Web 應用以一個 WAR 包的方式存在(如 Weblogic),此時因為無法指定對應文件系統的 Web 應用根目錄,使用這兩個監聽器將會發生問題。

Log4jConfigListener 監聽器包括了 WebAppRootListener 的功能,也就是說,Log4jConfigListener 會自動完成將 Web 應用根目錄以 webAppRootKey 上下文參數指定的屬性名添加到系統參數中,所以當您使用 Log4jConfigListener 后,就沒有必須再使用 WebAppRootListener了。

Introspector 緩存清除監聽器

Spring 還提供了一個名為 org.springframework.web.util.IntrospectorCleanupListener 的監聽器。它主要負責處理由 JavaBean Introspector 功能而引起的緩存泄露。IntrospectorCleanupListener 監聽器在 Web 應用關閉的時會負責清除 JavaBean Introspector 的緩存,在 web.xml 中注冊這個監聽器可以保證在 Web 應用關閉的時候釋放與其相關的 ClassLoader 的緩存和類引用。如果您使用了 JavaBean Introspector 分析應用中的類,Introspector 緩存會保留這些類的引用,結果在應用關閉的時候,這些類以及Web 應用相關的 ClassLoader 不能被垃圾回收。不幸的是,清除 Introspector 的唯一方式是刷新整個緩存,這是因為沒法準確判斷哪些是屬于本 Web 應用的引用對象,哪些是屬于其它 Web 應用的引用對象。所以刪除被緩存的 Introspection 會導致將整個 JVM 所有應用的 Introspection 都刪掉。需要注意的是,Spring 托管的 Bean 不需要使用這個監聽器,因為 Spring 的 Introspection 所使用的緩存在分析完一個類之后會馬上從 javaBean Introspector 緩存中清除掉,并將緩存保存在應用程序特定的 ClassLoader 中,所以它們一般不會導致內存資源泄露。但是一些類庫和框架往往會產生這個問題。例如 Struts 和 Quartz 的 Introspector 的內存泄漏會導致整個的 Web 應用的 ClassLoader 不能進行垃圾回收。在 Web 應用關閉之后,您還會看到此應用的所有靜態類引用,這個錯誤當然不是由這個類自身引起的。解決這個問題的方法很簡單,您僅需在 web.xml 中配置 IntrospectorCleanupListener 監聽器就可以了:

<listener>
            <listener-class>
            org.springframework.web.util.IntrospectorCleanupListener
            </listener-class>
            </listener>
            





小結

本文介紹了一些常用的 Spring 工具類,其中大部分 Spring 工具類不但可以在基于 Spring 的應用中使用,還可以在其它的應用中使用。使用 JDK 的文件操作類在訪問類路徑相關、Web 上下文相關的文件資源時,往往顯得拖泥帶水、拐彎抹角,Spring 的 Resource 實現類使這些工作變得輕松了許多。

在 Web 應用中,有時你希望直接訪問 Spring 容器,獲取容器中的 Bean,這時使用 WebApplicationContextUtils 工具類從 ServletContext 中獲取 WebApplicationContext 是非常方便的。WebUtils 為訪問 Servlet API 提供了一套便捷的代理方法,您可以通過 WebUtils 更好的訪問 HttpSession 或 ServletContext 的信息。

Spring 提供了幾個 Servlet 過濾器和監聽器,其中 ServletContextRequestLoggingFilter 和 ServletContextRequestLoggingFilter 可以記錄請求訪問的跟蹤日志,你可以在程序調試時使用它們獲取請求調用的詳細信息。WebAppRootListener 可以將 Web 應用的根目錄以特定屬性名添加到系統參數中,以便第三方工具類通過 ${key} 的方式進行訪問。Log4jConfigListener 允許你指定 Log4J 日志配置文件的地址,且可以在配置文件中通過 ${key} 的方式引用 Web 應用根目錄,如果你需要在 Web 應用相關的目錄創建日志文件,使用 Log4jConfigListener 可以很容易地達到這一目標。

Web 應用的內存泄漏是最讓開發者頭疼的問題,雖然不正確的程序編寫可能是這一問題的根源,也有可能是一些第三方框架的 JavaBean Introspector 緩存得不到清除而導致的,Spring 專門為解決這一問題配備了 IntrospectorCleanupListener 監聽器,它只要簡單在 web.xml 中聲明該監聽器就可以了。

原文摘自:http://www.zhuoda.org/lunzi/85887.html



NG 2009-07-08 11:06 發表評論
]]>
在頁面上顯示圖片http://www.aygfsteel.com/nogocn/archive/2007/05/05/115470.htmlNGNGSat, 05 May 2007 15:20:00 GMThttp://www.aygfsteel.com/nogocn/archive/2007/05/05/115470.htmlhttp://www.aygfsteel.com/nogocn/comments/115470.htmlhttp://www.aygfsteel.com/nogocn/archive/2007/05/05/115470.html#Feedback0http://www.aygfsteel.com/nogocn/comments/commentRss/115470.htmlhttp://www.aygfsteel.com/nogocn/services/trackbacks/115470.htmljsp
1  <IMG width="200" height="200" border="0"    style="cursor: pointer;"    
2           src="<%=request.getContextPath()%>/xxxx.do?type=show&pkId=xx">


action.do

 1 if(type.equals("show"))
 2             {
 3                 InputStream inputsteam = null;
 4              Long     pkId=Long.parseLong(servletRequest.getParameter("pkId"));
 5                  if (pkId != null&&!pkId.equals(""))
 6                     {
 7                         inputsteam = bpo.getBlobByID(pkId).getBinaryStream();
 8 
 9                   
10                     BufferedImage image = ImageIO.read(inputsteam);
11                     ServletOutputStream sos = servletResponse.getOutputStream();
12                     JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(sos);
13                     encoder.encode(image);
14                     inputsteam.close();
15                   
16                     }
17                   return null;
18             }
19 


NG 2007-05-05 23:20 發表評論
]]>
主站蜘蛛池模板: 多伦县| 庆元县| 康保县| 兰溪市| 南木林县| 罗甸县| 安徽省| 阳东县| 车致| 余干县| 东至县| 阳新县| 精河县| 敦煌市| 阿拉尔市| 兰坪| 蓬莱市| 宁晋县| 柏乡县| 崇州市| 万载县| 武邑县| 宜川县| 茶陵县| 吴桥县| 个旧市| 木兰县| 长武县| 六安市| 凤冈县| 体育| 洛阳市| 神木县| 大安市| 禹城市| 水富县| 谢通门县| 静海县| 彩票| 深水埗区| 高平市|