2008年5月29日 #

          java 數據庫緩沖池 使用c3p0

          c3p0很容易使用的開源專業級jdbc數據庫緩沖池。
          它是sourceforge上的一個開源項目,
          項目在
          http://sourceforge.net/projects/c3p0
          他的眾多特性這里就不一一介紹了。
          比較爽的一點就是
          當Connection歸還緩沖池時,c3p0會很小心的關閉
          這條連接打開的Statement和ResultSet,免去了使用時
          自己動手小心翼翼的關閉。

          c3p0使用非常簡單,這里給一個例子

          package common.db;

          import java.sql.Connection;
          import java.sql.SQLException;
          import java.util.Properties;


          import com.mchange.v2.c3p0.ComboPooledDataSource;
          import com.mchange.v2.c3p0.DataSources;

          public final class ConnectionManager {
          ?private static ConnectionManager instance;
          ?
          ?public ComboPooledDataSource ds;
          ?private static String c3p0Properties="c3p0.properties";
          ?
          ?private ConnectionManager() throws Exception {
          ??Properties p = new Properties();
          ??p.load(this.getClass().getResourceAsStream(c3p0Properties));
          ??ds = new ComboPooledDataSource();
          ?}
          ?
          ?public static final ConnectionManager getInstance() {
          ??if (instance == null) {
          ???try {
          ????instance = new ConnectionManager();
          ???} catch (Exception e) {
          ????e.printStackTrace();
          ???}
          ??}
          ??return instance;
          ?}
          ?
          ?public synchronized final Connection getConnection() {
          ??try {
          ???return ds.getConnection();
          ??} catch (SQLException e) {
          ???e.printStackTrace();
          ??}
          ??return null;
          ?}

          ?protected void finalize() throws Throwable {
          ??DataSources.destroy(ds);?//關閉datasource
          ??super.finalize();
          ?}
          ?
          }

          然后在ConnectionManager類的目錄下再創建一個配置文件c3p0.properties
          內容如下:
          #db login parameters
          driverClass=com.mysql.jdbc.Driver
          jdbcUrl=jdbc:mysql://localhost/test?useUnicode=no&characterEncoding=GBK
          user=test
          password=test

          #pool parameters
          initialPoolSize=2
          maxPoolSize=5
          #maxIdleTime=10
          #idleConnectionTestPeriod=5
          autoCommitOnClose=true

          完整的配置文件參數參看c3p0的文檔

          使用connection時很簡單
          Connection conn = ConnectionManager.getInstance().getConnection();
          ...
          最后 conn.close() 即可,

          posted @ 2008-10-23 10:24 金家寶 閱讀(4350) | 評論 (2)編輯 收藏

          JDBC也分2.0和3.0?

          如題。研究中...以前沒有注意過。

          posted @ 2008-10-23 10:10 金家寶 閱讀(298) | 評論 (0)編輯 收藏

          鯉魚論壇 研究筆記(未)

          2008.10.21第一天
          網站地址: http://www.liyunet.com/
          源碼下載: http://www.liyunet.com/bbs/download.jsp

          我主要是利用該論壇的簡易性來研究JSP技術,以及緩存技術。雖然說目前大部分論壇應用的都是主流框架技術,但說回來,這些框架也只是對JSP等相關技術的一個封裝,而了解底部應用及其原理更容易讓我理解主流框架的內部原理機制。不至于讓框架把我們變成“傻子”。


          那就開始吧
          首先,和某些大蝦一樣的習慣,我們從WEB-INF下的web.xml開始,從以下代碼中可以發現隨著tomcat啟動時,自動加載了InitServlet類。這是一個Servlet類。
          WEB-INF\web.xml部分代碼:
          1
          2<servlet-name>InitServlet</servlet-name>
          3????????<servlet-class>com.bcxy.servlet.InitServlet</servlet-class>
          4????????<load-on-startup>1</load-on-startup>
          5??????</servlet>
          6

          查看com.bcxy.servlet.InitServlet類,代碼很簡單:
          開始做了一個對此類日志(log4j)的綁定。接著通過SystemConfig取得了系統配置中的連接類型conntype(查看classes目錄下的SystemConfig.xml可知此變量是判斷使用連接池的類型0:3P0; 1:Proxool)。接著是初始化操作,記錄一些必要的啟動信息(log4j)(其中做了本地地址和網絡訪問地址的轉換)。
          用DBUtil.close測試連接池是否加載成功。
          最后是釋放類所做的必要操作。判斷使用的是哪一種連接池,然后關閉。這樣我們的第一個類就分析完畢。
          接著需要了解到
          SystemConfig類是加載SystemConfig.xml配置文件,并設定方法去讀取。其中有一定的類型轉換。

          此時似乎已經找不到頭緒了。那這樣吧。我們就開始訪問我們自己搭建的論壇,從首頁開始,也就是index.jsp頁面。
          瀏覽index.jsp代碼,由上向下理解每一個語句的含義(整體分體)。

          設定頁面編碼;導入IPLocalizer類(應該是做IP顯示的工具類);插入INC/const.jsp頁(過后會有分析);設定stats變量數值(通過閱讀其他jsp頁面發現,此變量的作用主要是在于在首頁顯示用戶狀態時,兼并顯示當前用戶做瀏覽的頁面:也就是stats的值,這樣我們可以在客戶可訪問的范圍內對stats變量進行設置,就可以查看在線用戶的當前行為);繼續插入INC/theme.jsp(估計是定制論壇模板的文件);<table>標簽內部就是連接到相關顯示數據信息的jsp頁面并附加了參數。其中的一些格式是通過上面引用文件中的變量設置,相對不難理解;論壇消息廣播部分,通過一個可執行jsp頁面vector顯示在首頁頂部,當中訪問數據庫的細節需要進一步研究代碼);接著是帖子的遍歷,也就是首頁最關鍵的部分,這里看起來不是由jsp頁面來負責獲取數據,而是通過Forum類來獲取一些過濾之后的數據:這里所說的過濾是例如置頂帖子,最新帖子等有一些特殊標記的數據:;
          index.jsp的其他部分就都是大同小異了。都是通過一個遍歷來展現具有相同特性的數據。;大家需要注意一些關于頁面表現的而非java技術的部分,例如信息層的提示,和一些圖片連接。
          通過index.jsp的學習,我們大概已經了解了大部分jsp頁面代碼的表現形式和含義。當然,一定要注意在這個過程中,參數傳遞、參數獲取的代碼部分,不要遺漏。除了一些我們可以看到的頁面之外,上面部分也講到了一些并不用于顯示給用戶的頁面,這里我們認為它是可執行頁面,也就是說它對我們的數據和請求做了一些處理,或者說把我們的請求轉交給了服務器(比如servlet)。

          預計晚上要研究一下有關數據庫方面的存取類JdbcWrapper以及連接獲取和釋放、數據查詢插入。
          2008.10.22
          大概昨天寫的已經忘的差不多了,那么我們還是從index.jsp文件開始,前幾行沒有什么問題,都是一些導入文件的標簽,那么我們從SkinUtil.這個類入手。在查看SkinUtil類代碼的時候我們發現里面應用了一些com\bcxy\bbs\util?包中類,其中含有三個工具類。(偷笑,看了文件大小,應該代碼不多,我們看看里面都是什么)在開始之前我們應了解一下GCookie.java類的大概內容和作用。看導入包我們可以大概了解一下此類的作用:對URL的編碼與解碼,產生和讀取Cookie,還有就是做一些日志記錄(log4j)。
          我們仔細閱讀后,了解到,其中有一個重載方法,也就是setCookie方法,根據不同的參數,可以讓我們選擇直接賦予變量名和值的方法,或者是賦予變量名和值另外加上最大保存時間的方法。? 類中的另一個方法是獲取Cookie方法值。
          ParamUtil類也很簡單,是取得字符串和 取得整數的重載方法,其中的參數決定是否有默認值,是否需要轉碼。
          SysUtil類中根據SystemConfig類中的讀取方法讀取配置文件systemconfig.properties,按照里面的設置,來判斷是否對參數和數據庫讀取操作中的參數進行編碼。其中還有一個方法是取得真實地址,當然這些都是根據systemconfig.properties文件中配置而定的。
          BBSCconst.java類簡單的設置了一些常量。作用是設置數據庫表名的時候加上systemconfig.properties中設置的前綴。
          回到SkinUtil.java類似乎看起來一些剛剛還陌生的類方法,顯得明朗。前面設置了一個Cookie的變量名并賦了值。

          ret?=?new?JdbcWrapper().doIntSearch(sql,?0);

          這里用到了新類,也就是我們昨天說過的要了解的關于數據庫連接的類。也是今天要解決的重點。打開JdbcWrapper類,查看代碼.(插一句,看代碼的時候,我覺得先看包名,了解大概要用到的類和方法,去設想這個類要實現的功能),yi一眼看來,大概都熟悉,無非是連接數據庫 讀取,結果保存,異常,還有一些類似數組的HashMap還有遍歷用得Iterator。想想,大概就是數據層的一些基礎CRUD操作。但是其中有個類不是很熟悉,DatabaseMetaData類,查看sun公司的在線文檔,發現這是個接口而且方法奇多,文檔的第一句這么寫,

          Comprehensive?information?about?the?database?as?a?whole.?

          我也不能理解這句話包含了什么內容。不管他,在程序中慢慢體會吧。JdbcWrapper這個類有點長,不過,大部分方法都有類似的作用,也就是說真正不同功能的代碼也只有幾分之一而已。我看的都想睡覺了。

          在網吧,因為不便,先離開咯
          今天還好,自己有一臺電腦,可以不限制時間。繼續工作....
          之前研究JdbcWrapper類的時候有一個小小的疑問。如下

          ?

          ?1?????//######這里有一點不明白,為什么要判斷getAutoClose()
          ?2????/*
          ?3?????*?當需要事務支持時,需要設置autoClose=false,那就等到事務提交時再關閉數據庫連接。
          ?4?????*?*/

          ?5????public?void?closeConnection()?{
          ?6????????if?(getAutoClose())?{
          ?7????????????DBUtil.close(pstmt,?con);
          ?8????????}

          ?9????}

          10

          今天看群里鯉魚回答內容如下:

          當需要事務支持時,需要設置autoClose=false,那就等到事務提交時再關閉數據庫連接。


          一時還沒有理解開來。
          看過來看過去,JdbcWrapper類對我來講還是有一部分難以理解。索性不去管它,等在下面的代碼中出現時,反復查閱應該會有更多的收獲。

          posted @ 2008-10-21 11:47 金家寶 閱讀(365) | 評論 (0)編輯 收藏

          關于正則表達式

          *匹配除了換行之外的所有字符

          合法IP的正則表達式 ((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)

          \s匹配任意的空白符、(空格、制表符、換行符、中文全角空格)
          \w匹配字母或數字或下劃線或漢字

          表1.常用的元字符
          代碼說明
          .匹配除換行符以外的任意字符
          \w匹配字母或數字或下劃線或漢字
          \s匹配任意的空白符
          \d匹配數字
          \b匹配單詞的開始或結束
          ^匹配字符串的開始
          $匹配字符串的結束


          表2.常用的限定符
          代碼/語法說明
          *重復零次或更多次
          +重復一次或更多次
          ?重復零次或一次
          {n}重復n次
          {n,}重復n次或更多次
          {n,m}重復n到m次


          后向引用

          使用小括號指定一個子表達式后,匹配這個子表達式的文本(也就是此分組捕獲的內容)可以在表達式或其它程序中作進一步的處理。默認情況下,每個分組會自動擁有一個組號,規則是:從左向右,以分組的左括號為標志,第一個出現的分組的組號為1,第二個為2,以此類推。

          后向引用用于重復搜索前面某個分組匹配的文本。例如,\1代表分組1匹配的文本。難以理解?請看示例:

          \b(\w+)\b\s+\1\b可以用來匹配重復的單詞,像go go, 或者kitty kitty。這個表達式首先是一個單詞,也就是單詞開始處和結束處之間的多于一個的字母或數字(\b(\w+)\b),這個單詞會被捕獲到編號為1的分組中,然后是1個或幾個空白符(\s+),最后是分組1中捕獲的內容(也就是前面匹配的那個單詞)(\1)。

          你也可以自己指定子表達式的組名。要指定一個子表達式的組名,請使用這樣的語法:(?<Word>\w+)(或者把尖括號換成'也行:(?'Word'\w+)),這樣就把\w+的組名指定為Word了。要反向引用這個分組捕獲的內容,你可以使用\k<Word>,所以上一個例子也可以寫成這樣:\b(?<Word>\w+)\b\s+\k<Word>\b

          使用小括號的時候,還有很多特定用途的語法。下面列出了最常用的一些:

          表4.常用分組語法
          分類代碼/語法說明
          捕獲(exp)匹配exp,并捕獲文本到自動命名的組里
          (?<name>exp)匹配exp,并捕獲文本到名稱為name的組里,也可以寫成(?'name'exp)
          (?:exp)匹配exp,不捕獲匹配的文本,也不給此分組分配組號
          零寬斷言(?=exp)匹配exp前面的位置
          (?<=exp)匹配exp后面的位置
          (?!exp)匹配后面跟的不是exp的位置
          (?<!exp)匹配前面不是exp的位置
          注釋(?#comment)這種類型的分組不對正則表達式的處理產生任何影響,用于提供注釋讓人閱讀


          表5.懶惰限定符
          代碼/語法說明
          *?重復任意次,但盡可能少重復
          +?重復1次或更多次,但盡可能少重復
          ??重復0次或1次,但盡可能少重復
          {n,m}?重復n到m次,但盡可能少重復
          {n,}?重復n次以上,但盡可能少重復


          表6.常用的處理選項
          名稱說明
          IgnoreCase(忽略大小寫)匹配時不區分大小寫。
          Multiline(多行模式)更改^$的含義,使它們分別在任意一行的行首和行尾匹配,而不僅僅在整個字符串的開頭和結尾匹配。(在此模式下,$的精確含意是:匹配\n之前的位置以及字符串結束前的位置.)
          Singleline(單行模式)更改.的含義,使它與每一個字符匹配(包括換行符\n)。
          IgnorePatternWhitespace(忽略空白)忽略表達式中的非轉義空白并啟用由#標記的注釋。
          RightToLeft(從右向左查找)匹配從右向左而不是從左向右進行。
          ExplicitCapture(顯式捕獲)僅捕獲已被顯式命名的組。
          ECMAScript(JavaScript兼容模式)使表達式的行為與它在JavaScript里的行為一致。
          表7.尚未詳細討論的語法
          代碼/語法說明
          \a報警字符(打印它的效果是電腦嘀一聲)
          \b通常是單詞分界位置,但如果在字符類里使用代表退格
          \t制表符,Tab
          \r回車
          \v豎向制表符
          \f換頁符
          \n換行符
          \eEscape
          \0nnASCII代碼中八進制代碼為nn的字符
          \xnnASCII代碼中十六進制代碼為nn的字符
          \unnnnUnicode代碼中十六進制代碼為nnnn的字符
          \cNASCII控制字符。比如\cC代表Ctrl+C
          \A字符串開頭(類似^,但不受處理多行選項的影響)
          \Z字符串結尾或行尾(不受處理多行選項的影響)
          \z字符串結尾(類似$,但不受處理多行選項的影響)
          \G當前搜索的開頭
          \p{name}Unicode中命名為name的字符類,例如\p{IsGreek}
          (?>exp)貪婪子表達式
          (?<x>-<y>exp)平衡組
          (?im-nsx:exp)在子表達式exp中改變處理選項
          (?im-nsx)為表達式后面的部分改變處理選項
          (?(exp)yes|no)把exp當作零寬正向先行斷言,如果在這個位置能匹配,使用yes作為此組的表達式;否則使用no
          (?(exp)yes)同上,只是使用空表達式作為no
          (?(name)yes|no)如果命名為name的組捕獲到了內容,使用yes作為表達式;否則使用no
          (?(name)yes)同上,只是使用空表達式作為no

          posted @ 2008-10-17 11:10 金家寶 閱讀(326) | 評論 (1)編輯 收藏

          Lucene倒排索引原理(轉)

          Lucene是一個高性能的java全文檢索工具包,它使用的是倒排文件索引結構。該結構及相應的生成算法如下:

          0)設有兩篇文章1和2
          文章1的內容為:Tom?lives?in?Guangzhou,I?live?in?Guangzhou?too.
          文章2的內容為:He?once?lived?in?Shanghai.

          1)由于lucene是基于關鍵詞索引和查詢的,首先我們要取得這兩篇文章的關鍵詞,通常我們需要如下處理措施
          a.我們現在有的是文章內容,即一個字符串,我們先要找出字符串中的所有單詞,即分詞。英文單詞由于用空格分隔,比較好處理。中文單詞間是連在一起的需要特殊的分詞處理。
          b.文章中的”in”,?“once”?“too”等詞沒有什么實際意義,中文中的“的”“是”等字通常也無具體含義,這些不代表概念的詞可以過濾掉
          c.用戶通常希望查“He”時能把含“he”,“HE”的文章也找出來,所以所有單詞需要統一大小寫。
          d.用戶通常希望查“live”時能把含“lives”,“lived”的文章也找出來,所以需要把“lives”,“lived”還原成“live”
          e.文章中的標點符號通常不表示某種概念,也可以過濾掉
          在lucene中以上措施由Analyzer類完成

          經過上面處理后
          ????文章1的所有關鍵詞為:[tom]?[live]?[guangzhou]?[i]?[live]?[guangzhou]
          ????文章2的所有關鍵詞為:[he]?[live]?[shanghai]

          2)?有了關鍵詞后,我們就可以建立倒排索引了。上面的對應關系是:“文章號”對“文章中所有關鍵詞”。倒排索引把這個關系倒過來,變成:“關鍵詞”對“擁有該關鍵詞的所有文章號”。文章1,2經過倒排后變成
          關鍵詞???文章號
          guangzhou??1
          he?????????2
          i???????????1
          live???????1,2
          shanghai???2
          tom?????????1

          通常僅知道關鍵詞在哪些文章中出現還不夠,我們還需要知道關鍵詞在文章中出現次數和出現的位置,通常有兩種位置:a)字符位置,即記錄該詞是文章中第幾個字符(優點是關鍵詞亮顯時定位快);b)關鍵詞位置,即記錄該詞是文章中第幾個關鍵詞(優點是節約索引空間、詞組(phase)查詢快),lucene中記錄的就是這種位置。

          加上“出現頻率”和“出現位置”信息后,我們的索引結構變為:
          關鍵詞???文章號[出現頻率]???出現位置
          guangzhou?1[2]???????????????3,6
          he???????2[1]???????????????1
          i?????????1[1]???????????????4
          live??????1[2],2[1]???????????2,5,2
          shanghai??2[1]???????????????3
          tom??????1[1]???????????????1

          以live 這行為例我們說明一下該結構:live在文章1中出現了2次,文章2中出現了一次,它的出現位置為“2,5,2”這表示什么呢?我們需要結合文章號和出現頻率來分析,文章1中出現了2次,那么“2,5”就表示live在文章1中出現的兩個位置,文章2中出現了一次,剩下的“2”就表示live是文章2中第 2個關鍵字。
          ????
          以上就是lucene索引結構中最核心的部分。我們注意到關鍵字是按字符順序排列的(lucene沒有使用B樹結構),因此lucene可以用二元搜索算法快速定位關鍵詞。
          ????
          實現時?lucene將上面三列分別作為詞典文件(Term?Dictionary)、頻率文件(frequencies)、位置文件 (positions)保存。其中詞典文件不僅保存有每個關鍵詞,還保留了指向頻率文件和位置文件的指針,通過指針可以找到該關鍵字的頻率信息和位置信息。

          ????Lucene中使用了field的概念,用于表達信息所在位置(如標題中,文章中,url中),在建索引中,該field信息也記錄在詞典文件中,每個關鍵詞都有一個field信息(因為每個關鍵字一定屬于一個或多個field)。

          ???? 為了減小索引文件的大小,Lucene對索引還使用了壓縮技術。首先,對詞典文件中的關鍵詞進行了壓縮,關鍵詞壓縮為<前綴長度,后綴>,例如:當前詞為“阿拉伯語”,上一個詞為“阿拉伯”,那么“阿拉伯語”壓縮為<3,語>。其次大量用到的是對數字的壓縮,數字只保存與上一個值的差值(這樣可以減小數字的長度,進而減少保存該數字需要的字節數)。例如當前文章號是16389(不壓縮要用3個字節保存),上一文章號是16382,壓縮后保存7(只用一個字節)。
          ????
          ????下面我們可以通過對該索引的查詢來解釋一下為什么要建立索引。
          假設要查詢單詞?“live”,lucene先對詞典二元查找、找到該詞,通過指向頻率文件的指針讀出所有文章號,然后返回結果。詞典通常非常小,因而,整個過程的時間是毫秒級的。
          而用普通的順序匹配算法,不建索引,而是對所有文章的內容進行字符串匹配,這個過程將會相當緩慢,當文章數目很大時,時間往往是無法忍受的。

          posted @ 2008-10-17 09:43 金家寶 閱讀(2842) | 評論 (1)編輯 收藏

          struts2標簽

          ?

          一、
          寫jsp頁面的時候,在struts2中,用的是s標記,先引入標記:
          <%@ taglib prefix="s" uri="/struts-tags"%>
          二、
          struts2的標簽和1是完全不同的。
          struts2的標簽分為兩大類:非UI標志和UI標志 struts1 將標志庫按功能分成HTML、Tiles、Logic和Bean等幾部分
          下面就介紹strut2的具體標簽:
          1、UI
          UI標志又可以分為表單UI和非表單UI兩部分。表單UI部分基本與Struts 1.x相同,都是對HTML表單元素的包裝。不過,Struts 2.0加了幾個我們經常在項目中用到的控件如:datepicker、doubleselect、timepicker、optiontransferselect等。因為這些標志很多都經常用到,而且參數也很多,要在一篇文章詳細說明并非易事。
          下面主要是ui標簽的一些用法
          form:
          <s:form action="exampleSubmit" method="post" enctype="multipart/form-data">
          <s:submit />
          ??? <s:reset />

          </s:form>可以上傳文件的form。
          textfield:
          <s:textfield
          ??????????? label="姓名:"
          ??????????? name="name"
          ??????????? tooltip="Enter your Name here" />
          datepicker:
          <s:datepicker
          ??????????? tooltip="Select Your Birthday"
          ??????????? label="生日"
          ??????????? name="birthday" />
          textarea:
          <s:textarea
          ??????????? tooltip="Enter your remart"
          ??????????? label="備注"
          ??????????? name="remart"
          ??????????? cols="20"
          ??????????? rows="3"/>
          select:
          <s:select
          ??????????? tooltip="Choose user_type"
          ??????????? label=""
          ??????????? list="#{'free':'免費','vip':'收費'}" value="#{'free':'免費'}"??
          ?????????? name="bean.user_type"
          ??????????? emptyOption="true"
          ??????????? headerKey="None"
          ??????????? headerValue="None"/>
          <s:select
          ??????????? tooltip="Choose user_type"
          ??????????? label=""
          ??????????? list="#{'free':'免費','vip':'收費'}" value="#{'free':'免費'}"??
          ?????????? name="bean.user_type"
          ??????????? emptyOption="true"
          ??????????? headerKey="None"
          ??????????? headerValue="None"/>
          <s:select
          list="venderList"
          listKey="id"
          listValue="name"
          value="%{profile.companyName}"
          name="companyName" cssClass="sel_style_w_180"/>??
          挺好用的
          checkboxlist:
          <s:checkboxlist
          ??????????? tooltip="Choose your Friends"
          ??????????? label="朋友"
          ??????????? list="{'Patrick', 'Jason', 'Jay', 'Toby', 'Rene'}"
          ??????????? name="friends"/>
          checkbox:
          ?? <s:checkbox
          ??????????? tooltip="Confirmed that your are Over 18"
          ??????????? label="年齡"
          ??????????? name="legalAge"
          ??????????? value="18"/>
          file:
          ?? <s:file
          ??????????? tooltip="Upload Your Picture"
          ??????????? label="Picture"
          ??????????? name="picture" />
          a:
          <s:a href="getP.jsp">超鏈接提交</s:a>
          date :
          <s:date name="ad_end_time" format="yyyy-MM-dd"/>


          2、非UI
          if、elseif和else 描述:
          執行基本的條件流轉。
          參數:
          名稱必需默認類型描述備注test是Boolean決定標志里內容是否顯示的表達式else標志沒有這個參數id否Object/String用來標識元素的id。在UI和表單中為HTML的id屬性 例子:
          <%@ page c %>
          <%@ taglib prefix="s" uri="/struts-tags" %>
          <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
          <html>
          ??? <head>
          ??????? <title>Condition Flow</title>
          ??? </head>
          ??? <body>
          ??????? <h3>Condition Flow</h3>????????????
          ??????? <!--
          ??????????? 這里有點小技巧:
          ??????????? 本來可以用#parameters.name[0]來獲得,請求中name的值。但是,在我實現include例子時,
          ??????????? 無論我用param標志給name賦任何值,#parameters里面不會含有任何值,所以#parameters.name也為空值。
          ????????????
          ??????????? 其原因為:
          ??????????? 當使用include標志時,被包含的頁面(included)里#parameters拿到的是包含頁面里的請求參數。
          ????????????
          ??????????? 因此,這里必須手工調用request.getParameter("name")。
          ??????? -->
          ??? <s:iterator value="linkList" status="bean">
          ?? <tr>
          ??? <td class="data_tab_tdcl">
          ???? <s:property value="#bean.Index+1" />??? </td>
          ??? <td class="data_tab_tdcl"><s:property value="link_title" /></td>
          ??? <td class="data_tab_tdcl"><s:property value="link_url" /></td>
          ??? <td class="data_tab_tdcl">
          ??? <s:if test="link_type == 1">
          ?????????????????? 文字
          ??????????????? </s:if>
          ??????????????? <s:elseif test="link_type == 2">
          ?????????????????? 圖片
          ??????????????? </s:elseif>
          ???????????????? <s:else>
          ???????????????? -----
          ?????????????? </s:else>???
          ??? </td>

          ???
          ??? </body>
          </html>
          例1 condition.jsp
          iterator 描述:
          用于遍歷集合(java.util.Collection)或枚舉值(java.util.Iterator)。
          參數:
          名稱必需默認類型描述status否String如果設置此參數,一個IteratorStatus的實例將會壓入每個遍歷的堆棧value否Object/String要遍歷的可枚舉的(iteratable)數據源,或者將放入新列表(List)的對象id否Object/String用來標識元素的id。在UI和表單中為HTML的id屬性 例子:
          <%@ page c %>
          <%@ page import="java.util.List" %>
          <%@ page import="java.util.ArrayList" %>
          <%@ taglib prefix="s" uri="/struts-tags" %>

          <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
          <%
          ??? List list = new ArrayList();
          ??? list.add("Max");
          ??? list.add("Scott");
          ??? list.add("Jeffry");
          ??? list.add("Joe");
          ??? list.add("Kelvin");
          ??? request.setAttribute("names", list);
          %>
          <html>
          ??? <head>
          ??????? <title>Iterator</title>
          ??? </head>
          ??? <body>
          ??????? <h3>Names: </h3>
          ??????? <!--
          ??????????? 1、此處的空property元素用于獲得當前iterator的值
          ??????????? 2、status被設成stuts,在iterator的里面就可以通過#stuts取得IteratorStatus的對象。IteratorStatus類包含當前序號信息,如是否第一個或最后一個,是否為奇數序號。這些信息在我們做格式化的時候,顯得非常有用。
          ??????? -->
          ??????? <ol>
          ??????????? <s:iterator value="#request.names" status="stuts">????????????????
          ??????????????? <s:if test="#stuts.odd == true">
          ??????????????????? <li>White <s:property /></li>
          ??????????????? </s:if>
          ??????????????? <s:else>
          ??????????????????? <li style="background-color:gray"><s:property /></li>
          ??????????????? </s:else>
          ??????????? </s:iterator>
          ??????? </ol>
          ??? </body>
          </html>

          posted @ 2008-10-13 15:46 金家寶 閱讀(526) | 評論 (1)編輯 收藏

          Java筆試題(部分)

               摘要: EJB 方面 ? 94 、 EJB2.0 ...  閱讀全文

          posted @ 2008-10-09 11:56 金家寶 閱讀(723) | 評論 (0)編輯 收藏

          安裝WinCVS時Python不可用的問題

          在我裝完Wincvs之后,提示我沒有安裝python,但后來我又裝了python2.4,可Wincvs還是說配置不對.到底要怎么配置python呢?期待著您的回復,謝謝你了!
          ???
          TCL or Python are not available, shell is disabled。
          有的網友說是版本的問題,我今天安裝時也是一樣,裝了好三四個版本,都一樣,最后在CSDN上找到了答案。

          解決辦法:

          在admin-〉Preferences->wincvs中有關于python的設置, ?
          ? 其中python是指你的python虛擬機的位置,一般是python2X.dll的位置。在你來說就是python24.dll的位置,一般這個文件會在你的系統文件中找到。 ?
          ? Tcl則一般會在python文件架的dlls子文件架中找到,一般名稱為tclxx.dll,在你大概就是tcl84.dll。如果是安裝python2.3版本的話,會自動找到,不需要設置。

          設置好了,WinCVS輸出窗口:

          Python 2.5.2 (r252:60911, Feb 21 2008, 13:11:45) [MSC v.1310 32 bit (Intel)] on win32Tk is available, Tk-macros are enabledTCL is available, shell is enabled : help (select and press enter)

          posted @ 2008-09-21 08:52 金家寶 閱讀(3619) | 評論 (1)編輯 收藏

          [設計模式]jive中的[abstract Factory]

            AbstractFactory模式和可擴展性
            假如要實現較好的可擴展性,AbstractFactory模式確實是一件利器。如上面所說,假如要創建的Forum接口的不同實現,而又不想更改代碼的話,就需要用到抽象工廠了。再Jive中,AuthorizationFactory類是一個抽象類,用來創建Authorization對象。這是一個抽象工廠,可以通過不同的子類來創建不同的Authorization對象。這個工廠的實現方法是:
            
            在AuthorizationFactory中使用一個private static變量factory,用來引用具體的抽象工廠的實例:
            private static AuthorizationFactory factory = null;
            
            用一個private static的String,來指明具體的抽象工廠的子類類名:
            private static String className ="com.coolservlets.forum.database.DbAuthorizationFactory";
            
            然后是用一個private static的loadAuthorizationFactory方法來給這個factory變量賦值,生成具體的抽象工廠類:
            
              private static void loadAuthorizationFactory() {
                if (factory == null) {
                  synchronized(className) {
                    if (factory == null) {
                      String classNameProp = PropertyManager.getProperty(
                        "AuthorizationFactory.className"
                      );
                      if (classNameProp != null) {
                        className = classNameProp;
                      }
                      try {
                        Class c = Class.forName(className);
                        factory = (AuthorizationFactory)c.newInstance();
                      }
                      catch (Exception e) {
                        System.err.println("Exception loading class: " + e);
                        e.printStackTrace();
                      }
                    }
                  }
                }
            }
            
            在static的getAuthorization方法返回一個Authorization的過程中,先初始化工廠類factory變量,然后用factory的createAuthorization方法來創建:
            
              public static Authorization getAuthorization(String username,
                  String passWord) throws UnauthorizedException
              {
                loadAuthorizationFactory();
                return factory.createAuthorization(username, password);
            }
            
            不同的子類有不同的createAuthorization方法的實現。比如在DbAuthorizationFactory這個AuthorizationFactory的數據庫實現子類中,createAuthorization方法是這樣實現的:
            
              public Authorization createAuthorization(String username, String password)
                  throws UnauthorizedException
              {
                if (username == null password == null) {
                  throw new UnauthorizedException();
                }
                password = StringUtils.hash(password);
                int userID = 0;
                Connection con = null;
                PreparedStatement pstmt = null;
                try {
                  con = DbConnectionManager.getConnection();
                  pstmt = con.prepareStatement(AUTHORIZE);
                  pstmt.setString(1, username);
                  pstmt.setString(2, password);
            
                  ResultSet rs = pstmt.executeQuery();
                  if (!rs.next()) {
                    throw new UnauthorizedException();
                  }
                  userID = rs.getInt(1);
                }
                catch( SQLException sqle ) {
                  System.err.println("Exception in DbAuthorizationFactory:" + sqle);
                  sqle.printStackTrace();
                  throw new UnauthorizedException();
                }
                finally {
                  try { pstmt.close(); }
                  catch (Exception e) { e.printStackTrace(); }
                  try { con.close();  }
                  catch (Exception e) { e.printStackTrace(); }
                }
                return new DbAuthorization(userID);
              }
            
            在這個類中,可以看到抽象類和具體的子類之間的關系,它們是如何協作的,又是如何劃分抽象方法和非抽象方法的,這都是值得注重的地方。一般的,抽象方法需要子類來實現,而抽象類中的非抽象方法應該所有子類所能夠共享的,或者可是說,是定義在抽象方法之上的較高層的方法。這確實是一個抽象工廠的好例子!雖然實現的方法已經和GOF中給出的實現相差較遠了,但思想沒變,這兒的實現,也確實是要巧妙的些。
            
            還有就是靜態方法的使用,使得這個類看起來有些Singleton的意味。這使得對于AbstractFactory的創建變得簡單。
            
            在AuthorizationFactory中定義的其它方法,涉及到具體的如何創建Authorization,都是作為abstract方法出現,具體實現留給子類來完成。
            
            這樣,在需要生成一個Authorization的時候,只需要調用AuthorizationFactory的靜態方法getAuthorization就可以了,由子類實現了具體的細節。
            
            其它的,如同上面講到的,在創建Forum的時候用的ForumFactory,具有同上面一樣的實現,這就是模式之所以稱為模式的所在了。
          資料引用:http://www.knowsky.com/365144.html

          posted @ 2008-09-16 15:57 金家寶 閱讀(267) | 評論 (0)編輯 收藏

          題目: IOC 后臺機制學習

          ?
          題目: IOC 后臺機制學習
          給定:
          配置文件 config.txt, 文件內容
          className = test.JavaBean1
          field = username
          value = ABC

          該文件中的三個值會隨時可能變化, 唯一不變的是 className 指定的都是一個 JavaBean(為了簡化, 我們假定里面已經有一個 username 屬性, 例如:

          class JavaBeanxxxx {
          ?? ?private String username;

          ?? ?public String getUsername() {
          ?? ??? ?return username;
          ?? ?}

          ?? ?public void setUsername(String uname) {
          ?? ??? ?this.username = uname;
          ?? ?}
          }

          要求: 寫一段代碼, 讀取配置文件 config.txt, 然后實現把 className 指定的 JavaBean 類加載(注意這個類名是可以修改的, 可配置的), 然后生成一個實例,
          并把配置文件中field字段指定的值作為這個實例的屬性名(這里是username)所對應的值設置為 ABC(字符串), 并且要讀出最后設置的值.

          此題已經被 TigerTian 解答出來, 歡迎學習, 也感謝 TigerTian:
          package?com.gcoresoft.ioc;

          import?java.io.*;
          import?java.lang.reflect.*;
          import?java.util.*;
          import?java.beans.*;

          public?class?IOCStudy?{
          ????
          ????
          //Load?the?properties?file
          ????private?Properties?prop=new?Properties();
          ????
          ????
          public?void?loadPropFile(String?filename)
          ????{
          ????????
          try
          ????????{
          ????????????FileInputStream?fin
          =new?FileInputStream(filename);
          ????????????prop.load(fin);
          ????????????fin.close();
          ????????}
          catch(Exception?e){
          ????????????System.out.println(e.toString());
          ????????}
          ????}
          ????
          ????
          private?String?getValueByName(String?Name)
          ????{
          ????????
          return?prop.getProperty(Name);
          ????}
          ????
          ????
          public?static?void?main(String[]?args)
          ????{
          ????????IOCStudy?ioc
          =new?IOCStudy();
          ????????ioc.loadPropFile(
          "E:\\Work\\GetInIOC\\src\\com\\gcoresoft\\ioc\\Config.txt");
          ????????
          try
          ????????{
          ????????????Class?bean
          =Class.forName(ioc.getValueByName("className"));
          ????????????
          try?{
          ????????????????java.beans.BeanInfo?info
          =java.beans.Introspector.getBeanInfo(bean);
          ????????????????java.beans.PropertyDescriptor?pd[]
          =info.getPropertyDescriptors();
          ????????????????
          try?{
          ????????????????????Method?mSet
          =null,mRead=null;
          ????????????????????Object?obj
          =bean.newInstance();
          ????????????????????
          for(int?i=0;i<pd.length;i++)
          ????????????????????????
          if(pd[i].getName().equalsIgnoreCase(ioc.getValueByName("field")))
          ????????????????????????{
          ????????????????????????????mSet
          =pd[i].getWriteMethod();
          ????????????????????????????mRead
          =pd[i].getReadMethod();????????????????????????????
          ????????????????????????}
          ????????????????????
          try?{
          ????????????????????????mSet.invoke(obj,?ioc.getValueByName(
          "value"));
          ????????????????????????String?str
          =(String)mRead.invoke(obj,?null);
          ????????????????????????System.out.println(str);
          ????????????????????}?
          catch?(IllegalArgumentException?e)?{
          ????????????????????????
          //?TODO?Auto-generated?catch?block
          ????????????????????????e.printStackTrace();
          ????????????????????}?
          catch?(InvocationTargetException?e)?{
          ????????????????????????
          //?TODO?Auto-generated?catch?block
          ????????????????????????e.printStackTrace();
          ????????????????????}
          ????????????????}?
          catch?(InstantiationException?e)?{
          ????????????????????
          //?TODO?Auto-generated?catch?block
          ????????????????????e.printStackTrace();
          ????????????????}?
          catch?(IllegalAccessException?e)?{
          ????????????????????
          //?TODO?Auto-generated?catch?block
          ????????????????????e.printStackTrace();
          ????????????????}

          ????????????????
          ????????????}?
          catch?(IntrospectionException?e)?{
          ????????????????
          //?TODO?Auto-generated?catch?block
          ????????????????e.printStackTrace();
          ????????????}
          ????????????
          ????????????
          ????????}
          catch(ClassNotFoundException?e){
          ????????????System.out.println(e.toString());
          ????????}
          ????????
          ????}
          ????
          }

          posted @ 2008-08-27 20:41 金家寶 閱讀(296) | 評論 (0)編輯 收藏

          反向控制和面向切面編程在Spring的應用

          引言

            在J2EE的整個發展歷程中,現在正是一個非常時刻。從很多方面來說,J2EE都是一個偉大的成功:它成功地在從前沒有標準的地方建立了標準;大大提升了企業級軟件的開放程度,并且得到了整個行業和開發者的廣泛認可。然而,J2EE在一些方面已經開始捉襟見肘。J2EE應用開發的成本通常很高。J2EE應用項目至少和從前的非J2EE項目一樣容易失敗——如果不是更容易失敗的話。這樣的失敗率高得讓人難以接受。在這樣的失敗率之下,軟件開發幾乎變成了碰運氣。而在J2EE遭遇失敗的場景中,EJB通常都扮演著重要的角色。因此,J2EE社群不斷地向著更簡單的解決方案、更少使用EJB的方向發展[1]。然而,每個應用程序都需要一些基礎設施,拒絕使用EJB并不意味著拒絕EJB所采用的基礎設施解決方案。那么,如何利用現有的框架來提供這些基礎設施服務呢,伴隨著這個問題的提出,一個輕量級的J2EE解決方案出現了,這就是Spring Framework。

            Spring是為簡化企業級系統開發而誕生的,Spring框架為J2EE應用常見的問題提供了簡單、有效的解決方案,使用Spring,你可以用簡單的POJO(Plain Old Java Object)來實現那些以前只有EJB才能實現的功能。這樣不只是能簡化服務器端開發,任何Java系統開發都能從Spring的簡單、可測試和松耦合特征中受益。可以簡單的說,Spring是一個輕量級的反向控制(IoC)和面向切面編程(AOP)容器框架[3]。Spring IoC,借助于依賴注入設計模式,使得開發者不用理會對象自身的生命周期及其關系,而且能夠改善開發者對J2EE模式的使用;Spring AOP,借助于Spring實現的攔截器,開發者能夠實現以聲明的方式使用企業級服務,比如安全性服務、事務服務等。Spring IoC和 Spring ; AOP組合,一起形成了Spring,這樣一個有機整體,使得構建輕量級的J2EE架構成為可能,而且事實證明,非常有效。沒有Spring IoC的Spring AOP是不完善的,沒有Spring AOP的Spring IoC是不健壯的。本文是以Spring架構的成功的實際商務系統項目為背景,闡述了反向控制原理和面向切面的編程技術在Spring框架中的應用,同時抽取適量代碼示意具體應用,并和傳統開發模式進行對比,展示了Spring framework的簡單,高效,可維護等優點。

            1、Spring IoC 1.1 反向控制原理

            反向控制是Spring框架的核心。但是,反向控制是什么意思?到底控制的什么方面被反向了呢?2004年美國專家Martin Fowler發表了一篇論文《Inversion of Control Containers and the Dependency Injection pattern》闡述了這個問題,他總結說是獲得依賴對象的方式反向了,根據這個啟示,他還為反向控制提出了一個更貼切的名字:Dependency Injection(DI 依賴注入)。

            通常,應用代碼需要告知容器或框架,讓它們找到自身所需要的類,然后再由應用代碼創建待使用的對象實例。因此,應用代碼在使用實例之前,需要創建對象實例。然而,IoC模式中,創建對象實例的任務交給IoC容器或框架(實現了IoC設計模式的框架也被稱為IoC容器),使得應用代碼只需要直接使用實例,這就是IoC。相對IoC 而言,“依賴注入”的確更加準確的描述了這種設計理念。所謂依賴注入,即組件之間的依賴關系由容器在運行期決定,形象的來說,即由容器動態的將某種依賴關系注入到組件之中。

            1.2 IoC在Spring中的實現

            任何重要的系統都需要至少兩個相互合作的類來完成業務邏輯。通常,每個對象都要自己負責得到它的合作(依賴)對象。你會發現,這樣會導致代碼耦合度高而且難于測試。使用IoC,對象的依賴都是在對象創建時由負責協調系統中各個對象的外部實體提供的,這樣使軟件組件松散連接成為可能。下面示意了Spring IoC 應用,步驟如下:

            (1)定義Action接口,并為其定義一個execute方法,以完成目標邏輯。多年前,GoF在《Design Pattern:Elements of Reusable Object-Oriented Software》一書中提出“Programming to an Interface,not an implementation”的原則,這里首先將業務對象抽象成接口,正是為了實施這個原則。

            (2)類UpperAction實現Action接口,在此類中,定義一個String型的域message,并提供相應的setter和getter方法,實現的execute方法如下:

          public String execute (String str) {
           return (getMessage () + str).toUpperCase () ;
          }

            (3)編寫Spring配置文件(bean.xml)

          <beans>
          <bean id="TheAction" class="net.chen.spring.qs.UpperAction">
          <property name="message">
          <value>HeLLo</value>
          </property>
          </bean>
          </beans>

            (4)測試代碼

          public void testQuickStart () {
           ApplicationContext ctx=new
           FileSystemXmlApplicationContext ("bean.xml");
           Action a= (Action) ctx.getBean ("TheAction");
           System.out.println (a. execute ("Rod Johnson"));
          }

            上面的測試代碼中,我們根據"bean.xml"創建了一個ApplicationContext實例,并從此實例中獲取我們所需的Action實現,運行測試代碼,我們看到控制臺輸出:

          ……
          HELLO ROD JOHNSON

            仔細觀察一下上面的代碼,可以看到:

            (1)我們的組件并不需要實現框架指定的接口,因此可以輕松的將組件從Spring中脫離,甚至不需要任何修改,這在基于EJB框架實現的應用中是難以想象的。

            (2)組件間的依賴關系減少,極大改善了代碼的可重用性。Spring的依賴注入機制,可以在運行期為組件配置所需資源,而無需在編寫組件代碼時就加以指定,從而在相當程度上降低了組件之間的耦合。

            Spring給我們帶來了如此這般的好處,那么,反過來,讓我們試想一下,如果不使用Spring框架,回到我們傳統的編碼模式,情況會是怎樣呢?

            首先,我們必須編寫一個配置文件讀取類,以實現Message屬性的可配置化。

            其次,得有一個Factory模式的實現,并結合配置文件的讀寫完成Action的動態加載。于是,我們實現了一個ActionFactory來實現這個功能:

          public class ActionFactory {
           public static Action getAction (String actionName) {Properties pro = new Properties ();
           try {
            pro.load (new FileInputStream ("config.properties"));
            String actionImplName =(String)pro.get(actionName);
            String actionMessage =(String) pro.get (actionName+"_msg");
            Object obj =Class.forName (actionImplName).newInstance ();
            BeanUtils.setProperty(obj,"message",actionMessage);
            return (Action) obj;
           } catch (FileNotFoundException e) {
            ……
           }
          }

            配置文件則采用properties文件形式如下所示:

          TheAction=net.chen.spring.qs.UpperAction
          TheAction_msg=HeLLo

            測試代碼也作相應修改。現在不論實現的好壞,總之通過上面新增的多行代碼,終于實現了類似的功能。如果現在有了一個新的需求,這樣這個ActionFactory每次都新建一個類的實例,顯然這對系統性能不利,考慮到我們的兩個Action都是線程安全的,修改一下ActionFactory,保持系統中只有一個Action實例供其它線程調用。另外Action對象創建后,需要做一些初始化工作。修改一下ActionFactory,使其在創建Action實例之后,隨即就調用Action.init方法執行初始化。Action的處理這樣就差不多了。下面我們來看看另外一個Factory

            ……

            往往這些系統開發中最常見的需求,會導致我們的代碼迅速膨脹,而Spring IoC的出現,則大大緩解了這樣的窘境。通過以上實例,可以看出,Spring IoC為我們提供了如下幾方面的優勢:

            (1)應用組件不需要在運行時尋找其協作者,因此更易于開發和編寫應用;

            (2)由于借助于IoC容器管理組件的依賴關系,使得應用的單元測試和集成測試更利于展開;

            (3)通常,在借助于IoC容器關系業務對象的前提下,很少需要使用具體IoC容器提供的API,這使得集成現有的遺留應用成為可能。

            因此,通過使用IoC能夠降低組件之間的耦合度,最終,能夠提高類的重用性,利于測試,而且更利于整個產品或系統集成和配置。
          2、Spring AOP

            2.1 面向切面編程基礎

            通常,系統由很多組件組成,每個組件負責一部分功能,然而,這些組件也經常帶有一些除了核心功能之外的附帶功能 。系統服務如日志、事務管理和安全經常融入到一些其他功能模塊中。這些系統服務通常叫做交叉業務,這是因為它們總是分布在系統的很多組件中。通過將這些業務分布在多個組件中,給我們的代碼引入了雙重復雜性。

            (1) 實現系統級業務的代碼在多個組件中復制。這意味著如果你要改變這些業務邏輯,你就必須到各個模塊去修改。就算把這些業務抽象成一個獨立模塊,其它模塊只是調用它的一個方法,但是這個方法調用也還是分布在很多地方。

            (2) 組件會因為那些與自己核心業務無關的代碼變得雜亂。一個向地址錄中添加條目的方法應該只關心如何添加地址,而不是關心它是不是安全或支持事務的。

            此時,我們該怎么辦呢?這正是AOP用得著的地方。AOP幫助我們將這些服務模塊化,并把它們聲明式地應用在需要它們的地方,使得這些組件更加專注于自身業務,完全不知道其它涉及到的系統服務。

            這里的概念切面,就是我們要實現的交叉功能,是應用系統模塊化的一個方面或領域。切面的最常見例子就是日志記錄。日志記錄在系統中到處需要用到,利用繼承來重用日志模塊是不合適的,這樣,就可以創建一個日志記錄切面,并且使用AOP在系統中應用。下圖展示了切面應用方式


          圖表 1 應用切面

            其中,通知Advice是切面的實際實現。連接點Joinpoint是應用程序執行過程中插入切面的地點,這個地點可以是方法調用,異常拋出,甚至可以是要修改的字段,切面代碼在這些地方插入到你的應用流程中,添加新的行為。切入點Pointcut定義了Advice應該應用在那些連接點,通常通過指定類名和方法名,或者匹配類名和方法名式樣的正則表達式來指定切入點。

            2.2 AOP在Spring中的實現

            基于AOP,業界存在各種各樣的AOP實現,比如,JBoss AOP、Spring AOP、AspectJ、Aspect Werkz等。各自實現的功能也不一樣。AOP實現的強弱在很大程度上取決于連接點模型。目前,Spring只支持方法級的連接點。這和一些其他AOP框架不一樣,如AspectJ和JBoss,它們還提供了屬性接入點,這樣可以防止你創建特別細致的通知,如對更新對象屬性值進行攔截。然而,由于Spring關注于提供一個實現J2EE服務的框架,所以方法攔截可以滿足大部分要求,而且Spring的觀點是屬性攔截破壞了封裝,讓Advice觸發在屬性值改變而不是方法調用上無疑是破壞了這個概念。

            Spring的AOP框架的關鍵點如下:

            (1)Spring實現了AOP聯盟接口。在Spring AOP中,存在如下幾種通知(Advice)類型

            Before通知:在目標方法被調用前調用,涉及接口org.springframework.aop.MethodBeforeAdvice;

            After通知:在目標方法被調用后調用,涉及接口為org.springframework.aop.AfterReturningAdvice;

            Throws通知:目標方法拋出異常時調用,涉及接口org.springframework.aop.MethodBeforeAdvice;

            Around通知:攔截對目標對象方法調用,涉及接口為org.aopalliance.intercept.MethodInterceptor。

            (2)用java編寫Spring通知,并在Spring的配置文件中,定義在什么地方應用通知的切入點。

            (3)Spring的運行時通知對象。代理Bean只有在第一次被應用系統需要的時候才被創建。如果你使用的是ApplicationContext,代理對象在BeanFactory載入所有Bean的時候被創建。Spring有兩種代理創建方式。如果目標對象實現了一個或多個接口暴露的方法,Spring將使用JDK的java.lang.reflect.Proxy類創建代理。這個類讓Spring動態產生一個新的類,它實現所需的接口,織入了通知,并且代理對目標對象的所有請求。如果目標對象沒有實現任何接口,Spring使用CGLIB庫生成目標對象的子類。在創建這個子類的時候,Spring將通知織入,并且將對目標對象的調用委托給這個子類。此時,需要將Spring發行包lib/cglib目錄下的JAR文件發布到應用系統中。

            2.3 Spring AOP的優勢

            借助于Spring AOP,Spring IoC能夠很方便的使用到非常健壯、靈活的企業級服務,是因為Spring AOP能夠提供如下幾方面的優勢:

            (1)允許開發者使用聲明式企業服務,比如事務服務、安全性服務;EJB開發者都知道,EJB組件能夠使用J2EE容器提供的聲明式服務,但是這些服務要借助于EJB容器,而Spring AOP卻不需要EJB容器,借助于Spring的事務抽象框架就可以這些服務。

            (2)開發者可以開發滿足業務需求的自定義切面;

            (3)開發Spring AOP Advice很方便。因為這些AOP Advice僅是POJO類,借助于Spring提供的ProxyFactoryBean,能夠快速的搭建Spring AOP Advice。

            3、結語

            本文詳細闡述了Spring背后的IoC原理和AOP技術,以實際成功項目為背景,抽取簡短片斷,展示了Spring架構J2EE應用系統的原理。Spring IoC借助于依賴注入機制,減輕了組件之間的依賴關系,同時也大大提高了組件的可移植性,組件得到了更多的重用機會。借助于Spring AOP,使得開發者能聲明式、基于元數據訪問企業級服務,AOP合理補充了OOP技術,Spring AOP合理地補充了Spring IoC容器。Spring IoC與Spring AOP組合,使得Spring成為成功的J2EE架構框架,并能與標準的EJB等標準對抗,EJB不再是必需品。Spring已經沖入了J2EE的核心,將引領整個J2EE開發、架構的方向。

          posted @ 2008-08-25 18:08 金家寶 閱讀(234) | 評論 (0)編輯 收藏

          GoF設計模式

          著名的EJB領域頂尖的專家Richard Monson-Haefel在其個人網站:www.EJBNow.com中極力推薦的GoF的《設計模式》,原文如下:

          Design Patterns
          Most developers claim to experience an epiphany reading this book. If you've never read the Design Patterns book then you have suffered a very serious gap in your programming education that should be remedied immediately.

          翻譯: 很多程序員在讀完這本書,宣布自己相當于經歷了一次"主顯節"(紀念那穌降生和受洗的雙重節日),如果你從來沒有讀過這本書,你會在你的程序教育生涯里存在一個嚴重裂溝,所以你應該立即挽救彌補!

          可以這么說:GoF設計模式是程序員真正掌握面向對象核心思想的必修課。雖然你可能已經通過了SUN的很多令人炫目的技術認證,但是如果你沒有學習掌握GoF設計模式,只能說明你還是一個技工。

          在瀏覽《Thingking in Java》(第一版)時,你是不是覺得好象這還是一本Java基礎語言書籍?但又不純粹是,因為這本書的作者將面向對象的思想巧妙的融合在Java的具體技術上,潛移默化的讓你感覺到了一種新的語言和新的思想方式的誕生。

          但是讀完這本書,你對書中這些蘊含的思想也許需要一種更明晰更系統更透徹的了解和掌握,那么你就需要研讀GoF的《設計模式》了。

          《Thingking in Java》(第一版中文)是這樣描述設計模式的:他在由Gamma, Helm和Johnson Vlissides簡稱Gang of Four(四人幫),縮寫GoF編著的《Design Patterns》一書中被定義成一個“里程碑”。事實上,那本書現在已成為幾乎所有OOP(面向對象程序設計)程序員都必備的參考書。(在國外是如此)。

          GoF的《設計模式》是所有面向對象語言(C++ Java C#)的基礎,只不過不同的語言將之實現得更方便地使用。

          GOF的設計模式是一座"橋"
          就Java語言體系來說,GOF的設計模式是Java基礎知識和J2EE框架知識之間一座隱性的"橋"。

          會Java的人越來越多,但是一直徘徊在語言層次的程序員不在少數,真正掌握Java中接口或抽象類的應用不是很多,大家經常以那些技術只適合大型項目為由,避開或忽略它們,實際中,Java的接口或抽象類是真正體現Java思想的核心所在,這些你都將在GoF的設計模式里領略到它們變幻無窮的魔力。

          GoF的設計模式表面上好象也是一種具體的"技術",而且新的設計模式不斷在出現,設計模式自有其自己的發展軌道,而這些好象和J2EE .Net等技術也無關!

          實際上,GoF的設計模式并不是一種具體"技術",它講述的是思想,它不僅僅展示了接口或抽象類在實際案例中的靈活應用和智慧,讓你能夠真正掌握接口或抽象類的應用,從而在原來的Java語言基礎上躍進一步,更重要的是,GoF的設計模式反復向你強調一個宗旨:要讓你的程序盡可能的可重用。

          這其實在向一個極限挑戰:軟件需求變幻無窮,計劃沒有變化快,但是我們還是要尋找出不變的東西,并將它和變化的東西分離開來,這需要非常的智慧和經驗。

          而GoF的設計模式是在這方面開始探索的一塊里程碑。

          J2EE等屬于一種框架軟件,什么是框架軟件?它不同于我們以前接觸的Java API等,那些屬于Toolkist(工具箱),它不再被動的被使用,被調用,而是深刻的介入到一個領域中去,J2EE等框架軟件設計的目的是將一個領域中不變的東西先定義好,比如整體結構和一些主要職責(如數據庫操作 事務跟蹤 安全等),剩余的就是變化的東西,針對這個領域中具體應用產生的具體不同的變化需求,而這些變化東西就是J2EE程序員所要做的。

          由此可見,設計模式和J2EE在思想和動機上是一脈相承,只不過

          1.設計模式更抽象,J2EE是具體的產品代碼,我們可以接觸到,而設計模式在對每個應用時才會產生具體代碼。

          2.設計模式是比J2EE等框架軟件更小的體系結構,J2EE中許多具體程序都是應用設計模式來完成的,當你深入到J2EE的內部代碼研究時,這點尤其明顯,因此,如果你不具備設計模式的基礎知識(GoF的設計模式),你很難快速的理解J2EE。不能理解J2EE,如何能靈活應用?

          3.J2EE只是適合企業計算應用的框架軟件,但是GoF的設計模式幾乎可以用于任何應用!因此GoF的設計模式應該是J2EE的重要理論基礎之一。

          所以說,GoF的設計模式是Java基礎知識和J2EE框架知識之間一座隱性的"橋"。為什么說隱性的?

          GOF的設計模式是一座隱性的"橋"
          因為很多人沒有注意到這點,學完Java基礎語言就直接去學J2EE,有的甚至鴨子趕架,直接使用起Weblogic等具體J2EE軟件,一段時間下來,發現不過如此,挺簡單好用,但是你真正理解J2EE了嗎?你在具體案例中的應用是否也是在延伸J2EE的思想?

          如果你不能很好的延伸J2EE的思想,那你豈非是大炮轟蚊子,認識到J2EE不是適合所有場合的人至少是明智的,但我們更需要將J2EE用對地方,那么只有理解J2EE此類框架軟件的精髓,那么你才能真正靈活應用Java解決你的問題,甚至構架出你自己企業的框架來。(我們不能總是使用別人設定好的框架,為什么不能有我們自己的框架?)

          因此,首先你必須掌握GoF的設計模式。雖然它是隱性,但不是可以越過的。

          ?

          關于本站“設計模式”

          Java提供了豐富的API,同時又有強大的數據庫系統作底層支持,那么我們的編程似乎變成了類似積木的簡單"拼湊"和調用,甚至有人提倡"藍領程序員",這些都是對現代編程技術的不了解所至.

          在真正可復用的面向對象編程中,GoF的《設計模式》為我們提供了一套可復用的面向對象技術,再配合Refactoring(重構方法),所以很少存在簡單重復的工作,加上Java代碼的精煉性和面向對象純潔性(設計模式是java的靈魂),編程工作將變成一個讓你時刻體驗創造快感的激動人心的過程.

          為能和大家能共同探討"設計模式",我將自己在學習中的心得寫下來,只是想幫助更多人更容易理解GoF的《設計模式》。由于原著都是以C++為例, 以Java為例的設計模式基本又都以圖形應用為例,而我們更關心Java在中間件等服務器方面的應用,因此,本站所有實例都是非圖形應用,并且順帶剖析Jive論壇系統.同時為降低理解難度,盡量避免使用UML圖.

          如果你有一定的面向對象編程經驗,你會發現其中某些設計模式你已經無意識的使用過了;如果你是一個新手,那么從開始就培養自己良好的編程習慣(讓你的的程序使用通用的模式,便于他人理解;讓你自己減少重復性的編程工作),這無疑是成為一個優秀程序員的必備條件.

          整個設計模式貫穿一個原理:面對接口編程,而不是面對實現.目標原則是:降低耦合,增強靈活性.

          posted @ 2008-07-07 11:05 金家寶 閱讀(279) | 評論 (0)編輯 收藏

          什么是MIS

          所謂MIS(管理信息系統--Management Information System)系統,是一個由人、計算機及其他外圍設備等組成的能進行信息的收集、傳遞、存貯、加工、維護和使用的系統。它是一門新興的科學,其主要任務是最大限度的利用現代計算機及網絡通訊技術加強企業的信息管理,通過對企業擁有的人力、物力、財力、設備、技術等資源的調查了解,建立正確的數據,加工處理并編制成各種信息資料及時提供給管理人員,以便進行正確的決策,不斷提高企業的管理水平和經濟效益。目前,企業的計算機網絡已成為企業進行技術改造及提高企業管理水平的重要手段。隨著我國與世界信息高速公路的接軌,企業通過計算機網絡獲得信息必將為企業帶來巨大的經濟效益和社會效益,企業的辦公及管理都將朝著高效、快速、無紙化的方向發展。MIS系統通常用于系統決策,例如,可以利用MIS系統找出目前迫切需要解決的問題,并將信息及時反饋給上層管理人員,使他們了解當前工作發展的進展或不足。換句話說,MIS系統的最終目的是使管理人員及時了解公司現狀,把握將來的發展路徑。

            一個完整的MIS應包括:輔助決策系統(DSS)、工業控制系統(IPC)、辦公自動化系統(OA)以及數據庫、模型庫、方法庫、知識庫和與上級機關及外界交換信息的接口。其中,特別是辦公自動化系統(OA)、與上級機關及外界交換信息等都離不開Intranet的應用。可以這樣說,現代企業MIS不能沒有 Intranet,但Intranet的建立又必須依賴于MIS的體系結構和軟硬件環境。

            傳統的MIS系統的核心是CS (Client/Server——客戶端/服務器)架構,而基于Internet的MIS系統的核心是BS(Browser/Server——瀏覽器/服務器)架構。BS架構比起CS架構有著很大的優越性,傳統的MIS系統依賴于專門的操作環境,這意味著操作者的活動空間受到極大限制;而BS架構則不需要專門的操作環境,在任何地方,只要能上網,就能夠操作MIS系統,這其中的優劣差別是不言而喻的。

            基于Internet上的 MIS系統是對傳統MIS系統概念上的擴展,它不僅可以用于高層決策,而且可以用于進行普通的商務管理。通過用戶的具名登錄(或匿名登錄),以及相應的權限控制,可以實現在遠端對系統的瀏覽、查詢、控制和審閱。隨著Internet的擴展,現有的公司和學校不再局限于物理的有形的真實的地域,網絡本身成為事實上發展的空間。基于Internet上的MIS系統,彌補了傳統MIS系統的不足,充分體現了現代網絡時代的特點。隨著Internet技術的高速發展,因特網必將成為人類新社會的技術基石。基于Internet的MIS系統必將成為網絡時代的新一代管理信息系統,前景極為樂觀

          posted @ 2008-07-05 19:20 金家寶 閱讀(221) | 評論 (0)編輯 收藏

          開發者版本:你屬于哪個版本的程序員?

          國外開發者博客中有一篇有趣的文章,將程序員按水平像軟件版本號那樣劃分為不同的版本。相對于在招聘時分為初級,中級,高級程序員,直接表明需要某種語言N版本的程序員或許更方便直接。根據作者的觀點,可將WEB開發者大致分為以下幾個版本:

          Alpha:閱讀過一些專業書籍,大多數能用Dreamweaver或者FrontPage幫朋友制作一些Web頁面。但在他們熟練掌握HTML代碼以前,你大概不會雇傭他們成為職業的WEB制作人員。

          Beta:已經比較擅長整合站點頁面了,在HTML技巧方面也有一定造詣,但還是用Tables來制作頁面,不了解CSS,在面對動態頁面或數據庫連接時還是底氣不足。

          Pre Version 1 (0.1):比Beta版的開發者水平要高。熟悉HTML,開始了解CSS是如何運作的,懂一點JavaScript,但還是基于業余水準,逐步開始關心動態站點搭建和數據庫連接的知識。這個版本的WEB開發人員還遠不能成為雇主眼中的香餑餑。

          1.0: 能夠基本把控整個站點開發,針對每個問題盡可能的找到最直接的解決辦法。但對可測性,可擴展性以及在不同(層)框架下如何選擇最合適的WEB設計工具尚無概念。這個版本的WEB開發者有良好的技術基礎,需要有進一步的幫助和指導。



          2.0:懂面向對象的編程語言,理解分層開發的必要性,關注代碼分離,對問題尋找更完美的解決方法,偶然也會考慮設計模式的問題,但對此仍然概念不清。屬于優秀的初級開發者,能完成較松散的代碼開發(相對大型嚴謹的站點開發而言),在面對較復雜問題尋找解決辦法時需要周邊人的幫助。

          3.0:開始較為深入的理解面向對象編程和設計模式,了解他們的用途,當看到好的設計模式時能看透其本質,逐步關注分層的架構解決辦法和可測試性。理解不同的開發語言并能說出他們的異同(例如各自的優勢)。屬于優秀的中級別開發者,雇主也確信他們最終能找到問題的解決辦法,這個版本的人可以給1.0和2.0的開發者以指導。但他們對架構的理解仍然不夠清晰,值得一提的是,只要給予一些指導,他們能很快理解并熟記做出的決定,以及選定方案的優勢所在。

          4.0:
          理解模式,重視用戶的反饋。著手研究方法論,架構設計和軟件開發的最佳入口。頭腦中已經形成了超越開發語言,技術架構的整體方案,可根據需求解構程序。能從理論的角度,不同模式如何融合成最佳形態,將多種X-驅動的模式應用到不同的方案中。是精通多語言的高手,理解不同系統和方法論的細微差別,屬于高級程序員。這個級別的人能夠輕易的輔導2.0和3.0的程序員,將他們推向更高的級別。

          5.0:從系統的角度考慮問題。對各種系統結構有深入研究,能對整個代碼架構中的問題進行改進。在團隊粘合性以及代碼安全性方面有杰出貢獻。對1.0到4.0版本的開發人員出現的問題能及時察覺,讓整個團隊保持積極性且保持興奮的狀態創建軟件解決辦法。舉例來說,他們總是對新的技術和信息保持饑渴狀態,試圖用最簡便的方案解決開發任務。在整個IT團隊中獲得信任,屬于高級程序員和架構師。

          那么,您屬于哪個版本的程序員呢?

          posted @ 2008-06-27 10:47 金家寶 閱讀(245) | 評論 (0)編輯 收藏

          Eclipse官方網站已經正式宣布 Eclipse 3.4發布

          感謝Wendal,匿名人士的投遞
          Eclipse官方網站已經正式宣布 Eclipse 3.4發布,代號為ganymede (Ganymede (英語發音"GAN uh meed")為最大的木星已知衛星,也是第七顆發現的木星衛星,在伽利略發現的衛星中離木星第三近,在希臘神話中 Ganymede是一個特洛伊美人的男孩(一個美少男),被宙斯帶去給眾神斟酒)。

          ?

          關注Eclipse項目的開發者朋友們可以下載3.4版本嘗試一下,在JavaEye上還專門介紹一個很酷的Eclipse3.4帶的實時結對編程插件

          目前3.4版本是Eclipse項目發布的10周年慶典版;至今Eclipse項目共有23個子項目。此次發布的Ganymede 版本引入不少亮點,其中包括新的p2平臺(provisioning platform),點擊查看p2的介紹、新增的Equinox(OSGi實現)安全方面的特性、全新的Ecore建模工具、支持SOA等。

          posted @ 2008-06-26 09:01 金家寶 閱讀(815) | 評論 (1)編輯 收藏

          [轉]Java語言的細節

          Java作為一門優秀的面向對象的程序設計語言,正在被越來越多的人使用。本文試圖列出作者在實際開發中碰到的一些Java語言的容易被人忽視的細節,希望能給正在學習Java語言的人有所幫助。
          ?
          1,拓寬數值類型會造成精度丟失嗎?
          ??? Java語言的8種基本數據類型中7種都可以看作是數值類型,我們知道對于數值類型的轉換有一個規律:從窄范圍轉化成寬范圍能夠自動類型轉換,反之則必須強制轉換。請看下圖:
          byte-->short-->int-->long-->float-->double
          char-->int
          我們把順箭頭方向的轉化叫做拓寬類型,逆箭頭方向的轉化叫做窄化類型。一般我們認為因為順箭頭方向的轉化不會有數據和精度的丟失,所以Java語言允許自動轉化,而逆箭頭方向的轉化可能會造成數據和精度的丟失,所以Java語言要求程序員在程序中明確這種轉化,也就是強制轉換。那么拓寬類型就一定不會造成數據和精度丟失嗎?請看下面代碼:
          int i=2000000000;
          int num=0;
          for(float f=i;f<i+50;f++){
          ??? num++;
          }
          System.out.println(num);
          請考察以上代碼輸出多少?
          如果你回答50 ,那么請運行一下,結果會讓你大吃一驚!沒錯,輸出結果是0,難道這個循環根本就沒有執行哪怕一次?確實如此,如果你還不死心,我帶你看一個更詫異的現象,運行以下代碼,看輸出什么?
          int i=2000000000;
          float f1=i;
          float f2=i+50;
          System.out.println(f1==f2);
          ??? 哈哈,你快要不相信你的眼睛了,結果竟然是true;難道f1和f2是相等的嗎?是的,就是這樣,這也就能解釋為什么上一段代碼輸出的結果是0,而不是50了。那為什么會這樣呢?關鍵原因在于你將int值自動提升為float時發生了數據精度的丟失,i的初始值是2000000000,這個值非常接近Integer.MAX_VALUE,因此需要用31位來精確表示,而float只能提供24位數據的精度(另外8位是存儲位權,見IEEE745浮點數存儲規則)。所以在這種自動轉化的過程中,系統會將31位數據的前24位保留下來,而舍棄掉最右邊的7位,所以不管是2000000000還是2000000050,舍棄掉最右邊7位后得到的值是一樣的。這就是為什么f1==f2的原因了。
          ??? 類似的這種數值拓寬類型的過程中會造成精度丟失的還有兩種情況,那就是long轉化成float和long轉化成double,所以在使用的時候一定要小心。
          ?
          2,i=i+1和i+=1完全等價嗎?
          ??? 可能有很多程序員認為i+=1只是i=i+1的簡寫方式,其實不然,它們一個使用簡單賦值運算,一個使用復合賦值運算,而簡單賦值運算和復合賦值運算的最大差別就在于:復合賦值運算符會自動地將運算結果轉型為其左操作數的類型。看看以下的兩種寫法,你就知道它們的差別在哪兒了:
          ? (1) byte i=5;
          ????? i+=1;
          ? (2) byte i=5;
          ????? i=i+1;
          ??? 第一種寫法編譯沒問題,而第二種寫法卻編譯通不過。原因就在于,當使用復合賦值運算符進行操作時,即使右邊算出的結果是int類型,系統也會將其值轉化為左邊的byte類型,而使用簡單賦值運算時沒有這樣的優待,系統會認為將i+1的值賦給i是將int類型賦給byte,所以要求強制轉換。理解了這一點后,我們再來看一個例子:
          ? byte b=120;
          ? b+=20;
          ? System.out.println("b="+b);
          ? 說到這里你應該明白了,上例中輸出b的值不是140,而是-116。因為120+20的值已經超出了一個byte表示的范圍,而當我們使用復合賦值運算時系統會自動作類型的轉化,將140強轉成byte,所以得到是-116。由此可見,在使用復合賦值運算符時還得小心,因為這種類型轉換是在不知不覺中進行的,所以得到的結果就有可能和你的預想不一樣。
          ?
          3,位移運算越界怎么處理
          ??? 考察下面的代碼輸出結果是多少?
          ??? int a=5;
          ??? System.out.println(a<<33);
          ??? 按照常理推測,把a左移33位應該將a的所有有效位都移出去了,那剩下的都是零啊,所以輸出結果應該是0才對啊,可是執行后發現輸出結果是10,為什么呢?因為Java語言對位移運算作了優化處理,Java語言對a<<b轉化為a<<(b%32)來處理,所以當要移位的位數b超過32時,實際上移位的位數是b%32的值,那么上面的代碼中a<<33相當于a<<1,所以輸出結果是10。
          ?
          4,判斷奇數
          ? 以下的方法判斷某個整數是否是奇數,考察是否正確:
          ?? public boolean isOdd(int n){
          ?????? return (n%2==1);
          ?? }
          ?? 很多人認為上面的代碼沒問題,但實際上這段代碼隱藏著一個非常大的BUG,當n的值是正整數時,以上的代碼能夠得到正確結果,但當n的值是負整數時,以上方法不能做出正確判斷。例如,當n=-3時,以上方法返回false。因為根據Java語言規范的定義,Java語言里的求余運算符(%)得到的結果與運算符左邊的值符號相同,所以,-3%2的結果是-1,而不是1。那么上面的方法正確的寫法應該是:
          ?? public boolean isOdd(int n){
          ?????? return (n%2!=0);
          ?? }
          ?
          5,可以讓i!=i嗎?
          在本題中,要求你聲明一個i值,使得以下程序輸出"No i!=i":
          //在此聲明i,并賦值。
          if(i==i){
          ????? System.out.println("Yes i==i");
          ? }else{
          ????? System.out.println("No i!=i");
          ? }
          ?
          ??? 當你看到這個命題的時候一定會以為我瘋了,或者Java語言瘋了。這看起來是絕對不可能的,一個數怎么可能不等于它自己呢?或許就真的是Java語言瘋了,不信請將i做出以下聲明,再運行上面的代碼。
          ? double i=0.0/0.0;
          ??? 上面的代碼輸出"No i!=i",為什么會這樣呢?關鍵在0.0/0.0這個值,在IEEE 754浮點算術規則里保留了一個特殊的值用來表示一個不是數字的數量。這個值就是NaN("Not a Number"的縮寫),對于所有沒有良好定義的浮點計算都將得到這個值,比如:0.0/0.0;其實我們還可以直接使用Double.NaN來得到這個值。在IEEE 754規范里面規定NaN不等于任何值,包括它自己。所以就有了i!=i的代碼。
          ?
          6,2.0-1.1==0.9嗎?
          ?考察下面的代碼:
          ?double a=2.0,b=1.1,c=0.9;
          ?if(a-b==c){
          ?? System.out.println("YES!");
          ?}else{
          ?? System.out.println("NO!");
          ?}
          以上代碼輸出的結果是多少呢?你認為是“YES!”嗎?那么,很遺憾的告訴你,不對,Java語言再一次欺騙了你,以上代碼會輸出“NO!”。為什么會這樣呢?其實這是由實型數據的存儲方式決定的。我們知道實型數據在內存空間中是近似存儲的,所以2.0-1.1的結果不是0.9,而是0.88888888889。所以在做實型數據是否相等的判斷時要非常的謹慎。一般來說,我們不建議在代碼中直接判斷兩個實型數據是否相等,如果一定要比較是否相等的話我們也采用以下方式來判斷:
          ? if(Math.abs(a-b)<1e-5){
          ???? //相等
          ? }else{
          ??? //不相等
          ? }
          上面的代碼判斷a與b之差的絕對值是否小于一個足夠小的數字,如果是,則認為a與b相等,否則,不相等。

          posted @ 2008-06-13 14:41 金家寶 閱讀(490) | 評論 (2)編輯 收藏

          WEB交互界面易用性設計和驗收的指導性原則

          隨著企業intranet和國際internet的迅速發展,越來越多的工作流程,商務交易,教育、培訓、會議和講座,以及個人消費娛樂都被轉移到所謂的萬維網(world wide web,以下簡稱web)上來了。與此相對應的是交互操作的復雜性越來越高。

          隨著browser rver模式的日漸流行,很多操作都是在瀏覽器環境下的網頁上完成的,并不是只有失效的鏈接和意外的出錯才會使操作者感到煩惱,即便是一次完整的成功操作過程,也可能因為操作的繁復性過高或者使用上的不方便而給操作者帶來不愉快的體驗。

          本文試圖闡述web交互頁面設計的一些指導性原則,這些原則有利于避免發生不愉快的操作體驗。這些原則是用戶友好性的,是在完成同一種操作要求下,使用戶最感到輕松、簡單、舒適的web交互界面設計原則。我們假定我們討論的web頁面都是功能正常的,符合美學觀點的。需要說明我們討論的原則可能會和設計上的美學觀點以及既有的功能設計有所沖突。如果發生這種情況,基于“實用的就是美的”觀點,我們會建議您酌情放棄原先的美學觀點與功能設計。

          一、輸入控件的自動聚焦和可用鍵盤切換輸入焦點

          ??? 使用javascript實現頁面加載完成后立即自動聚焦(focus)到第一個輸入控件。可用tab鍵(ie缺省實現)或方向鍵切換聚焦到下一個輸入控件。

          ??? 輸入控件指web頁面表單(<form> )中顯式的,需要用戶進行修改、編輯操作的表單元素。對于這些控件,如果沒有自動聚焦操作,不可避免的出現一次用戶鼠標定位操作(如果用戶此前處于鍵盤輸入操作狀態或鼠標定位后需要進行鍵盤輸入操作,實際上是鍵盤鼠標切換操作)。如果鼠標定位后需要進行鍵盤輸入操作,如果不能鍵盤切換輸入焦點,那么不可避免的在切換輸入焦點時需要反復的鍵盤鼠標切換操作,這是很繁瑣的。

          ??? 如果實現了頁面加載完成即自動聚焦到第一個輸入控件,并且可以鍵盤切換輸入焦點標定位操作,那么對于用戶來說整個頁面的輸入操作可能都不需要鼠標操作,或次數較少,這是一種便利。畢竟頻繁的鍵盤鼠標切換操作是比較累人的。

          ??? 對于有輸入欄的對話框或網頁,在不干預的情況下就應將當前控制焦點定位在待輸入的輸入欄上;如果輸入欄在一般情況下不需要更改其中的內容,則應直接將焦點定在“確定”按鈕上;在幾個輸入欄之間應支持tab,shift+tab切換操作,“確定”和“取消”應該是切換操作的終點,與具體所在位置無關。

          二、可用enter(或ctrl+enter)鍵提交,確保和點擊提交按鈕的效果是相同的

          不要在提交按鈕上加入onclick=”…”這樣的javascript代碼。

          ??? 用enter鍵提交頁面是原則1的自然延伸,而且這也是瀏覽器所缺省支持的。只所以單獨列出來是因為實際上有些設計者設計的頁面不能達到這種效果,結果導致使用enter鍵提交和點擊“確定”按鈕提交帶來的效果不一樣。大部分情況下是設計者在“確定”按鈕上加入了onclik=”…”這樣的代碼,通過點擊“確定”按鈕后,會執行一段javascript代碼,比如對某些hidden類型的input元素設值。而使用enter鍵提交時就不會執行這段代碼。

          ??? 正確的做法是把這段代碼移到表單標簽<form>中,以onsubmit=”…”屬性引入。

          ??? 對于<textarea>表單元素,它會消耗enter鍵,因此會使得enter鍵提交失效。可以引入javascript代碼捕捉ctrl+enter復合鍵,一旦捕捉到即執行表單的submit()方法。對于需要頻繁提交的場合,比如bbs上,這種代碼是很有必要的。

          三、鼠標動作提示和回應

          ??? 對用戶的鼠標定位操作,當移動到可響應的位置上時,應給予視覺或聽覺的提示。

          ??? 動作回應的最簡單形式就是鼠標icon變成手狀。瀏覽器只對具有href屬性的html標簽會自動進行這種變換icon的行為。對于沒有href屬性(或沒有設置href屬性)的標簽,可以通過javascript設置style屬性的cursor為hand。

          ??? 目標區域發生變化是更為主動的響應形式。當鼠標指針移到目標區域,此時指針圖形改變或文字顏色發生改變均能較大的減輕用戶搜索定位目標區域的注意力負擔。在按鈕上增添直觀的圖形,盡可能的增大按鈕面積;按鈕間保持適當的距離,太近增加了用戶區別它們之間界限以防誤操作的負擔,太遠增加了用戶搜索定位按鈕的負擔。

          四、盡可能早的在客戶端完成輸入數據合法性驗證

          ??? 輸入數據的合法性檢驗應該在客戶端使用javascript進行驗證。除非驗證只能在服務器端完成,否則驗證工作應在最早能完成的情況下進行。

          ??? 在客戶端完成數據合法性驗證,可以避免一次服務器請求和回復通訊,這種通訊是需要用戶等待的,如果用戶等待很長時間后從服務器返回的結果提示出現的錯誤是在輸入時即可發現的,那么這種設計就是不友好的。諸如密碼長度限制,用戶名允許字符限制等等,顯然應該在客戶端提交前就應該進行驗證。

          五、根據應用場景決定在表單頁面和提交后返回頁面間是否使用中間過渡頁面

          ??? 根據應用場景,決定是否顯示接收表單頁面(表單頁面和提交后返回頁面間的中間過渡頁面),以及使用何種方式顯示接收表單頁面。

          表單頁面和接收表單頁面是大部分web交互操作賴以實現的配合模式。關于表單頁面和接收表單頁面的相互關系的設計,要做如下幾個方面的考慮。

          1.對于需要頻繁操作的場合,從操作便利和快捷性出發,盡可能的減少服務器和客戶端交互次數,應該避免使用中間過渡頁面。提交完畢直接返回原來的表單頁面或默認頁面。在這種情況下要考慮到數據安全和可恢復性。

          如果因為用戶輸入的數據不合格,需要重新輸入,那么,去除中間頁面,把錯誤信息直接顯示在原表單頁面上的設計方式,將是最簡潔的處理方式。用戶只需要根據錯誤提示進行更正即可。當然這樣做稍微增加了編程負擔。在表單接收頁面上需要包含原表單頁面的內容,而且輸入數據項都必須用服務器端代碼或客戶端javascript設置成用戶輸入的值。為了開發快捷,可以這樣做:表單頁面和接收表單頁面用同一個服務器端腳本頁面實現。這個頁面按如下流程完成原來兩個頁面的工作:

          頁面腳本初始化

          檢查“提交”變量是否設置

          ┠已設置,做數據驗證

          ┃ ┠驗證通過->業務邏輯處理->使用包含頁面方式或重定向方式返回到特定頁面

          ┃ ┗驗證不通過->保存用戶輸入的數據->退出表單提交處理到表單頁面流程中

          ┗未設置,做表單頁面流程,如有來自提交流程中產生的用戶輸入數據,則顯示出來

          其中,使用包含頁面方式返回到特定頁面可以避免一次客戶端重定向過程,比客戶端重定向過程還要快捷和穩定一些。但是有些情況下因為代碼變量沖突或其他原因,使用包含頁面方式可能并不方便,這時候可以使用服務器端重定向技術,在asp里是server.transfer方法,在java servlet里是requestdispatcher.forward()方法。不要使用response.redirect或者httpservletresponse.sendredirect()這種客戶端http重定向方法。不使用中間過渡頁面也就意味著用戶不能后退瀏覽原先已經填好的表單頁面,因為使用的是同一個url。所以在驗證不通過情況下保存用戶輸入的數據就是必不可少的。

          不使用中間過渡頁面帶來的另一個問題就是使用包含頁面方式或服務器端重定向方式返回會使得url和頁面內容不能一一對應。對于用戶可能會直接用這個url(會收藏這個url)訪問返回頁面的情況,他會發現實際上到達的是表單頁面,不是他想要的那個返回結果頁面。所以,去除中間過渡頁面,確實會帶來url和內容含混不清的情況,因而不適合需要url和頁面內容一一對應的場合。

          2.從技術角度考慮,使用中間過渡頁面能保證url和頁面內容一一對應,簡化頁面開發工作。

          為了保證頁面內容總是和固定的url聯系起來,必須使用客戶端重定向:

          提交?? 業務邏輯處理 (中間過渡頁面)

          表單頁面――――->接收表單頁面―――――――――>顯示處理結果―――>客戶端重定向到特定頁面

          客戶端重定向分幾種情況:

          1.使用http header重定向,location: http://www.netall.com.cn,這種定向是最快的,在窗口一片空白的情況下就迅速訪問(get)另一個頁面。這種方式實際上不能顯示處理結果,只能說是向第一種快速重定向方式的一種折衷處理;

          2.html標簽刷新,<meta http-equiv=”refresh” content=”5;url=http://www.netall.com.cn”>,這種定向比較友好,在這個頁面加載完畢后訪問另一個頁面。很多設計者把這個作為一個技巧使用,在載入一個大頁面前放置一個緩沖頁面以避免用戶乏味的等待;

          3.javascript重定向。由于是用代碼控制重定向,可以做的更靈活。比如根據用戶習慣,控制操作完畢后的轉向流程。

          4.被動式的重定向。在頁面上放置按鈕或鏈接,由用戶手動決定返回到特定頁面。這種情況適合于處理結果的顯示頁面包含相當多的信息,需要用戶仔細瀏覽,而決定下一步的操作。

          ?? 在使用中間過渡頁面的情況下,不能再使用頁面過期失效了。否則一旦出現錯誤,需要用戶重新輸入表單數據,用戶就不能用后退按鈕恢復此前填寫的表單數據了。除非設計者有意禁止這種恢復。

          六、防止表單重復提交處理

          ??? 對提交按鈕點擊后做變灰處理避免在網絡響應較慢情況下用戶重復提交同一個表單。使用頁面過期失效避免用戶后退瀏覽重復提交表單。

          ??? 有些復雜的應用會導致需要較長時間的等待才會返回處理結果。而在較慢的網絡環境中,這種情況更是頻繁發生。焦急等待的用戶往往會重復點擊提交按鈕。這種情況是設計者所不希望看到的。

          ??? 使用javascript在點擊提交按鈕后使按鈕失效變灰是一個最直接的辦法(根據原則2這段代碼應該放在<form>標簽里onsubmit=”…”做)。此外,在表單頁面上,用服務器端腳本設置http header的expires為立即過期可以保證用戶沒辦法使用后退瀏覽恢復表單頁面。注意這樣做的代價可能是用戶辛辛苦苦填寫很長的內容,結果一旦操作失誤就沒法恢復。所以應該避免在包含<textarea>表單元素的頁面上使用頁面過期失效。

          ??? 應該說,更嚴格的方法是,服務器端腳本就應該具備抵抗重復提交的能力。例如,為這個表單分配一個唯一id或一個使用一次即失效的驗證碼。此外,這個表單處理還應具有事務性質,如果表單不被接受,所做的改變還是能恢復的。在金融應用場合,重復提交同一筆交易是肯定不被允許的。能在重復提交中獲利的一方總是會想辦法繞過瀏覽器的限制,所以不能依賴于客戶端的技術。

          七、頁面鏈接是打開新窗口、使用原窗口還是彈出窗口的原則

          ??? 一般而言,首頁上鏈接可以使用target=”_blank”屬性打開新窗口,而其他頁面上的鏈接都應使用原窗口或彈出窗口。如果鏈接頁面內容相對原頁面來說不重要,是附屬性質的,可以使用彈出窗口方式。

          ??? 一般情況下應該使用原窗口,把是否保留原窗口內容的權利留給用戶。除非設計者相信原頁面是如此重要,在用戶發出點擊指令后還有使用上的價值,以至于不能被隨便更新或覆蓋。一般來說,只有首頁才會處于這樣一個地位,用戶在首頁上打開一個鏈接后,一般還會在這個首頁上去打開另一個鏈接。比如首頁包含極多鏈接的門戶網站,或者搜索引擎的搜索結果頁面。google.com以前的搜索結果頁面上的鏈接是使用原窗口的,后來他們意識到用戶會反復使用這個頁面,而改成打開新窗口了。一般的網站如果首頁鏈接不多,就不必使用新窗口,這是用戶友好的設計原則。

          ??? 上述情形的一個極端情況就是新頁面內容比起原頁面內容的重要性差很多,以至于都未必需要打開一個新頁面。這時候使用彈出窗口比較合適。用javascript彈出窗口有好幾種:一個是window.open()函數。這里有個技巧。應該使用window.open()先打開一個空白窗口,再使用location.replace()用目標頁面替換。這樣做可以避免在打開新頁面的過程中導致原頁面失去響應。window.open()將打開一個新的瀏覽器窗口進程,因此資源消耗比較大。另一個是由微軟dynamichtml規范中擴充的方法createpopup()。createpopup()可以創建無邊框的彈出窗口,消耗系統資源較小。還有一個就是用頁面中隱藏的層<div>來模擬一個彈出頁面。后兩種可以使用javascript代碼填充彈出窗口內容。如果需要下載網頁作為其內容的話,需要微軟dynamichtml規范中的<download>標簽。

          八、盡可能少的排列可選項,盡可能少的安排操作步驟

          ??? 根據用戶操作習慣安排盡可能少的操作菜單選項,同時要保證盡可能少的操作步驟。 在不降低功能多樣性的前提下減少菜單項和操作步驟是用戶友好的設計。要做到這一點很不容易。要從用戶出發考慮他們最頻繁的操作是什么。正常情況下一個用戶需要的操作總可以歸類為5個以下的種類,如果出現更多的種類,那一定是沒有針對用戶興趣去區分主次。一個用戶同時有5個以上的強烈興趣中心是難以想像的,走馬觀花似的隨意點擊瀏覽的用戶,是不大可能在某個種類上進行深入的交互操作的。在這5個種類中,每個種類都可能有若干個可操作的二級種類。如果這些二級操作項是不可見的,那么意味著要做兩次選擇才能進入可操作頁面。這就違背了“盡可能少的安排操作步驟”這一原則。如果使用javascript制作二級菜單,避免請求服務器,會好一些。如果二級菜單項總共不超過20個左右,不妨將二級菜單直接顯示出來,比如放在左列一字向下排開,這樣只需要一次選擇到可操作項,更加明了方便。

          九、操作邏輯無漏洞,保證數據是操作安全的

          ??? 多個頁面間的操作和同個頁面上的多個操作間的邏輯關系在設計上是安全和嚴謹的。保證不會出現不被允許的用戶操作組合,至少不會因為用戶的不適當的操作導致出錯。

          ??? 這最典型的表現則是在頁面上廣泛采用的所謂聯動下拉框設計。一個下拉框中允許的選項受另一個下拉框中的選擇而變。另外一個例子是根據選擇使表單元素有效或者失效。如果在多個頁面間也要維持某種合法性邏輯,那么就需要服務器端腳本的參與。這樣會使表單設計跟操作有關,應該說這不是一個好的設計。可以通過變更操作步驟順序、組合方式來盡可能避免這種情況出現。

          ??? 操作邏輯的設計既要保證用戶任意的輸入不會導致錯誤,也要保證是用戶輸入的數據能購被安全處理。在session控制下的表單中輸入大幅文字可能會導致超時出錯,這時候往往還伴隨重定向過程,導致用戶的長篇輸入蕩然無存。用javascript提醒用戶已超時,請保存輸入后重新提交,是一個好辦法。某些表單元素如<input type=”text”>接受esc鍵清除數據,并且無法撤銷,這也是很危險的。在中文輸入法中常常使用esc鍵清楚輸入的碼位,一旦不小心多按一下esc就會使得輸入數據消失。因此有必要用javascript禁用<input>和<textarea>的esc鍵處理過程。(Edit From:Internet,By Aaron)

          posted @ 2008-05-29 22:35 金家寶 閱讀(584) | 評論 (0)編輯 收藏

          主站蜘蛛池模板: 海南省| 雷山县| 贞丰县| 卓尼县| 临澧县| 湾仔区| 宝山区| 余庆县| 辛集市| 玛纳斯县| 怀远县| 舒城县| 龙南县| 福鼎市| 新密市| 尚志市| 牙克石市| 嘉定区| 文化| 民勤县| 招远市| 望都县| 凤庆县| 连江县| 克什克腾旗| 华池县| 保康县| 聂拉木县| 唐河县| 楚雄市| 深水埗区| 泰和县| 罗定市| 中宁县| 怀安县| 南和县| 天镇县| 通河县| 北安市| 睢宁县| 望都县|