軟體專案實訓及課程設計指導——如何最佳化Web應用資料訪問實現方式以提高軟體應用系統的響應效能
在軟體應用系統中離不開資料訪問和資料處理兩個方面的功能,而資料處理之前首先要進行資料訪問,也就是隻有快速地獲得了資料,才能進行下一步的資料處理。因此,如果資料訪問比較耗時,儘管有高效的資料處理,但軟體應用系統的最終效能也有可能仍然是低下的。
怎麼進行資料訪問?怎麼能夠高效地獲得資料?怎麼能夠以最經濟的技術手段高效地獲得資料?作者在下文中為讀者介紹如何最佳化軟體應用系統中兩種最常見的資料訪問方式——檔案讀寫和資料庫表資料存取。
1、在軟體應用系統中應用輕量級的XML檔案IO訪問技術
在J2EE Web應用系統的開發實現中,設計和開發實現人員可以將應用系統中不需要頻繁修改的資料(如軟體系統的配置資料、執行環境有關的引數、彙總統計查詢中使用者查詢頻繁但資料變動並不大等應用狀況下的資料)儲存到XML格式的配置檔案,而不要儲存到物理資料庫系統的資料庫表中。如下示例圖顯示Struts2應用框架中的struts.xml檔案中的配置定義資料。
因為對資料庫表中的資料頻繁地訪問會加重軟體應用系統的負擔——訪問物理資料庫系統的資料庫表中的資料是重量級的IO(Input/Output,輸入輸出)操作,而且物理資料庫系統一般都在遠端資料庫伺服器主機中,頻繁地進行遠端資料訪問的效能更加低下;而訪問XML格式檔案中的資料是輕量級的IO訪問操作,對軟體應用系統的資源消耗相對比較小、不太可能會出現效能問題。
因此,可將軟體應用系統中的比如業務查詢結果生成XML格式檔案,並儲存在Web伺服器主機上,使客戶端能夠直接和XML格式檔案進行互動,以節省訪問物理資料庫的系統性能開銷。這樣的效能最佳化設計方案是最經濟、也是最有效的效能最佳化方法。
在示例專案銀行賬戶資訊管理系統中,作者將Web應用系統中的公告資訊存放在XML格式的配置檔案中——請見下圖所示及如下的示例程式碼。
<?xml version="1.0" encoding="gb2312" ?><information> <index-information> <marquee-text> 系統公告:系統升級給大家帶來的不便還請原諒,本系統正在招聘前臺工作人員 </marquee-text> </index-information></information>
因為這些"公告"和"通知"等型別的資訊,本身並不會頻繁地被修改,在一段時間內基本上是處於穩定不變化的狀況。但對它的查詢訪問卻是頻繁的、高頻次的——每個使用者都有可能會查詢和訪問以瞭解具體的內容。
然後在程式中再利用SAX(Simple API for XML)的XML解析技術程式設計讀取該XML配置檔案中的資料、並在頁面顯示輸出。如下為對應的SAX解析的程式程式碼示例,並請注意其中黑體標識的語句。
package com.bluedream.webbank.util;import org.xml.sax.helpers.DefaultHandler;import com.bluedream.webbank.exception.WebBankException;import java.io.*;import org.xml.sax.Attributes;import org.xml.sax.SAXException;import javax.xml.parsers.SAXParser;import javax.xml.parsers.SAXParserFactory;public class SAXInformationConfig extends DefaultHandler implements XMLInformationConfig{ private String marqueeText = ""; public String getMarqueeText(){ return marqueeText; } public SAXInformationConfig(){ } /** * 讀取配置檔案資訊,並設定相關引數。 * @param configFileName String 配置檔案路徑及檔名。 */ public void xmlInit(String configFilePathAndName) throws WebBankException { SAXParserFactory saxParserFactory = null; SAXParser saxParser = null; saxParserFactory = SAXParserFactory.newInstance(); //獲取SAX工廠物件 saxParserFactory.setNamespaceAware(false); saxParserFactory.setValidating(false); try{ saxParser = saxParserFactory.newSAXParser(); //創建出SAX解析 /** 將解析器和解析物件XMLDOMData.xml聯絡起來,同時指定事件回撥方法的物件開始解析*/ saxParser.parse(new File(configFilePathAndName), this); } catch (javax.xml.parsers.ParserConfigurationException pe){ throw new WebBankException("在SAXInformationConfig類中的xmlInit方法中出現ParserConfigurationException"); } catch (SAXException se) { throw new WebBankException("在SAXInformationConfig類中的xmlInit方法中出現SAXException"); } catch (java.io.IOException ioe) { throw new WebBankException("在SAXInformationConfig類中的xmlInit方法中出現IOException"); } catch (Exception ex){ throw new WebBankException("在SAXInformationConfig類中的xmlInit方法中出現Exception"); } finally{ saxParserFactory = null; saxParser = null; } } private String tagElementName = null; /** 定義開始解析元素的方法. 這裡是將<xxx>中的名稱xxx提取出來。*/ public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { this.tagElementName = qName; //獲得該標籤的名稱 } /** 這裡是將<xxx></xxx>之間的標籤體的值加入到currentValue */ public void characters(char[] ch, int start, int length) throws SAXException{ String tagBodyText = new String(ch, start, length); //獲得標籤體的文字串內容 if (this.tagElementName.equals("marquee-text") && !tagBodyText.trim().equals("")) { marqueeText = tagBodyText; } }}
考慮到本文的篇幅關係,作者在此文中不能詳細地介紹SAX的XML解析技術程式設計。感興趣的讀者可以參考作者的《J2EE Web核心技術——XHTML與XML應用開發》的教材。
2、在軟體應用系統中應用資料庫連線池技術
(1)JDBC2.0中的DataSource介面
javax.sql包中的DataSource介面可以採用三種不同的方式實現——簡單的實現(只提供Connection物件的建立)、資料庫連線池的實現和分散式事務支援的資料庫連線實現。使用DataSource 介面獲得資料庫連線Connection物件,不僅提高了專案的資料來源的可移植性,也還能夠提高資料訪問的效能。
如下示例圖為JDK API技術幫助文件中對javax.sql包中的DataSource 介面的定義及功能介紹的區域性截圖。
(2)建立資料庫連線和釋放資料庫連線物件都是需要消耗系統資源的
軟體應用系統程式建立與物理資料庫系統之間的TCP連線時,資料庫管理系統需要分配多種系統資源以完成連線的建立過程;而釋放資料庫連線時,資料庫管理系統也需要釋放掉這些被佔用的系統資源,"分配"和"釋放"系統資源其實都是比較耗時的工作。
因此,如果軟體應用系統中存在反覆建立資料庫連線和釋放與資料庫連線的應用狀況,勢必會影響整個軟體應用系統的總體效能。而基於javax.sql包中的DataSource 介面的資料庫連線池技術能夠重用已經存在的資料庫連線物件,而不是每次請求都重新再建立新的資料庫連線物件。如下示例圖顯示JDBC2.0版本中的javax.sql.DataSource介面的常見的實現形式。
(3)利用資料庫連線池技術以提高軟體應用系統的資料訪問效能
當在軟體應用系統的持久層資料訪問元件中應用資料庫連線池技術實現方案時,Web伺服器在啟動時首先會創建出一定數量的資料庫連線物件並快取在記憶體中(也就是連線池中)。如果應用系統的持久層中的資料訪問元件(DAO)需要建立資料庫連線時,只須從記憶體中獲取一個由資料庫連線池管理物件預先建立好的資料庫連線物件,而不用重新建立新的資料庫連線物件。
當然,在DAO程式中的資料訪問操作完畢後,只需放回到資料庫連線池的記憶體中——資料庫連線的建立、關閉都由連線池管理程式來完成。
透過應用資料庫連線池技術能夠有效地減少建立和釋放資料庫連線物件時的系統消耗,並允許軟體應用程式重複地使用一個現有的資料庫連線物件;資料庫連線池管理器程式能夠釋放空閒時間超過最大空閒時間的資料庫連線物件來避免因為沒有釋放資料庫連線而引起的資料庫連線遺漏——這能明顯地提高對資料庫操作的效能和系統程式的穩定性。因此,該設計方案也是最經濟和最有效的效能調優方法。
作者在下文中以Apache DBCP資料庫連線池元件為示例,為讀者介紹如何在Web專案中應用資料庫連線池技術以提高軟體應用系統中的資料庫訪問和資料庫連線的效能。DBCP(DataBase Connection Pool)資料庫連線池是Apache基金會上的一個Java連線池專案,它也是目前在J2EE系統平臺中廣泛應用的開源資料庫連線池元件。
3、在軟體應用系統中如何應用Apache DBCP資料庫連線池元件
(1)在Web專案中新增DBCP連線池相關的系統庫的*.jar包檔案
DBCP元件所依賴的JAR程式包主要為:commons-dbcp-1.2.2.jar、commons-pool-1.2.jar和commons-collections.jar 三個檔案。最終的配置結果請參考如下示例圖所示的配置結果示例圖。
(2)為Web專案中實現資料庫連線的ConnectDBInterface介面提供一個新的實現類
該實現類名稱為DBCPConnectDBBean,程式包名稱為com.px1987.webbbs.dao,該程式類主要實現初始化DBCP資料庫連線池,然後根據需要從資料庫連線池中獲得資料庫連線物件例項,並返送給資料訪問類(DAO)中相關的資料訪問方法。該程式類的建立過程請參看如下示例圖所示。
(3)程式設計實現類DBCPConnectDBBean中的程式程式碼
如下程式程式碼示例為DBCPConnectDBBean程式類的最終程式程式碼,請注意其中黑體所標識的語句,這些程式程式碼實現資料庫連線池的初始化,並獲得資料庫連線池中快取的資料庫連線物件。
package com.px1987.webbbs.dao;import org.apache.commons.dbcp.BasicDataSource;import java.sql.*;import com.px1987.webbbs.exception.WebBBSException;import com.px1987.webbbs.config.*;import java.util.logging.*;public class DBCPConnectDBBean implements ConnectDBInterface { String JDBC_DBDriver= null; String JDBC_URL = null; String dbUserName=null; String dbUserPassWord=null; String dbcp_maxActive=null; private java.sql.Connection con = null; BasicDataSource oneDataSourceImple=null; private Logger logger = Logger.getLogger(this.getClass().getName()); public DBCPConnectDBBean() throws WebBBSException{ JDBC_DBDriver = ClassNameConfig.getProperty("JDBC_DBDriver"); JDBC_URL = ClassNameConfig.getProperty("JDBC_URL"); dbUserName = ClassNameConfig.getProperty("dbUserName"); dbUserPassWord = ClassNameConfig.getProperty("dbUserPassWord"); dbcp_maxActive = ClassNameConfig.getProperty("dbcp_maxActive"); oneDataSourceImple=new BasicDataSource(); oneDataSourceImple.setDriverClassName(JDBC_DBDriver); oneDataSourceImple.setUrl(JDBC_URL); oneDataSourceImple.setUsername(dbUserName); oneDataSourceImple.setPassword(dbUserPassWord); //最大的連線數目 oneDataSourceImple.setMaxActive(Integer.parseInt(dbcp_maxActive)); oneDataSourceImple.setDefaultAutoCommit(true); try { con=oneDataSourceImple.getConnection(); } catch (java.sql.SQLException e) { logger.log(Level.INFO, e.getMessage()); throw new WebBBSException("不能正確地連線資料庫並且出現SQLException"); } } public void closeDBCon() throws WebBBSException { try{ con.close(); con = null; } catch (SQLException e){ logger.log(Level.INFO, e.getMessage()); throw new WebBBSException("不能正確地關閉資料庫連線"); } } public Connection getConnection() throws WebBBSException { return con; }}
4、測試應用Apache DBCP資料庫連線池元件的功能正確性
(1)修改Web專案中的classNameConfig.properties屬性配置檔案中的下面的屬性專案"connectDBBean.className"的類名稱為DBCPConnectDBBean(該程式類獲得資料庫連線池中的資料庫連線物件例項)
connectDBBean.className=com.px1987.webbbs.dao.DBCPConnectDBBean
為了能夠在專案中動態載入相關的程式類從而實現利用Java發射技術動態建立類的物件例項,需要修改專案中程式類名稱的屬性配置檔案中的資料庫連線類名稱。修改後的結果可以參看如下示例圖所示。
(2)再次執行在JUnit單元測試專案中的TestConnectDBBean測試用例類
在MyEclipse開發工具中啟動並執行TestConnectDBBean測試用例,該測試用例的執行結果參看如下示例圖所示。從測試用例的執行結果可以獲知本Web專案的資料庫連線是正確的,也就是正確地獲得了從資料庫連線池中快取的資料庫連線物件例項。