潛心學習 技術強身

            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
            14 隨筆 :: 0 文章 :: 8 評論 :: 0 Trackbacks

          #

          轉自:http://www.aygfsteel.com/Unmi/archive/2007/12/04/165035.html
          一:要解決的問題

          我們在嘗鮮 JDK1.5 的時候,相信不少人遇到過 Unsupported major.minor version 49.0 錯誤,當時定會茫然不知所措。因為剛開始那會兒,網上與此相關的中文資料還不多,現在好了,網上一找就知道是如何解決,大多會告訴你要使用 JDK 1.4 重新編譯。那么至于為什么,那個 major.minor 究竟為何物呢?這就是本篇來講的內容,以使未錯而先知。

          我覺得我是比較幸運的,因為在遇到那個錯誤之前已研讀過《深入 Java 虛擬機》第二版,英文原書名為《Inside the Java Virtual Machine》( Second Edition),看時已知曉 major.minor 藏匿于何處,但沒有切身體會,待到與 Unsupported major.minor version 49.0 真正會面試,正好是給我驗證了一個事實。

          首先我們要對 Unsupported major.minor version 49.0 建立的直接感覺是:JDK1.5 編譯出來的類不能在 JVM 1.4 下運行,必須編譯成 JVM 1.4 下能運行的類。(當然,也許你用的還是 JVM 1.3 或 JVM 1.2,那么就要編譯成目標 JVM 能認可的類)。這也解決問題的方向。

          二:major.minor 棲身于何處

          何謂 major.minor,且又居身于何處呢?先感性認識并找到 major.minor 來。

          寫一個 Java Hello World! 代碼,然后用 JDK 1.5 的編譯器編譯成,HelloWorld.java
           1package com.unmi;
           2
           3public class HelloWorld
           4{
           5    public static void main(String[] args)
           6    {
           7        System.out.println("Hello, World!");
           8    }

           9}

          10
          用 JDK 1.5 的 javac -d .  HelloWorld.java 編譯出來的字節碼 HelloWorld.class 用 UltraEdit 打開來的內容如圖所示:

          HelloWorldClassUnmi.jpg


          從上圖中我們看出來了什么是 major.minor version 了,它相當于一個軟件的主次版本號,只是在這里是標識的一個 Java Class 的主版本號和次版本號,同時我們看到 minor_version 為 0x0000,major_version 為 0x0031,轉換為十制數分別為0 和 49,即 major.minor 就是 49.0 了。

          三:何謂 major.minor 以及何用

          Class 文件的第 5-8 字節為 minor_version 和 major_version。Java class 文件格式可能會加入新特性。class 文件格式一旦發生變化,版本號也會隨之變化。對于 JVM 來說,版本號確定了特定的 class 文件格式,通常只有給定主版本號和一系列次版本號后,JVM 才能夠讀取 class 文件。如果 class 文件的版本號超出了 JVM 所能處理的有效范圍,JVM 將不會處理該 class 文件。

          在 Sun 的 JDK 1.0.2 發布版中,JVM 實現支持從 45.0 到 45.3 的 class 文件格式。在所有 JDK 1.1 發布版中的 JVM 都能夠支持版本從 45.0 到 45.65535 的 class 文件格式。在 Sun 的 1.2 版本的 SDK 中,JVM 能夠支持從版本 45.0 到46.0 的 class 文件格式。

          1.0 或 1.2 版本的編譯器能夠產生版本號為 45.3 的 class 文件。在 Sun 的 1.2 版本 SDK 中,Javac 編譯器默認產生版本號為 45.3  的 class 文件。但如果在 javac 命令行中指定了 -target 1.2 標志,1.2 版本的編譯器將產生版本號為 46.0 的 class 文件。1.0 或 1.1 版本的 JVM 上不能運行使用-target 1.2 標志所產生的 class 文件。

          JVM 實現的 第二版中修改了對 class 文件主版本號和次版本號的解釋。對于第二版而言,class 文件的主版本號與 Java 平臺主發布版的版本號保持一致(例如:在 Java 2 平臺發布版上,主版本號從 45 升至 46),次版本號與特定主平臺發布版的各個發布版相關。因此,盡管不同的 class 文件格式可以由不同的版本號表示,但版本號不一樣并不代表 class 文件格式不同。版本號不同的原因可能只是因為 class 文件由不同發布版本的 java 平臺產生,可能 class 文件的格式并沒有改變。

          上面三段節選自《深入 Java 虛擬機》,啰嗦一堆,JDK 1.2 開啟了 Java 2 的時代,但那個年代仍然離我們很遠,我們當中很多少直接跳在 JDK 1.4 上的,我也差不多,只是項目要求不得不在一段時間里委屈在 JDK 1.3 上。不過大致我們可以得到的信息就是每個版本的 JDK 編譯器編譯出的 class 文件中都帶有一個版本號,不同的 JVM 能接受一個范圍 class 版本號,超出范圍則要出錯。不過一般都是能向后兼容的,知道 Sun 在做 Solaris 的一句口號嗎?保持對先前版本的 100% 二進制兼容性,這也是對客戶的投資保護。

          四:其他確定 class 的 major.minor version 辦法

          1)Eclipse 中查看
                Eclipse 3.3 加入的新特征,當某個類沒有關聯到源代碼,打開它會顯示比較詳細的類信息,當然還未到源碼級別了,看下圖是打開 2.0 spring.jar 中 ClasspathXmlApplicationContext.class 顯示的信息

          eclipseclass1.jpg

          2)命令 javap -verbose
                 對于編譯出的 class 文件用 javap -verbose 能顯示出類的 major.minor 版本,見下圖:

          JavapVerboseUnmi.jpg

          3)  MANIFEST 文件
                把 class 打成的 JAR 包中都會有文件 META-INF\MANIFEST,這個文件一般會有編譯器的信息,下面列幾個包的 META-INF\MANIFEST 文件內容大家看看
                ·Velocity-1.5.jar 的 META-INFO\MANIFEST 部份內容
                            Manifest-Version: 1.0
                            Ant-Version: Apache Ant 1.7.0
                            Created-By: Apache Ant
                            Package: org.apache.velocity
                            Build-Jdk: 1.4.2_08
                            Extension-Name: velocity
                      我們看到是用 ant 打包,構建用的JDK是 1.4.2_08,用 1.4 編譯的類在 1.4 JVM 中當然能運行。如果那人用 1.5 的 JDK 來編譯,然后用 JDK 1.4+ANT 來打包就太無聊了。
                ·2.0 spring.jar 的 META-INFO\MANIFEST 部份內容
                            Manifest-Version: 1.0
                            Ant-Version: Apache Ant 1.6.5
                            Created-By: 1.5.0_08-b03 (Sun Microsystems Inc.)
                            Implementation-Title: Spring Framework
                     這下要注意啦,它是用的 JDK 1.5 來編譯的,那么它是否帶了 -target 1.4 或 -target 1.3 來編譯的呢?確實是的,可以查看類的二進制文件,這是最保險的。所在 spring-2.0.jar 也可以在 1.4 JVM 中加載執行。
                ·自已一個項目中用 ant 打的 jar 包的 META-INFO\MANIFEST
                            Manifest-Version: 1.0
                            Ant-Version: Apache Ant 1.7.0
                            Created-By: 1.4.2-b28 (Sun Microsystems Inc.)
                      用的是 JDK 1.4 構建打包的。

          第一第二種辦法能明確知道 major.minor version,而第三種方法應該也沒問題,但是碰到變態構建就難說了,比如誰把那個 META-INFO\MANIFEST 打包后換了也未可知。直接查看類的二進制文件的方法可以萬分保證,準確無誤,就是工具篡改我也認了。

          五:編譯器比較及癥節之所在

          現在不妨從 JDK 1.1 到 JDK 1.7 編譯器編譯出的 class 的默認 minor.major version 吧。(又走到 Sun 的網站上翻騰出我從來都沒用過的古董來)

          JDK 編譯器版本 target 參數 十六進制 minor.major 十進制 minor.major
          jdk1.1.8 不能帶 target 參數 00 03   00 2D 45.3
          jdk1.2.2 不帶(默認為 -target 1.1) 00 03   00 2D 45.3
          jdk1.2.2 -target 1.2 00 00   00 2E 46.0
          jdk1.3.1_19 不帶(默認為 -target 1.1) 00 03   00 2D 45.3
          jdk1.3.1_19 -target 1.3 00 00   00 2F 47.0
          j2sdk1.4.2_10 不帶(默認為 -target 1.2) 00 00   00 2E 46.0
          j2sdk1.4.2_10 -target 1.4 00 00   00 30 48.0
          jdk1.5.0_11 不帶(默認為 -target 1.5) 00 00   00 31 49.0
          jdk1.5.0_11 -target 1.4 -source 1.4 00 00   00 30 48.0
          jdk1.6.0_01 不帶(默認為 -target 1.6) 00 00   00 32 50.0
          jdk1.6.0_01 -target 1.5 00 00   00 31 49.0
          jdk1.6.0_01 -target 1.4 -source 1.4 00 00   00 30 48.0
          jdk1.7.0 不帶(默認為 -target 1.6) 00 00   00 32 50.0
          jdk1.7.0 -target 1.7 00 00   00 33 51.0
          jdk1.7.0 -target 1.4 -source 1.4 00 00   00 30 48.0
          Apache Harmony 5.0M3 不帶(默認為 -target 1.2) 00 00   00 2E 46.0
          Apache Harmony 5.0M3 -target 1.4 00 00   00 30 48.0

          上面比較是 Windows 平臺下的 JDK 編譯器的情況,我們可以此作些總結:

          1) -target 1.1 時 有次版本號,target 為 1.2 及以后都只用主版本號了,次版本號為 0
          2) 從 1.1 到 1.4 語言差異比較小,所以 1.2 到 1.4 默認的 target 都不是自身相對應版本
          3) 1.5 語法變動很大,所以直接默認 target 就是 1.5。也因為如此用 1.5 的 JDK 要生成目標為 1.4 的代碼,光有 -target 1.4 不夠,必須同時帶上 -source 1.4,指定源碼的兼容性,1.6/1.7 JDk 生成目標為 1.4 的代碼也如此。
          4) 1.6 編譯器顯得較為激進,默認參數就為 -target 1.6。因為 1.6 和 1.5 的語法無差異,所以用 -target 1.5 時無需跟著 -source 1.5。
          5) 注意 1.7 編譯的默認 target 為 1.6
          6) 其他第三方的 JDK 生成的 Class 文件格式版本號同對應 Sun 版本 JDK
          7) 最后一點最重要的,某個版本的 JVM 能接受 class 文件的最大主版本號不能超過對應 JDK 帶相應 target 參數編譯出來的 class 文件的版本號

          上面那句話有點長,一口氣讀過去不是很好理解,舉個例子:1.4 的 JVM 能接受最大的 class 文件的主版本號不能超過用 1.4 JDK 帶參數 -target 1.4 時編譯出的 class 文件的主版本號,也就是 48。

          因為 1.5 JDK 編譯時默認 target 為 1.5,出來的字節碼 major.minor version 是 49.0,所以 1.4 的 JVM 是無法接受的,只有拋出錯誤。

          那么又為什么從 1.1 到 1.2、從 1.2 到 1.3 或者從 1.3 到 1.4 的 JDK 升級不會發生 Unsupported major.minor version 的錯誤呢,那是因為 1.2/1.3/1.4 都保持了很好的二進制兼容性,看看 1.2/1.3/1.4 的默認 target 分別為 1.1/1.1/1.2 就知道了,也就是默認情況下1.4 JDK 編譯出的 class 文件在 JVM 1.2 下都能加載執行,何況于 JVM 1.3 呢?(當然要去除使用了新版本擴充的 API 的因素)

          六:找到問題解決的方法

          那么現在如果碰到這種問題該知道如何解決了吧,還會像我所見到有些兄弟那樣,去找個 1.4 的 JDK 下載安裝,然后用其重新編譯所有的代碼嗎?其實大可不必如此費神,我們一定還記得 javac 還有個 -target 參數,對啦,可以繼續使用 1.5 JDK,編譯時帶上參數 -target 1.4 -source 1.4 就 OK 啦,不過你一定要對哪些 API 是 1.5 JDK 加入進來的了如指掌,不能你的 class 文件拿到 JVM 1.4 下就會 method not found。目標 JVM 是 1.3 的話,編譯選項就用 -target 1.3 -source 1.3 了。

          相應的如果使用 ant ,它的 javac 任務也可對應的選擇 target 和 source

          <javac target="1.4" source="1.4" ............................/>

          如果是在開發中,可以肯定的是現在真正算得上是 JAVA IDE 對于工程也都有編譯選項設置目標代碼的。例如 Eclipse 的項目屬性中的 Java Compiler 設置,如圖

          EclipseCompiler.JPG


          自已設定編譯選項,你會看到選擇不同的 compiler compliance level 是,Generated class files compatibility 和 Source compatibility 也在變,你也可以手動調整那兩項,手動設置后你就不用很在乎用的什么版本的編譯器了,只要求他生成我們希望的字節碼就行了,再引申一下就是即使源代碼是用 VB 寫的,只要能編譯成 JVM 能執行的字節碼都不打緊。在其他的 IDE 也能找到相應的設置對話框的。

          其他時候,你一定要知道當前的 JVM 是什么版本,能接受的字節碼主版本號是多少(可對照前面那個表)。獲息當前 JVM 版本有兩種途徑:

          第一:如果你是直接用 java 命令在控制臺執行程序,可以用 java -version 查看當前的 JVM 版本,然后確定能接受的 class 文件版本

          第二:如果是在容器中執行,而不能明確知道會使用哪個 JVM,那么可以在容器中執行的程序中加入代碼 System.getProperty("java.runtime.version"); 或 System.getProperty("java.class.version"),獲得 JVM 版本和能接受的 class 的版本號。

          最后一絕招,如果你不想針對低版本的 JVM 用 target 參數重新編譯所有代碼;如果你仍然想繼續在代碼中用新的 API 的話;更有甚者,你還用了 JDK 1.5 的新特性,譬如泛型、自動拆裝箱、枚舉等的話,那你用 -target 1.4 -source 1.4 就沒法編譯通過,不得不重新整理代碼。那么告訴你最后一招,不需要再從源代碼著手,直接轉換你所正常編譯出的字節碼,繼續享用那些新的特性,新的 API,那就是:請參考之前的一篇日志:Retrotranslator讓你用JDK1.5的特性寫出的代碼能在JVM1.4中運行,我就是這么用的,做好測試就不會有問題的。

          七:再議一個實際發生的相關問題

          這是一個因為拷貝 Tomcat 而產生的 Unsupported major.minor version 49.0 錯誤。情景是:我本地安裝的是 JDK 1.5,然后在網上找了一個 EXE 的 Tomcat 安裝文件安裝了并且可用。后來同事要一個 Tomcat,不想下載或安裝,于是根據我以往的經驗是把我的 Tomcat 整個目錄拷給他應該就行了,結果是拿到他那里瀏覽 jsp 文件都出現 Unsupported major.minor version 49.0 錯誤,可以確定的是他安裝的是 1.4 的 JDK,但我還是有些納悶,先前對這個問題還頗有信心的我傻眼了。慣性思維是編譯好的 class 文件拿到低版本的 JVM 會出現如是異常,可現并沒有用已 JDK 1.5 編譯好的類要執行啊。

          后來仔細看異常信息,終于發現了 %TOMCAT_HOME%\common\lib\tools.jar 這一眉目,因為 jsp 文件需要依賴它來編譯,打來這個 tools.jar 中的一個 class 文件來看看,49.0,很快我就明白原來這個文件是在我的機器上安裝 Tomcat 時由 Tomcat 安裝程序從 %JDK1.5%\lib 目錄拷到 Tomcat 的 lib 目錄去的,造成在同事機器上編譯 JSP 時是 1.4 的 JVM 配搭著 49.0 的 tools.jar,那能不出錯,于是找來 1.4  JDK 的 tools.jar 替換了 Tomcat 的就 OK 啦。

          八:小結

          其實理解 major.minor 就像是我們可以這么想像,同樣是微軟件的程序,32 位的應用程序不能拿到 16 位系統中執行那樣。

          如果我們發布前了解到目標 JVM 版本,知道怎么從 java class 文件中看出 major.minor 版本來,就不用等到服務器報出異常才著手去解決,也就能預知到可能發生的問題。

          其他時候遇到這個問題應具體解決,總之問題的根由是低版本的  JVM 無法加載高版本的 class 文件造成的,找到高版本的 class 文件處理一下就行了。

          posted @ 2009-08-05 16:27 平濤 閱讀(405) | 評論 (0)編輯 收藏

               摘要: VO(value object) 值對象     通常用于業務層之間的數據傳遞,和PO一樣也是僅僅包含數據而已。但應是抽象出的業務對象,可以和表對應,也可以不,這根據業務的需要.個人覺得同DTO(數據傳輸對象),在web上傳遞。     對于數據庫而言,每一個VO對象可以表示出一張表中的一行記錄,此類的名稱要與表的名稱一致。 B...  閱讀全文
          posted @ 2009-08-01 11:12 平濤 閱讀(526) | 評論 (1)編輯 收藏

                 有許多標準和實踐準則可適用于Java開發者,但此處要說的,是每個Java開發者需堅守的基本原則。

            一、為代碼加注釋。雖然每個人都知道這點,但有時卻不自覺忘了履行,今天你“忘了”加注釋了嗎?雖然注釋對程序的功能沒什么“貢獻”,但過一段時間,比如說兩星期之后或者更長,回過頭來看看自己的代碼,說不定已經記不住它是干什么的了。如果這些代碼是你個人的,那還算是走運了,不幸的是,當然了,大多數時候都是別人的不幸,很多時候大家都是在為公司寫代碼,寫代碼的人也許早已經離開了公司,但別忘了一句古話,有來有往嘛,為他人,也為我們自己,請為你的代碼加上注釋。

            二、不要讓事情復雜化。程序員有時候總是對簡單問題想出復雜的解決方案,比如說,在只有五個用戶的程序中引入EJB、對程序實現了并不需要的框架(framework),之類的還有屬性文件、面向對象解決方案、多線程等等。為什么要這樣做呢?也許我們并不知道是否這樣會更好,但這樣做也許可以學到一些新東西,或者讓自己更感興趣一些。如果是不知道為什么這樣做,建議多請教經驗豐富的程序員,如果是為了個人的目的,麻煩讓自己更專業一點。

            三、始終牢記——“少即是好(Less is more)并不總是對的”。代碼效率雖然很重要,但在許多解決方案中,編寫更少的代碼并不能改善這些代碼的效率,請看下面這個簡單的例子:

          if(newStatusCode.equals("SD") && (sellOffDate == null ||
          todayDate.compareTo(sellOffDate)<0 || (lastUsedDate != null &&
          todayDate.compareTo(lastUsedDate)>0)) ||
          (newStatusCode.equals("OBS") && (OBSDate == null ||
          todayDate.compareTo(OBSDate)<0))){
          newStatusCode = "NYP"; 
          }

            能看明白if條件語句是干什么的嗎?能想出來是誰寫的這段代碼嗎?如果把它分成兩段獨立的if語句,是不是更容易理解呢,下面是修改后的代碼:

          if(newStatusCode.equals("SD") && (sellOffDate == null ||
          todayDate.compareTo(sellOffDate)<0 || (lastUsedDate != null &&
          todayDate.compareTo(lastUsedDate)>0))){
          newStatusCode = "NYP"; 
          }else
          if(newStatusCode.equals("OBS") && (OBSDate == null ||
          todayDate.compareTo(OBSDate)<0))
          {
          newStatusCode = "NYP"; 
          }

            是不是讀起來容易多了呢,在此只是多加了一個if和兩個花括號,但代碼的可讀性與可理解性就一下子提高了一大截。

            四、請不要硬編碼。開發者經常有意“忘記”或忽略掉這點,因為有些時候開發日程逼得實在太緊。其實,多寫一行定義靜態變量的代碼能花多少時間呢?

          public class A {
          public static final String S_CONSTANT_ABC = "ABC"; 
          public boolean methodA(String sParam1){
          if (A.S_CONSTANT_ABC.equalsIgnoreCase(sParam1)){
          return true; 
          }
          return false; 
          }
          }

           

            現在,每次需要將“ABC”與其他變量進行比較時,不必記住實際代碼,直接引用A.S_CONSTANT_ABC就行了,而且在今后需要進行修改時,也可在一處修改,不會翻遍整個源代碼逐個修改了。

            五、不要“創造”自己的框架(framework)。確切來說,有數以千計的各種框架存在,而且大多數是開源的,這些框架都是優秀的解決方案,可用于日常程序開發中,我們只需使用這些框架的最新版本就行了,至少表面上要跟上形勢吧。被大家廣為接受的最為明顯的一個例子就是Struts了,這個開源web框架非常適合用在基于web的應用程序中。是不是想開發出自己的Struts呢,還是省點力氣吧,回頭看看第二條——不要讓事情復雜化。另外,如果正在開發的程序只有3個窗口,就不要使用Struts了,對這種程序來說,不需要那么多的“控制”。

            六、不要使用println及字符串連接。通常為了調試方便,開發者喜歡在可能的所有地方都加上System.out.println,也許還會提醒自己回過頭來再來刪除,但有些時候,經常會忘了刪除或者不愿意刪除它們。既然使用System.out.println是為了測試,那么測試完之后,為什么還要留著它們呢,因為在刪除時,很可能會刪除掉真正有用的代碼,所以不能低估System.out.println危害啊,請看下面的代碼:

          public class BadCode {
          public static void calculationWithPrint(){
          double someValue = 0D; 
          for (int i = 0; i <10000; i++) {
          System.out.println(someValue = someValue + i); 
          }
          }
          public static void calculationWithOutPrint(){
          double someValue = 0D; 
          for (int i = 0; i < 10000; i++) {
          someValue = someValue + i; 
          }
          }
          public static void main(String [] n) {
          BadCode.calculationWithPrint(); 
          BadCode.calculationWithOutPrint(); 
          }
          }

            從測試中可以發現,方法calculationWithOutPrint()執行用了0.001204秒,作為對比,方法calculationWithPrint()執行可是用了10.52秒。

            要避免浪費CPU時間,最好的方法是引入像如下的包裝方法:

          public class BadCode {
          public static final int DEBUG_MODE = 1; 
          public static final int PRODUCTION_MODE = 2; 
          public static void calculationWithPrint(int logMode){
          double someValue = 0D; 
          for (int i = 0; i < 10000; i++) {
          someValue = someValue + i; 
          myPrintMethod(logMode, someValue); 
          }
          }
          public static void myPrintMethod(int logMode, double value) {
          if (logMode > BadCode.DEBUG_MODE) { return; }
          System.out.println(value); 
          }
          public static void main(String [] n) {
          BadCode.calculationWithPrint(BadCode.PRODUCTION_MODE); 
          }
          }

           

            另外,字符串連接也是浪費CPU時間的一個大頭,請看下面的示例代碼:

          public static void concatenateStrings(String startingString) {
          for (int i = 0; i < 20; i++) {
          startingString = startingString + startingString; 
          }
          }
          public static void concatenateStringsUsingStringBuffer(String startingString) {
          StringBuffer sb = new StringBuffer(); 
          sb.append(startingString); 
          for (int i = 0; i < 20; i++) {
          sb.append(sb.toString()); 
          }
          }

            在測試中可發現,使用StringBuffer的方法只用了0.01秒執行完畢,而使用連接的方法則用了0.08秒,選擇顯而易見了。

            七、多關注GUI(用戶界面)。再三強調,GUI對商業客戶來說,與程序的功能及效率同等重要,GUI是一個成功程序的最基本部分,而很多IT經理往往都沒注意到GUI的重要性。在現實生活中,許多公司可能為了節省開支,沒有雇用那些有著設計“用戶友好”界面豐富經驗的網頁設計者,此時Java開發者只能依賴他們自身的HTML基本功及在此領域有限的知識,結果,很多開發出來的程序都是“計算機友好”甚于“用戶友好”。很少有開發者同時精通軟件開發及GUI設計,如果你在公司“不幸”被分配負責程序界面,就應該遵守下面三條原則:

            1、 不要再發明一次輪子,即不做無用功。現有的程序可能會有類似的界面需求。
            2、 先創建一個原型。這是非常重要一步,用戶一般想看到他們將使用的東西,而且可以先利用這個原型征求用戶的意見,再慢慢修改成用戶想要的樣子。
            3、 學會換位思考。換句話來說,就是從用戶的角度來審查程序的需求。舉例來講,一個匯總的窗口可以跨頁或者不跨頁,作為一個軟件開發者,可能會傾向于不跨頁,因為這樣簡單一些。但是,從用戶的角度來看,可能不希望看到上百行數據都擠在同一頁上。

            八、文檔需求不放松。每個商業需求都必須記錄在案,這可能聽上去像童話,似乎在現實生活中很難實現。而我們要做的是,不管開發時間多緊迫,不管最終期限多臨近,對每個商業需求都必須記錄在案。

             九、單元測試、單元測試、單元測試。關于什么是單元測試的最好方法,在此不便細說,只是強調,單元測試一定要完成,這也是編程中最基本的原則。當然了,如果有人幫你做單元測試自然是最好,如果沒有,就自己來做吧,當創建一個單元測試計劃時,請遵守以下三條最基本的原則:

            1、 先于編寫類代碼之前編寫單元測試。
            2、 記錄單元測試中的代碼注釋。
            3、 測試所有執行關鍵功能的公有方法,這里不是指set和get方法,除非它們是以自己獨特方式執行set和get方法。

            十、質量,而不是數量。有些時候因為產品問題、期限緊迫、或一些預料之外的事情,導致常常不能按時下班,但一般而言,公司不會因為雇員經常加班而對之表揚和獎勵,公司只看重高質量的工作。如果遵守了前九條原則,你會發現自己寫出的代碼bug少且可維護性高,無形中質量提高了一大步。

          posted @ 2009-07-31 15:38 平濤 閱讀(173) | 評論 (0)編輯 收藏

              Tomcat5 及 Tomcat6 下CP配置。
              主要是根據MLDN的一個實例進行配置的。實例中用的是Tomcat5.5,所以他的CP配置如下:
              第一:將jdbc驅動的jar包,如:MySQL、Oracle的jdbc驅動,拷貝到tomcat中lib下。
              注意:必須是tomcat中lib下,如:D:\Tomcat 6.0\lib,但不能是項目中的WEB-INF/lib下,因為這時tomcat解析不到此類目錄。
              第二:在Tomcat的安裝目錄下conf下找到server.xml,在<host></host>之間加上如下代碼:
           1<Context path="/zhinangtuan" docBase="F:\Eclipse\MyZNTProject\MyZhiNangTuanDemo\WebRoot"
           2        debug="5" reloadable="true" crossContext="true">
           3 
           4     <Logger className="org.apache.catalina.logger.FileLogger"
           5                 prefix="localhost_MysqlTest_log." suffix=".txt"
           6                 timestamp="true"/>
           7                 
           8    <Resource name="jdbc/mldn"  auth="Container" type="javax.sql.DataSource"/>
           9    
          10    <ResourceParams name="jdbc/mldn">
          11        <parameter>
          12            <name>factory</name>
          13            <value>org.apache.commons.dbcp.BasicDataSourceFactory</value>
          14        </parameter>
          15        <parameter>
          16            <name>maxActive</name>
          17            <value>100</value>
          18        </parameter>
          19        <parameter>
          20            <name>maxIdle</name>
          21            <value>30</value>
          22        </parameter>
          23        <parameter>
          24            <name>maxWait</name>
          25            <value>5000</value>
          26        </parameter>
          27        <parameter>
          28            <name>username</name>
          29            <value>root</value>
          30        </parameter>
          31        <parameter>
          32            <name>password</name>
          33            <value>mysqladmin</value>
          34        </parameter>
          35        <parameter>
          36            <name>driverClassName</name>
          37            <value>org.gjt.mm.mysql.Driver</value>
          38        </parameter>
          39        <parameter>
          40            <name>url</name>
          41            <value>jdbc:mysql://localhost:3306/mldn</value>
          42        </parameter>
          43      </ResourceParams>
          44    </Context>
              第一部分為項目的發布路徑。其他的就是數據庫的配置了。
              第三:在Spring的配置文件applicationContext.xml中加上如下代碼:
          1<bean id="dataSource"
          2        class="org.springframework.jndi.JndiObjectFactoryBean"
          3        destroy-method="close">
          4        <property name="jndiName">
          5            <value>java:comp/env/jdbc/mldn</value>
          6        </property>
          7    </bean>

              而我用的Tomcat是6.0.18,在根據這個配置后就會出現javax.naming.NameNotFoundException: Name jdbc is not bound in this Context的錯誤。原因是,Tomcat5和Tomcat6的配置有所區別:
              第一:將jdbc驅動的jar包,拷貝到Tomcat的lib下
              第二:在tomcat中conf下的context.xml文件中<context>與</context>之間加入以下部分:
           1<Resource name="jdbc/mldn"   
           2        auth="Container"       
           3         type="javax.sql.DataSource"
           4         factory="org.apache.tomcat.dbcp.dbcp.BasicDataSourceFactory"
           5         driverClassName="com.mysql.jdbc.Driver"       
           6         url="jdbc:mysql://localhost:3306/mldn?useUnicode=true&amp;characterEncoding=utf-8"       
           7         username="root"       
           8         password="123"       
           9         maxActive="100"       
          10         maxIdle="30"       
          11        maxWait="10000" />
              同時在項目的web.xml中加入如下代碼(網上說可以加也可以不加)
          1<resource-ref>       
          2     <description>DB Connection</description>       
          3     <res-ref-name>jdbc/mldn</res-ref-name>       
          4      <res-type>javax.sql.DataSource</res-type>       
          5      <res-auth>Container</res-auth>       
          6    </resource-ref>

              第三:在Spring的配置文件applicationContext.xml中加上如下代碼:
          1<bean id="dataSource"
          2        class="org.springframework.jndi.JndiObjectFactoryBean"
          3        destroy-method="close">
          4        <property name="jndiName">
          5            <value>java:comp/env/jdbc/mldn</value>
          6        </property>
          7    </bean>

              這樣,Tomcat6下的CP才能正常的運行起來。
          posted @ 2009-07-30 01:21 平濤 閱讀(1762) | 評論 (4)編輯 收藏

              主要是mysql數據庫亂碼問題,關于網頁中的亂碼(JSP頁面亂碼,action亂碼)都好解決,只要頁面的編碼統一就可以了。我比較喜歡用UTF-8編碼,不是很喜歡GBK或GB2312編碼。
                關于JSP頁面及action只需添加一個EncodingFilter就可以了,代碼如下:
           1package cn.zhang.myznt.filter;
           2
           3import java.io.IOException;
           4
           5import javax.servlet.Filter;
           6import javax.servlet.FilterChain;
           7import javax.servlet.FilterConfig;
           8import javax.servlet.ServletException;
           9import javax.servlet.ServletRequest;
          10import javax.servlet.ServletResponse;
          11
          12public class EncodingFilter implements Filter {
          13    private String charset = null;
          14    public void destroy() {
          15
          16    }

          17
          18    public void doFilter(ServletRequest request, ServletResponse response,
          19            FilterChain chain) throws IOException, ServletException {
          20        request.setCharacterEncoding(this.charset);
          21        chain.doFilter(request, response);
          22
          23    }

          24
          25    public void init(FilterConfig arg0) throws ServletException {
          26        this.charset = arg0.getInitParameter("charset");
          27    }

          28
          29}

          30

          在web.xml中注冊這個Filter,注意他的位置必須放在需要調用action及jsp或其他頁面聲明之前
           1<filter>
           2        <filter-name>encoding</filter-name>
           3        <filter-class>cn.zhang.myznt.filter.EncodingFilter</filter-class>
           4        <init-param>
           5            <param-name>charset</param-name>
           6            <param-value>UTF-8</param-value>
           7        </init-param>
           8    </filter>
           9    <filter-mapping>
          10        <filter-name>encoding</filter-name>
          11        <url-pattern>/*</url-pattern>
          12    </filter-mapping>

          同時在連接mysql數據庫的時候也要改為jdbc:mysql://localhost:3306/mldn?useUnicode=true&amp;characterEncoding=utf-8 如果連接在java代碼中使用,請使用&,如果是在xml中使用請寫成&amp;

              關于mysql的collation字符集和mysql的characterSet字符集問題,查了很多資料都說要設置成一樣,我想那只是針對如果你選GBK作為數據庫編碼的時候所用,但如果選用UTF8作為數據庫編碼的時候就不一定正確了。
              今天根據網上的資料將所有的characterSet設置成utf8(可用mysql> SHOW VARIABLES LIKE 'character_set_%';查看),一直在測試亂碼問題解決沒有。在測試過程發現傳遞的中文都是正確的,可就是在MySQL Client中查看的是亂碼,所以繼續找方法解決,但其實這個時候你插進去的中文已經是正常的了,只是在MySQL Client中顯示是亂碼。那么為什么會這樣?MySQL Client設置成UTF8的時候中文不能正常顯示,此時我們應該將charact_set_client :設置成gbk,這樣就可以正常顯示中文了。
              可以在my.ini中

          [mysql]

          default-character-set=gbk


          而下面的default-character-set=utf8,兩個default-character-set的設置是不一樣的。當然如果你選用gbk作為數據庫編碼,是需要設置成一樣的。
          posted @ 2009-07-23 17:40 平濤 閱讀(524) | 評論 (0)編輯 收藏

          tomcat下jsp出現getOutputStream() has already been called for this response異常的原因和解決方法

          在tomcat下jsp中出現此錯誤一般都是在jsp中使用了輸出流(如輸出圖片驗證碼,文件下載等),
          沒有妥善處理好的原因。
          具體的原因就是
          在tomcat中jsp編譯成servlet之后在函數_jspService(HttpServletRequest request, HttpServletResponse response)的最后
          有一段這樣的代碼
          finally {
                if (_jspxFactory != null) _jspxFactory.releasePageContext(_jspx_page_context);
              }
          這里是在釋放在jsp中使用的對象,會調用response.getWriter(),因為這個方法是和
          response.getOutputStream()相沖突的!所以會出現以上這個異常。

          然后當然是要提出解決的辦法,其實挺簡單的(并不是和某些朋友說的那樣--
          將jsp內的所有空格和回車符號所有都刪除掉),

          在使用完輸出流以后調用以下兩行代碼即可:
          out.clear();
          out = pageContext.pushBody();

          最后這里是一個輸出彩色驗證碼例子(這樣的例子幾乎隨處可見)
          imag.jsp

          <%@ page  import="java.awt.*,java.awt.image.*,java.util.*,javax.imageio.*" %>
          <%@ page import="java.io.OutputStream" %>
          <%!
          Color getRandColor(int fc,int bc){
          Random random = new Random();
          if(fc>255) fc=255;
          if(bc>255) bc=255;
          int r=fc+random.nextInt(bc-fc);
          int g=fc+random.nextInt(bc-fc);
          int b=fc+random.nextInt(bc-fc);
          return new Color(r,g,b);
          }
          %>
          <%
          try{
          response.setHeader("Pragma","No-cache");
          response.setHeader("Cache-Control","no-cache");
          response.setDateHeader("Expires", 0);
          int width=60, height=20;
          BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
          OutputStream os=response.getOutputStream();
          Graphics g = image.getGraphics();
          Random random = new Random();
          g.setColor(getRandColor(200,250));
          g.fillRect(0, 0, width, height);

          g.setFont(new Font("Times New Roman",Font.PLAIN,18));
          g.setColor(getRandColor(160,200));
          for (int i=0;i<155;i++)
          {
          int x = random.nextInt(width);
          int y = random.nextInt(height);
          int xl = random.nextInt(12);
          int yl = random.nextInt(12);
          g.drawLine(x,y,x+xl,y+yl);
          }
          String sRand="";
          for (int i=0;i<4;i++){
          String rand=String.valueOf(random.nextInt(10));
          sRand+=rand;
          g.setColor(new Color(20+random.nextInt(110),20+random.nextInt(110),20+random.nextInt(110)));
          g.drawString(rand,13*i+6,16);
          }
          session.setAttribute("rand",sRand);
          g.dispose();

          ImageIO.write(image, "JPEG",os);
          os.flush();
          os.close();
          os=null;
          response.flushBuffer();
          out.clear();
          out = pageContext.pushBody();
          }
          catch(IllegalStateException e)
          {
          System.out.println(e.getMessage());
          e.printStackTrace();
          }%>

          posted @ 2009-07-23 08:40 平濤 閱讀(475) | 評論 (0)編輯 收藏

          這個錯誤主要是struts-config.xml或web.xml上配置出錯
          仔細對比后就可以解決
          struts-config.xml
          <controller
            processorClass="org.springframework.web.struts.DelegatingRequestProcessor">
           </controller>

            <plug-in className="org.springframework.web.struts.ContextLoaderPlugIn">
              <set-property property="contextConfigLocation" value="/WEB-INF/classes/applicationContext.xml" />
            </plug-in>

          web.xml
          <context-param>
             <param-name>contextConfigLocation</param-name>
             <param-value>/WEB-INF/classes/applicationContext.xml</param-value>
            </context-param>
           
            <servlet>
             <servlet-name>context</servlet-name>
             <servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>
             <load-on-startup>1</load-on-startup>
            </servlet>
          主要是要保證contextConfigLocation名字沒有錯誤即可。
          仔細對比 要一字不差
          posted @ 2009-07-22 23:15 平濤 閱讀(297) | 評論 (0)編輯 收藏

               摘要:   二、Struts2和Spring2.5的整合 (1)在web.xml中加入Struts2過濾器 <filter>   <filter-name>struts2</filter-name> ...  閱讀全文
          posted @ 2009-07-22 17:02 平濤 閱讀(1664) | 評論 (0)編輯 收藏

               摘要:   一、Srping 與 Hibnernate 的整合 (4)開發        這里采用自底向上的開發模式。        (a)底層的開發        打開DB Explorer,連接上數據庫,...  閱讀全文
          posted @ 2009-07-20 22:04 平濤 閱讀(1589) | 評論 (2)編輯 收藏

                   使用的版本為:Struts 2.1.6 + Spring 2.5 + Hibernate 3.2

                  開發環境為:MyEclipse 7.1.1 + Jdk 1.6.0_13 + Tomcat 6.0.18 + MySQL 6.0.10 alpha

                 所用表:test.employee

           

          id        int(11)       NO   PRI | NULL     auto_increment

          name     varchar(
          255) YES        NULL                   

          address   varchar(
          255)   YES      NULL    

          phone    varchar(
          255)   YES       NULL  

                 這個實例主要是實現簡單的CRUD方法。在搭建環境前,需要處理一個問題:因為Spring2.5 AOP Liberaries里的asm2.2.3.jarHiberate中的生成代理用的asm.jar沖突,我們需要刪除asm2.2.3.jar,不然就會發生異常:java.lang.NoClassDefFoundError: org/objectweb/asm/CodeVisitor 。具體的刪除方法(避免在Tomcat中的lib下刪除了,然后又重新發布項目時此jar又重新發不到lib下):在MyEclipse中【WindowàPreferencesàMyEclipse Enterprise WorkbenchàProject CapabilitiesàSpring】在spring2.5 AOP Libraries中刪除asm2.2.3.jar

                 在開發S2SH架構時,先整合Spring + Hibernate ,然后再整合Struts + Spring 完成S2SH架構的搭建。

          一、Srping Hibnernate 的整合

                 1)添加Hibernate支持

                 在添加Hibernate支持之前,我們先在【MyEclipse DataBase Explorer】中設置數據庫連接。所用的mysql/jdbc的jar包為mysql-connector-java-5.1.8-bin.jar。


              注意:
          MySQLURL為:jdbc:mysql://localhost:3306/test(我所根據學的視頻案例是連接oracle的,根據視頻中所學的時候,url沒有添加數據庫,在測試過程當中怎么樣也找到實體,原因就是因為在url中沒有添加數據庫的選擇。)

                 后面按照默認設置一直到【finish】即可。

                 a)在導航中找到【MyEclipse】,然后選擇【Add Hibernate Capbilities

          這里可以直接選擇jar包拷貝到lib下面,也可以先不拷貝,到時候MyEclipse會自動拷貝到lib下。

          b)由于Hibernate要與Spring整合,所以在這一步無需再配置更詳細的hibernate.cfg.xml

                 在這個按列中,我們使用JDBC Driver模式。

          c)不創建sessionFactory。然后【finish


          2)添加Spring支持

          a)在導航中找到【MyEclipse】,然后選擇【Add Spring Capbilities

          添加5個類庫。

          b)生成applicationContext.xml,建議將其放在WEB-INF下面。

          c)創建sessionFactory。然后【finish

          3)整合SpringHibernate

          打開applicationContext,在當中增加DataSourceSessionFactory

          <bean id="sessionFactory"

              class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">

                 <property name="configLocation"

                     value="classpath:hibernate.cfg.xml">

                 </property>

          </bean>

          先將原先自動生成的上述代碼刪除。

          然后再Outline的導航中選擇【New DataSource and SessionFactory

          后面要用到Mapping Resources,所以你可以在這里隨便填一個值進去。

          最后添加部分屬性(添加了
          hibernate.show_sql)后形成代碼如下:

          <bean id="dataSource"

                 class="org.apache.commons.dbcp.BasicDataSource">

                 <property name="driverClassName"

                     value="com.mysql.jdbc.Driver">

                 </property>

                 <property name="url" value="jdbc:mysql://localhost:3306/test"></property>

                 <property name="username" value="root"></property>

                 <property name="password" value="123"></property>

              </bean>

              <bean id="sessionFactory"

                  class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">

                 <property name="dataSource">

                     <ref bean="dataSource" />

                 </property>

                 <property name="hibernateProperties">

                     <props>

                        <prop key="hibernate.dialect">

                            org.hibernate.dialect.MySQLDialect

                        </prop>

                        <prop key="hibernate.show_sql">true</prop>

                     </props>

                 </property>

                 <property name="mappingDirectoryLocations">

                     <list>

                        <value>file:src</value>

                     </list>

                 </property>

              </bean>

          自此已經完成了SpringHibernate的整合。

          posted @ 2009-07-20 11:17 平濤 閱讀(5625) | 評論 (1)編輯 收藏

          僅列出標題
          共2頁: 1 2 下一頁 
          主站蜘蛛池模板: 增城市| 滨海县| 容城县| 伊吾县| 青田县| 东莞市| 务川| 民丰县| 平利县| 民乐县| 鄯善县| 丁青县| 衡南县| 原阳县| 乌鲁木齐市| 江门市| 娄烦县| 双柏县| 平湖市| 全椒县| 彩票| 沭阳县| 夏河县| 瓦房店市| 金川县| 深州市| 上饶市| 鹤峰县| 新民市| 绵竹市| 德昌县| 报价| 阿坝| 金坛市| 霍山县| 睢宁县| 太保市| 云南省| 石城县| 鄢陵县| 桐庐县|