<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    posts - 120,  comments - 19,  trackbacks - 0
     
    Java Mail API簡介
    JavaMail API是一種可選的、能用于讀取、編寫和發(fā)送電子消息的包(標準擴展)。您可使用這種包創(chuàng)建郵件用戶代理(Mail User Agent ,MUA) 類型的程序,它類似于Eudora、Pine及Microsoft Outlook這些郵件程序。其主要目的不是像發(fā)送郵件或其他郵件傳輸代理(Mail Transfer Agent,MTA)類型的程序那樣用于傳輸、發(fā)送和轉(zhuǎn)發(fā)消息。換句話說,用戶可以與MUA類型的程序交互,以閱讀和撰寫電子郵件。MUA依靠MTA處理實際的發(fā)送任務(wù)。
    JavaMail API的設(shè)計是,為收發(fā)信息提供與協(xié)議無關(guān)的訪問。方式是把該API劃分成兩個部分:
    · 該API的第一個部分是本課程的重點?;旧鲜侨绾伟l(fā)送和接收獨立于提供程序/協(xié)議的消息。
    · 第二個部分則使用特定的協(xié)議語言,如:SMTP、POP、IMAP和NNTP。如果要讓JavaMail API與服務(wù)器通信,就需要為之提供協(xié)議。由于Sun公司對特定協(xié)議提供程序有充分的介紹,用戶可以免費獲取,所以本課程沒有介紹創(chuàng)建特定協(xié)議提供程序的內(nèi)容。
    復(fù)習相關(guān)協(xié)議
    在學習JavaMail API的深層知識之前,讓我們回過頭來看一看在該API中使用的協(xié)議,本質(zhì)上有4種人們常用的協(xié)議:
    · SMTP
    · POP
    · IMAP
    · MIME
    您還需要了解NNTP及其他一些協(xié)議。理解這些協(xié)議的基本原理有助于您理解如何使用JavaMail API。而該API的設(shè)計要與協(xié)議無關(guān),所以不能克服這些基礎(chǔ)協(xié)議的限制。如果選用的協(xié)議不支持某種功能,那么JavaMail API也無法在其上添加這種功能。(正如您一會兒就會看到的,在操作POP協(xié)議時,常常會碰到這種問題)。
    SMTP
    簡單郵件傳輸協(xié)議(SMTP)是用于傳送電子郵件的機制。在JavaMail API環(huán)境中,您的基于JavaMail的程序將與您公司或Internet服務(wù)提供商(ISP)的SMTP服務(wù)器通信。該SMTP服務(wù)器將會把消息轉(zhuǎn)發(fā)給用作接收消息的SMTP服務(wù)器,最后用戶可通過POP或IMAP協(xié)議獲取該消息。由于支持身份驗證,所以不需要SMTP服務(wù)器是一種開放的轉(zhuǎn)發(fā)器,但需要確保SMTP服務(wù)器配置正確。JavaMail API中沒有集成用于處理諸如配置服務(wù)器以轉(zhuǎn)發(fā)消息或添加/刪除電子郵件帳戶這一類任務(wù)的功能。
    POP
    POP的含義是郵局協(xié)議,當前的版本為3,也稱作POP3,該協(xié)議是在RFC 1939中定義的。POP是Internet上的大多數(shù)人用來接收郵件的機制。它為每個用戶的每個郵箱定義支持,這是它所做的全部工作,也是大多數(shù)問題的根源。在使用POP協(xié)議時,人們熟悉的很多功能,如查看收到了多少新郵件消息的功能,POP根本不支持。這些功能都內(nèi)置到諸如Eudora或Microsoft Outlook之類的郵件程序中,能為您記住接收的上一封郵件,以及計算有多少新郵件這類信息。因此,使用JavaMail API時,如果想獲取這類信息,將需要由自己進行計算。
    IMAP
    IMAP是用于接收消息的更加高級的協(xié)議,它是在RFC 2060中定義的。IMAP的含義是“Internet消息訪問協(xié)議”,當前版本是第4版,也稱作IMAP4。使用IMAP時,您的郵件服務(wù)器必須支持該協(xié)議。您不能只是簡單地把程序轉(zhuǎn)變?yōu)橹С諭MAP,而不是支持POP,就指望能支持IMAP中的一切。假定您的郵件服務(wù)器支持IMAP,那么基于JavaMail的程序就可利用在服務(wù)器上擁有多個文件夾的用戶,并且這些文件夾可以被多個用戶共享的功能。
    由于IMAP協(xié)議具有更高級的功能,您也許會想IMAP應(yīng)該被每一個人使用,但事實不是這樣。因為IMAP會加重郵件服務(wù)器的負荷,它需要服務(wù)器接收新消息,發(fā)送消息給請求的用戶,并在多個文件夾中為每個用戶維護這些消息。而這要集中備份,因而長期下去用戶的文件夾會變得越來越大,當磁盤空間用光了時,每個人都會遭受損失。而使用POP協(xié)議時,已保存消息可以解除服務(wù)器的重負。
    MIME
    MIME的含義是“多用途的網(wǎng)際郵件擴充協(xié)議”。它不是一種郵件傳輸協(xié)議,相反,它定義傳輸?shù)膬?nèi)容:消息的格式、附件等。許多文檔都定義了MIME協(xié)議,包含:RFC 822、RFC 2045、RFC 2046和RFC 2047。作為JavaMail API的用戶,一般不需要擔心這些格式。但是,這些格式確實存在,并為您的程序所用。
    NNP和其他協(xié)議
    由于JavaMail API分開了提供程序和其他部分,所以您可以輕松地為附加協(xié)議添加支持。Sun公司提供第3方提供程序清單,這些提供程序要利用 Sun公司不支持的少見的協(xié)議。在這份清單中,您將會看到對NNTP(網(wǎng)絡(luò)新聞傳輸協(xié)議)[新聞組]、S/MIME(安全多用途的網(wǎng)際郵件擴充協(xié)議)及其他協(xié)議的提供支持的第3方提供程序
    安裝
    目前有兩種版本的JavaMail API最常用:1.2和1.1.3。本課程中的所有例子都適用于這兩種版本。其中JavaMail API 1.2是最新的,而JavaMail API 1.1.3中包含了Java 2企業(yè)版(J2EE)平臺1.2.1版,所以它仍然很常用。使用JavaMail API的版本會對您的下載和安裝產(chǎn)生一些影響。這兩種版本的JavaMail API都能與JDK 1.1.6、Java 2標準版(J2SE)平臺1.2.x和1.3.x協(xié)同工作。
    注意:在安裝了Sun公司的JavaMail工具后,會在演示目錄下看到許多示例程序。
    安裝JavaMail 1.2
    要使用JavaMail 1.2 API,可以下載JavaMail 1.2工具,然后解壓縮javamail-1_2.zip文件,并把mail.jar文件添加到典型安裝路徑下。JavaMail 1.2工具帶有SMTP、IMAP4和POP3提供程序以及核心類。
    安裝完JavaMail 1.2后,再安裝JavaBeans Activation Framework。
    安裝JavaMail 1.1.3
    要使用JavaMail 1.1.3 API,可以下載JavaMail 1.1.3工具,然后解壓縮javamail1_1_3.zip文件,并把mail.jar文件添加到典型安裝路徑下。JavaMail 1.1.3工具帶有SMTP和IMAP4提供程序以及核心類。
    如果您想用JavaMail 1.1.3訪問POP服務(wù)器,需要下載并安裝POP3提供程序。Sun公司擁有一個獨立于 JavaMail 工具的提供程序。在下載并解壓縮pop31_1_1.zip文件后,也還需要把pop3.jar添加到典型安裝路徑下。
    安裝完JavaMail 1.1.3后,再安裝JavaBeans Activation Framework。
    安裝JavaBeans Activation Framework
    JavaMail API的所有版本都需要JavaBeans Activation Framework(JavaBeans激活框架),這種框架提供了對輸入任意數(shù)據(jù)塊的支持,并能相應(yīng)地對其進行處理??瓷先バЧ孟癫惶?,但該框架是在當今的許多瀏覽器和郵件工具中可以找到的基本MIME類型支持。下載該框架后,解壓縮jaf1_0_1.zip文件,并將activation.jar文件添加到典型安裝路徑下。
    對于JavaMail 1.2用戶,現(xiàn)在應(yīng)該把mail.jar和activation.jar文件添加到典型安裝路徑下。
    對于JavaMail 1.1.3用戶,現(xiàn)在應(yīng)該把mail.jar、pop3.jar和activation.jar添加到典型安裝路徑下。如果您不打算使用POP3,就不需要把pop3.jar文件添加到典型安裝路徑下。
    如果您不想更改安裝路徑環(huán)境變量,可以把JAR文件復(fù)制到Java運行時環(huán)境(JRE)目錄下的lib/ext目錄下。例如,對于J2SE 1.3版本,Windows平臺上的默認目錄應(yīng)該是C:\jdk1.3\jre\lib\ext。
    使用Java 2企業(yè)版
    如果您使用的是J2EE,則在使用基本JavaMail API時,不需要做什么特殊的工作;JavaMail API帶有J2EE類。只要確保j2ee.jar文件位于典型安裝路徑下,并完成了所有的設(shè)置工作。
    對于J2EE 1.2.1,POP3提供程序是單獨提供的,因此需要下載該提供程序,并按安裝JavaMail 1.1.3的步驟,在J2EE 1.2.1中包含POP3提供程序。J2EE 1.3的用戶會獲得J2EE和POP3提供程序,因而不需要對POP3提供程序執(zhí)行獨立安裝。使用這兩種版本的J2EE用戶,都不需要安裝JavaBeans Activation Framework。
    練習
    設(shè)置您的 JavaMail 環(huán)境。
    復(fù)習核心類
    在開始深入研究JavaMail類之前,首先讓用戶瀏覽一下構(gòu)成API的核心類:會話、消息、地址、驗證程序、傳輸,存儲和文件夾。所有這些類都可以在JavaMail API即javax.mail的頂層包中找到,盡管您將頻繁地發(fā)現(xiàn)您自己使用的子類是在javax.mail.internet包中找到的。
    Session類
    Session類定義了一個基本的郵件會話。通過該會話可讓別的工作順利執(zhí)行。Session對象利用java.util.Properties對象獲取諸如郵件服務(wù)器、用戶名、密碼等信息,以及其他可在整個應(yīng)用程序中共享的信息。
    Session類的構(gòu)造器是私有的。您可以獲得一個可被getDefaultInstance()方法共享的單一的默認會話:
    Properties props = new Properties();
    // fill props with any information
    Session session = Session.getDefaultInstance(props, null);
    或者,您可以用getInstance()方法創(chuàng)建一個獨特的會話:
    Properties props = new Properties();
    // fill props with any information
    Session session = Session.getInstance(props, null);
    這兩種情形下的null參數(shù)都是一種Authenticator對象,它不是在此時使用的。詳細信息請參閱其后的“Autherticator”一節(jié)。
    在大多數(shù)情況下,使用共享會話就足夠了,即使為多個用戶郵箱處理郵件會話也是如此。您可以在通信過程的后面一步添加上用戶名和密碼的組合,并保持所有的一切是獨立的。
    Message類
    一旦創(chuàng)建了自己的Session對象,就是該去創(chuàng)建要發(fā)送的消息的時候了。這時就要用到消息類型。作為一個抽象類,您必須操作一個子類,在大多數(shù)情況下,該子類是javax.mail.internet.MimeMessage。一個MimeMessage是一種理解MIME類型和報頭(在不同的RFC文檔中均有定義)的消息。消息的報頭被嚴格限制成只能使用US-ASCII字符,盡管非ASCII字符可以被編碼到某些報頭字段中。
    可以通過將Session對象傳遞給MimeMessage構(gòu)造器的方法來創(chuàng)建消息:
    MimeMessage message = new MimeMessage(session);
    注意:還有其他的構(gòu)造器,像用于創(chuàng)建消息的源于RFC822格式化的輸入流的構(gòu)造器。
    一旦創(chuàng)建了消息,就可以設(shè)置其各個部分,如Message(消息)實現(xiàn)Part(部分)接口(以MimeMessage實現(xiàn)MimePart)。設(shè)置內(nèi)容的基本機制是setContent()方法,它帶有表示內(nèi)容和MIME類型的參數(shù):
    message.setContent("Hello", "text/plain");
    但是,如果正在使用 MimeMessage,并且您的消息是純文本,那么您就可以使用setText()方法。該方法只需要一個表示實際內(nèi)容的參數(shù),默認的MIME類型為純文本:
    message.setText("Hello");
    對于純文本消息,setText()方法更常常被用來設(shè)置內(nèi)容。要發(fā)送其他類型的消息,如HTML消息,就要使用setContent方法()?,F(xiàn)在用的更多的是HTML消息。
    要設(shè)置主題,可以使用setSubject()方法:
    message.setSubject("First");
    Address類
    一旦創(chuàng)建了會話和消息,并為消息填充了內(nèi)容,就需要用Address類為您的信件標上地址了。同Message類一樣,Address類也是一種抽象類。您可以使用javax.mail.internet.InternetAddress類。
    要創(chuàng)建只帶有電子郵件地址的地址,可以把電子郵件地址傳遞給Address類的構(gòu)造器:
    Address address = new InternetAddress("president@whitehouse.gov");
    如果想讓一個名字出現(xiàn)在電子郵件地址后,也可以將其傳遞給構(gòu)造器:
    Address address = new InternetAddress("president@whitehouse.gov", "George Bush");
    您要為消息的from(發(fā)送者)字段和to(接收者)字段創(chuàng)建地址對象。除非您的郵件服務(wù)器阻止這樣做,否則要在發(fā)送的消息中注明該消息的發(fā)送者。
    一旦創(chuàng)建好了地址,有兩種方法可讓您將地址與消息連接起來。為了鑒別發(fā)送者,您可以使用setFrom()和setReplyTo()方法。
    message.setFrom(address)
    如果您的消息需要顯示多個地址來源,則可以使用addFrom()方法:
    Address address[] = ...;
    message.addFrom(address);
    為了鑒別消息接收者,您可以使用addRecipient()方法。該方法除了需要一個地址參數(shù)外,還需要一個Message.RecipientType屬性(消息的接收類型)。
    message.addRecipient(type, address)
    地址的3種預(yù)定義類型如下:
    · Message.RecipientType.TO
    · Message.RecipientType.CC
    · Message.RecipientType.BCC
    因此,如果一條消息將發(fā)送給副總統(tǒng),同時還將發(fā)送該消息的副本給第一夫人,則采用下面的代碼:
    Address toAddress = new InternetAddress("vice.president@whitehouse.gov");
    Address ccAddress = new InternetAddress("first.lady@whitehouse.gov");
    message.addRecipient(Message.RecipientType.TO, toAddress);
    message.addRecipient(Message.RecipientType.CC, ccAddress);
    JavaMail API沒有提供檢查電子郵件地址有效性的機制。您可以自己編寫支持掃描有效字符(在RFC 822文檔中所定義的)的程序或檢驗MX(郵件交換)記錄,這些都超越了JavaMail API的范圍。
    Authenticator類
    java.net類一樣,JavaMail API可以利用Authenticator(驗證程序)類通過用戶名和密碼來訪問受保護的資源。對于JavaMail API來說,這種受保護的資源是指郵件服務(wù)器。JavaMail的Authenticator類可以在javax.mail包中找到,并有別于同名的java.net類。當JavaMail API在Java 1.1下工作時,JavaMail和java.net不會共享同一個Authenticator類名稱,這是因為Java 1.1中不含有java.net。
    要使用Authenticator類,您可以使用該抽象類的子類,并通過getPasswordAuthentication()方法返回一個PasswordAuthentication實例。在創(chuàng)建時,您必須用會話記錄Authentication類。其后,當需要進行身份驗證時,會通知您的Authenticator。會彈出一個窗口,或從一個配置文件(盡管不加密就不安全)中讀取用戶名和密碼,并把它們作為一個PasswordAuthentication對象返回給調(diào)用程序
    Properties props = new Properties();
    // fill props with any information
    Authenticator auth = new MyAuthenticator();
    Session session = Session.getDefaultInstance(props, auth);
    Transport類
    發(fā)送消息的最后一步操作是使用Transport類。該類使用特定于協(xié)議(通常是SMTP)的語言來發(fā)送消息。它是一個抽象類,其操作與Session類有些相似。您可以通過只調(diào)用靜態(tài)的send()方法來使用該類的默認版本:
    Transport.send(message);
    或者,您可以從用于您的協(xié)議的會話中獲取一個特定的實例,然后傳遞用戶名和密碼(不必要時可以為空)并發(fā)送消息,最后關(guān)閉連接:
    message.saveChanges(); // implicit with send()
    Transport transport = session.getTransport("smtp");
    transport.connect(host, username, password);
    transport.sendMessage(message, message.getAllRecipients());
    transport.close();
    當您需要發(fā)送多個消息時,建議采用后一種方法,因為它將保持消息間活動服務(wù)器的連接。而基本的send()機制會為每一個方法調(diào)用都建立一條獨立的連接。
    注意:要查看經(jīng)過郵件服務(wù)器郵件命令,可以用session.setDebug(true)方法設(shè)置調(diào)試標志。
    Store和Folder類
    使用Session類來獲取消息,開始時與發(fā)送消息很相似。但是,在獲取會話后,很有可能使用用戶名和密碼或Authenticator類來連接Store類。與Transport類一樣,您要告訴Store類將使用什么協(xié)議:
    // Store store = session.getStore("imap");
    Store store = session.getStore("pop3");
    store.connect(host, username, password);
    在連接Store類后,就可以獲取一個Folder類,在讀取其中的消息前必須先打開該類。
    Folder folder = store.getFolder("INBOX");
    folder.open(Folder.READ_ONLY);
    Message message[] = folder.getMessages();
    對于POP3協(xié)議,惟一可用的文件夾是INBOX。如果使用的是IMAP協(xié)議,則可以使用其他的文件夾。
    注意:Sun公司的提供程序本來想提供方便。而Message message[]=folder.getMessages();這條語句卻是一種從服務(wù)器逐條讀取消息的緩慢操作,所以僅當您確實需要獲取消息部分(該內(nèi)容是所檢索消息的內(nèi)容)時可以使用這條語句。
    一旦讀取消息,就可以使用getContent()方法獲取其內(nèi)容,或使用writeTo()方法將其內(nèi)容寫到一個流中。getContent()方法只獲取消息內(nèi)容,而writeTo()方法則還會輸出報頭。
    System.out.println(((MimeMessage)message).getContent());
    一旦您閱讀完郵件,就可以關(guān)閉對文件夾和存儲的連接。
    folder.close(aBoolean);
    store.close();
    傳遞給文件夾的close()方法的布爾變量指定了是否通過清除已刪除的消息來更新文件夾。
    繼續(xù)前進
    實際上,理解使用這7個類的方式,是使用JavaMail API處理幾乎所有事情所需要的全部內(nèi)容。用這7個類以外的方式構(gòu)建的JavaMail API,其大多數(shù)功能都是以幾乎完全相同或特定的方式來執(zhí)行任務(wù)的,就好像內(nèi)容是附件。特定的任務(wù),如:搜索、隔離等將在后面進行介紹。
    使用JavaMail API
    您已經(jīng)看到了如何操作JavaMail API的核心部分。在下面幾節(jié)中,您將學習如何連接幾個部分以執(zhí)行特定的任務(wù)。
    發(fā)送消息
    發(fā)送電子郵件消息涉及到獲取會話、創(chuàng)建和填充消息并發(fā)送消息這些操作。您可以在獲取Session時,通過為要傳遞的Properties對象設(shè)置mail.smtp.host屬性來指定您的SMTP服務(wù)器。
    String host = ...;
    String from = ...;
    String to = ...;
    // Get system properties
    Properties props = System.getProperties();
    // Setup mail server
    props.put("mail.smtp.host", host);
    // Get session
    Session session = Session.getDefaultInstance(props, null);
    // Define message
    MimeMessage message = new MimeMessage(session);
    message.setFrom(new InternetAddress(from));
    message.addRecipient(Message.RecipientType.TO,
    new InternetAddress(to));
    message.setSubject("Hello JavaMail");
    message.setText("Welcome to JavaMail");
    // Send message
    Transport.send(message);
    您應(yīng)該在try-catch塊中編寫代碼,以在創(chuàng)建消息并發(fā)送它時可以拋出一個異常。
    練習
    發(fā)送您的第一個消息
    獲取消息
    對于閱讀郵件來說,首先您要獲取一個會話,然后獲取并連接到一個相應(yīng)的用于您的收件箱的存儲上,接著打開相應(yīng)的文件夾,再獲取消息。同時,不要忘記了操作完成后關(guān)閉連接。
    String host = ...;
    String username = ...;
    String password = ...;
    // Create empty properties
    Properties props = new Properties();
    // Get session
    Session session = Session.getDefaultInstance(props, null);
    // Get the store
    Store store = session.getStore("pop3");
    store.connect(host, username, password);
    // Get folder
    Folder folder = store.getFolder("INBOX");
    folder.open(Folder.READ_ONLY);
    // Get directory
    Message message[] = folder.getMessages();
    for (int i=0, n=message.length; i<n; i++) {
    System.out.println(i + ": " + message[i].getFrom()[0]
    + "\t" + message[i].getSubject());
    }
    // Close connection
    folder.close(false);
    store.close();
    每一條消息執(zhí)行何種操作取決于自己決定。上面的代碼塊只是顯示了消息的發(fā)送者和主題。從技術(shù)上講,發(fā)送者地址列表可以為空,此時getFrom()[0]調(diào)用會拋出一個異常。
    為了顯示整條消息,您可以提示用戶在看完消息的發(fā)送者和主題字段后,如果想看到消息的內(nèi)容,可以再調(diào)用消息的writeTo()方法。
    BufferedReader reader = new BufferedReader (
    new InputStreamReader(System.in));
    // Get directory
    Message message[] = folder.getMessages();
    for (int i=0, n=message.length; i<n; i++) {
    System.out.println(i + ": " + message[i].getFrom()[0]
    + "\t" + message[i].getSubject());
    System.out.println("Do you want to read message? " +
    "[YES to read/QUIT to end]");
    String line = reader.readLine();
    if ("YES".equals(line)) {
    message[i].writeTo(System.out);
    } else if ("QUIT".equals(line)) {
    break;
    }
    }


    發(fā)送附件
    MimeMessage message =
    ???????? new MimeMessage(session);
    ????? message.setFrom(
    ???????? new InternetAddress(from));
    ????? message.addRecipient(
    ???????? Message.RecipientType.TO,
    ???????? new InternetAddress(to));
    ????? message.setSubject(
    ???????? "Hello JavaMail Attachment");

    ????? // create the message part
    MimeBodyPart messageBodyPart =
    ???????? new MimeBodyPart();

    ????? //fill message
    ????? messageBodyPart.setText("Hi");

    ????? Multipart multipart = new MimeMultipart();
    ????? multipart.addBodyPart(messageBodyPart);

    ????? // Part two is attachment
    ????? messageBodyPart = new MimeBodyPart();
    ????? DataSource source =
    ???????? new FileDataSource(fileAttachment);
    ????? messageBodyPart.setDataHandler(
    ???????? new DataHandler(source));
    ????? messageBodyPart.setFileName(fileAttachment);
    ????? multipart.addBodyPart(messageBodyPart);

    ????? // Put parts in message
    ????? message.setContent(multipart);

    ????? // Send the message
    ????? Transport.send( message );
    posted @ 2006-01-24 15:14 阿成 閱讀(401) | 評論 (0)編輯 收藏
    ??????????最近接手一個小程序,其實功能說起來不復(fù)雜??蓪τ谖疫@剛接觸J2EE的新手來說,不是那么容易的。因為對遇到的問題,很多時候需要很長時間才能解決,有時無從下手。雖然有老員工幫忙,但也不好意思總麻煩別人。眼看著一天一天過去,程序完不成,還挺不爽的。只有努力學習,趕緊提高自己的技術(shù),才能使程序員生活好一點。
    posted @ 2006-01-24 14:49 阿成 閱讀(174) | 評論 (0)編輯 收藏

    ???????我是2005年從西電(可能很多人不知道這個學校)畢業(yè)的,簽了一家北京的公司,搞IT的,我在的部門是對公司進行信息化的,開始了程序員的生活,說實話當時找個北京的工作不太容易。
    ???????過的很快,工作差不多半年了。技術(shù)方面也有了提高。但開始感覺到還有很多很多要學。
    ??????比如說話,以前沒有太多的重視,在學校時同學之間無話不談,工作了以后變化很多。首先是沒那沒隨便了,而且說話要注意。我從小比較內(nèi)向,覺得自己性格也有些直,怎沒想的就怎沒說。這樣會得罪人。最近就在說話上沒注意,讓別人不高興。自己也很后悔.
    ???????我這時才對媽媽說過的一句話有更深的體會;
    ?????????一萬句話不一定能為下一個人(北京話,“為人”指交個朋友,別人對自己有好印象之類的)
    ?????????一句話能得罪一個人。
    ??????所以自己要在說話這方面有所改進了,否則會處處碰壁。

    posted @ 2006-01-20 21:15 阿成 閱讀(193) | 評論 (0)編輯 收藏
    ???本人簡介:
    ???出生在北京市房山區(qū),一個普通的農(nóng)民家庭,父母親,哥哥和我。

    ???05年畢業(yè)于西安電子科技大學計算機學院。
    ???宿舍7個人,除了我那六個哥們兒都上研了,那是相當?shù)拿桶。臀乙粋€工作了,好像有點頹廢



    在blog火熱的今天,我也來湊湊熱鬧,開始寫blog,對平時工作進行一些總結(jié)。

    主要寫一下技術(shù)的方面的心得,經(jīng)驗總結(jié),好文轉(zhuǎn)載。
    posted @ 2006-01-20 20:43 阿成 閱讀(405) | 評論 (2)編輯 收藏
    Oracle已經(jīng)內(nèi)建了許多函數(shù),不同的函數(shù)有不同的作用和用法,有的函數(shù)只能作用在一個記錄行上,有的能夠作用在多個記錄行上,不同的函數(shù)可能處理不同的數(shù)據(jù)類型。常見的有兩類,單行函數(shù)和分組函數(shù) 。

    單行函數(shù):

    單行函數(shù)

    分類 函數(shù) 功能 示例
    字符函數(shù) LPAD(<c1>,<i>[,<c2>]) 在字符串c1的左邊添加字符串c2直到c1字符串的長度等于i。

    SELECT  LPAD(‘Hello!’,8,’ ’) leftpad,RPAD(‘Hello!’,8,’ ’) rightpad

    FROM DUAL;

     

    RPAD(<c1>,<i>[,<c2>]) 在字符串c1的右邊添加字符串c2直到c1字符串的長度等于i。
    LOWER(<c1>) 把字符串c1轉(zhuǎn)換為小寫 SELECT LOWER(ename)  one,UPPER(ename) two, INITCAP(ename) FROM EMP;
    UPPER(<c1>) 把字符串c1轉(zhuǎn)換為大寫
    INITCAP(<c1>) c1字符串的每一個單詞的第一個字母轉(zhuǎn)換成大寫字母
    LENGTH(<c1>) 返回字符串c1的長度 SELECT LENGTH(‘How are you’) FROM DUAL;
    SUBSTR(<c1>,<i>[,<j>]) 返回字符串c1中從第i個位置開始的j個字符(向右)。如果省略j,則返回c1中從第i個位置開始的所有字符。如果j為負,則返回字符串c1中從第i個位置開始的j個字符(向左)。 SELECT SUBSTR(‘Hello,World’,1,5) FROM DUAL;
    INSTR(<c1>,<c2>[,<i>[,<j>]]) c1中從位置i開始查找c2c1中出第j次的位置,i可以為負(此時,從c1的尾部開始)。

    SELECT INSTR(‘Mississippi’,’i’,3,3) FROM DUAL; 返回結(jié)果11。

    SELECT INSTR(‘Mississippi’,’i’,-2,3) FROM DUAL; 返回結(jié)果2。

     

    LTRIM(<c1>,<c2>) c1前面開始去掉出現(xiàn)在c2的中任何前導(dǎo)字符集。 SELECT LTRIM(‘Mississippi’,’Mis’) FROM DUAL; 返回結(jié)果’ppi’。

    SELECT RTRIM(‘Mississippi’,’ip’) FROM DUAL; 返回結(jié)果’Mississ’。
     
    RTRIM(<c1>,<c2>) c1后面開始去掉出現(xiàn)在c2的中任何前導(dǎo)字符集。
    數(shù)學函數(shù) ABS(<n>) 返回n的絕對值 SELECT ABC(-2),ABS(2) FROM DUAL;
    ROUND(<n1>,<n2>) n1的小數(shù)點后保留n2位(四舍五入)并返回。如果n2小于零,n1舍入到小數(shù)點左邊。

    SELECT ROUND(12345.678,-2),

    ROUND(12345.678,2)

     FROM  DUAL;

    分別返回結(jié)果:12300,12345.68。

     

    CEIL(<n>) n 向上取整,并返回。

    SELECT CEIL(5.1),CEIL(-21.4) FROM  DUAL;

    分別返回:6, -21。

     

    FLOOR(<n>) n 向下取整,并返回。

    SELECT FLOOR(5.1),FLOOR(-21.4) FROM  DUAL;

    分別返回:5, -22。

     

    MOD(<n1>,<n2>) 返回n1n2后的余數(shù)。

    SELECT MOD(14,5),MOD(8,25),MOD(-64,7) FROM DUAL;

    分別返回結(jié)果:4,0.5,-1。

     

    SIGN(<n>)

    符號函數(shù),n>0,返回1。

    n<0,返回-1。

    n=0,返回0。

     

    SELECT SIGN(-2.3),SIGN(2.3),SIGN(0) FROM DUAL;
    SQRT(<n>) 返回n的平方根 SELECT SQRT(9) FROM DUAL;
    TRUNC(<n1>,<n2>) 功能類似ROUND函數(shù)。但不做四舍五入。

    SELECT TRUNC(123.456,2),TRUNC(123.456,-1) FROM DUAL;

    分別返回結(jié)果:123.45120。

     

    VSIZE(n) 返回數(shù)字n的存儲字節(jié) SELECT VSIZE(123) FROM DUAL;
    日期函數(shù)(日期可以進行算術(shù)運算) SYSDATE 返回相同日期 SELECT SYSDATE FROM DUAL;
    ADD_MONTHS(<d>,<i>) 返回日期d 加上i個月后的新日期(i正可負)

    SELECT SYSDATE, ADD_MONTHS(SYSDATE,2),

    ADD_MONTHS(SYSDATE,-2)

    FROM DUAL;

     

    LAST_DAY(<d>) 返回日期d所在的月的最后一天。 SELECT SYSDATE,LAST_DAY(SYSDATE) FROM DUAL
    MONTHS_BETWEEN(<d1>,<d2>) 返回日期d1d2大多少月數(shù)。 SELECT MONTHS_BETWEEN(’19-Dec-1999’,’19-Mar-2000’ FROM DUAL;
    NEW_TIME(<d>,<tz1>,<tz2>) 將時區(qū)tz1的時間d,轉(zhuǎn)換為時區(qū)tz2里的時間。 SELECT SYSDATE,NEW_TIME(SYSDATE,’CDT’,’PDT’) FROM DUAL;
    NEXT_DAY(<d>,<dow>) 返回日期d后的第一個dow。(dowday of week) SELECT NEXT_DAY(SYSDATE,’Monday’) FROM DUAL;
    常用轉(zhuǎn)換函數(shù) TO_CHAR(<x>[,<fmt>[,<nlsparm>]]) x轉(zhuǎn)換成字符串。(參數(shù)含義請看ORACLE的聯(lián)機幫助) SELECT TO_CHAR(SYSDATE,’YYYY-MM-DD’) FROM DUAL;
    TO_NUMBER(<c>[,<fmt>[,<nlsparm>]]) 將字符串c轉(zhuǎn)換成數(shù)字。(參數(shù)含義請看ORACLE的聯(lián)機幫助) SELECT TO_NUMBER(‘123’) FROM DUAL;

    TO_DATE(<c>[,<fmt>[,<nlsparm>]])

    (常見的日期格式請查聯(lián)機幫助。)

    將字符串c轉(zhuǎn)換成日期。 SELECT TO_DATE(’19-Mar-99’,’DD-Mon-YYYY’) FROM DUAL;
    兩個重要函數(shù)

    DECODE(<x>,<m1>,<r1>[,<m2>,

    <r2…>][,<d>])

    (DECODE函數(shù)功能非常強大,請仔細玩味。)

     

    一個功能非常強大的函數(shù),它使得SQL非常高效。它的功能類似于一系列的if…then…else語句。

    SELECT sid,serial#,username,

    DECODE(command

    ,0,’None’

    ,2,’Insert’

    ,3,’Select’

    ,6,’Update’

    ,7,’Delete’

    ,8,’Drop

    ,’Other’) cmd

    FROM V$SESSION WHERE type<>’BACKGROUND’;

     

    NVL(x1,x2)

    注意ORACLE中的NULL值,注意該函數(shù)作用

     

    如果x1為空返回x2,否則返回x1 SELECT NVL(ename,’無姓名’)  FROM  EMP;

    分組函數(shù)

      AVG([{DISTINCT|ALL}]<n>) 求返回行的指定列的平均值

    SELECT AVG(sal),AVG(ALL sal),AVG(DISTINCT sal)

    FROM SCOTT.EMP;

     

    COUNT({*|[DISTINCT|ALL]}<x>) 統(tǒng)計返回的行數(shù)

    SELECT COUNT (*), COUNT(DISTINCT mgr),COUNT(mgr)

    FROM SCOTT.EMP

     

    MAX([{DISTINCT|ALL}]<x>) 求返回行的指定列的最大值 SELECT MAX(sal),MAX(DISTINCT sal) FROM EMP;
    MIN([{DISTINCT|ALL}]<x>) 求返回行的指定列的最小值 SELECT MIN(sal),MIN(DISTINCT sal) FROM EMP;
    STDDEV([{DISTINCT|ALL}]<x>) 求返回行的指定列的標準方差 SELECT STDDEV(sal),STDDEV(DISTINCT sal) FROM EMP;
    SUM() 求返回行的指定列的和 SELECT SUM(sal) FROM EMP;
    VARIANCE() 求返回行的指定列的差異值  

     

     

         注意:

    A、 分組函數(shù)不會處理空值,也不會返回空值

    B、  所有的分組函數(shù)既可以作用于指定列的所有值上,也可以只作用于指定列的差異列值上;

    C、 當指定ALL選項時,分組函數(shù)作用于所有非空列值行上。當指定DISTINCT選項時,分組函數(shù)只作用于非空的且具有不同列值的行上(即,重復(fù)列值的行只計算一行);

    posted @ 2006-01-18 17:41 阿成 閱讀(755) | 評論 (0)編輯 收藏
    問:??
    若通過ObjectOutputStream向一個文件中多次以追加方式寫入object,為什么用ObjectInputStream讀取這些object時會產(chǎn)生StreamCorruptedException???

    答:??
    使用缺省的serializetion的實現(xiàn)時,一個ObjectOutputStream的構(gòu)造和一個ObjectInputStream的構(gòu)造必須一一對應(yīng).ObjectOutputStream的構(gòu)造函數(shù)會向輸出流中寫入一個標識頭,而ObjectInputStream會首先讀入這個標識頭.因此,多次以追加方式向一個文件中寫入object時,該文件將會包含多個標識頭.所以用ObjectInputStream來deserialize這個ObjectOutputStream時,將產(chǎn)生StreamCorruptedException.一種解決方法是可以構(gòu)造一個ObjectOutputStream的子類,并覆蓋writeStreamHeader()方法.被覆蓋后的writeStreamHeader()方法應(yīng)判斷是否為首次向文件中寫入object,羰?則調(diào)用super.writeStreamHeader();若否,即以追加方式寫入object時,則應(yīng)調(diào)用ObjectOutputStream.reset()方法.??

    問:??
    對象的序列化(serialization)類是面向流的,應(yīng)如何將對象寫入到隨機存取文件中???

    答:??
    目前,沒有直接的方法可以將對象寫入到隨機存取文件中.??
    但是可以使用ByteArray輸入/輸出流作為中介,來向隨機存取文件中寫入或從隨機存取文件中讀出字節(jié),并且可以利用字節(jié)流來創(chuàng)建對象輸入/輸出流,以用于讀寫對象.需要注意的是在字節(jié)流中要包含一個完整的對象,否則讀寫對象時將發(fā)生錯誤. 例如,java.io.ByteArrayOutputStream可用于獲取ObjectOutputStream的字節(jié)流,從中可得到byte數(shù)組并可將之寫入到隨機存取文件中.相反,我們可以從隨機存取文件中讀出字節(jié)數(shù)組,利用它可構(gòu)造ByteArrayInputStream,進而構(gòu)造出ObjectInputStream,以讀取對象.??

    問:??
    運行RMI應(yīng)用時,可不可以不手工啟動名字服務(wù)rmiregistry,而是從程序中啟動之???

    答:??
    可以. java.rmi包中提供了類java.rmi.registry.LocateRegistry,用于獲取名字服務(wù)或創(chuàng)建名字服務(wù).調(diào)用LocateRegistry.createRegistry(int port)方法可以在某一特定端口創(chuàng)建名字服務(wù),從而用戶無需再手工啟動rmiregistry.此外,LocateRegistry.getRegistry(String host,int port)方法可用于獲取名字服務(wù).??

    問:??
    使用類PrintJob進行打印操作時,應(yīng)如何設(shè)置打印機名等打印屬性???

    答:??
    使用如下方法可以獲得PrintJob的實例用于控制打印操作:??


    Toolkit.getPrintJob(Frame f, String jobtitle, Properties prop)??

    那么對于打印屬性的設(shè)置可以通過對prop的屬性設(shè)置來實現(xiàn),打印屬性包括:??

    awt.print.destination: 可以是"printer"或"file"??

    awt.print.printer: 打印機名??

    awt.print.fileName: 打印文件名??

    awt.print.numCopies: 打印份數(shù)??

    awt.print.options: 打印命令的打印選項??

    awt.print.orientation: 打印方向,可以是"portrait"或"landscape"??

    awt.print.paperSize: 紙張大小,可以是"letter","legal","executive"或"a4"??






    問:??
    在JDK1.1中Thread類定義了suspend()和resume()方法,但是在JDK1.2中已經(jīng)過時,應(yīng)使用什么方法來替代之???

    答:??
    Thread.suspend本身易于產(chǎn)生死鎖.如果一個目標線程對某一關(guān)鍵系統(tǒng)資源進行了加鎖操作,然后該線程被suspend,那么除非該線程被resume,否則其它線程都將無法訪問該系統(tǒng)資源.如果另外一個線程將調(diào)用resume,使該線程繼續(xù)運行,而在此之前,它也需要訪問這一系統(tǒng)資源,則將產(chǎn)生死鎖.??

    因此,在Java 2中,比較流行的方式是定義線程的狀態(tài)變量,并使目標線程輪詢該狀態(tài)變量,當狀態(tài)為懸掛狀態(tài)時,可以使用wait()方法使之處于等待狀態(tài).一旦需要該線程繼續(xù)運行,其它線程會調(diào)用notify()方法來通知它.??

    問:??
    使用JDBC編程,應(yīng)如何控制結(jié)果集ResultSet的指針,使之能夠上下移動,以及移動到結(jié)果集的第一行和最后一行???

    答:??
    在JDK1.1中,ResultSet類中只定義了next()方法支持數(shù)據(jù)指針的下移.但在Java 2中,ResultSet類增加了如下方法支持數(shù)據(jù)指針的移動,包括:??


    ResultSet.first():將數(shù)據(jù)指針移到結(jié)果集的第一行??

    ResultSet.last(): 將數(shù)據(jù)指針移到結(jié)果集的最后一行??

    ResultSet.previous(): 將數(shù)據(jù)指針上移一行??


    以上的方法定義在JDBC2.0的規(guī)范中,所有支持JDBC 2.0的JDBC驅(qū)動程序都可以支持上述方法.目前Intersolv和OpenLink等JDBC驅(qū)動程序廠商均有產(chǎn)品支持JDBC 2.0 .??


    問:??
    哪幾種Web Server支持Servlet?如何使IIS支持Servlet???

    答:??
    目前,支持Servlet的服務(wù)器端產(chǎn)品主要有: Sun公司的Java WebServer,Lotus DominoGo WebServer,BEA weblogic Tengah Server,Jigsaw,NetForge,AcmeServer和Mot Bays Jetty等.??

    此外,一些第三方廠商也開發(fā)了Servlet engine,以使其它WebServer(如Netscape Web Server,IIS等)能夠運行Servlet,如LiveSoftware的Jrun(http://www.livesoftware.com/ products/jrun/)等.??

    問:??
    如何在Java應(yīng)用中將圖像存儲到圖像文件中???

    答:??
    Java Advanced Imaging API(包含在Java Media API中)允許在Java應(yīng)用中執(zhí)行復(fù)雜的,高性能的圖像處理.JAI API提供了存儲圖像的能力.目前,JAI API支持以下幾種圖像文件格式:BMP,JEPG,PNG,PNM,TIFF.下面給出了將圖像存儲到BMP文件的一段代碼:??


    OutputStream os = new FileOutputStream(fileToWriteTo);??

    BMPEncodeParam param = new BMPEncodeParam();??

    ImageEncoder enc = ImageCodec.createImageEncoder("BMP", os, param);??

    enc.encode(img);??

    os.close();??

    有關(guān)存儲圖像文件的編程指南請參考以下網(wǎng)頁:??

    http://java.sun.com/products/java-media/jai/forDevelopers/jai-guide/??




    問:??
    如何用Java語言向串口讀寫數(shù)據(jù)? font>??

    答:??
    Sun公司的Java Communication API2.0可用于讀寫串口,它支持RS232串口和IEEE 1284 并口,提供了一種與平臺無關(guān)的串/并口通信機制.
    posted @ 2006-01-18 15:39 阿成 閱讀(363) | 評論 (0)編輯 收藏
    第一,談?wù)刦inal,?finally,?finalize的區(qū)別。

    ????第二,Anonymous?Inner?Class?(匿名內(nèi)部類)?是否可以extends(繼承)其它類,是否可以implements(實現(xiàn))interface(接口)?

    ????第三,Static?Nested?Class?和?Inner?Class的不同,說得越多越好(面試題有的很籠統(tǒng))。

    ????第四,&和&&的區(qū)別。

    ????第五,HashMap和Hashtable的區(qū)別。

    ????第六,Collection?和?Collections的區(qū)別。

    ????第七,什么時候用assert.

    ????第八,GC是什么??為什么要有GC?

    ????第九,String?s?=?new?String("xyz");創(chuàng)建了幾個String?Object?

    ????第十,Math.round(11.5)等於多少??Math.round(-11.5)等於多少?

    ????第十一,short?s1?=?1;?s1?=?s1?+?1;有什么錯??short?s1?=?1;?s1?+=?1;有什么錯?

    ????第十二,sleep()?和?wait()?有什么區(qū)別?

    ????第十三,Java有沒有g(shù)oto?

    ????第十四,數(shù)組有沒有l(wèi)ength()這個方法??String有沒有l(wèi)ength()這個方法?

    ????第十五,Overload和Override的區(qū)別。Overloaded的方法是否可以改變返回值的類型?

    ????第十六,Set里的元素是不能重復(fù)的,那么用什么方法來區(qū)分重復(fù)與否呢??是用==還是equals()??它們有何區(qū)別?

    ????第十七,給我一個你最常見到的runtime?exception.

    ????第十八,error和exception有什么區(qū)別?

    ????第十九,List,?Set,?Map是否繼承自Collection接口?

    ????第二十,abstract?class和interface有什么區(qū)別?

    ????第二十一,abstract的method是否可同時是static,是否可同時是native,是否可同時是synchronized?

    ????第二十二,接口是否可繼承接口??抽象類是否可實現(xiàn)(implements)接口??抽象類是否可繼承實體類(concrete?class)?

    ????第二十三,啟動一個線程是用run()還是start()?

    ????第二十四,構(gòu)造器Constructor是否可被override?

    ????第二十五,是否可以繼承String類?

    ????第二十六,當一個線程進入一個對象的一個synchronized方法后,其它線程是否可進入此對象的其它方法?

    ????第二十七,try?{}里有一個return語句,那么緊跟在這個try后的finally?{}里的code會不會被執(zhí)行,什么時候被執(zhí)行,在return前還是后?

    ????第二十八,編程題:?用最有效率的方法算出2乘以8等於幾?

    ????第二十九,兩個對象值相同(x.equals(y)?==?true),但卻可有不同的hash?code,這句話對不對?

    ????第三十,當一個對象被當作參數(shù)傳遞到一個方法后,此方法可改變這個對象的屬性,并可返回變化后的結(jié)果,那么這里到底是值傳遞還是引用傳遞?

    ????第三十一,swtich是否能作用在byte上,是否能作用在long上,是否能作用在String上?

    ????第三十二,編程題:?寫一個Singleton出來。

    以下是答案

    ????第一,談?wù)刦inal,?finally,?finalize的區(qū)別。

    ????final?修飾符(關(guān)鍵字)如果一個類被聲明為final,意味著它不能再派生出新的子類,不能作為父類被繼承。因此一個類不能既被聲明為?abstract的,又被聲明為final的。將變量或方法聲明為final,可以保證它們在使用中不被改變。被聲明為final的變量必須在聲明時給定初值,而在以后的引用中只能讀取,不可修改。被聲明為final的方法也同樣只能使用,不能重載finally?再異常處理時提供?finally?塊來執(zhí)行任何清除操作。如果拋出一個異常,那么相匹配的?catch?子句就會執(zhí)行,然后控制就會進入?finally?塊(如果有的話)。

    ????finalize?方法名。Java?技術(shù)允許使用?finalize()?方法在垃圾收集器將對象從內(nèi)存中清除出去之前做必要的清理工作。這個方法是由垃圾收集器在確定這個對象沒有被引用時對這個對象調(diào)用的。它是在?Object?類中定義的,因此所有的類都繼承了它。子類覆蓋?finalize()?方法以整理系統(tǒng)資源或者執(zhí)行其他清理工作。finalize()?方法是在垃圾收集器刪除對象之前對這個對象調(diào)用的。

    ????第二,Anonymous?Inner?Class?(匿名內(nèi)部類)?是否可以extends(繼承)其它類,是否可以implements(實現(xiàn))interface(接口)?

    ????匿名的內(nèi)部類是沒有名字的內(nèi)部類。不能extends(繼承)?其它類,但一個內(nèi)部類可以作為一個接口,由另一個內(nèi)部類實現(xiàn)。

    ????第三,Static?Nested?Class?和?Inner?Class的不同,說得越多越好(面試題有的很籠統(tǒng))。

    ????Nested?Class?(一般是C++的說法),Inner?Class?(一般是JAVA的說法)。Java內(nèi)部類與C++嵌套類最大的不同就在于是否有指向外部的引用上。具體可見http:?//www.frontfree.net/articles/services/view.asp?id=704&page=1

    ????注:?靜態(tài)內(nèi)部類(Inner?Class)意味著1創(chuàng)建一個static內(nèi)部類的對象,不需要一個外部類對象,2不能從一個static內(nèi)部類的一個對象訪問一個外部類對象

    ????第四,&和&&的區(qū)別。

    ????&是位運算符。&&是布爾邏輯運算符。

    ????第五,HashMap和Hashtable的區(qū)別。

    ????都屬于Map接口的類,實現(xiàn)了將惟一鍵映射到特定的值上。

    ????HashMap?類沒有分類或者排序。它允許一個?null?鍵和多個?null?值。

    ????Hashtable?類似于?HashMap,但是不允許?null?鍵和?null?值。它也比?HashMap?慢,因為它是同步的。

    ????第六,Collection?和?Collections的區(qū)別。

    ????Collections是個java.util下的類,它包含有各種有關(guān)集合操作的靜態(tài)方法。

    ????Collection是個java.util下的接口,它是各種集合結(jié)構(gòu)的父接口。

    第七,什么時候用assert。?

      斷言是一個包含布爾表達式的語句,在執(zhí)行這個語句時假定該表達式為?true。如果表達式計算為?false,那么系統(tǒng)會報告一個?AssertionError。它用于調(diào)試目的:?

    assert(a?>?0);?//?throws?an?AssertionError?if?a?<=?0?

      斷言可以有兩種形式:?

      assert?Expression1?;?
      assert?Expression1?:?Expression2?;?

      Expression1?應(yīng)該總是產(chǎn)生一個布爾值。?
      Expression2?可以是得出一個值的任意表達式。這個值用于生成顯示更多調(diào)試信息的?String?消息。?
    斷言在默認情況下是禁用的。要在編譯時啟用斷言,需要使用?source?1.4?標記:?

      javac?-source?1.4?Test.java?

      要在運行時啟用斷言,可使用?-enableassertions?或者?-ea?標記。?
      要在運行時選擇禁用斷言,可使用?-da?或者?-disableassertions?標記。?
      要系統(tǒng)類中啟用斷言,可使用?-esa?或者?-dsa?標記。還可以在包的基礎(chǔ)上啟用或者禁用斷言。?

      可以在預(yù)計正常情況下不會到達的任何位置上放置斷言。斷言可以用于驗證傳遞給私有方法的參數(shù)。不過,斷言不應(yīng)該用于驗證傳遞給公有方法的參數(shù),因為不管是否啟用了斷言,公有方法都必須檢查其參數(shù)。不過,既可以在公有方法中,也可以在非公有方法中利用斷言測試后置條件。另外,斷言不應(yīng)該以任何方式改變程序的狀態(tài)。?


      第八,GC是什么??為什么要有GC??(基礎(chǔ))。?

      GC是垃圾收集器。Java?程序員不用擔心內(nèi)存管理,因為垃圾收集器會自動進行管理。要請求垃圾收集,可以調(diào)用下面的方法之一:?

      System.gc()?
      Runtime.getRuntime().gc()?

      第九,String?s?=?new?String("xyz");創(chuàng)建了幾個String?Object??

      兩個對象,一個是“xyx”,一個是指向“xyx”的引用對象s。?

      第十,Math.round(11.5)等於多少??Math.round(-11.5)等於多少??

      Math.round(11.5)返回(long)12,Math.round(-11.5)返回(long)-11;?

      第十一,short?s1?=?1;?s1?=?s1?+?1;有什么錯??short?s1?=?1;?s1?+=?1;有什么錯??

      short?s1?=?1;?s1?=?s1?+?1;有錯,s1是short型,s1+1是int型,不能顯式轉(zhuǎn)化為short型。可修改為s1?=(short)(s1?+?1)?。short?s1?=?1;?s1?+=?1正確。?

      第十二,sleep()?和?wait()?有什么區(qū)別??搞線程的最愛?

      sleep()方法是使線程停止一段時間的方法。在sleep?時間間隔期滿后,線程不一定立即恢復(fù)執(zhí)行。這是因為在那個時刻,其它線程可能正在運行而且沒有被調(diào)度為放棄執(zhí)行,除非(a)“醒來”的線程具有更高的優(yōu)先級,(b)正在運行的線程因為其它原因而阻塞。?

      wait()是線程交互時,如果線程對一個同步對象x?發(fā)出一個wait()調(diào)用,該線程會暫停執(zhí)行,被調(diào)對象進入等待狀態(tài),直到被喚醒或等待時間到。?

      第十三,Java有沒有g(shù)oto??

      Goto?java中的保留字,現(xiàn)在沒有在java中使用。?

      第十四,數(shù)組有沒有l(wèi)ength()這個方法??String有沒有l(wèi)ength()這個方法??

      數(shù)組沒有l(wèi)ength()這個方法,有l(wèi)ength的屬性。?
      String有有l(wèi)ength()這個方法。?

      第十五,Overload和Override的區(qū)別。Overloaded的方法是否可以改變返回值的類型??

      方法的重寫Overriding和重載Overloading是Java多態(tài)性的不同表現(xiàn)。重寫Overriding是父類與子類之間多態(tài)性的一種表現(xiàn),重載Overloading是一個類中多態(tài)性的一種表現(xiàn)。如果在子類中定義某方法與其父類有相同的名稱和參數(shù),我們說該方法被重寫?(Overriding)。子類的對象使用這個方法時,將調(diào)用子類中的定義,對它而言,父類中的定義如同被“屏蔽”了。如果在一個類中定義了多個同名的方法,它們或有不同的參數(shù)個數(shù)或有不同的參數(shù)類型,則稱為方法的重載(Overloading)。Overloaded的方法是可以改變返回值的類型。?

      第十六,Set里的元素是不能重復(fù)的,那么用什么方法來區(qū)分重復(fù)與否呢??是用==還是equals()??它們有何區(qū)別??

      Set里的元素是不能重復(fù)的,那么用iterator()方法來區(qū)分重復(fù)與否。equals()是判讀兩個Set是否相等。?

      equals()和==方法決定引用值是否指向同一對象equals()在類中被覆蓋,為的是當兩個分離的對象的內(nèi)容和類型相配的話,返回真值。?

      第十七,給我一個你最常見到的runtime?exception。?

      ArithmeticException,?ArrayStoreException,?BufferOverflowException,?BufferUnderflowException,??CannotRedoException,???
    CannotUndoException,??ClassCastException,?CMMException,???ConcurrentModificationException,??
    DOMException,?EmptyStackException,?IllegalArgumentException,??IllegalMonitorStateException,??
    IllegalPathStateException,??IllegalStateException,??
    ImagingOpException,??
    IndexOutOfBoundsException,??MissingResourceException,??NegativeArraySizeException,??NoSuchElementException,??
    NullPointerException,??ProfileDataException,?ProviderException,?
    ?RasterFormatException,??SecurityException,?SystemException,
    ?UndeclaredThrowableException,??
    UnmodifiableSetException,??UnsupportedOperationException??

      第十八,error和exception有什么區(qū)別??

      error?表示恢復(fù)不是不可能但很困難的情況下的一種嚴重問題。比如說內(nèi)存溢出。不可能指望程序能處理這樣的情況。

      exception?表示一種設(shè)計或?qū)崿F(xiàn)問題。也就是說,它表示如果程序運行正常,從不會發(fā)生的情況。?


      第十九,List,?Set,?Map是否繼承自Collection接口??

      List,Set是?

      Map不是?

      第二十,abstract?class和interface有什么區(qū)別??

      聲明方法的存在而不去實現(xiàn)它的類被叫做抽象類(abstract?class),它用于要創(chuàng)建一個體現(xiàn)某些基本行為的類,并為該類聲明方法,但不能在該類中實現(xiàn)該類的情況。不能創(chuàng)建abstract?類的實例。然而可以創(chuàng)建一個變量,其類型是一個抽象類,并讓它指向具體子類的一個實例。不能有抽象構(gòu)造函數(shù)或抽象靜態(tài)方法。Abstract?類的子類為它們父類中的所有抽象方法提供實現(xiàn),否則它們也是抽象類為。取而代之,在子類中實現(xiàn)該方法。知道其行為的其它類可以在類中實現(xiàn)這些方法。?

      接口(interface)是抽象類的變體。在接口中,所有方法都是抽象的。多繼承性可通過實現(xiàn)這樣的接口而獲得。接口中的所有方法都是抽象的,沒有一個有程序體。接口只可以定義static?final成員變量。接口的實現(xiàn)與子類相似,除了該實現(xiàn)類不能從接口定義中繼承行為。當類實現(xiàn)特殊接口時,它定義(即將程序體給予)所有這種接口的方法。然后,它可以在實現(xiàn)了該接口的類的任何對象上調(diào)用接口的方法。由于有抽象類,它允許使用接口名作為引用變量的類型。通常的動態(tài)聯(lián)編將生效。引用可以轉(zhuǎn)換到接口類型或從接口類型轉(zhuǎn)換,instanceof?運算符可以用來決定某對象的類是否實現(xiàn)了接口。?

      第二十一,abstract的method是否可同時是static,是否可同時是native,是否可同時是synchronized??

      都不能?

      第二十二,接口是否可繼承接口??抽象類是否可實現(xiàn)(implements)接口??抽象類是否可繼承實體類(concrete?class)??

      接口可以繼承接口。抽象類可以實現(xiàn)(implements)接口,抽象類是否可繼承實體類,但前提是實體類必須有明確的構(gòu)造函數(shù)。?

      第二十三,啟動一個線程是用run()還是start()??

      啟動一個線程是調(diào)用start()方法,使線程所代表的虛擬處理機處于可運行狀態(tài),這意味著它可以由JVM調(diào)度并執(zhí)行。這并不意味著線程就會立即運行。run()方法可以產(chǎn)生必須退出的標志來停止一個線程。?



      第二十四,構(gòu)造器Constructor是否可被override??

      構(gòu)造器Constructor不能被繼承,因此不能重寫Overriding,但可以被重載Overloading。?

      第二十五,是否可以繼承String類??

      String類是final類故不可以繼承。?

      第二十六,當一個線程進入一個對象的一個synchronized方法后,其它線程是否可進入此對象的其它方法??

      不能,一個對象的一個synchronized方法只能由一個線程訪問。?

      第二十七,try?{}里有一個return語句,那么緊跟在這個try后的finally?{}里的code會不會被執(zhí)行,什么時候被執(zhí)行,在return前還是后??

      會執(zhí)行,在return前執(zhí)行。?

      第二十八,編程題:?用最有效率的方法算出2乘以8等於幾??

      有C背景的程序員特別喜歡問這種問題。?

      2?<<?3?

      第二十九,兩個對象值相同(x.equals(y)?==?true),但卻可有不同的hash?code,這句話對不對??

      不對,有相同的hash?code。?

      第三十,當一個對象被當作參數(shù)傳遞到一個方法后,此方法可改變這個對象的屬性,并可返回變化后的結(jié)果,那么這里到底是值傳遞還是引用傳遞??

      是值傳遞。Java?編程語言只由值傳遞參數(shù)。當一個對象實例作為一個參數(shù)被傳遞到方法中時,參數(shù)的值就是對該對象的引用。對象的內(nèi)容可以在被調(diào)用的方法中改變,但對象的引用是永遠不會改變的。?


      第三十一,swtich是否能作用在byte上,是否能作用在long上,是否能作用在String上??

      switch(expr1)中,expr1是一個整數(shù)表達式。因此傳遞給?switch?和?case?語句的參數(shù)應(yīng)該是?int、?short、?char?或者?byte。long,string?都不能作用于swtich。?

      第三十二,編程題:?寫一個Singleton出來。

      Singleton模式主要作用是保證在Java應(yīng)用程序中,一個類Class只有一個實例存在。?

      一般Singleton模式通常有幾種種形式:?

      第一種形式:?定義一個類,它的構(gòu)造函數(shù)為private的,它有一個static的private的該類變量,在類初始化時實例話,通過一個public的getInstance方法獲取對它的引用,繼而調(diào)用其中的方法。?

    public?class?Singleton?{?
      private?Singleton(){}?
      //在自己內(nèi)部定義自己一個實例,是不是很奇怪??
      //注意這是private?只供內(nèi)部調(diào)用?
      private?static?Singleton?instance?=?new?Singleton();?
      //這里提供了一個供外部訪問本class的靜態(tài)方法,可以直接訪問  ?
      public?static?Singleton?getInstance()?{?
        return?instance;?  ?
      ?}?
    }?

      第二種形式:?

    public?class?Singleton?{?
      private?static?Singleton?instance?=?null;?
      public?static?synchronized?Singleton?getInstance()?{?
      //這個方法比上面有所改進,不用每次都進行生成對象,只是第一次   ? ?
      //使用時生成實例,提高了效率!?
      if?(instance==null)?
        instance=new?Singleton();?
    return?instance;?  }?
    }?

      其他形式:?

      定義一個類,它的構(gòu)造函數(shù)為private的,所有方法為static的。?

      一般認為第一種形式要更加安全些?

      第三十三?Hashtable和HashMap?

      Hashtable繼承自Dictionary類,而HashMap是Java1.2引進的Map?interface的一個實現(xiàn)?

      HashMap允許將null作為一個entry的key或者value,而Hashtable不允許?

      還有就是,HashMap把Hashtable的contains方法去掉了,改成containsvalue和containsKey。因為contains方法容易讓人引起誤解。?

      最大的不同是,Hashtable的方法是Synchronize的,而HashMap不是,在多個線程訪問Hashtable時,不需要自己為它的方法實現(xiàn)同步,而HashMap就必須為之提供外同步。?

      Hashtable和HashMap采用的hash/rehash算法都大概一樣,所以性能不會有很大的差異。
    posted @ 2006-01-18 15:36 阿成 閱讀(489) | 評論 (0)編輯 收藏
    判斷在某段時間內(nèi)
    一.select * from table where talbe.time between to_date('2006-1-15','yyyy-mm-dd') and to_date('2008-1-15','yyyy-mm-dd') order by talbe.time;

    二.select * from table where talbe.time between trunc(date(formDate) )and trunc(date(toDate) )order by talbe.time;

    查詢匹配
    select * from table where lower(table.username) like?% + username.toLower()%;

    三.如何獲得前N條記錄

    1. ORACLE
    SELECT * FROM TABLE1 WHERE ROWNUM<=N

    2. INFORMIX
    SELECT FIRST N * FROM TABLE1 where 1=1

    3. DB2
    SELECT * ROW_NUMBER() OVER(ORDER BY COL1 DESC) AS ROWNUM WHERE ROWNUM<=N
    或者
    SELECT COLUMN FROM TABLE where 1=1 FETCH FIRST N ROWS ONLY

    4. SQL SERVER
    SELECT TOP N * FROM TABLE1 where 1=1
    or
    SET ROWCOUNT N SELECT * FROM TABLE1 where 1=1 SET ROWCOUNT N1

    5. SYBASE
    SET ROWCOUNT N SELECT * FROM TABLE1 where 1=1 SET ROWCOUNT N1

    6. MYSQL
    SELECT * FROM TABLE1 where 1=1 LIMIT N

    7. FOXPRO
    SELECT * TOP N FROM TABLE ORDER BY COLUMN

    8. ACCESS
    SELECT TOP N * FROM TABLE1 where 1=1

    posted @ 2006-01-14 15:27 阿成 閱讀(316) | 評論 (0)編輯 收藏

    引言

      文件的上傳和下載在J2EE編程已經(jīng)是一個非常古老的話題了,也許您馬上就能掰著指頭數(shù)出好幾個著名的大件:如SmartUpload、Apache的FileUpload。但如果您的項目是構(gòu)建在Struts+Spring+Hibernate(以下稱SSH)框架上的,這些大件就顯得笨重而滄桑了,SSH提供了一個簡捷方便的文件上傳下載的方案,我們只需要通過一些配置并輔以少量的代碼就可以完好解決這個問題了。

      本文將圍繞SSH文件上傳下載的主題,向您詳細講述如何開發(fā)基于SSH的Web程序。SSH各框架的均為當前最新版本:

      ·Struts 1.2

      ·Spring 1.2.5

      ·Hibernate 3.0

      本文選用的數(shù)據(jù)庫為Oracle 9i,當然你可以在不改動代碼的情況下,通過配置文件的調(diào)整將其移植到任何具有Blob字段類型的數(shù)據(jù)庫上,如MySQL,SQLServer等。

      總體實現(xiàn)

      上傳文件保存到T_FILE表中,T_FILE表結(jié)構(gòu)如下:


    圖 1 T_FILE表結(jié)構(gòu)

      其中:

      ·FILE_ID:文件ID,32個字符,用Hibernate的uuid.hex算法生成。

      ·FILE_NAME:文件名。

      ·FILE_CONTENT:文件內(nèi)容,對應(yīng)Oracle的Blob類型。

      ·REMARK:文件備注。

      文件數(shù)據(jù)存儲在Blob類型的FILE_CONTENT表字段上,在Spring中采用OracleLobHandler來處理Lob字段(包括Clob和Blob),由于在程序中不需要引用到oracle數(shù)據(jù)驅(qū)動程序的具體類且屏蔽了不同數(shù)據(jù)庫處理Lob字段方法上的差別,從而撤除程序在多數(shù)據(jù)庫移植上的樊籬。

      1.首先數(shù)據(jù)表中的Blob字段在Java領(lǐng)域?qū)ο笾新暶鳛閎yte[]類型,而非java.sql.Blob類型。

      2.數(shù)據(jù)表Blob字段在Hibernate持久化映射文件中的type為org.springframework.orm.hibernate3.support.BlobByteArrayType,即Spring所提供的用戶自定義的類型,而非java.sql.Blob。

      3.在Spring中使用org.springframework.jdbc.support.lob.OracleLobHandler處理Oracle數(shù)據(jù)庫的Blob類型字段。

      通過這樣的設(shè)置和配置,我們就可以象持久化表的一般字段類型一樣處理Blob字段了。

      以上是Spring+Hibernate將文件二進制數(shù)據(jù)持久化到數(shù)據(jù)庫的解決方案,而Struts通過將表單中file類型的組件映射為ActionForm中類型為org.apache.struts.upload. FormFile的屬性來獲取表單提交的文件數(shù)據(jù)。

      綜上所述,我們可以通過圖 2,描繪出SSH處理文件上傳的方案:


    圖 2 SSH處理文件上傳技術(shù)方案

      文件上傳的頁面如圖 3所示:


    圖 3 文件上傳頁面

      文件下載的頁面如圖 4所示:


    圖 4 文件下載頁面

      該工程的資源結(jié)構(gòu)如圖 5所示:


    圖 5 工程資源結(jié)構(gòu)

      工程的類按SSH的層次結(jié)構(gòu)劃分為數(shù)據(jù)持久層、業(yè)務(wù)層和Web層;WEB-INF下的applicationContext.xml為Spring的配置文件,struts-config.xml為Struts的配置文件,file-upload.jsp為文件上傳頁面,file-list.jsp為文件列表頁面。

      本文后面的章節(jié)將從數(shù)據(jù)持久層->業(yè)務(wù)層->W(wǎng)eb層的開發(fā)順序,逐層講解文件上傳下載的開發(fā)過程。

      數(shù)據(jù)持久層

      1、領(lǐng)域?qū)ο蠹坝成湮募?br />
      您可以使用Hibernate Middlegen、HIbernate Tools、Hibernate Syhchronizer等工具或手工的方式,編寫Hibernate的領(lǐng)域?qū)ο蠛陀成湮募?。其中對?yīng)T_FILE表的領(lǐng)域?qū)ο骉file.java為:

      代碼 1 領(lǐng)域?qū)ο骉file

    1. package sshfile.model;
    2. public class Tfile
    3.{
    4. private String fileId;
    5. private String fileName;
    6. private byte[] fileContent;
    7. private String remark;
    8. …//getter and setter
    9. }

      特別需要注意的是:數(shù)據(jù)庫表為Blob類型的字段在Tfile中的fileContent類型為byte[]。Tfile的Hibernate映射文件Tfile.hbm.xml放在Tfile .java類文件的相同目錄下:

      代碼 2 領(lǐng)域?qū)ο笥成湮募?br />
    1. <?xml version="1.0"?>
    2. <!DOCTYPE hibernate-mapping PUBLIC
    3. "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    4. "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
    5. <hibernate-mapping>
    6. <class name="sshfile.model.Tfile" table="T_FILE">
    7. <id name="fileId" type="java.lang.String" column="FILE_ID">
    8. <generator class="uuid.hex"/>
    9. </id>
    10. <property name="fileContent"
    11. type="org.springframework.orm.hibernate3.support.BlobByteArrayType"
    12. column="FILE_CONTENT" lazy="true"/>
    13. …//其它一般字段的映射
    14. </class>
    15. </hibernate-mapping>

      fileContent字段映射為Spring所提供的BlobByteArrayType類型,BlobByteArrayType是用戶自定義的數(shù)據(jù)類型,它實現(xiàn)了Hibernate 的org.hibernate.usertype.UserType接口。BlobByteArrayType使用從sessionFactory獲取的Lob操作句柄lobHandler將byte[]的數(shù)據(jù)保存到Blob數(shù)據(jù)庫字段中。這樣,我們就再沒有必要通過硬編碼的方式,先insert然后再update來完成Blob類型數(shù)據(jù)的持久化,這個原來難伺候的老爺終于被平民化了。關(guān)于lobHandler的配置請見本文后面的內(nèi)容。

      此外lazy="true"說明地返回整個Tfile對象時,并不返回fileContent這個字段的數(shù)據(jù),只有在顯式調(diào)用tfile.getFileContent()方法時才真正從數(shù)據(jù)庫中獲取fileContent的數(shù)據(jù)。這是Hibernate3引入的新特性,對于包含重量級大數(shù)據(jù)的表字段,這種抽取方式提高了對大字段操作的靈活性,否則加載Tfile對象的結(jié)果集時如果總是返回fileContent,這種批量的數(shù)據(jù)抽取將可以引起數(shù)據(jù)庫的"洪泛效應(yīng)"。

      2、DAO編寫和配置

      Spring強調(diào)面向接口編程,所以我們將所有對Tfile的數(shù)據(jù)操作的方法定義在TfileDAO接口中,這些接口方法分別是:

      ·findByFildId(String fileId)

      ·save(Tfile tfile)

      ·List findAll()

      TfileDAOHibernate提供了對TfileDAO接口基于Hibernate的實現(xiàn),如代碼 3所示:

      代碼 3 基于Hibernate 的fileDAO實現(xiàn)類

    1. package sshfile.dao;
    2.
    3. import sshfile.model.*;
    4. import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
    5. import java.util.List;
    6.
    7. public class TfileDAOHibernate
    8. extends HibernateDaoSupport implements TfileDAO
    9. {
    10. public Tfile findByFildId(String fileId)
    11. {
    12. return (Tfile) getHibernateTemplate().get(Tfile.class, fileId);
    13. }
    14. public void save(Tfile tfile)
    15. {
    16. getHibernateTemplate().save(tfile);
    17. getHibernateTemplate().flush();
    18. }
    19. public List findAll()
    20. {
    21. return getHibernateTemplate().loadAll(Tfile.class);
    22. }
    23. }

      TfileDAOHibernate通過擴展Spring提供的Hibernate支持類HibernateDaoSupport而建立,HibernateDaoSupport封裝了HibernateTemplate,而HibernateTemplate封裝了Hibernate所提供幾乎所有的的數(shù)據(jù)操作方法,如execute(HibernateCallback action),load(Class entityClass, Serializable id),save(final Object entity)等等。

      所以我們的DAO只需要簡單地調(diào)用父類的HibernateTemplate就可以完成幾乎所有的數(shù)據(jù)庫操作了。

      由于Spring通過代理Hibernate完成數(shù)據(jù)層的操作,所以原Hibernate的配置文件hibernate.cfg.xml的信息也轉(zhuǎn)移到Spring的配置文件中:

      代碼 4 Spring中有關(guān)Hibernate的配置信息

    1. <beans>
    2. <!-- 數(shù)據(jù)源的配置 //-->
    3. <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
    4. destroy-method="close">
    5. <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
    6. <property name="url" value="jdbc:oracle:thin:@localhost:1521:ora9i"/>
    7. <property name="username" value="test"/>
    8. <property name="password" value="test"/>
    9. </bean>
    10. <!-- Hibernate會話工廠配置 //-->
    11. <bean id="sessionFactory"
    12. class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
    13. <property name="dataSource" ref="dataSource"/>
    14. <property name="mappingDirectoryLocations">
    15. <list>
    16. <value>classpath:/sshfile/model</value>
    17. </list>
    18. </property>
    19. <property name="hibernateProperties">
    20. <props>
    21. <prop key="hibernate.dialect">org.hibernate.dialect.OracleDialect</prop>
    22. <prop key="hibernate.cglib.use_reflection_optimizer">true</prop>
    23. </props>
    24. </property>
    25. </bean>
    26. <!-- Hibernate 模板//-->
    27. <bean id="hibernateTemplate"
    28. class="org.springframework.orm.hibernate3.HibernateTemplate">
    29. <property name="sessionFactory" ref="sessionFactory"/>
    30. </bean>
    31. <!--DAO配置 //-->
    32. <bean id="tfileDAO" class="sshfile.dao.TfileDAOHibernate">
    33. <property name="hibernateTemplate" ref="hibernateTemplate" />
    34. </bean>
    35. …
    36. </beans>

      第3~9行定義了一個數(shù)據(jù)源,其實現(xiàn)類是apache的BasicDataSource,第11~25行定義了Hibernate的會話工廠,會話工廠類用Spring提供的LocalSessionFactoryBean維護,它注入了數(shù)據(jù)源和資源映射文件,此外還通過一些鍵值對設(shè)置了Hibernate所需的屬性。

      其中第16行通過類路徑的映射方式,將sshfile.model類包目錄下的所有領(lǐng)域?qū)ο蟮挠成湮募b載進來,在本文的例子里,它將裝載進Tfile.hbm.xml映射文件。如果有多個映射文件需要聲明,使用類路徑映射方式顯然比直接單獨指定映射文件名的方式要簡便。

      第27~30行定義了Spring代理Hibernate數(shù)據(jù)操作的HibernateTemplate模板,而第32~34行將該模板注入到tfileDAO中。

      需要指定的是Spring 1.2.5提供了兩套Hibernate的支持包,其中Hibernate 2相關(guān)的封裝類位于org.springframework.orm.hibernate2.*包中,而Hibernate 3.0的封裝類位于org.springframework.orm.hibernate3.*包中,需要根據(jù)您所選用Hibernate版本進行正確選擇。

      3、Lob字段處理的配置

      我們前面已經(jīng)指出Oracle的Lob字段和一般類型的字段在操作上有一個明顯的區(qū)別--那就是你必須首先通過Oracle的empty_blob()/empty_clob()初始化Lob字段,然后獲取該字段的引用,通過這個引用更改其值。所以要完成對Lob字段的操作,Hibernate必須執(zhí)行兩步數(shù)據(jù)庫訪問操作,先Insert再Update。

      使用BlobByteArrayType字段類型后,為什么我們就可以象一般的字段類型一樣操作Blob字段呢?可以確定的一點是:BlobByteArrayType不可能逾越Blob天生的操作方式,原來是BlobByteArrayType數(shù)據(jù)類型本身具體數(shù)據(jù)訪問的功能,它通過LobHandler將兩次數(shù)據(jù)訪問的動作隱藏起來,使Blob字段的操作在表現(xiàn)上和其他一般字段業(yè)類型無異,所以LobHandler即是那個"苦了我一個,幸福十億人"的那位幕后英雄。

      LobHandler必須注入到Hibernate會話工廠sessionFactory中,因為sessionFactory負責產(chǎn)生與數(shù)據(jù)庫交互的Session。LobHandler的配置如代碼 5所示:

      代碼 5 Lob字段的處理句柄配置

    1. <beans>
    2. …
    3. <bean id="nativeJdbcExtractor"
    4. class="org.springframework.jdbc.support.nativejdbc.CommonsDbcpNativeJdbcExtractor"
    5. lazy-init="true"/>
    6. <bean id="lobHandler"
    7. class="org.springframework.jdbc.support.lob.OracleLobHandler" lazy-init="true">
    8. <property name="nativeJdbcExtractor">
    9. <ref local="nativeJdbcExtractor"/>
    10. </property>
    11. </bean>
    12. …
    13. </beans>

      首先,必須定義一個能夠從連接池中抽取出本地數(shù)據(jù)庫JDBC對象(如OracleConnection,OracleResultSet等)的抽取器:nativeJdbcExtractor,這樣才可以執(zhí)行一些特定數(shù)據(jù)庫的操作。對于那些僅封裝了Connection而未包括Statement的簡單數(shù)據(jù)連接池,SimpleNativeJdbcExtractor是效率最高的抽取器實現(xiàn)類,但具體到apache的BasicDataSource連接池,它封裝了所有JDBC的對象,這時就需要使用CommonsDbcpNativeJdbcExtractor了。Spring針對幾個著名的Web服務(wù)器的數(shù)據(jù)源提供了相應(yīng)的JDBC抽取器:

      ·WebLogic:WebLogicNativeJdbcExtractor

      ·WebSphere:WebSphereNativeJdbcExtractor

      ·JBoss:JBossNativeJdbcExtractor

      在定義了JDBC抽取器后,再定義lobHandler。Spring 1.2.5提供了兩個lobHandler:

      ·DefaultLobHandler:適用于大部分的數(shù)據(jù)庫,如SqlServer,MySQL,對Oracle 10g也適用,但不適用于Oracle 9i(看來Oracle 9i確實是個怪胎,誰叫Oracle 公司自己都說Oracle 9i是一個過渡性的產(chǎn)品呢)。

      ·OracleLobHandler:適用于Oracle 9i和Oracle 10g。

      由于我們的數(shù)據(jù)庫是Oracle9i,所以使用OracleLobHandler。

      在配置完LobHandler后, 還需要將其注入到sessionFactory的Bean中,下面是調(diào)用后的sessionFactory Bean的配置:

      代碼 6 將lobHandler注入到sessionFactory中的配置

    1. <beans>
    2. …
    3. <bean id="sessionFactory"
    4. class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
    5. <property name="dataSource" ref="dataSource"/>
    6. <!-- 為處理Blob類型字段的句柄聲明 //-->
    7. <property name="lobHandler" ref="lobHandler"/>
    8. …
    9. </bean>
    10. …
    11. </beans>

      如第7所示,通過sessionFactory的lobHandler屬性進行注入。

      業(yè)務(wù)層

      1、業(yè)務(wù)層接口

      "面向接口而非面向類編程"是Spring不遺余力所推薦的編程原則,這條原則也已經(jīng)為大部開發(fā)者所接受;此外,JDK的動態(tài)代理只對接口有效,否則必須使用CGLIB生成目標類的子類。我們依從于Spring的倡導(dǎo)為業(yè)務(wù)類定義一個接口:

      代碼 7 業(yè)務(wù)層操作接口

    1. public interface FileService
    2. {
    3. void save(FileActionForm fileForm);//將提交的上傳文件保存到數(shù)據(jù)表中
    4. List getAllFile();//得到T_FILE所示記錄
    5. void write(OutputStream os,String fileId);//將某個文件的文件數(shù)據(jù)寫出到輸出流中
    6. String getFileName(String fileId);//獲取文件名
    7. }

      其中save(FileActionForm fileForm)方法,將封裝在fileForm中的上傳文件保存到數(shù)據(jù)庫中,這里我們使用FileActionForm作為方法入?yún)ⅲ現(xiàn)ileActionForm是Web層的表單數(shù)據(jù)對象,它封裝了提交表單的數(shù)據(jù)。將FileActionForm直接作為業(yè)務(wù)層的接口入?yún)ⅲ喈斢趯eb層傳播到業(yè)務(wù)層中去,即將業(yè)務(wù)層綁定在特定的Web層實現(xiàn)技術(shù)中,按照分層模型學院派的觀點,這是一種反模塊化的設(shè)計,但在"一般"的業(yè)務(wù)系統(tǒng)并無需提供多種UI界面,系統(tǒng)Web層將來切換到另一種實現(xiàn)技術(shù)的可能性也微乎其微,所以筆者覺得沒有必要為了這個業(yè)務(wù)層完全獨立于調(diào)用層的過高目標而去搞一個額外的隔離層,浪費了原材料不說,還將系統(tǒng)搞得過于復(fù)雜,相比于其它原則,"簡單"始終是最大的一條原則。

      getAllFile()負責獲取T_FILE表所有記錄,以便在網(wǎng)頁上顯示出來。

      而getFileName(String fileId)和write(OutputStream os,String fileId)則用于下載某個特定的文件。具體的調(diào)用是將Web層將response.getOutputStream()傳給write(OutputStream os,String fileId)接口,業(yè)務(wù)層直接將文件數(shù)據(jù)輸出到這個響應(yīng)流中。具體實現(xiàn)請參見錯誤!未找到引用源。節(jié)下載文件部分。

      2、業(yè)務(wù)層接口實現(xiàn)類

      FileService的實現(xiàn)類為FileServiceImpl,其中save(FileActionForm fileForm)的實現(xiàn)如下所示:

      代碼 8 業(yè)務(wù)接口實現(xiàn)類之save()

    1. …
    2. public class FileServiceImpl
    3. implements FileService
    4. {
    5. private TfileDAO tfileDAO;
    6. public void save(FileActionForm fileForm)
    7. {
    8. Tfile tfile = new Tfile();
    9. try
    10. {
    11. tfile.setFileContent(fileForm.getFileContent().getFileData());
    12. }
    13. catch (FileNotFoundException ex)
    14. {
    15. throw new RuntimeException(ex);
    16. }
    17. catch (IOException ex)
    18. {
    19. throw new RuntimeException(ex);
    20. }
    21. tfile.setFileName(fileForm.getFileContent().getFileName());
    22. tfile.setRemark(fileForm.getRemark());
    23. tfileDAO.save(tfile);
    24. }
    25. …
    26. }

      在save(FileActionForm fileForm)方法里,完成兩個步驟:

      其一,象在水桶間倒水一樣,將FileActionForm對象中的數(shù)據(jù)倒入到Tfile對象中;

      其二,調(diào)用TfileDAO保存數(shù)據(jù)。

      需要特別注意的是代碼的第11行,F(xiàn)ileActionForm的fileContent屬性為org.apache.struts.upload.FormFile類型,F(xiàn)ormFile提供了一個方便的方法getFileData(),即可獲取文件的二進制數(shù)據(jù)。通過解讀FormFile接口實現(xiàn)類DiskFile的原碼,我們可能知道FormFile本身并不緩存文件的數(shù)據(jù),只有實際調(diào)用getFileData()時,才從磁盤文件輸入流中獲取數(shù)據(jù)。由于FormFile使用流讀取方式獲取數(shù)據(jù),本身沒有緩存文件的所有數(shù)據(jù),所以對于上傳超大體積的文件,也是沒有問題的;但是,由于數(shù)據(jù)持久層的Tfile使用byte[]來緩存文件的數(shù)據(jù),所以并不適合處理超大體積的文件(如100M),對于超大體積的文件,依然需要使用java.sql.Blob類型以常規(guī)流操作的方式來處理。

      此外,通過FileForm的getFileName()方法就可以獲得上傳文件的文件名,如第21行代碼所示。

      write(OutputStream os,String fileId)方法的實現(xiàn),如代碼 9所示:

      代碼 9 業(yè)務(wù)接口實現(xiàn)類之write()

    1. …
    2. public class FileServiceImpl
    3. implements FileService
    4. {
    5.
    6. public void write(OutputStream os, String fileId)
    7. {
    8. Tfile tfile = tfileDAO.findByFildId(fileId);
    9. try
    10. {
    11. os.write(tfile.getFileContent());
    12. os.flush();
    13. }
    14. catch (IOException ex)
    15. {
    16. throw new RuntimeException(ex);
    17. }
    18. }
    19. …
    20. }

      write(OutputStream os,String fileId)也簡單地分為兩個操作步驟,首先,根據(jù)fileId加載表記錄,然后將fileContent寫入到輸出流中。

      3、Spring事務(wù)配置

      下面,我們來看如何在Spring配置文件中為FileService配置聲明性的事務(wù)

    1. <beans>
    2. …
    3. <bean id="transactionManager"
    4. class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    5. <property name="sessionFactory" ref="sessionFactory"/>
    6. </bean>
    7. <!-- 事務(wù)處理的AOP配置 //-->
    8. <bean id="txProxyTemplate" abstract="true"
    9. class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
    10. <property name="transactionManager" ref="transactionManager"/>
    11. <property name="transactionAttributes">
    12. <props>
    13. <prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
    14. <prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>
    15. <prop key="save">PROPAGATION_REQUIRED</prop>
    16. <prop key="write">PROPAGATION_REQUIRED,readOnly</prop>
    17. </props>
    18. </property>
    19. </bean>
    20. <bean id="fileService" parent="txProxyTemplate">
    21. <property name="target">
    22. <bean class="sshfile.service.FileServiceImpl">
    23. <property name="tfileDAO" ref="tfileDAO"/>
    24. </bean>
    25. </property>
    26. </bean>
    27. </beans>

      Spring的事務(wù)配置包括兩個部分:

      其一,定義事務(wù)管理器transactionManager,使用HibernateTransactionManager實現(xiàn)事務(wù)管理;

      其二,對各個業(yè)務(wù)接口進行定義,其實txProxyTemplate和fileService是父子節(jié)點的關(guān)系,本來可以將txProxyTemplate定義的內(nèi)容合并到fileService中一起定義,由于我們的系統(tǒng)僅有一個業(yè)務(wù)接口需要定義,所以將其定義的一部分抽象到父節(jié)點txProxyTemplate中意義確實不大,但是對于真實的系統(tǒng),往往擁有為數(shù)眾多的業(yè)務(wù)接口需要定義,將這些業(yè)務(wù)接口定義內(nèi)容的共同部分抽取到一個父節(jié)點中,然后在子節(jié)點中通過parent進行關(guān)聯(lián),就可以大大簡化業(yè)務(wù)接口的配置了。

      父節(jié)點txProxyTemplate注入了事務(wù)管理器,此外還定義了業(yè)務(wù)接口事務(wù)管理的方法(允許通過通配符的方式進行匹配聲明,如前兩個接口方法),有些接口方法僅對數(shù)據(jù)進行讀操作,而另一些接口方法需要涉及到數(shù)據(jù)的更改。對于前者,可以通過readOnly標識出來,這樣有利于操作性能的提高,需要注意的是由于父類節(jié)點定義的Bean僅是子節(jié)點配置信息的抽象,并不能具體實現(xiàn)化一個Bean對象,所以需要特別標注為abstract="true",如第8行所示。

      fileService作為一個目標類被注入到事務(wù)代理器中,而fileService實現(xiàn)類所需要的tfileDAO實例,通過引用3.2節(jié)中定義的tfileDAO Bean注入。

      Web層實現(xiàn)

      1、Web層的構(gòu)件和交互流程

      Web層包括主要3個功能:

      ·上傳文件。

      ·列出所有已經(jīng)上傳的文件列表,以供點擊下載。

      ·下載文件。

      Web層實現(xiàn)構(gòu)件包括與2個JSP頁面,1個ActionForm及一個Action:

      ·file-upload.jsp:上傳文件的頁面。

      ·file-list.jsp:已經(jīng)上傳文件的列表頁面。

      ·FileActionForm:file-upload.jsp頁面表單對應(yīng)的ActionForm。

      ·FileAction:繼承org.apache.struts.actions.DispatchAction的Action,這樣這個Action就可以通過一個URL參數(shù)區(qū)分中響應(yīng)不同的請求。

      Web層的這些構(gòu)件的交互流程如圖 6所示:


    圖 6 Web層Struts流程圖

      其中,在執(zhí)行文件上傳的請求時,F(xiàn)ileAction在執(zhí)行文件上傳后,forward到loadAllFile出口中,loadAllFile加載數(shù)據(jù)庫中所有已經(jīng)上傳的記錄,然后forward到名為fileListPage的出口中,調(diào)用file-list.jsp頁面顯示已經(jīng)上傳的記錄。

      2、FileAction功能

      Struts 1.0的Action有一個弱項:一個Action只能處理一種請求,Struts 1.1中引入了一個DispatchAction,允許通過URL參數(shù)指定調(diào)用Action中的某個方法,如http://yourwebsite/fileAction.do?method=upload即調(diào)用FileAction中的upload方法。通過這種方式,我們就可以將一些相關(guān)的請求集中到一個Action當中編寫,而沒有必要為某個請求操作編寫一個Action類。但是參數(shù)名是要在struts-config.xml中配置的:

    1. <struts-config>
    2. <form-beans>
    3. <form-bean name="fileActionForm" type="sshfile.web.FileActionForm" />
    4. </form-beans>
    5. <action-mappings>
    6. <action name="fileActionForm" parameter="method" path="/fileAction"
    7. type="sshfile.web.FileAction">
    8. <forward name="fileListPage" path="/file-list.jsp" />
    9. <forward name="loadAllFile" path="/fileAction.do?method=listAllFile" />
    10. </action>
    11. </action-mappings>
    12. </struts-config>

      第6行的parameter="method"指定了承載方法名的參數(shù),第9行中,我們還配置了一個調(diào)用FileAction不同方法的Action出口。

      FileAction共有3個請求響應(yīng)的方法,它們分別是:

      ·upload(…):處理上傳文件的請求。

      ·listAllFile(…):處理加載數(shù)據(jù)庫表中所有記錄的請求。

      ·download(…):處理下載文件的請求。

      下面我們分別對這3個請求處理方法進行講解。

      2.1 上傳文件

      上傳文件的請求處理方法非常簡單,簡之言之,就是從Spring容器中獲取業(yè)務(wù)層處理類FileService,調(diào)用其save(FileActionForm form)方法上傳文件,如下所示:

    1. public class FileAction
    2. extends DispatchAction
    3. {
    4. //將上傳文件保存到數(shù)據(jù)庫中
    5. public ActionForward upload(ActionMapping mapping, ActionForm form,
    6. HttpServletRequest request,
    7. HttpServletResponse response)
    8. {
    9. FileActionForm fileForm = (FileActionForm) form;
    10. FileService fileService = getFileService();
    11. fileService.save(fileForm);
    12. return mapping.findForward("loadAllFile");
    13. }
    14. //從Spring容器中獲取FileService對象
    15. private FileService getFileService()
    16. {
    17. ApplicationContext appContext = WebApplicationContextUtils.
    18. getWebApplicationContext(this.getServlet().getServletContext());
    19. return (FileService) appContext.getBean("fileService");
    20. }
    21. …
    22. }

      由于FileAction其它兩個請求處理方法也需要從Spring容器中獲取FileService實例,所以我們特別提供了一個getFileService()方法(第15~21行)。重構(gòu)的一條原則就是:"發(fā)現(xiàn)代碼中有重復(fù)的表達式,將其提取為一個變量;發(fā)現(xiàn)類中有重復(fù)的代碼段,將其提取為一個方法;發(fā)現(xiàn)不同類中有相同的方法,將其提取為一個類"。在真實的系統(tǒng)中,往往擁有多個Action和多個Service類,這時一個比較好的設(shè)置思路是,提供一個獲取所有Service實現(xiàn)對象的工具類,這樣就可以將Spring 的Service配置信息屏蔽在一個類中,否則Service的配置名字散落在程序各處,維護性是很差的。

      2.2 列出所有已經(jīng)上傳的文件

      listAllFile方法調(diào)用Servie層方法加載T_FILE表中所有記錄,并將其保存在Request域中,然后forward到列表頁面中:

    1. public class FileAction
    2. extends DispatchAction
    3. {
    4. …
    5. public ActionForward listAllFile(ActionMapping mapping, ActionForm form,
    6. HttpServletRequest request,
    7. HttpServletResponse response)
    8. throws ModuleException
    9. {
    10. FileService fileService = getFileService();
    11. List fileList = fileService.getAllFile();
    12. request.setAttribute("fileList",fileList);
    13. return mapping.findForward("fileListPage");
    14. }
    15. }

      file-list.jsp頁面使用Struts標簽展示出保存在Request域中的記錄:

    1. <%@page contentType="text/html; charset=GBK"%>
    2. <%@taglib uri="/WEB-INF/struts-logic.tld" prefix="logic"%>
    3. <%@taglib uri="/WEB-INF/struts-bean.tld" prefix="bean"%>
    4. <html>
    5. <head>
    6. <title>file-download</title>
    7. </head>
    8. <body bgcolor="#ffffff">
    9. <ol>
    10. <logic:iterate id="item" name="fileList" scope="request">
    11. <li>
    12. <a href='fileAction.do?method=download&fileId=
    13. <bean:write name="item"property="fileId"/>'>
    14. <bean:write name="item" property="fileName"/>
    15. </a>
    16. </li>
    17. </logic:iterate>
    18. </ol>
    19. </body>
    20. </html>

      展現(xiàn)頁面的每條記錄掛接著一個鏈接地址,形如:fileAction.do?method=download&fileId=xxx,method參數(shù)指定了這個請求由FileAction的download方法來響應(yīng),fileId指定了記錄的主鍵。

      由于在FileActionForm中,我們定義了fileId的屬性,所以在download響應(yīng)方法中,我們將可以從FileActionForm中取得fileId的值。這里涉及到一個處理多個請求Action所對應(yīng)的ActionForm的設(shè)計問題,由于原來的Action只能對應(yīng)一個請求,那么原來的ActionForm非常簡單,它僅需要將這個請求的參數(shù)項作為其屬性就可以了,但現(xiàn)在一個Action對應(yīng)多個請求,每個請求所對應(yīng)的參數(shù)項是不一樣的,此時的ActionForm的屬性就必須是多請求參數(shù)項的并集了。所以,除了文件上傳請求所對應(yīng)的fileContent和remark屬性外還包括文件下載的fileId屬性:


    圖 7 FileActionForm

      當然這樣會造成屬性的冗余,比如在文件上傳的請求中,只會用到fileContent和remark屬性,而在文件下載的請求時,只會使用到fileId屬性。但這種冗余是會帶來好處的--它使得一個Action可以處理多個請求。

      2.3 下載文件

      在列表頁面中點擊一個文件下載,其請求由FileAction的download方法來響應(yīng),download方法調(diào)用業(yè)務(wù)層的FileService方法,獲取文件數(shù)據(jù)并寫出到response的響應(yīng)流中。通過合理設(shè)置HTTP響應(yīng)頭參數(shù),將響應(yīng)流在客戶端表現(xiàn)為一個下載文件對話框,其代碼如下所示:

      代碼 10 業(yè)務(wù)接口實現(xiàn)類之download

    1. public class FileAction
    2. extends DispatchAction
    3. {
    4. …
    5. public ActionForward download(ActionMapping mapping, ActionForm form,
    6. HttpServletRequest request,
    7. HttpServletResponse response)
    8. throws ModuleException
    9. {
    10. FileActionForm fileForm = (FileActionForm) form;
    11. FileService fileService = getFileService();
    12. String fileName = fileService.getFileName(fileForm.getFileId());
    13. try
    14. {
    15. response.setContentType("application/x-msdownload");
    16. response.setHeader("Content-Disposition",
    17. "attachment;" + " filename="+
    18. new String(fileName.getBytes(), "ISO-8859-1"));
    19. fileService.write(response.getOutputStream(), fileForm.getFileId());
    20. }
    21. catch (Exception e)
    22. {
    23. throw new ModuleException(e.getMessage());
    24. }
    25. return null;
    26. }
    27. }

      第15~18行,設(shè)置HTTP響應(yīng)頭,將響應(yīng)類型設(shè)置為application/x-msdownload MIME類型,則響應(yīng)流在IE中將彈出一個文件下載的對話框,如圖 4所示。IE所支持的MIME類型多達26種,您可以通過這個網(wǎng)址查看其他的MIME類型:

    http://msdn.microsoft.com/workshop/networking/moniker/overview/appendix_a.asp。

      如果下載文件的文件名含有中文字符,如果不對其進行硬編碼,如第18行所示,客戶文件下載對話框中出現(xiàn)的文件名將會發(fā)生亂碼。
    第19行代碼獲得response的輸出流,作為FileServie write(OutputStream os,String fileId)的入?yún)?,這樣文件的內(nèi)容將寫到response的輸出流中。

      3、web.xml文件的配置

      Spring容器在何時啟動呢?我可以在Web容器初始化來執(zhí)行啟動Spring容器的操作,Spring提供了兩種方式啟動的方法:

      ·通過org.springframework.web.context .ContextLoaderListener容器監(jiān)聽器,在Web容器初始化時觸發(fā)初始化Spring容器,在web.xml中通過<listener></listener>對其進行配置。

      ·通過Servlet org.springframework.web.context.ContextLoaderServlet,將其配置為自動啟動的Servlet,在Web容器初始化時,通過這個Servlet啟動Spring容器。

      在初始化Spring容器之前,必須先初始化log4J的引擎,Spring也提供了容器監(jiān)聽器和自動啟動Servlet兩種方式對log4J引擎進行初始化:

      ·org.springframework.web.util .Log4jConfigListener

      ·org.springframework.web.util.Log4jConfigServlet

      下面我們來說明如何配置web.xml啟動Spring容器:

      代碼 11 web.xml中對應(yīng)Spring的配置內(nèi)容

    1. <web-app>
    2. <context-param>
    3. <param-name>contextConfigLocation</param-name>
    4. <param-value>/WEB-INF/applicationContext.xml</param-value>
    5. </context-param>
    6. <context-param>
    7. <param-name>log4jConfigLocation</param-name>
    8. <param-value>/WEB-INF/log4j.properties</param-value>
    9. </context-param>
    10. <servlet>
    11. <servlet-name>log4jInitServlet</servlet-name>
    12. <servlet-class>org.springframework.web.util.Log4jConfigServlet</servlet-class>
    13. <load-on-startup>1</load-on-startup>
    14. </servlet>
    15. <servlet>
    16. <servlet-name>springInitServlet</servlet-name>
    17. <servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>
    18. <load-on-startup>2</load-on-startup>
    19. </servlet>
    20. …
    21. </web-app>

      啟動Spring容器時,需要得到兩個信息:Spring配置文件的地址和Log4J屬性文件,這兩上信息分別通過contextConfigLocationWeb和log4jConfigLocation容器參數(shù)指定,如果有多個Spring配置文件,則用逗號隔開,如:

    /WEB-INF/applicationContext_1.xml, /WEB-INF/applicationContext_1.xm2

      由于在啟動ContextLoaderServlet之前,必須事先初始化Log4J的引擎,所以Log4jConfigServlet必須在ContextLoaderServlet之前啟動,這通過<load-on-startup>來指定它們啟動的先后順序。

      亂碼是開發(fā)Web應(yīng)用程序一個比較老套又常見問題,由于不同Web應(yīng)用服務(wù)器的默認編碼是不一樣的,為了方便Web應(yīng)用在不同的Web應(yīng)用服務(wù)器上移植,最好的做法是Web程序自身來處理編碼轉(zhuǎn)換的工作。經(jīng)典的作法是在web.xml中配置一個編碼轉(zhuǎn)換過濾器,Spring就提供了一個編碼過濾器類CharacterEncodingFilter,下面,我們?yōu)閼?yīng)用配置上這個過濾器:

    1. <web-app>
    2. …
    3. <filter>
    4. <filter-name>encodingFilter</filter-name>
    5. <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    6. <init-param>
    7. <param-name>encoding</param-name>
    8. <param-value>GBK</param-value>
    9. </init-param>
    10. </filter>
    11. <filter-mapping>
    12. <filter-name>encodingFilter</filter-name>
    13. <url-pattern>/*</url-pattern>
    14. </filter-mapping>
    15. …
    16. </web-app>

      Spring的過濾器類是org.springframework.web.filter.CharacterEncodingFilter,通過encoding參數(shù)指定編碼轉(zhuǎn)換類型為GBK,<filter-mapping>的配置使該過濾器截獲所有的請示。

      Struts的框架也需要在web.xml中配置,想必讀者朋友對Struts的配置都很熟悉,故在此不再提及,請參見本文所提供的源碼。

      總結(jié)

      本文通過一個文件上傳下載的Web應(yīng)用,講解了如何構(gòu)建基于SSH的Web應(yīng)用,通過Struts和FormFile,Spring的LobHandler以及Spring為HibernateBlob處理所提供的用戶類BlobByteArrayType ,實現(xiàn)上傳和下載文件的功能僅需要廖廖數(shù)行的代碼即告完成。讀者只需對程序作稍許的調(diào)整,即可處理Clob字段:

      ·領(lǐng)域?qū)ο髮?yīng)Clob字段的屬性聲明為String類型;

      ·映射文件對應(yīng)Clob字段的屬性聲明為org.springframework.orm.hibernate3.support.ClobStringType類型。

      本文通過SSH對文件上傳下載簡捷完美的實現(xiàn)得以管中窺豹了解SSH強強聯(lián)合構(gòu)建Web應(yīng)用的強大優(yōu)勢。在行文中,還穿插了一些分層的設(shè)計經(jīng)驗,配置技巧和Spring所提供的方便類,相信這些知識對您的開發(fā)都有所裨益。
    posted @ 2006-01-13 20:31 阿成 閱讀(1093) | 評論 (0)編輯 收藏
     其實,就算用Java建造一個不是很煩瑣的web應(yīng)用程序,也不是件輕松的事情。當為一個應(yīng)用程序建造一個構(gòu)架時有許多事情需要考慮。從高層來說,開發(fā)者需要考慮:怎樣建立用戶接口(user interfaces)?在哪里處理業(yè)務(wù)邏輯?和怎樣持久化應(yīng)用數(shù)據(jù)。這三層每一層都有它們各自的問題需要回答。 各個層次應(yīng)該使用什么技術(shù)?怎樣才能把應(yīng)用程序設(shè)計得松耦合和能靈活改變?構(gòu)架允許層的替換不會影響到其它層嗎?應(yīng)用程序怎樣處理容器級的服務(wù)(container level services),比如事務(wù)處理(transactions)?

      當為你的web應(yīng)用程序創(chuàng)建一個構(gòu)架時,需要涉及到相當多的問題。幸運的是,已經(jīng)有不少開發(fā)者已經(jīng)遇到過這類重復(fù)發(fā)生的問題,并且建立了處理這類問題的框架。一個好框架具備以下幾點: 減輕開發(fā)者處理復(fù)雜的問題的負擔(“不重復(fù)發(fā)明輪子”);內(nèi)部定義為可擴展的;有一個強大的用戶群支持??蚣芡ǔD軌蚝芎玫慕鉀Q一方面的問題。然而,你的應(yīng)用程序有幾個層可能都需要它們各自的框架。就如解決你的用戶接口(UI)問題時你就不應(yīng)該把事務(wù)邏輯和持久化邏輯摻雜進來。例如,你不應(yīng)該在控制器(controller)里面寫jdbc代碼,使它包含有業(yè)務(wù)邏輯,這不是控制器應(yīng)該提供的功能。它應(yīng)該是輕量級的,代理來自用戶接口(UI)外的調(diào)用請求給其它服務(wù)于這些請求的應(yīng)用層。好的框架自然的形成代碼如何分布的指導(dǎo)。更重要的是,框架減輕開發(fā)者從頭開始寫像持久層這樣的代碼的痛苦,使他們專注于對客戶來說很重要的應(yīng)用邏輯。

      這篇文章將討論怎樣組合幾個著名的框架去做到松耦合的目的,怎樣建立你的構(gòu)架,怎樣讓你的各個應(yīng)用層保持一致。富于挑戰(zhàn)的是:組合這些框架使得每一層都以一種松耦合的方式彼此溝通,而與底層的技術(shù)無關(guān)。這篇文章將使用3種流行的開源框架來討論組合框架的策略。表現(xiàn)層我們將使用Struts(http://jakarta.apache.org/struts);業(yè)務(wù)層我們將使用Spring(http://www.springframework.org/);持久層使用Hibrenate(http://www.hibernate.org/).你也可以在你的應(yīng)用程序中替換這些框架中的任何一種而得到同樣的效果。圖1展示了當這些框架組合在一起時從高層看是什么樣子。


    圖1用Struts, Spring, 和 Hibernate框架構(gòu)建的概覽

    應(yīng)用程序的分層 (Application Layering)

    大多數(shù)不復(fù)雜的web應(yīng)用都能被分成至少4個各負其責的層次。這些層次是:表現(xiàn)層(presentation)、持久層(persistence)、業(yè)務(wù)層(business)、領(lǐng)域模型層(domain model)。每層在應(yīng)用程序中都有明確的責任,不應(yīng)該和其它層混淆功能。每一應(yīng)用層應(yīng)該彼此獨立但要給他們之間放一個通訊接口。讓我們從審視各個層開始,討論這些層應(yīng)該提供什么和不應(yīng)該提供什么。

    表現(xiàn)層 (The Presentation Layer)

      在一個典型的web應(yīng)用的一端是表現(xiàn)層。很多Java開發(fā)者也理解Struts所提供的。然而,太常見的是,他們把像業(yè)務(wù)邏輯之類的耦合的代碼放進了一個org.apache.struts.Action。所以,讓我們在像Struts這樣一個框架應(yīng)該提供什么上取得一致意見。這兒是Struts負責的:

    為用戶管理請求和響應(yīng);
    提供一個控制器(controller)代理調(diào)用業(yè)務(wù)邏輯和其它上層處理;
    處理從其它層擲出給一個Struts Action的異常;
    為顯示提供一個模型;
    執(zhí)行用戶接口(UI)驗證。

    這兒是一些經(jīng)常用Struts編寫的但是卻不應(yīng)該和Struts表現(xiàn)層相伴的項目:
    直接和數(shù)據(jù)庫通訊,比如JDBC調(diào)用;
    業(yè)務(wù)邏輯和與你的應(yīng)用程序相關(guān)的驗證;
    事務(wù)管理;
    在表現(xiàn)層中引入這種代碼將導(dǎo)致典型耦合(type coupling)和討厭的維護。

    持久層 (The Persistence Layer )

      在典型web應(yīng)用的另一端是持久層。這通常是使事情迅速失控的地方。開發(fā)者低估了構(gòu)建他們自己的持久層框架的挑戰(zhàn)性。一般來說,機構(gòu)內(nèi)部自己寫的持久層不僅需要大量的開發(fā)時間,而且還經(jīng)常缺少功能和變得難以控制。有幾個開源的“對象-關(guān)系映射”(ORM)框架非常解決問題。尤其是,Hibernate框架為java提供了"對象-關(guān)系持久化"(object-to-relational persistence)機制和查詢服務(wù)。Hibernate對那些已經(jīng)熟悉了SQL和JDBC API的Java開發(fā)者有一個適中的學習曲線。Hibernate持久對象是基于簡單舊式Java對象(POJO)和Java集合(Java collections)。此外,使用Hibernate并不妨礙你正在使用的IDE。下面的列表包含了你該寫在一個持久層框架里的代碼類型:

      查詢相關(guān)的信息成為對象。Hibernate通過一種叫作HQL的面向?qū)ο螅∣O)的查詢語言或者使用條件表達式API(expressive criteria API)來做這個事情。 HQL非常類似于SQL-- 只是把SQL里的table和columns用Object和它的fields代替。有一些新的專用的HQL語言成分要學;不過,它們?nèi)菀桌斫舛椅臋n做得好。HQL是一種使用來查詢對象的自然語言,花很小的代價就能學習它。

    保存、更新、刪除儲存在數(shù)據(jù)庫中的信息。

    像Hibernate這樣的高級“對象-關(guān)系”映射(object-to-relational mapping)框架提供對大多數(shù)主流SQL數(shù)據(jù)庫的支持,它們支持“父/子”(parent/child)關(guān)系、事務(wù)處理、繼承和多態(tài)。


    這兒是一些應(yīng)該在持久層里被避免的項目:


    業(yè)務(wù)邏輯應(yīng)該在你的應(yīng)用的一個高一些的層次里。持久層里僅僅允許數(shù)據(jù)存取操作。

      你不應(yīng)該把持久層邏輯(persistence logic)和你的表現(xiàn)層邏輯(presentation logic)攪在一起。避免像JSPs或基于servlet的類這些表現(xiàn)層組件里的邏輯和數(shù)據(jù)存取直接通訊。通過把持久層邏輯隔離進它自己的層,應(yīng)用程序變得易于修改而不會影響在其它層的代碼。例如:Hebernate能夠被其它持久層框架或者API代替而不會修改在其它任何層的代碼。


    業(yè)務(wù)層(The Business Layer)

      在一個典型的web應(yīng)用程序的中間的組件是業(yè)務(wù)層或服務(wù)層。從編碼的視角來看,這個服務(wù)層是最容易被忽視的一層。不難在用戶接口(UI)層或者持久層里找到散布在其中的這種類型的代碼。這不是正確的地方,因為這導(dǎo)致了應(yīng)用程序的緊耦合,這樣一來,隨著時間推移代碼將很難維護。幸好,針對這一問題有好幾種Frameworks存在。在這個領(lǐng)域兩個最流行的框架是Spring和PicoContainer,它們叫作微容器(microcontainers),你可以不費力不費神的把你的對象連在一起。所有這些框架都工作在一個簡單的叫作“依賴注入”(dependency injection)(也通稱“控制反轉(zhuǎn)”(inversion of control))的概念上。這篇文章將著眼于Spring的為指定的配置參數(shù)通過bean屬性的setter注入(setter injection)的使用。Spring也提供了一個構(gòu)建器注入(constructor injection)的復(fù)雜形式作為setter注入的一個替代。對象們被一個簡單的XML文件連在一起,這個XML文件含有到像事務(wù)管理器(transaction management handler)、對象工廠(object factories)、包含業(yè)務(wù)邏輯的服務(wù)對象(service objects)、和數(shù)據(jù)存取對象(DAO)這些對象的引用(references)。

      這篇文章的后面將用例子來把Spring使用這些概念的方法說得更清楚一些。業(yè)務(wù)層應(yīng)該負責下面這些事情:
    處理應(yīng)用程序的業(yè)務(wù)邏輯和業(yè)務(wù)驗證;
    管理事務(wù);
    預(yù)留和其它層交互的接口;
    管理業(yè)務(wù)層對象之間的依賴;
    增加在表現(xiàn)層和持久層之間的靈活性,使它們互不直接通訊;
    從表現(xiàn)層中提供一個上下文(context)給業(yè)務(wù)層獲得業(yè)務(wù)服務(wù)(business services );
    管理從業(yè)務(wù)邏輯到持久層的實現(xiàn)。

    領(lǐng)域模型層 (The Domain Model Layer )
      最后,因為我們討論的是一個不是很復(fù)雜的、基于web的應(yīng)用程序,我們需要一組能在不同的層之間移動的對象。領(lǐng)域?qū)ο髮佑赡切┐憩F(xiàn)實世界中的業(yè)務(wù)對象的對象們組成,比如:一份訂單(Order)、訂單項(OrderLineItem)、產(chǎn)品(Product)等等。這個層讓開發(fā)者停止建立和維護不必要的數(shù)據(jù)傳輸對象(或者叫作DTOs),來匹配他們的領(lǐng)域?qū)ο蟆@?,Hibernate允許你把數(shù)據(jù)庫信息讀進領(lǐng)域?qū)ο螅╠omain objects)的一個對象圖,這樣你可以在連接斷開的情況下把這些數(shù)據(jù)顯示到UI層。那些對象也能被更新和送回到持久層并在數(shù)據(jù)庫里更新。而且,你不必把對象轉(zhuǎn)化成DTOs,因為DTOs在不同的應(yīng)用層間移動,可能在轉(zhuǎn)換中丟失。這個模型使得Java開發(fā)者自然地以一種面向?qū)ο蟮娘L格和對象打交道,沒有附加的編碼。

    結(jié)合一個簡單的例子

      既然我們已經(jīng)從一個高的層次上理解了這些組件, 現(xiàn)在就讓我們開始實踐吧。在這個例子中,我們還是將合并Struts、Spring、Hibernate框架。每一個這些框架在一篇文章中都有太多的細節(jié)覆蓋到。這篇文章將用一個簡單的例子代碼展示怎樣把它們結(jié)合在一起,而不是進入每個框架的許多細節(jié)。示例應(yīng)用程序?qū)⑹痉兑粋€請求怎樣跨越每一層被服務(wù)的。這個示例應(yīng)用程序的一個用戶能保存一個訂單到數(shù)據(jù)庫中和查看一個在數(shù)據(jù)庫中存在的訂單。進一步的增強可以使用戶更新或刪除一個存在的訂單。  

    你可以下載這個應(yīng)用的源碼(http://www.onjava.com/onjava/2004/04/07/examples/wiring.zip)。

      因為領(lǐng)域?qū)ο螅╠omain objects)將和每一層交互,我們將首先創(chuàng)建它們。這些對象將使我們定義什么應(yīng)該被持久化,什么業(yè)務(wù)邏輯應(yīng)該被提供,和哪種表現(xiàn)接口應(yīng)該被設(shè)計。然后,我們將配置持久層和用Hibernate為我們的領(lǐng)域?qū)ο螅╠omain objects)定義“對象-關(guān)系”映射(object-to-relational mappings)。然后,我們將定義和配置我們的業(yè)務(wù)對象(business objects)。在有了這些組件后,我們就能討論用Spring把這些層連在一起。最后,我們將提供一個表現(xiàn)層(presentation layer),它知道怎樣和業(yè)務(wù)服務(wù)層(business service layer)交流和知道怎樣處理從其它層產(chǎn)生的異常(exceptions)。

    領(lǐng)域?qū)ο髮樱―omain Object Layer)

      因為這些對象將和所有層交互,這也許是一個開始編碼的好地方。這個簡單的領(lǐng)域模型將包括一個代表一份訂單(order)的對象和一個代表一個訂單項(line item for an order)的對象。訂單(order)對象將和一組訂單項(a collection of line item)對象有一對多(one-to-many)的關(guān)系。例子代碼在領(lǐng)域?qū)佑袃蓚€簡單的對象:
    com.meagle.bo.Order.java: 包括一份訂單(oder)的概要信息;
    com.meagle.bo.OrderLineItem.java: 包括一份訂單(order)的詳細信息;
    考慮一下為你的對象選擇包名,它將反映你的應(yīng)用程序是怎樣分層的。例如:簡單應(yīng)用的領(lǐng)域?qū)ο螅╠omain objects)可以放進com.meagle.bo包[譯者注:bo-business object?]。更多專門的領(lǐng)域?qū)ο髮⒎湃朐赾om.meagle.bo下面的子包里。業(yè)務(wù)邏輯在com.meagle.service包里開始打包,DAO對象放進com.meagle.service.dao.hibernate包。對于forms和actions的表現(xiàn)類(presentation classes)分別放入com.meagle.action 和 com.meagle.forms包。準確的包命名為你的類提供的功能提供一個清楚的區(qū)分,使當故障維護時更易于維護,和當給應(yīng)用程序增加新的類或包時提供一致性。


    持久層配置(Persistence Layer Configuration)

      用Hibernate設(shè)置持久層涉及到幾個步驟。第一步是進行配置持久化我們的領(lǐng)域業(yè)務(wù)對象(domain business objects )。因為我們用于領(lǐng)域?qū)ο螅╠omain objects )持久化的Hibernate和POJOs一起工作( 此句原文:Since Hibernate works with POJOs we will use our domain objects for persistence.),因此,訂單和訂單項對象包括的所有的字段的都需要提供getter和setter方法。訂單對象將包括像ID、用戶名、合計、和訂單項這樣一些字段的標準的JavaBean格式的setter和getter方法。訂單項對象將同樣的用JavaBean的格式為它的字段設(shè)置setter和getter方法。
      Hibernate在XML文件里映射領(lǐng)域?qū)ο蟮疥P(guān)系數(shù)據(jù)庫。訂單和訂單項對象將有兩個映射文件來表達這種映射。有像XDoclet(http://xdoclet.sourceforge.net/)這樣的工具來幫助這種映射。Hibernate將映射領(lǐng)域?qū)ο蟮竭@些文件:
    Order.hbm.xml
    OrderLineItem.hbm.xml
    你可以在WebContent/WEB-INF/classes/com/meagle/bo目錄里找到這些生成的文件。配置Hibernate SessionFactory(http://www.hibernate.org/hib_docs/api/net/sf/hibernate/SessionFactory.html)使它知道是在和哪個數(shù)據(jù)庫通信,使用哪個數(shù)據(jù)源或連接池,加載哪些持久對象。SessionFactory提供的Session(http://www.hibernate.org/hib_docs/api/net/sf/hibernate/Session.html)對象是Java對象和像選取、保存、更新、刪除對象這樣一些持久化功能間的翻譯接口。我們將在后面的部分討論Hibernate操作Session對象需要的SessionFactory配置。

    業(yè)務(wù)層配置(Business Layer Configuration )

      既然我們已經(jīng)有了領(lǐng)域?qū)ο螅╠omain objects),我們需要有業(yè)務(wù)服務(wù)對象來執(zhí)行應(yīng)用邏輯、執(zhí)行向持久層的調(diào)用、獲得從用戶接口層(UI layer)的請求、處理事務(wù)、處理異常。為了將所有這些連接起來并且易于管理,我們將使用Spring框架的bean管理方面(bean management aspect)。Spring使用“控制反轉(zhuǎn)”(IoC),或者“setter依賴注入”來把這些對象連好,這些對象在一個外部的XML文件中被引用?!翱刂品崔D(zhuǎn)”是一個簡單的概念,它允許對象接受其它的在一個高一些的層次被創(chuàng)建的對象。使用這種方法,你的對象從必須創(chuàng)建其它對象中解放出來并降低對象耦合。

      這兒是個不使用IoC的對象創(chuàng)建它的從屬對象( object creating its dependencies without IoC)的例子,這導(dǎo)致緊的對象耦合:


    圖2:沒有使用IoC的對象組織。對象A創(chuàng)建對象B和C。

      這兒是一個使用IoC的例子,它允許對象在一個高一些層次被創(chuàng)建和傳進另外的對象,所以另外的對象能直接使用現(xiàn)成的對象·[譯者注:另外的對象不必再親自創(chuàng)建這些要使用的對象](allows objects to be created at higher levels and passed into objects so that they can use the implementations directly):


     圖3:對象使用IoC組織。對象A包含setter方法,它們接受到對象B和C的接口。這也可以用對象A里的接受對象B和C的構(gòu)建器完成。

    建立我們的業(yè)務(wù)服務(wù)對象(Building Our Business Service Objects)
      我們將在我們的業(yè)務(wù)對象中使用的setter方法接受的是接口,這些接口允許對象的松散定義的實現(xiàn),這些對象將被設(shè)置或者注入。在我們這個例子里我們將使我們的業(yè)務(wù)服務(wù)對象接受一個DAO去控制我們的領(lǐng)域?qū)ο蟮某志没?。當我們在這篇文章的例子中使用Hibernate( While the examples in this article use Hibernate),我們可以容易的轉(zhuǎn)換到一個不同的持久框架的實現(xiàn),通知Spring使用新的實現(xiàn)的DAO對象。你能明白編程到接口和使用“依賴注入”模式是怎樣寬松耦合你的業(yè)務(wù)邏輯和你的持久化機制的。
      這兒是業(yè)務(wù)服務(wù)對象的接口,它是一個DAO對象依賴的樁。(Here is the interface for the business service object that is stubbed for a DAO object dependency: )



    public interface IOrderService { public abstract Order saveNewOrder(Order order) throws OrderException, OrderMinimumAmountException; public abstract List findOrderByUser( String user) throws OrderException; public abstract Order findOrderById(int id) throws OrderException; public abstract void setOrderDAO( IOrderDAO orderDAO); }


      注意上面的代碼有一個為DAO對象準備的setter方法。這兒沒有一個getOrderDAO方法因為它不是必要的,因為不太有從外面訪問連著的OrderDAO對象的需要。DAO對象將被用來和我們的持久層溝通。我們將用Spring把業(yè)務(wù)服務(wù)對象和DAO對象連在一起。因為我們編碼到接口,我們不會緊耦合實現(xiàn)。

    下一步是寫我們的DAO實現(xiàn)對象。因為Spring有內(nèi)建的對Hibernate的支持,這個例子DAO將繼承HibernateDaoSupport(http://www.springframework.org/docs/api/org/springframework/
    orm/hibernate/support/HibernateDaoSupport.html)類,這使得我們?nèi)菀兹〉靡粋€到HibernateTemplate(http://www.springframework.org/docs/api/org/springframework/
    orm/hibernate/HibernateTemplate.html)類的引用,HibernateTemplate是一個幫助類,它能簡化Hibernate Session的編碼和處理HibernateExceptions。這兒是DAO的接口:



    public interface IOrderDAO { public abstract Order findOrderById( final int id); public abstract List findOrdersPlaceByUser( final String placedBy); public abstract Order saveOrder( final Order order); }


      我們還有兩個對象要和我們的業(yè)務(wù)層連在一起。這包括HibernateSessionFactory和一個TransactionManager對象。這在Spring配置文件里直接完成。Spring提供一個HibernateTransactionManager(http://www.springframework.org/docs/api/org/springframework/
    orm/hibernate/HibernateTransactionManager.html),它將從工廠綁定一個Hibernate Session到一個線程來支持事務(wù)(見ThreadLocal(http://java.sun.com/j2se/1.4.2/docs/api/java/lang/ThreadLocal.html)獲取更多的信息)。這兒是HibernateSessionFactory和HibernateTransactionManager的Spring配置。

    class="org.springframework.orm.hibernate. LocalSessionFactoryBean"> com/meagle/bo/Order.hbm.xml com/meagle/bo/OrderLineItem.hbm.xml net.sf.hibernate.dialect.MySQLDialect false C:/MyWebApps/.../WEB-INF/proxool.xml spring class="org. springframework. orm. hibernate. HibernateTransactionManager">

      每一個對象能被Spring配置里的一個標記引用。在這個例子里,bean “mySessionFactory”代表一個HibernateSessionFactory,bean “myTransactionManager”代表一個Hibernate transaction manager。注意transactionManger bean有一個叫作sessionFactory的屬性元素。HibernateTransactionManager有一個為sessionFactory準備的setter和getter方法,它們是用來當Spring容器啟動時的依賴注入。sessionFactory屬性引用mySessionFactory bean。這兩個對象現(xiàn)在當Spring容器初始化時將被連在一起。這種連接把你從為引用和創(chuàng)建這些對象而創(chuàng)建singleton對象和工廠中解放出來,這減少了你應(yīng)用程序中的代碼維護。mySessionFactory bean有兩個屬性元素,它們翻譯成為mappingResources 和 hibernatePropertes準備的setter方法。通常,如果你在Spring之外使用Hibernate,這個配置將被保存在hibernate.cfg.xml文件中。不管怎樣,Spring提供了一個便捷的方式--在Spring配置文件中合并Hibernate的配置。獲得更多的信息查閱Spring API(http://www.springframework.org/docs/api/index.html)。

    既然我們已經(jīng)配置了我們的容器服務(wù)beans和把它們連在了一起,我們需要把我們的業(yè)務(wù)服務(wù)對象和我們的DAO對象連在一起。然后,我們需要把這些對象連接到事務(wù)管理器。

    這是在Spring配置文件里的樣子:


    class="org. springframework. transaction. interceptor. TransactionProxyFactoryBean"> PROPAGATION_REQUIRED,readOnly,-OrderException PROPAGATION_REQUIRED,-OrderException class="com. meagle. service. spring. OrderServiceSpringImpl"> class="com. meagle. service. dao. hibernate. OrderHibernateDAO">


    圖4是我們已經(jīng)連在一起的東西的一個概覽。它展示了每個對象是怎樣相關(guān)聯(lián)的和怎樣被Spring設(shè)置進其它對象中。把這幅圖和示例應(yīng)用中的Spring配置文件對比查看它們之間的關(guān)系。


    圖4:這是Spring怎樣將在這個配置的基礎(chǔ)上裝配beans。

      這個例子使用一個TransactionProxyFactoryBean,它有一個為我們已經(jīng)定義了的事務(wù)管理者準備的setter方法。這是一個有用的對象,它知道怎樣處理聲明的事務(wù)操作和你的服務(wù)對象。你可以通過transactionAttributes屬性定義事務(wù)怎樣被處理,transactionAttributes屬性為方法名定義模式和它們怎樣參與進一個事務(wù)。獲得更多的關(guān)于在一個事務(wù)上配置隔離層和提交或回滾查閱TransactionAttributeEditor(http://www.springframework.org/docs/api/org/springframework/
    transaction/interceptor/TransactionAttributeEditor.html)。

      TransactionProxyFactoryBean(http://www.springframework.org/docs/api/org/springframework/
    transaction/interceptor/TransactionProxyFactoryBean.html)類也有一個為一個target準備的setter,target將是一個到我們的叫作orderTarget的業(yè)務(wù)服務(wù)對象的引用(a reference)。 orderTarget bean定義使用哪個業(yè)務(wù)服務(wù)對象并有一個指向setOrderDAO()的屬性。orderDAO bean將居于這個屬性中,orderDAO bean是我們的和持久層交流的DAO對象。

      還有一個關(guān)于Spring和bean要注意的是bean能以兩種模式工作。這兩種模式被定義為singleton和prototype。一個bean默認的模式是singleton,意味著一個共享的bean的實例將被管理。這是用于無狀態(tài)操作--像一個無狀態(tài)會話bean將提供的那樣。當bean由Spring提供時,prototype模式允許創(chuàng)建bean的新實例。你應(yīng)當只有在每一個用戶都需要他們自己的bean的拷貝時才使用prototype模式。

    提供一個服務(wù)定位器(Providing a Service Locator)
      既然我們已經(jīng)把我們的服務(wù)和我們的DAO連起來了,我們需要把我們的服務(wù)暴露給其它層。通常是一個像使用Struts或Swing這樣的用戶接口層里的代碼來使用這個服務(wù)。一個簡單的處理方法是使用一個服務(wù)定位器模式的類從一個Spring上下文中返回資源。這也可以靠引用bean ID通過Spring來直接完成。
      這兒是一個在Struts Action中怎樣配置一個服務(wù)定位器的例子:



    public abstract class BaseAction extends Action { private IOrderService orderService; public void setServlet(ActionServlet actionServlet) { super.setServlet(actionServlet); ServletContext servletContext = actionServlet.getServletContext(); WebApplicationContext wac = WebApplicationContextUtils. getRequiredWebApplicationContext( servletContext); this.orderService = (IOrderService) wac.getBean("orderService"); } protected IOrderService getOrderService() { return orderService; } }
    用戶接口層配置 (UI Layer Configuration)   示例應(yīng)用的用戶接口層使用Struts框架。這兒我們將討論當為一個應(yīng)用分層時和Struts相關(guān)的部分。讓我們從在struts-config.xml文件里檢查一個Action配置開始。
    type="com.meagle.action.SaveOrderAction" name="OrderForm" scope="request" validate="true" input="/NewOrder.jsp"> Save New Order path="/NewOrder.jsp" scope="request" type="com.meagle.exception.OrderException"/> path="/NewOrder.jsp" scope="request" type="com. meagle. exception. OrderMinimumAmountException"/>


      SaveNewOrder Action被用來持久化一個用戶從用戶接口層提交的訂單。這是一個典型的Struts Action;然而,注意這個action的異常配置。這些Exceptions為我們的業(yè)務(wù)服務(wù)對象也在Spring 配置文件(applicationContext-hibernate.xml)中配置了(在transactionAttributes屬性里)。當這些異常被從業(yè)務(wù)層擲出我們能在我們的用戶接口里恰當?shù)奶幚硭鼈?。第一個異常,OrderException,當在持久層里保存訂單對象失敗時將被這個action使用。這將引起事務(wù)回滾和通過業(yè)務(wù)對象傳遞把異常傳回給Struts層。OrderMinimumAmountException,在業(yè)務(wù)對象邏輯里的一個事務(wù)因為提交的訂單達不到最小訂單數(shù)量而失敗也將被處理。然后,事務(wù)將回滾和這個異常能被用戶接口層恰當?shù)奶幚怼?

      最后一個連接步驟是使我們的表現(xiàn)層和我們的業(yè)務(wù)層交互。這已經(jīng)通過使用前面討論的服務(wù)定位器來完成了。服務(wù)層充當一個到我們的業(yè)務(wù)邏輯和持久層的接口。這兒是 Struts中的SaveNewOrder Action可能怎樣使用一個服務(wù)定位器調(diào)用一個業(yè)務(wù)方法:



    public ActionForward execute( ActionMapping mapping, ActionForm form, javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws java.lang.Exception { OrderForm oForm = (OrderForm) form; // Use the form to build an Order object that // can be saved in the persistence layer. // See the full source code in the sample app. // Obtain the wired business service object // from the service locator configuration // in BaseAction. // Delegate the save to the service layer and // further upstream to save the Order object. getOrderService().saveNewOrder(order); oForm.setOrder(order); ActionMessages messages = new ActionMessages(); messages.add( ActionMessages.GLOBAL_MESSAGE, new ActionMessage( "message.order.saved.successfully")); saveMessages(request, messages); return mapping.findForward("success"); }


    結(jié)論
      這篇文章按照技術(shù)和架構(gòu)覆蓋了許多話題。從中而取出的主要思想是怎樣更好的給你的應(yīng)用程序分層:用戶接口層、持久邏輯層、和其它任何你需要的應(yīng)用層。這樣可以解耦你的代碼,允許添加新的代碼組件,使你的應(yīng)用在將來更易維護。這里覆蓋的技術(shù)能很好的解決這類的問題。不管怎樣,使用這樣的構(gòu)架可以讓你用其他技術(shù)代替現(xiàn)在的層。
    posted @ 2006-01-12 15:20 阿成 閱讀(585) | 評論 (0)編輯 收藏
    僅列出標題
    共10頁: First 上一頁 2 3 4 5 6 7 8 9 10 下一頁 
    主站蜘蛛池模板: a在线视频免费观看| 一级一级毛片免费播放| 国产精品亚洲va在线观看| 深夜福利在线免费观看| 亚洲免费日韩无码系列| 可以免费观看的国产视频| 57PAO成人国产永久免费视频| 好男人看视频免费2019中文| 亚洲国产成人精品91久久久| 亚洲无线观看国产精品| 久久亚洲AV无码精品色午夜麻豆 | 在线观看免费a∨网站| 四虎影视永久免费观看地址| 亚洲精品字幕在线观看| 亚洲综合网美国十次| 亚洲国产成人久久一区二区三区| 欧亚一级毛片免费看| 日韩av无码久久精品免费| 在线免费不卡视频| 亚洲日韩欧洲无码av夜夜摸 | 国产亚洲欧洲精品| 亚洲人成高清在线播放| 美女视频黄a视频全免费网站色| a级毛片无码免费真人久久| 91在线品视觉盛宴免费| 亚洲精品国产综合久久一线| 久久久亚洲欧洲日产国码二区| 国产精品亚洲综合网站| 久久大香香蕉国产免费网站| 免费理论片51人人看电影| 久久精品国产亚洲麻豆| 91在线亚洲综合在线| 三级网站免费观看| 女人张开腿给人桶免费视频| 国产亚洲成av人片在线观看| 亚洲国产成人久久综合| 51在线视频免费观看视频| 亚洲av中文无码| 亚洲中文字幕无码一去台湾| 国产一区二区三区免费观看在线| 精品国产免费观看|