2008年4月16日 #

          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 金家寶 閱讀(364) | 評論 (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 金家寶 閱讀(325) | 評論 (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 金家寶 閱讀(2841) | 評論 (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 金家寶 閱讀(266) | 評論 (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 金家寶 閱讀(233) | 評論 (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)編輯 收藏

          JSP安全幾個小問題

          記得還是去年,剛到據說是高手云集的威威公司上班的時候,一個新到的同事給我講他花了半天的時間寫,并做了很長時間的實踐,寫了個關于攻擊.jsp頁面的程序。下面我把具體的實現過程和大家分享一下。測試平臺是Tomcat,當然,版本有點低,他的目的只是想證實一下他的某些想法。首先,他在Tomcat的WEB目錄下建立了一個Hello.jsp文件,內容是:

          <%out.print(hello);%>

          通過IE的正常請求地址為:http://localhost:8080/examples/jsp/hello.jsp,顯示結果為:hello。然后開始具體的攻擊測試。測試時,發出的請求地址為:http://localhost:8080/examples/jsp/////////hello.jsp?,瀏覽器上顯示編譯錯誤,錯誤的原因是500?java.lang.NullPointerException。這個應該是比較常見的錯誤了。現在,恢復正常的請求http://localhost:8080/examples/jsp/hello.jsp,問題就出現了,即出錯,而且所報的錯誤和剛才造成它錯誤的請求是一樣的:“500?java.lang.NullPointerException”。難道是緩存在瀏覽器里了嗎?換臺機器訪問http://192.168.10.188/examples/jsp/hello.jsp。問題依然如故,哎!可憐的Hello.jsp呀!

            雖然這個問題有些弱智,不過,他的目的也達到了,即找出“.jsp”流程中存在的一些問題。所以,JSP程序同ASP一樣,還是存在著很多安全上的問題的。因此,對于一心研究論壇或者其他安全信息的朋友來說,要想發現JSP的BUG,了解一些JSP的工作原理是十分重要的。

            需要指出的是,雖然是一門網絡編程語言,JSP和PHP、ASP的工作機制還存在很大的區別,首次調用JSP文件時,JSP頁面在執行時是編譯式,而不是解釋式的。首次調用JSP文件其實是執行一個編譯為Servlet的過程。當瀏覽器向服務器請求這一個JSP文件的時候,服務器將檢查自上次編譯后JSP文件是否有改變,如果沒有改變,就直接執行Servlet,而不用再重新編譯,這樣,工作效率得到了明顯提高。這也是目前JSP論壇開始逐漸風靡的一個重要原因。

            小提示:Servlet是用Java編寫的Server端程序,它與協議和平臺無關;Servlet運行于Java-enabled?WEB?Server中;Java?Servlet可以動態地擴展Server的能力,并采用請求-響應模式提供WEB服務;最早支持Servlet技術的是JavaSoft的Java?WEB?Server;Servlet的主要功能在于交互式地瀏覽和修改數據,生成動態WEB內容。

            說到這里,我們自然就會關心一些JSP的安全問題。一般來說,常見的JSP安全問題有源代碼暴露(包括程序源代碼以明文的方式返回給訪問者,如添加特殊后綴引起jsp源代碼暴露;插入特殊字符串引起Jsp源代碼暴露;路徑權限引起的文件Jsp源代碼暴露;文件不存在引起的絕對路徑暴露問題等)、遠程程序執行類、數據庫如SQL?Server、Oracle?、DB2等的漏洞,操作系統漏洞等。不過,為了突出Jsp的安全問題,本文將結合目前的一些比較流行的Jsp論壇分類闡述和提出解決的建議。為了講解方便,本文還采用一些公開了原代碼的論壇實例代碼,至于安裝軟件版本、操作系統等,可以查看安裝提示。

            論壇用戶管理缺陷

            為了加強實戰效果,我們可以到http://down.chinaz.com/S/5819.asp這個地址下載一個典型的論壇代碼,根據提示,數據源名稱為yyForum,用戶名為xyworker,密碼:999。到baidu、Google等網站搜索一下,我們可以看到,安裝這個代碼的論壇不少。仔細分析后,可以發現,用戶管理的頁面是user_manager.jsp文件。首先,我們看看這個系統是如何加強它的代碼安全性的。其中,在代碼的開始部分有一個if限制條件,代碼的第三行到第十行具體如下:

          <%
          if?((session.getValue(UserName)==null)||(session.getValue(UserClass)==null)||(!session.getValue(UserClass).equals(系統管理員)))

          %>

            其中,Session.getValue表示檢索出Session的值;sendRedirect()執行后,地址欄鏈接會改變,相當于客戶端又重新發了一個get請求,要服務器傳輸另一個文件過來。

            下面,我們再來看看修改用戶信息的文件modifyuser_manager.jsp。典型代碼如下:

          <%@page?contentType=text/html;?charset=gb2312?language=java?import=java.sql.*,java.util.*??%>
          <jsp:useBean?id=yy?scope=page?class=yy.jdbc/>
          <%!String?User_Name,User_Password,sql,?User_Sign;%>
          <%
          User_Name=request.getParameter(name);

          //out.println(User_Name);
          User_Password=request.getParameter(password);
          User_Password=yy.ex_chinese(User_Password);
          ……
          User_Sign=request.getParameter(sign);
          User_Sign=yy.ex_chinese(User_Sign);

          Connection?con=yy.getConn();
          Statement??stmt=con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY);
          sql=update?用戶表?set?用戶密碼='+User_Password+',用戶性別='+User_Sex+',用戶郵箱='+User_Email+',居住地址='+User_Address+',手機號碼='+User_Mobile+',Oicq='+User_Oicq+',出生日期='+User_Birthay+',用戶等級='+User_Class+',簽名='+User_Sign+'?where?用戶名='+User_Name+';
          //out.println(sql);
          stmt.executeUpdate(sql);
          out.println(<font?size=2?color=blue>正在處理你的用戶信息,請稍后...</font><meta?http-equiv='refresh'?content='2;url=user_manager.jsp'>);
          %>
          <jsp:include?page=inc/online.jsp?flush=true/>

            看看這個文件,我們就好像看到了一個簡單的教學文件。現在,假設管理員提交如下地址,即http://www.51dz.net/bbs/modifyuser_manager.jsp?modifyid=51,需要查看、修改ID為51的用戶的資料(管理員默認的用戶ID為51)。問題就出來了。同樣的,我們可以通過搜索引擎得到如下地址

          很明顯,這個用戶管理文件缺乏認證,即使是普通的用戶,甚至包括我們這些搭不上邊的“游客”,也可以直接提交上述請求,從而將其資料一覽無余,更讓人動心的是,密碼也是明文存儲的。
            
            http://www.51dz.net/bbs/modifyuser_manager.jsp同樣是大開山門,直到惡意用戶把數據更新的操作執行完畢,重定向到user_manager.jsp的時候,管理員才會看見那個顯示錯誤的頁面,但這個時候為時已晚,更談不上“亡羊補牢”了。類似的錯誤存在于很多JSP的站點上,面對這樣的論壇,我們能夠放心的說“安全”嗎?解決之道有很多,不過,最基本的要求是為每個需要加身份認證的地方加上身份認證,如果借用別人的代碼,一定要對涉及到用戶管理、密碼認證等重要文件修改一下,照搬雖然省事,但代碼毫無安全性可言。

            再就是SQL注入的問題。比如,這個典型的問題:“昨天公司的數據庫被人SQL注入,9萬條記錄都被update了,同事寫了個JSP程序來把他改回來,可是這JSP沒有一點信息返回,看不到進度,在運行些什么都不知道。”不過,這和JSP程序沒有什么必然的聯系,根據國情,國內的網站用ASP+Access或SQLServer的占70%以上,PHP+MySQL占20%,其它的不足10%。因此,ASP的SQL注入比較常見也不足為怪。不過,SQL注入漏洞可謂是“千里之堤,潰于蟻穴”,這種漏洞在網上極為普遍,即使是JSP程序也不能幸免。歸根結底,通常是由于程序員對注入不了解,或者程序過濾不嚴格,或者某個參數忘記檢查導致。看看這個教材式的JSP程序就可以窺見一般:

          Statement?stmt?=?conn.createStatement();?
          String?checkUser?=?select?*?from?login?where?username?=?'?+?userName?+?'?and?userpassword?=?'?+?userPassword?+?';?
          ResultSet?rs?=?stmt.executeQuery(checkUser);?
          if(rs.next())?
           response.sendRedirect(SuccessLogin.jsp);?
          else?
           response.sendRedirect(FailureLogin.jsp);

            針對這種情況,如果數據庫里存在一個名叫“Tom”的用戶,那么在不知道密碼的情況下至少有下面幾種方法可以登錄:?
          用戶名:Tom????????????密碼:'?or?'a'='a
          用戶名:Tom????????????密碼:'?or?1=1/*
          用戶名:Tom'?or?1=1/*?????密碼:(任意)

          posted @ 2008-05-23 18:20 金家寶 閱讀(1247) | 評論 (0)編輯 收藏

          Servlet使用

          1.1重定向(如果對方不支持cookie,回寫sessionID進行session跟蹤)
          ?response.sendRedirect(response.encodeRedirectURL(request.getContextPath()+"/next"));
          ******************************************************************
          1.2轉發
          ?RequestDispatcher dispatcher = getServletContext().getRequestDispatcher(url);
          ?dispatcher.forward(request,response);
          ******************************************************************
          1.3字符
          ??request.setCharacterEncoding("utf-8");
          ??response.setContentType("text/html;charset=utf-8");
          ******************************************************************
          String servletPath = request.getServletPath();
          ??servletPath = servletPath.substring(servletPath.lastIndexOf("/") + 1);
          ??String operation = servletPath.substring(0, servletPath.indexOf(".do"));
          1.設置連接超時時間(分鐘)
          ?<session-config>
          ??<session-timeout>50</session-timeout>
          ?</session-config>
          ******************************************************************
          4.相對路徑匹配
          ?1>絕對匹配?/xx/yy
          ?2>后綴匹配?*.xx
          ?3>后面匹配?/xx/*
          ******************************************************************
          5.監聽器
          5.1ServletRequestListener
          ???getServletContext()
          ???getServletRequest()
          ?requestDestroyed(ServletRequestEvent)
          ?requestInitialized(ServletRequestEvent)
          5.2HttpSessionListener
          ???getSession()
          ?sessionCreated(HttpSessionEvent)
          ?sessionDestroyed(HttpSessionEvent)
          5.3ServletContextListener
          ???getServletContext()
          ?contextInitialized(ServletContextEvent)
          ?contextDestroyed(ServletContextEvent)
          ?
          5.4ServletRequestAttributeListener
          ???getName()
          ???getValue()
          ?attributeAdded(ServletRequestAttributeEvent)
          ?attributeRemoved(ServletRequestAttributeEvent)
          ?attributeReplaced(ServletRequestAttributeEvent)
          5.5HttpSessionAttributeListener
          ???getName()
          ???getValue()
          ???getSession()
          ?attributeAdded(HttpSessionBindingEvent)
          ?attributeRemoved(HttpSessionBindingEvent)
          ?attributeReplaced(HttpSessionBindingEvent)
          5.6ServletContextAttributeListener
          ???getName()
          ???getValue()
          ?attributeAdded(ServletContextAttributeEvent)
          ?attributeRemoved(ServletContextAttributeEvent)
          ?attributeReplaced(ServletContextAttributeEvent)

          posted @ 2008-04-19 20:31 金家寶 閱讀(405) | 評論 (0)編輯 收藏

          linux關機和重啟命令

          ?
          ?
          Linux中常用的關機和重新啟動命令有shutdown、halt、reboot以及init,它們都可以達到關機和重新啟動的目的,但是每個命令的內部工作過程是不同的,下面將逐一進行介紹。

          1. shutdown

          shutdown命令用于安全關閉Linux系統。有些用戶會使用直接斷掉電源的方式來關閉Linux,這是十分危險的。因為Linux與Windows不同,其后臺運行著許多進程,所以強制關機可能會導致進程的數據丟失,使系統處于不穩定的狀態,甚至會損壞硬件設備。

          執 行shutdown命令時,系統會通知所有登錄的用戶系統將要關閉,并且login指令會被凍結,即新的用戶不能再登錄系統。使用shutdown命令可 以直接關閉系統,也可以延遲指定的時間再關閉系統,還可以重新啟動。延遲指定的時間再關閉系統,可以讓用戶有時間儲存當前正在處理的文件和關閉已經打開的 程序。

          shutdown命令的部分參數如下:

          [-t] 指定在多長時間之后關閉系統

          [-r] 重啟系統

          [-k] 并不真正關機,只是給每個登錄用戶發送警告信號

          [-h] 關閉系統(halt)

          shutdown命令的工作實質是給init程序發送信號(signal),要求其切換系統的運行級別(Runlevel)。系統的運行級別包括:

          0:關閉系統

          1:單用戶模式,如果沒有為shutdown命令指定-h或-r參數而直接執行,則默認將切換到此運行級別

          2:多用戶模式(不支持NFS)

          3:多用戶模式(支持NFS),一般常用此種運行級別

          5:多用戶模式(GUI模式)

          6:重新啟動系統

          2. halt

          halt是最簡單的關機命令,其實際上是調用shutdown -h命令。halt執行時,殺死應用進程,文件系統寫操作完成后就會停止內核。

          halt命令的部分參數如下:

          [-f] 沒有調用shutdown而強制關機或重啟

          [-i] 關機或重新啟動之前,關掉所有的網絡接口

          [-p] 關機時調用poweroff,此選項為缺省選項

          3.reboot

          reboot的工作過程與halt類似,其作用是重新啟動,而halt是關機。其參數也與halt類似。

          4.init

          init是所有進程的祖先,其進程號始終為1。init用于切換系統的運行級別,切換的工作是立即完成的。init 0命令用于立即將系統運行級別切換為0,即關機;init 6命令用于將系統運行級別切換為6,即重新啟動。

          posted @ 2008-04-19 16:33 金家寶 閱讀(297) | 評論 (0)編輯 收藏

          轉 MYSQL中的my.ini或my.cnf配置說明

          ?

          本文中的配置都是從《MySQL5權威指南(3rd)》中摘抄出來的,個人認為對于使用MySQL十分有用。放在此處方便自己隨時查閱,也希望對其他朋友有所助益。(2007.05.30最后更新)

          mysqld程序--目錄和文件
          basedir = path 使用給定目錄作為根目錄(安裝目錄)。
          character-sets-dir = path 給出存放著字符集的目錄。
          datadir = path 從給定目錄讀取數據庫文件。
          pid-file = filename 為mysqld程序指定一個存放進程ID的文件(僅適用于UNIX/Linux系統); Init-V腳本需要使用這個文件里的進程ID結束mysqld進程。
          socket = filename 為MySQL客戶程序與服務器之間的本地通信指定一個套接字文件(僅適用于UNIX/Linux系統; 默認設置一般是/var/lib/mysql/mysql.sock文件)。
          ??? 在Windows環境下,如果MySQL客戶與服務器是通過命名管道進行通信的,--sock選項給出的將是該命名管道的名字(默認設置是MySQL)。
          lower_case_table_name = 1/0 新目錄和數據表的名字是否只允許使用小寫字母; 這個選項在Windows環境下的默認設置是1(只允許使用小寫字母)。

          mysqld程序--語言設置
          character-sets-server = name 新數據庫或數據表的默認字符集。為了與MySQL的早期版本保持兼容,這個字符集也可以用--default-character-set選項給出; 但這個選項已經顯得有點過時了。
          collation-server = name 新數據庫或數據表的默認排序方式。
          lanuage = name 用指定的語言顯示出錯信息。

          mysqld程序--通信、網絡、信息安全
          enable-named-pipes 允許Windows 2000/XP環境下的客戶和服務器使用命名管道(named pipe)進行通信。這個命名管道的默認名字是MySQL,但可以用--socket選項來改變。
          local-infile [=0] 允許/禁止使用LOAD DATA LOCAL語句來處理本地文件。
          myisam-recover [=opt1, opt2, ...] 在啟動時自動修復所有受損的MyISAM數據表。這個選項的可取值有4種:DEFAULT、BACKUP、QUICK和FORCE; 它們與myisamchk程序的同名選項作用相同。
          old-passwords 使用MySQL 3.23和4.0版本中的老算法來加密mysql數據庫里的密碼(默認使用MySQL 4.1版本開始引入的新加密算法)。
          port = n 為MySQL程序指定一個TCP/IP通信端口(通常是3306端口)。
          safe-user-create 只有在mysql.user數據庫表上擁有INSERT權限的用戶才能使用GRANT命令; 這是一種雙保險機制(此用戶還必須具備GRANT權限才能執行GRANT命令)。
          shared-memory 允許使用內存(shared memory)進行通信(僅適用于Windows)。
          shared-memory-base-name = name 給共享內存塊起一個名字(默認的名字是MySQL)。
          skip-grant-tables 不使用mysql數據庫里的信息來進行訪問控制(警告:這將允許用戶任何用戶去修改任何數據庫)。
          skip-host-cache 不使用高速緩存區來存放主機名和IP地址的對應關系。
          skip-name-resovle 不把IP地址解析為主機名; 與訪問控制(mysql.user數據表)有關的檢查全部通過IP地址行進。
          skip-networking 只允許通過一個套接字文件(Unix/Linux系統)或通過命名管道(Windows系統)進行本地連接,不允許ICP/IP連接; 這提高了安全性,但阻斷了來自網絡的外部連接和所有的Java客戶程序(Java客戶即使在本地連接里也使用TCP/IP)。
          user = name mysqld程序在啟動后將在給定UNIX/Linux賬戶下執行; mysqld必須從root賬戶啟動才能在啟動后切換到另一個賬戶下執行; mysqld_safe腳本將默認使用--user=mysql選項來啟動mysqld程序。

          mysqld程序--內存管理、優化、查詢緩存區
          bulk_insert_buffer_size = n 為一次插入多條新記錄的INSERT命令分配的緩存區長度(默認設置是8M)。
          key_buffer_size = n 用來存放索引區塊的RMA值(默認設置是8M)。
          join_buffer_size = n 在參加JOIN操作的數據列沒有索引時為JOIN操作分配的緩存區長度(默認設置是128K)。
          max_heap_table_size = n HEAP數據表的最大長度(默認設置是16M); 超過這個長度的HEAP數據表將被存入一個臨時文件而不是駐留在內存里。
          max_connections = n MySQL服務器同時處理的數據庫連接的最大數量(默認設置是100)。
          query_cache_limit = n 允許臨時存放在查詢緩存區里的查詢結果的最大長度(默認設置是1M)。
          query_cache_size = n 查詢緩存區的最大長度(默認設置是0,不開辟查詢緩存區)。
          query_cache_type = 0/1/2 查詢緩存區的工作模式:0, 禁用查詢緩存區; 1,啟用查詢緩存區(默認設置); 2,"按需分配"模式,只響應SELECT SQL_CACHE命令。
          read_buffer_size = n 為從數據表順序讀取數據的讀操作保留的緩存區的長度(默認設置是128KB); 這個選項的設置值在必要時可以用SQL命令SET SESSION read_buffer_size = n命令加以改變。
          read_rnd_buffer_size = n 類似于read_buffer_size選項,但針對的是按某種特定順序(比如使用了ORDER BY子句的查詢)輸出的查詢結果(默認設置是256K)。
          sore_buffer = n 為排序操作分配的緩存區的長度(默認設置是2M); 如果這個緩存區太小,則必須創建一個臨時文件來進行排序。
          table_cache = n 同時打開的數據表的數量(默認設置是64)。
          tmp_table_size = n 臨時HEAP數據表的最大長度(默認設置是32M); 超過這個長度的臨時數據表將被轉換為MyISAM數據表并存入一個臨時文件。

          mysqld程序--日志
          log [= file] 把所有的連接以及所有的SQL命令記入日志(通用查詢日志); 如果沒有給出file參數,MySQL將在數據庫目錄里創建一個hostname.log文件作為這種日志文件(hostname是服務器的主機名)。
          log-slow-queries [= file] 把執行用時超過long_query_time變量值的查詢命令記入日志(慢查詢日志); 如果沒有給出file參數,MySQL將在數據庫目錄里創建一個hostname-slow.log文件作為這種日志文件(hostname是服務器主機名)。
          long_query_time = n 慢查詢的執行用時上限(默認設置是10s)。
          long_queries_not_using_indexs 把慢查詢以及執行時沒有使用索引的查詢命令全都記入日志(其余同--log-slow-queries選項)。
          log-bin [= filename] 把對數據進行修改的所有SQL命令(也就是INSERT、UPDATE和DELETE命令)以二進制格式記入日志(二進制變更日志,binary update log)。這種日志的文件名是filename.n或默認的hostname.n,其中n是一個6位數字的整數(日志文件按順序編號)。
          log-bin-index = filename 二進制日志功能的索引文件名。在默認情況下,這個索引文件與二進制日志文件的名字相同,但后綴名是.index而不是.nnnnnn。
          max_binlog_size = n 二進制日志文件的最大長度(默認設置是1GB)。在前一個二進制日志文件里的信息量超過這個最大長度之前,MySQL服務器會自動提供一個新的二進制日志文件接續上。
          binlog-do-db = dbname 只把給定數據庫里的變化情況記入二進制日志文件,其他數據庫里的變化情況不記載。如果需要記載多個數據庫里的變化情況,就必須在配置文件使用多個本選項來設置,每個數據庫一行。
          binlog-ignore-db = dbname 不把給定數據庫里的變化情況記入二進制日志文件。
          sync_binlog = n 每經過n次日志寫操作就把日志文件寫入硬盤一次(對日志信息進行一次同步)。n=1是最安全的做法,但效率最低。默認設置是n=0,意思是由操作系統來負責二進制日志文件的同步工作。
          log-update [= file] 記載出錯情況的日志文件名(出錯日志)。這種日志功能無法禁用。如果沒有給出file參數,MySQL會使用hostname.err作為種日志文件的名字。

          mysqld程序--鏡像(主控鏡像服務器)
          server-id = n 給服務器分配一個獨一無二的ID編號; n的取值范圍是1~2的32次方啟用二進制日志功能。
          log-bin = name 啟用二進制日志功能。這種日志的文件名是filename.n或默認的hostname.n,其中的n是一個6位數字的整數(日志文件順序編號)。
          binlog-do/ignore-db = dbname 只把給定數據庫里的變化情況記入二進制日志文件/不把給定的數據庫里的變化記入二進制日志文件。

          mysqld程序--鏡像(從屬鏡像服務器)
          server-id = n 給服務器分配一個唯一的ID編號
          log-slave-updates 啟用從屬服務器上的日志功能,使這臺計算機可以用來構成一個鏡像鏈(A->B->C)。
          master-host = hostname 主控服務器的主機名或IP地址。如果從屬服務器上存在mater.info文件(鏡像關系定義文件),它將忽略此選項。
          master-user = replicusername 從屬服務器用來連接主控服務器的用戶名。如果從屬服務器上存在mater.info文件,它將忽略此選項。
          master-password = passwd 從屬服務器用來連接主控服務器的密碼。如果從屬服務器上存在mater.info文件,它將忽略此選項。
          master-port = n 從屬服務器用來連接主控服務器的TCP/IP端口(默認設置是3306端口)。
          master-connect-retry = n 如果與主控服務器的連接沒有成功,則等待n秒(s)后再進行管理方式(默認設置是60s)。如果從屬服務器存在mater.info文件,
          ??? 它將忽略此選項。
          master-ssl-xxx = xxx 對主、從服務器之間的SSL通信進行配置。
          read-only = 0/1 0: 允許從屬服務器獨立地執行SQL命令(默認設置); 1: 從屬服務器只能執行來自主控服務器的SQL命令。
          read-log-purge = 0/1 1: 把處理完的SQL命令立刻從中繼日志文件里刪除(默認設置); 0: 不把處理完的SQL命令立刻從中繼日志文件里刪除。
          replicate-do-table = dbname.tablename 與--replicate-do-table選項的含義和用法相同,但數據庫和數據庫表名字里允許出現通配符"%"
          ??? (例如: test%.%--對名字以"test"開頭的所有數據庫里的所以數據庫表進行鏡像處理)。
          replicate-do-db = name 只對這個數據庫進行鏡像處理。
          replicate-ignore-table = dbname.tablename 不對這個數據表進行鏡像處理。
          replicate-wild-ignore-table = dbn.tablen 不對這些數據表進行鏡像處理。
          replicate-ignore-db = dbname 不對這個數據庫進行鏡像處理。
          replicate-rewrite-db = db1name > db2name 把主控數據庫上的db1name數據庫鏡像處理為從屬服務器上的db2name數據庫。
          report-host = hostname 從屬服務器的主機名; 這項信息只與SHOW SLAVE HOSTS命令有關--主控服務器可以用這條命令生成一份從屬服務器的名單。
          slave-compressed-protocol = 1 主、從服務器使用壓縮格式進行通信--如果它們都支持這么做的話。
          slave-skip-errors = n1, n2, ...或all 即使發生出錯代碼為n1、n2等的錯誤,鏡像處理工作也繼續進行(即不管發生什么錯誤,鏡像處理工作也繼續進行)。
          ??? 如果配置得當,從屬服務器不應該在執行SQL命令時發生錯誤(在主控服務器上執行出錯的SQL命令不會被發送到從屬服務器上做鏡像處理); 如果不使用
          ??? slave-skip-errors選項,從屬服務器上的鏡像工作就可能國為發生錯誤而中斷,中斷后需要有人工參與才能繼續進行。

          mysqld--InnoDB--基本設置、表空間文件
          skip-innodb 不加載InnoDB數據表驅動程序--如果用不著InnoDB數據表,可以用這個選項節省一些內存。
          innodb-file-per-table 為每一個新數據表創建一個表空間文件而不是把數據表都集中保存在中央表空間里(后者是默認設置)。該選項始見于MySQL 4.1。
          innodb-open-file = n InnoDB數據表驅動程序最多可以同時打開的文件數(默認設置是300)。如果使用了innodb-file-per-table選項并且需要同時打開很多
          ??? 數據表的話,這個數字很可能需要加大。
          innodb_data_home_dir = p InnoDB主目錄,所有與InnoDB數據表有關的目錄或文件路徑都相對于這個路徑。在默認的情況下,這個主目錄就是MySQL的數據目錄。
          innodb_data_file_path = ts 用來容納InnoDB為數據表的表空間: 可能涉及一個以上的文件; 每一個表空間文件的最大長度都必須以字節(B)、兆字節(MB)或
          ??? 千兆字節(GB)為單位給出; 表空間文件的名字必須以分號隔開; 最后一個表空間文件還可以帶一個autoextend屬性和一個最大長度(max:n)。
          ??? 例如,ibdata1:1G; ibdata2:1G:autoextend:max:2G的意思是: 表空間文件ibdata1的最大長度是1GB,ibdata2的最大長度也是1G,但允許它擴充到2GB。
          ??? 除文件名外,還可以用硬盤分區的設置名來定義表空間,此時必須給表空間的最大初始長度值加上newraw關鍵字做后綴,給表空間的最大擴充長度值加上
          ??? raw關鍵字做后綴(例如/dev/hdb1:20Gnewraw或/dev/hdb1:20Graw); MySQL 4.0及更高版本的默認設置是ibdata1:10M:autoextend。
          innodb_autoextend_increment = n 帶有autoextend屬性的表空間文件每次加大多少兆字節(默認設置是8MB)。這個屬性不涉及具體的數據表文件,那些文件的
          ??? 增大速度相對是比較小的。
          innodb_lock_wait_timeout = n 如果某個事務在等待n秒(s)后還沒有獲得所需要的資源,就使用ROLLBACK命令放棄這個事務。這項設置對于發現和處理未能被
          ??? InnoDB數據表驅動程序識別出來的死鎖條件有著重要的意義。這個選項的默認設置是50s。
          innodb_fast_shutdown 0/1 是否以最快的速度關閉InnoDB,默認設置是1,意思是不把緩存在INSERT緩存區的數據寫入數據表,那些數據將在MySQL服務器下次
          ??? 啟動時再寫入(這么做沒有什么風險,因為INSERT緩存區是表空間的一個組成部分,數據不會丟失)。把這個選項設置為0反面危險,因為在計算機關閉時,
          ??? InnoDB驅動程序很可能沒有足夠的時間完成它的數據同步工作,操作系統也許會在它完成數據同步工作之前強行結束InnoDB,而這會導致數據不完整。

          mysqld程序--InnoDB--日志
          innodb_log_group_home_dir = p 用來存放InnoDB日志文件的目錄路徑(如ib_logfile0、ib_logfile1等)。在默認的情況下,InnoDB驅動程序將使用MySQL數據目
          ??? 錄作為自己保存日志文件的位置。???
          innodb_log_files_in_group = n 使用多少個日志文件(默認設置是2)。InnoDB數據表驅動程序將以輪轉方式依次填寫這些文件; 當所有的日志文件都寫滿以后,
          ??? 之后的日志信息將寫入第一個日志文件的最大長度(默認設置是5MB)。這個長度必須以MB(兆字節)或GB(千兆字節)為單位進行設置。
          innodb_flush_log_at_trx_commit = 0/1/2 這個選項決定著什么時候把日志信息寫入日志文件以及什么時候把這些文件物理地寫(術語稱為"同步")到硬盤上。
          ??? 設置值0的意思是每隔一秒寫一次日志并進行同步,這可以減少硬盤寫操作次數,但可能造成數據丟失; 設置值1(設置設置)的意思是在每執行完一條COMMIT
          ??? 命令就寫一次日志并進行同步,這可以防止數據丟失,但硬盤寫操作可能會很頻繁; 設置值2是一般折衷的辦法,即每執行完一條COMMIT命令寫一次日志,
          ??? 每隔一秒進行一次同步。
          innodb_flush_method = x InnoDB日志文件的同步辦法(僅適用于UNIX/Linux系統)。這個選項的可取值有兩種: fdatasync,用fsync()函數進行同步; O_DSYNC,
          ??? 用O_SYNC()函數進行同步。
          innodb_log_archive = 1 啟用InnoDB驅動程序的archive(檔案)日志功能,把日志信息寫入ib_arch_log_n文件。啟用這種日志功能在InnoDB與MySQL一起使用時沒有
          ??? 多大意義(啟用MySQL服務器的二進制日志功能就足夠用了)。

          mysqld程序--InnoDB--緩存區的設置和優化
          innodb_log_buffer_pool_size = n 為InnoDB數據表及其索引而保留的RAM內存量(默認設置是8MB)。這個參數對速度有著相當大的影響,如果計算機上只運行有
          ??? MySQL/InnoDB數據庫服務器,就應該把全部內存的80%用于這個用途。
          innodb_log_buffer_size = n 事務日志文件寫操作緩存區的最大長度(默認設置是1MB)。
          innodb_additional_men_pool_size = n 為用于內部管理的各種數據結構分配的緩存區最大長度(默認設置是1MB)。
          innodb_file_io_threads = n I/O操作(硬盤寫操作)的最大線程個數(默認設置是4)。
          innodb_thread_concurrency = n InnoDB驅動程序能夠同時使用的最大線程個數(默認設置是8)。

          mysqld程序--其它選項
          bind-address = ipaddr MySQL服務器的IP地址。如果MySQL服務器所在的計算機有多個IP地址,這個選項將非常重要。
          default-storage-engine = type 新數據表的默認數據表類型(默認設置是MyISAM)。這項設置還可以通過--default-table-type選項來設置。
          default-timezone = name 為MySQL服務器設置一個地理時區(如果它與本地計算機的地理時區不一樣)。
          ft_min_word_len = n 全文索引的最小單詞長度工。這個選項的默認設置是4,意思是在創建全文索引時不考慮那些由3個或更少的字符構建單詞。
          Max-allowed-packet = n 客戶與服務器之間交換的數據包的最大長度,這個數字至少應該大于客戶程序將要處理的最大BLOB塊的長度。這個選項的默認設置是1MB。
          Sql-mode = model1, mode2, ... MySQL將運行在哪一種SQL模式下。這個選項的作用是讓MySQL與其他的數據庫系統保持最大程度的兼容。這個選項的可取值包括
          ??? ansi、db2、oracle、no_zero_date、pipes_as_concat。

          注意:如果在配置文件里給出的某個選項是mysqld無法識別的(如,因為犯了一個愚蠢的打字錯誤),MySQL服務器將不啟動。

          來源: http://blog.chinaunix.net/u1/41728/showart_350147.html

          posted @ 2008-04-17 14:05 金家寶 閱讀(2326) | 評論 (0)編輯 收藏

          mysql data文件夾下的ibdata1 文件作用

          ?
          這個文件超級大, 查了一下, 大概的作用如下

          是儲存的格式
          INNODB類型數據狀態下,
          ibdata用來儲存文件的數據
          而庫名的文件夾里面的那些表文件只是結構而已

          由于mysql4.1默認試innodb,所以這個文件默認就存在了http://man.chinaunix.net/database/mysql/inonodb_zh/2.htm 這個鏈接試innodb的中文參考, innodb的東西可以在my.ini中設置

          innodo中文參考全文如下

          InnoDB 啟動選項

          為了在 MySQL-Max-3.23 中使用 InnoDB 表,你必須在配置文件‘my.cnf’‘my.ini’(WINDOWS系統)中的 [mysqld] 區中詳細指定配置參數。

          作為最小設置,在 3.23 中你必須在 innodb_data_file_path 上指定數據文件名能及大小。如果在‘my.cnf’中沒有指定innodb_data_home_dir,系統將在 MySQL 的 datadir 目錄下創建數據文件。如果將 innodb_data_home_dir 設為一個空串,那可以在 innodb_data_file_path 中給定一個絕對路徑。在 MySQL-4.0 中可以不設定 innodb_data_file_path :MySQL-4.0 將默認地在 datadir 目錄下建立一個 10 MB 大小自擴充(auto-extending)的文件‘ibdata1’(在MySQL-4.0.0 與 4.0.1 中數據文件的大小為 64 MB 并且是非自擴充的(not auto-extending))。

          為了得到更好的性能你必須所示的例子明確地設定 InnoDB 啟動參數。

          從 3.23.50 版和 4.0.2 版開始,InnoDB 允許在 innodb_data_file_path 中設置的最一個數據文件描述為 auto-extendinginnodb_data_file_path 語法如下所示:

          pathtodatafile:sizespecification;pathtodatafile:sizespec;...
          ...;pathtodatafile:sizespec[:autoextend[:max:sizespecification]]
          如果用 autoextend 選項描述最后一個數據文件,當 InnoDB 用盡所有表自由空間后將會自動擴充最后一個數據文件,每次增量為 8 MB。示例:
          innodb_data_home_dir =
          innodb_data_file_path = /ibdata/ibdata1:100M:autoextend
          指定 InnoDB 只建立一個最初大小為 100 MB 并且當表空間被用盡時以 8MB 每塊增加的數據文件。如果硬盤空間不足,可以再添加一個數據文件并將其放在其它的硬盤中。 舉例來說:先檢查硬盤空間的大小,設定 ibdata1 文件使它接近于硬盤空余空間大小并為 1024 * 1024 bytes (= 1 MB)的倍數, 將 ibdata1 明確地指定在 innodb_data_file_path 中。在此之后可以添加另一個數據文件:
          innodb_data_home_dir =
          innodb_data_file_path = /ibdata/ibdata1:988M;/disk2/ibdata2:50M:autoextend
          注意:設定文件大小時一定要注意你的OS是否有最大文件尺寸為2GB的限制!InnoDB是不會注意你的OS文件尺寸限制的, 在一些文件系統中你可能要設定最大容量限制:
          innodb_data_home_dir =
          innodb_data_file_path = /ibdata/ibdata1:100M:autoextend:max:2000M

           

          一個簡單的 my.cnf 例子。 假設你的計算機有 128 MB RAM 和一個硬盤。下面的例子是為了使用 InnoDB 而在 my.cnfmy.ini 文件中可能所作的一些配置。我們假設你運行的是 MySQL-Max-3.23.50 及以上版本,或 MySQL-4.0.2 及以上版本。

          這個示例適合大部分不需要將 InnoDB 數據文件和日志文件放在幾個盤上的 Unix 和 Windows 用戶。這個例子在 MySQL 的datadir 目錄(典型的為 /mysql/data)中創建一個自擴充(auto-extending)的數據文件 ibdata1 和兩個 InnoDB 運行日志文件ib_logfile0 ib_logfile1 以及 ib_arch_log_0000000000 檔案文件。

          [mysqld]
          #在這里加入其它 的 MySQL 服務器配置
          #...
          # 數據文件必須
          # 能夠容下數據與索引
          # 確定有足夠的
          # 磁盤空間
          innodb_data_file_path = ibdata1:10M:autoextend
          # 設置緩沖池的大小為
          # 你的主內存大小的
          # 50 - 80 %
          set-variable = innodb_buffer_pool_size=70M
          set-variable = innodb_additional_mem_pool_size=10M
          # 設置日志文件的大小約為
          # 緩沖池(buffer pool)
          # 大小的 25 %
          set-variable = innodb_log_file_size=20M
          set-variable = innodb_log_buffer_size=8M
          # 如果丟失最近幾個事務影響
          # 不大的話可以設置
          # .._flush_log_at_trx_commit = 0
          innodb_flush_log_at_trx_commit=1

          InnoDB 不會自己建立目錄,必須自己使用操作系統命令建立相應的目錄。檢查你的 MySQL 服務程序在 datadir 目錄里 有足夠的權限建立文件。

          注意:在某些文件系統中 數據文件大小必須小于2G! 所有運行日志文件的大小總和必須小于 2G 或 4G,這依賴于具體的 MySQL 系統版本。 數據文件的總和必須大于等于 10 MB.

          當第一次建立 InnoDB 數據庫時,建議最好以命令行方式啟動 MySQL 服務。這樣 InnoDB 數據庫建立時的提示信息將在屏幕上顯示,從而可以看到建立過程。 下面第 3 節所示就是 InnoDB 數據庫建立時的屏幕顯示。例如,在 Windows 下使用下列指令啟動 mysqld-max.exe

          your-path-to-mysqld>mysqld-max --console

           

          在 Windows 系統下 my.cnfmy.ini 放在哪里?規則如下 :

          • 只能存在一個 my.cnf my.ini 文件
          • my.cnf 文件必須放在 C: 的根目錄下
          • my.ini 文件必須放在 WINDIR 目錄下,例:C:\WINDOWSC:\WINNT。可以使用 MS-DOS 的 SET 命令查看 WINDIR 目錄值
          • 如果你的 PC 使用啟動引導程序引導系統而 C: 不是啟動磁盤,那只能唯一地使用 my.ini 作為設置文件

           

          Unix 下在哪里指定配置文件?在 Unix 下 mysqld 按下列順序搜索配置文件:

          • /etc/my.cnf 全局選項
          • COMPILATION_DATADIR/my.cnf 服務器范圍的選項
          • defaults-extra-file 采用 --defaults-extra-file=.... 設置的默認文件
          • ~/.my.cnf 用戶指定文件
          COMPILATION_DATADIR 是 MySQL 的數據文件目錄,它是在 mysqld 被編譯時以 ./configure 設置指定 (典型的是 /usr/local/mysql/data 二進制安裝或 /usr/local/var 以源安裝)。

           

          如果不有確定 mysqld 從哪里讀取 my.cnfmy.ini,可以在第一命令行上詳細指定它的目錄:mysqld --defaults-file=your_path_to_my_cnf

          InnoDB 的數據文件目錄是對 innodb_data_home_dirinnodb_data_file_path 的數據文件名或目錄聯合 ,如果需要將在它們之間增加一個“/”或“\”。如果關鍵字 innodb_data_home_dir 沒有在 my.cnf 中明確指定,它的默認值為“.”,即目錄“./”,這意味著 MySQL 的 datadir of MySQL.

          一個高級的 my.cnf 示例。假設你有一臺 2 GB RAM 和3個 60 GB 硬盤(路徑分別為 "/", "/dr2"“/dr3”)裝有 Linux。下面的例子是為了使用 InnoDB 而在 my.cnf 文件中可能所作的一些配置。

          注意:InnoDB 不會自己創建文件目錄:你必須自己創建它們。使用 Unix 或 MS-DOS mkdir 命令建立相應的數據與日志文件目錄。

          [mysqld]
          #在這里加入其它 的 MySQL 服務器配置
          #...
          # 如果不使用InnoDB表將一列一行注釋去除
          # skip-innodb
          #
          # 數據文件必須
          # 能夠容下數據與索引
          # 確定有足夠的
          # 磁盤空間
          innodb_data_file_path = /ibdata/ibdata1:2000M;/dr2/ibdata/ibdata2:2000M:autoextend
          # 設置緩沖池的大小為
          # 你的主內存大小的
          # 50 - 80 %,但是
          # 在 Linux x86 總內存
          # 使用必須小于 2 GB
          set-variable = innodb_buffer_pool_size=1G
          set-variable = innodb_additional_mem_pool_size=20M
          innodb_log_group_home_dir = /dr3/iblogs
          # .._log_arch_dir 必須和
          # .._log_group_home_dir一樣;
          # 從 4.0.6開始,可以省略它
          innodb_log_arch_dir = /dr3/iblogs
          set-variable = innodb_log_files_in_group=3
          # 設置日志文件的大小約為
          # 緩沖池(buffer pool)
          # 大小的 15 %
          set-variable = innodb_log_file_size=150M
          set-variable = innodb_log_buffer_size=8M
          # 如果丟失最近幾個事務影響
          # 不大的話可以設置
          # .._flush_log_at_trx_commit = 0
          innodb_flush_log_at_trx_commit=1
          set-variable = innodb_lock_wait_timeout=50
          #innodb_flush_method=fdatasync
          #set-variable = innodb_thread_concurrency=5

          注意:我們已在不同的硬盤上放置了兩個數據文件, InnoDB 將從數據文件的底部填充表空間。在某些情況下所有的數據被分配到不同的物理硬盤中會提高數據庫的性能。 將日志文件與數據文件分別放在不同的物理硬盤中對提高性能通常是很有益的。你同樣可以使用一個 RAW 磁盤分區( raw disk partitions(raw devices)) 作為數據文件, 在一些 Unixe 系統中這將提高 I/O 能力。 如何在 my.cnf 中詳細指定它們請查看第 12.1 節。

          警告:在 Linux x86 上必須小心不能將內存使用設置太高, glibc 會把進程堆增長到線程堆棧之上,這將會使服務器崩潰。下面的接近或超過于 2G 將會很危險:

          innodb_buffer_pool_size + key_buffer +
          max_connections * (sort_buffer + record_buffer) + max_connections * 2 MB
          每個線程將使用 2MB(MySQL AB 二進制版本為 256 KB)的堆棧,在最壞的環境下還會使用 sort_buffer + record_buffer 的附加內存。

           

          如何調整其它的 mysqld 服務器參數?查看 MySQL 用戶手冊可以得到更詳細的信息。適合大多數用戶的典型參數如下所示:

                   skip-locking
          set-variable = max_connections=200
          set-variable = record_buffer=1M
          set-variable = sort_buffer=1M
          # 設置索引緩沖(key_buffer)大小為
          # 你的 RAM 的 5 - 50% ,這主要依賴于
          # 系統中 MyISAM 表使用量。
          # 但是必須保證索引緩沖(key_buffer)與 InnoDB
          # 的緩沖池(buffer pool)大小總和
          # 小于 RAM 的 80%。
          set-variable = key_buffer=...

           

          注意:在 my.cnf 文件中有些參數是為了設置數字的,它們的設置格式為:set-variable = innodb... = 123,而其它(字符串和邏輯型)的采用另一設置格式:innodb_... = ... .

          各設置參數的含義如下:

          innodb_data_home_dir

          這是InnoDB表的目錄共用設置。如果沒有在 my.cnf 進行設置,InnoDB 將使用MySQL的 datadir 目錄為缺省目錄。如果設定一個空字串,可以在 innodb_data_file_path 中設定絕對路徑。

          innodb_data_file_path單獨指定數據文件的路徑與大小。數據文件的完整路徑由 innodb_data_home_dir 與這里所設定值的組合。 文件大小以 MB 單位指定。因此在文件大小指定后必有“M”。 InnoDB 也支持縮寫“G”, 1G = 1024M。從 3.23.44 開始,在那些支持大文件的操作系統上可以設置數據文件大小大于 4 GB。而在另一些操作系統上數據文件必須小于 2 GB。數據文件大小總和至少要達到 10 MB。在 MySQL-3.23 中這個參數必須在 my.cnf 中明確指定。在 MySQL-4.0.2 以及更新版本中則不需如此,系統會默認在 MySQL 的 datadir 目錄下創建一個 16 MB 自擴充(auto-extending)的數據文件 ibdata1。你同樣可以使用一個 原生磁盤分區(RAW raw disk partitions(raw devices)) 作為數據文件, 如何在 my.cnf 中詳細指定它們請查看第 12.1 節。
          innodb_mirrored_log_groups為了保護數據而設置的日志文件組的拷貝數目,默認設置為 1。在 my.cnf 中以數字格式設置。
          innodb_log_group_home_dirInnoDB 日志文件的路徑。必須與 innodb_log_arch_dir 設置相同值。 如果沒有明確指定將默認在 MySQL 的 datadir 目錄下建立兩個 5 MB 大小的 ib_logfile... 文件。
          innodb_log_files_in_group日志組中的日志文件數目。InnoDB 以環型方式(circular fashion)寫入文件。數值 3 被推薦使用。在 my.cnf 中以數字格式設置。
          innodb_log_file_size日志組中的每個日志文件的大小(單位 MB)。如果 n 是日志組中日志文件的數目,那么理想的數值為 1M 至下面設置的緩沖池(buffer pool)大小的 1/n。較大的值,可以減少刷新緩沖池的次數,從而減少磁盤 I/O。但是大的日志文件意味著在崩潰時需要更長的時間來恢復數據。 日志文件總和必須小于 2 GB,3.23.55 和 4.0.9 以上為小于 4 GB。在 my.cnf 中以數字格式設置。
          innodb_log_buffer_sizeInnoDB 將日志寫入日志磁盤文件前的緩沖大小。理想值為 1M 至 8M。大的日志緩沖允許事務運行時不需要將日志保存入磁盤而只到事務被提交(commit)。 因此,如果有大的事務處理,設置大的日志緩沖可以減少磁盤I/O。 在 my.cnf 中以數字格式設置。
          innodb_flush_log_at_trx_commit通常設置為 1,意味著在事務提交前日志已被寫入磁盤, 事務可以運行更長以及服務崩潰后的修復能力。如果你愿意減弱這個安全,或你運行的是比較小的事務處理,可以將它設置為 0 ,以減少寫日志文件的磁盤 I/O。這個選項默認設置為 0。
          innodb_log_arch_dirThe directory where fully written log files would be archived if we used log archiving. 這里設置的參數必須與 innodb_log_group_home_dir 相同。 從 4.0.6 開始,可以忽略這個參數。
          innodb_log_archive這個值通常設為 0。 既然從備份中恢復(recovery)適合于 MySQL 使用它自己的 log files,因而通常不再需要 archive InnoDB log files。這個選項默認設置為 0。
          innodb_buffer_pool_sizeInnoDB 用來高速緩沖數據和索引內存緩沖大小。 更大的設置可以使訪問數據時減少磁盤 I/O。在一個專用的數據庫服務器上可以將它設置為物理內存的 80 %。 不要將它設置太大,因為物理內存的使用競爭可能會影響操作系統的頁面調用。在 my.cnf 中以數字格式設置。
          innodb_additional_mem_pool_sizeInnoDB 用來存儲數據字典(data dictionary)信息和其它內部數據結構(internal data structures)的存儲器組合(memory pool)大小。理想的值為 2M,如果有更多的表你就需要在這里重新分配。如果 InnoDB 用盡這個池中的所有內存,它將從操作系統中分配內存,并將錯誤信息寫入 MySQL 的錯誤日志中。在 my.cnf 中以數字格式設置。
          innodb_file_io_threadsInnoDB 中的文件 I/O 線程。 通常設置為 4,但是在 Windows 下可以設定一個更大的值以提高磁盤 I/O。在 my.cnf 中以數字格式設置。
          innodb_lock_wait_timeout在回滾(rooled back)之前,InnoDB 事務將等待超時的時間(單位 秒)。InnoDB 會自動檢查自身在鎖定表與事務回滾時的事務死鎖。如果使用 LOCK TABLES 命令,或在同一個事務中使用其它事務安全型表處理器(transaction safe table handlers than InnoDB),那么可能會發生一個 InnoDB 無法注意到的死鎖。在這種情況下超時將用來解決這個問題。這個參數的默認值為 50 秒。在 my.cnf 中以數字格式設置。
          innodb_flush_method這個參數僅僅與 Unix 相關。這個參數默認值為 fdatasync。 另一個設置項為 O_DSYNC。這僅僅影響日志文件的轉儲,在 Unix 下以 fsync 轉儲數據。InnoDB 版本從 3.23.40b 開始,在 Unix 下指定 fdatasync 為使用 fsync 方式、指定 O_DSYNC 為使用 O_SYNC 方式。由于這在某些 Unix 環境下還有些問題所以在 'data' versions 并沒有被使用。
          innodb_force_recovery警告:此參數只能在你希望從一個被損壞的數據庫中轉儲(dump)數據的緊急情況下使用! 可能設置的值范圍為 1 - 6。查看下面的章節 'Forcing recovery' 以了解這個參數的具體含義。參數設置大于 0 的值代表著 InnoDB 防止用戶修改數據的安全度。從 3.23.44 開始,這個參數可用。在 my.cnf 中以數字格式設置。
          innodb_fast_shutdownInnoDB 缺少在關閉之前清空插入緩沖。這個操作可能需要幾分鐘,在極端的情況下可以需要幾個小時。如果這個參數據設置為 1 ,InnoDB 將跳過這個過程而直接關閉。從 3.23.44 和 4.0.1 開始,此參數可用。從 3.23.50 開始,此參數的默認值為 1。
          innodb_thread_concurrencyInnoDB 會試圖將 InnoDB 服務的使用的操作系統進程小于或等于這里所設定的數值。此參數默認值為 8。如果計算機系統性能較低或 innodb_monitor 顯示有很多線程等侍信號,應該將這個值設小一點。如果你的計算機系統有很我的處理器與磁盤系統,則可以將這個值設高一點以充分利用你的系統資源。建議設值為處理器數目+ 磁盤數目。 從 3.23.44 和 4.0.1 開始,此參數可用。在 my.cnf 中以數字格式設置。

          posted @ 2008-04-17 13:56 金家寶 閱讀(37401) | 評論 (0)編輯 收藏

          Jforum社區論壇分析

          http://www.javaeye.com/topic/181726
          http://blog.csdn.net/gohands/archive/2008/01/21/2056713.aspx
          http://blog.csdn.net/JForum/archive/2007/10/31/1859718.aspx
          http://www.javaeye.com/topic/180504
          http://gohands.javaeye.com/blog/monthblog/2008-01?page=2&show_full=false

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

          主站蜘蛛池模板: 花莲市| 大连市| 永春县| 土默特左旗| 仁化县| 和平县| 北京市| 浮梁县| 从江县| 墨脱县| 宁德市| 平凉市| 青海省| 伊通| 潞城市| 清徐县| 桓台县| 鞍山市| 怀远县| 同江市| 鸡东县| 闻喜县| 岗巴县| 荔浦县| 凌海市| 井陉县| 景洪市| 嘉鱼县| 铁岭县| 清镇市| 长宁区| 黄大仙区| 深圳市| 新民市| 临武县| 庆城县| 南涧| 泰顺县| 云霄县| 长兴县| 六安市|