????
??? 2.EncodingFilter.java:
?
??? 3.Struts-config.xml:
?設置資源文件的路徑
?
??? 4.role-list.jsp
?頁面編碼和調用資源
?
?
??? 5.ApplicationResources.properties
?
??? 6.build.xml
?編譯資源文件生成Unicode
??? 7.轉換后的ApplicationResources.properties
?
??? 8.server.xml
?tomcat5.0以后post跟get提交方法不了不同的編碼處理,應該用下面的方式來處理
????
??? 9.界面
??? 以上操作基本實現徹底消滅中文亂碼問題,當然還有其他更好的辦法和應用,我們這里只講struts的簡單應用中如何避免相應問題的處理辦法。
Struts在1.1以后的版本引入了模塊(Module)的概念,克服了1.0的并行開發所遇到的問題。但是模板的規劃往往又是比較麻煩,如果不注意,可能適得其反,影響你的開發速度。下面我們看看如何去規劃一個對模塊的Struts項目。
首先我們需要建立一個web的項目,接下來就要進行目錄規劃。如下圖:
1 首先確定項目的模塊,如此項目中包含兩個模塊:mFirst和mSecond,當然還有缺省模板。 2 本例子中考慮到使用XDoclet自動生成Struts配置和校驗文件,所以有些特殊性。在WEB-INF下建立兩個目錄mFirst和mSecond,用于模塊配置文件的存放。同時建立merge目錄,并包含相應的模塊目錄,主要存放XDoclet生成Struts配置文件所需要的合并文件。模板的java源碼和測試代碼也分別存放,這樣便于管理。為了包含某些不允許從瀏覽器端直接訪問的jsp文件,我們在WEB-INF下同時建立webpages目錄,該目錄下同時包含模塊目錄,主要存放各個模塊的受限文件。 3 本例中為了集成方便,所有編譯后的代碼均存在同一目錄下(WEB-INF\classes),編譯后的測試代碼同樣存在同一目錄下。 4 經過以上的設置,為XDoclet生成配置文件提供了方便,我們的原則是同一個模塊只有一個配置文件(struts-config.xml)和校驗文件(validation.xml)。 |
|
5 接下來我們只需在web.xml中進行模塊聲明即可,其他信息請參考struts的sample。 6 完成這些信息以后,我們要注意一些事情: 7 接下來就是模塊的之間的切換問題,因為在現時過程必須需要解決的問題。其實很簡單,如果你想使用項目下絕對路徑(包含模塊目錄信息),只需將forward的contextRelative屬性值設置為true即可,這樣模塊之間切換也很容易實現。 8 接下來就是你的代碼啦,可能你要在編碼中適當地考慮到項目模塊之間的關系。 總結:多模塊設計不太難,但是你也的考慮周全點,這樣會給你的開發帶來方便。在IntelliJ IDEA下,雖然在模塊比較多的情況下目錄很亂,通過設置excluded,讓你的目錄更整潔,這樣就可以關注你的模塊。在有些情況下,取消excluded,這樣你有可以把握項目全局。 |
用Swing編寫反應靈敏的圖形用戶界面 作者:徐皓 作者簡介 徐皓,北京航空航天大學計算機系本科生,你可以通過ertri@163.com與他聯系。 正文 不靈敏的圖形用戶界面會降低應用程序的可用性。當以下現象出現的時候,我們通常說這個用戶界面反應不靈敏。
這些現象在很大程度上與事件的處理方法相關,而在編寫Swing應用程序的時候,我們幾乎必然要編寫方法去響應鼠標點擊按鈕,鍵盤回車等事件。在這些方法中我們要編寫一些代碼,在運行時去觸發一些動作。常見動作包括查找,更新數據庫等。在這篇文章中通過對一個實例的分析,介紹了一些基本概念,常見的錯誤以及提出了一個解決方案。 event-dispatching thread 我們一定要記住,事件響應方法的代碼都是在event-dispatching thread中執行的,除非你啟用另一個線程。 那么,什么是event-dispatching thread呢?在《Java Tutorial》[1]中,作者給出了一條單一線程規則:一旦一個Swing組件被實現(realized),所有的有可能影響或依賴于這個組件的狀態的代碼都應該在event-dispatching thread中被執行。而實現一個組件有兩種方式:
單一線程規則的根源是由于Swing組件庫的大部分方法是對多線程不安全的,盡管存在一些例外。這些例外的情況可以在《Java Tutorial》[1]的相關章節找到,這里不再展開。 為了支持單一線程模型,Swing組件庫提供了一個專門來完成這些與Swing組件相關的操作的線程,而這一線程就是event-dispatching thread。我們的事件響應方法通常都是由這一線程調用的,除非你自己編寫代碼來調用這些事件響應方法。在這里初學者經常犯的一個錯誤就是在事件響應方法中完成過多的與修改組件沒有直接聯系的代碼。其最有可能的效果就是導致組件反應緩慢。比如以下響應按鈕事件的代碼: String str = null; this.textArea.setText("Please wait..."); try { //do something that is really time consuming str = "Hello, world!"; Thread.sleep(1000L); } catch (InterruptedException e) { e.printStackTrace(); } this.textArea.setText(str); 執行之后的效果就是按鈕似乎定住了一段時間,直到Done.出現之后才彈起來。原因就是Swing組件的更新和事件的響應都是在event-dispatching thread中完成的,而事件響應的時候,event-dispatching thread被事件響應方法占據,所以組件不會被更新。而直到事件響應方法退出時才有可能去更新Swing組件。 為了解決這個問題,有人也許會試圖通過調用repaint()方法來更新組件: final String[] str = new String[1]; this.jTextArea1.setText("Please wait..."); this.repaint(); try { Thread.sleep(1000L); }catch(InterruptedException e) { e.printStackTrace(); } str[0] = "Done."; jTextArea1.setText(str[0]); 但是這一個方法沒有起到預期的作用,按鈕仍然定住一段時間,在察看了repaint()方法的源代碼之后就知道原因了。 PaintEvent e = new PaintEvent(this, PaintEvent.UPDATE, new Rectangle(x, y, width, height)); Toolkit.getEventQueue().postEvent(e); repaint()方法實際上是在事件隊列里加了一個UPDATE的事件,而沒有直接去重畫組件,而且這一個事件只能等待當前的事件響應方法結束之后才能被分配。因此只有繞過分配機制直接調用paint方法才能達到目的。 final String[] str = new String[1]; this.jTextArea1.setText("Please wait..."); this.paint(this.getGraphics()); try { Thread.sleep(1000L); }catch(InterruptedException e) { e.printStackTrace(); } str[0] = "Done."; jTextArea1.setText(str[0]); 這樣卻是實現了更新,但是還存在著以下的問題。雖然從感覺上,按鈕已經彈起來了,但是在Done.出現之前,我們卻無法按下這個按鈕。可以說按鈕還是定住了,只不過定在了彈起的狀態。調用重繪方法無法從根本上解決問題,因此我們需要尋求其他的方法。 使用多線程 有效的解決方法是使用多線程。首先看一看一個更好的解決方案,這一方案是在參考《Rethinking Swing Threading》[3]的一個程序片段完成的: final String[] str = new String[1]; this.jTextArea1.setText("Please wait..."); this.repaint(); new Thread() { public void run() { try { Thread.sleep(1000L); }catch(InterruptedException e) { e.printStackTrace(); } str[0] = "Done."; javax.swing.SwingUtilities.invokeLater(new Runnable() { public void run() { jTextArea1.setText(str[0]); } }); } }.start(); 在這個程序中,要花費大量時間的操作被放到另一個線程當中,從而使事件響應方法能快速返回,event-dispatching thread就可以更新UI和響應其它事件了。注意到這個程序使用了invokeLater()方法。invokeLater()方法的作用是讓event-dispatching thread去運行制定的代碼。當然也可以不使用invokeLater()方法,但是這樣就違背了單一線程原則,同時帶來了一定程度的相對多線程的不安全性。到現在,解決方案似乎是完美的了,但是我們看一看在原來的程序添加下面的代碼,盡管我們通常不這樣做。 public void paint(java.awt.Graphics g) { super.paint(g); g.drawRect(1, 1, 100, 100); } 我們會發現以前畫的矩形被覆蓋了一部分,原因是由于我們沒用重畫這一個矩形,因此在結尾加上對repaint()方法的調用。 final String[] str = new String[1]; this.jTextArea1.setText("Please wait..."); this.repaint(); new Thread() { public void run() { try { Thread.sleep(1000L); }catch(InterruptedException e) { e.printStackTrace(); } str[0] = "Done."; javax.swing.SwingUtilities.invokeLater(new Runnable() { public void run() { jTextArea1.setText(str[0]); repaint(); } }); } }.start(); 如果你認為這段代碼過于缺乏可讀性,通過在《Java Tutorial》[1]里面介紹的SwingWorker來簡化編程的方法。可以通過實現一個construct()方法來實現花費大量時間的操作和重寫finished()方法來完成組件更新的工作。 this.jTextArea1.setText("Please wait..."); final SwingWorker worker = new SwingWorker() { public Object construct() { try { Thread.sleep(1000L); }catch(InterruptedException e) { e.printStackTrace(); } return "Done."; } public void finished() { jTextArea1.setText(getValue().toString()); repaint(); } }; worker.start(); 在《Rethinking Swing Threading》[3],作者將以上的編程方式稱為同步方式。另外作者提出了一個通過消息機制來實現相同功能的更清晰,但是需要編寫更多代碼的"異步"的方法。 結論 總之,我們在編寫使用Swing組件的程序是要記住以下幾點: 1、不要過多地占用event-dispatching thread; 2、與更新組件相關的代碼要使用event-dispatching thread去執行; 3、要更新組件。 編寫反應靈敏的圖形用戶界面還需要考慮很多問題,以上只是最基本的一部分。歡迎有興趣的讀者來信進行討論。 參考資料 [1]Sun Microsystems, The Java Tutorial Third Edition, java.sun.com [2]Sun Microsystems, JavaTM 2 SDK, Standard Edition Documentation Version 1.4.2, java.sun.com [3]Jonathan Simon, Rethinking Swing Threading, java.net |