一.前言:
近年來,隨著互聯網業務的迅猛發展,企業間、企業與消費者間實現電子商 務已經成為可能,建立企業級的電子商務平台不僅可以拓寬企業的營銷渠道,而 且對提升企業品牌形象等方面有重要的戰略意義。
本文將以目前國內最大服務交易平台如易網(http://www.routease.com/) 為例,來深入剖析一個電子商務交易平台搭建的全過程。
《准備篇》
一個項目的實施首先確定項目目標、項目需求與開發環境(為方便理解,將 如易網作為項目來描述)。本篇主要討論這三個方面:
一.實施目標
如易網的創辦目標:建設為國內最大的服務類交易平台和在線工作平台。由 於本篇以技術講解為主,如要了解更多的背景資料,可以訪問: http://www.routease.com/AboutUs.htm 。
二.實施需求
目標決定需求,定制清晰明確的項目需求是整個項目成敗的關鍵。可以使用 Rose工具來建立項目對象實體圖,這裡就不再贅述了,以下就幾個重要對象做一 些描述:(可以對照http://www.routease.com/來浏覽下面內容):
TotradeEntity:交易實體對象。該對象為核心對象,標識交易的服務對象。 比如翻譯服務,開鎖服務等。
ServiceRequirement:服務需求對象。該對象標識用戶需求。比如需要電腦 維修的信息等。
SHOP:店鋪對象。該對象為中小企業或者個人開的網店,一個店鋪對應多個交 易實體。
USER:用戶對象。該對象標識從事網站的合法注冊用戶,它保留用戶信息。
Account:帳戶對象。該對象標識用戶的帳戶信息。
Message:消息對象。該對象標識用戶之間交流的信息。
Credit:信譽對象。該對象標識用戶交易的信譽等級信息,為交易提供有力 參考。
三.開發/運行環境
基於以上需求分析,本站采用J2EE/Structs應用架構,服務器主機采用 WIN2003 SERVER+APACHE2.0.54+TOMCAT 5.5.4的系統環境,開發環境: Eclipse+JDK1.5,數據庫DAO采用的著名的ORM工具TopLink9.0.4.5。以下對相應 開發技術及其工具做一個簡要介紹:
1.Structs技術
Web應用的開發經歷了一個由P2P(Page to Page)到MVC(model view controller)的發展過程。早期的Web應用對用戶請求的處理和響應均是在頁面上 完成的,如圖1-1所示,即所的JSP1.0。這樣的Web架構最大的好處就是開發效率 較高,然後近幾年隨著互聯網的迅猛發展,網站功能日益增強,而這種P2P的網 站架構(因為其業務規則代碼與頁面代碼混為一團,不利於維護)已經不再適應 大規模應用的發展要求,取而代之的是基於MVC的Web架構。MVC的核心思想是將 應用分為模型、視圖和控制器三部分。模型是指應用程序的數據,以及對這些數 據的操作;視圖是指用戶界面;controller負責用戶界面和程序數據之間的同步 ,也就是完成兩個方向的動作:a.在根據用戶界面(view)的操作完成對程序數據 (model)的更新,b.將程序數據(model)的改變及時反應到用戶界面(view)上 。通過MVC的Web架構,可以弱化各個部分的耦合關系,並將業務邏輯處理與頁面 以及數據分離開來,這樣當其中一個模塊的代碼發生改變時,並不影響其他模塊 的正常運行,所以基於MVC的Web架構更適應於大規模軟件應用開發的潮流。
圖1
圖2
目前基於MVC的開發框架主要有Structs、Spring等。本站選用其中的Structs 作為開發框架,采用Structs應用框架開發應用程序,將開發人員從繁瑣的代碼 編制中解放出來,取而代之的是配置一些含有對應關系的XML文件,這樣當應用 環境發生變化時,不需重新編譯程序即可運行,並且使得應用更加靈活、高效, 而且重用度高。
從開發角度,Struts主要有如下的功能:
·包含一個controller servlet,能將用戶的請求發送到相應的Action對象 。通過Web.xml文件來配置其相關參數。
·tag庫,並且在controller servlet中提供關聯支持,幫助開發人員創建交 互式表單應用。
·通過配置Structs-config.xml文件,將Action對象與用戶請求以及請求結 果頁面關聯起來。
如需更多了解Structs的相關信息,請其官方網站: http://jakarta.apache.org/struts
2.TopLink技術
過去,對模型數據的存取訪問往往是直接是應用通過ODBC這樣的數據庫接口 訪問數據庫。但是這樣處理並不符合OOP的精神,而且應用開發人員必須熟悉後 台數據庫的模型構造,這就加大開發的難度。為此,ORM(Object Relational Mapping)技術應運而生.ORM技術實際是一個對象持久化的框架,其核心思想是 建立了Java對象與後台數據庫之間的映射關系。這樣對這些Java對象的訪問實際 就是對後台數據庫的訪問,從而屏蔽了數據庫訪問的細節,開發人員甚至可以在 不了解後台數據庫的情況下進行開發工作。此外,Toplink在數據緩存優化上也 有很好的表現。本項目采用著名的ORM工具Toplink進行開發。
《實施篇》
本篇主要介紹該平台的具體實現過程。根據軟件工程的相關理論,結合筆者 多年的開發經驗,網站開發一般尊循以下六步驟:
1.收集、整理網站需求。
2.根據網站需求,構想網頁的交互情景(即USE CASE),並設計出網站的原 形(Prototype)。
3.設計出實例化對象以及後台數據庫結構。
4.采用ORM工具,建立實例化對象與後台數據庫之間的映射關系。
5.根據網站交互需求,定制後台Action,以處理用戶動作。
6.修改網站原形(Prototype)為動態頁面(JSP文件),將Action處理結果 嵌入到動態頁面中返回給客戶端。
在這六個步驟中,第一步實際已經在《准備篇》裡已經給出了,下面重點講 解後面幾個步驟。
1.網站原形(Prototype)
網站原形是對一個網站功能的頁面級描述,即看到網站原形就好比看到一個 真實的網站一樣,只是網站原形並沒有嵌入動態代碼,而且頁面之間也缺乏關聯 而已。
網站原形的開發為純靜態頁面的開發,制作網站原形的關鍵在於將網站功能 需求轉化為人機界面。
如易網的網站原形制作下載地址: http://www.routease.com/download/ruyinew924.rar
2.OOP設計與後台數據庫設計
借助強大的ORM開發工具,可以將OOP與數據庫的設計同時進行(即可以同時 實施上面步驟的3,4步),這也是ORM工具最大特點。本項目采用Oracle公司的 Toplink作為ORM開發工具。以下簡要介紹Toplink開發過程。
1) 打開Toplink的Mapping Workbench組件,然後新建一個Mapping 工程。
2) 配置工程的屬性,即在"選項"面板上設置工程路徑以及Java對象源代碼 的路徑。
3) 配置數據庫登陸參數,包括應用訪問數據庫的URL、用戶名、密碼等。
完成以上三步,就可以根據應用的需求來開發Java類。在Mapping Workbench 裡新建一個描述符(實際就是有一個Java類),根據需求來添加屬性,並自動生 成Set/Get方法。一旦完成Java類的開發後,選擇"自動映射到數據庫"的選項, 即可實現數據庫表的自動創建。(Toplink的最大優勢就是在定制好Java類之後 可以自動生成數據庫的表結構)。
鑒於國內Toplink方面的資料較少,這裡介紹一下Toplink生成的工程文件 RouteaseMappingProject,該工程文件在web服務器啟動的時候裝載,可以理解為 客戶程序對數據庫訪問的接口程序,他有三類方法:
·構造函數
主要是調用oracle.toplink.sessions.Project的addDescriptor方法,其作 用是將數據庫和Java對象之間的映射關系加入到Project 中。代碼示范如下:
public RouteaseMappingProject() {
addDescriptor(buildAccountDescriptor());
addDescriptor(buildPhoneDescriptor());
…….
}
·applyLogin方法
它處理客戶程序登陸數據,並配置一些存取數據庫的參數,比如緩沖池等。 代碼示范為:
public void applyLogin() {
//配置數據庫訪問參數
DatabaseLogin login = new DatabaseLogin();
login.usePlatform(new oracle.toplink.oraclespecific.Oracle9Platform());
login.setDriverClassName("oracle.jdbc.driver.OracleDriver"); login.setConnectionString(ApplicationConfiguration.get (ConfigurationConstants.DB_CON_STR)); login.setUserName (ApplicationConfiguration.get(ConfigurationConstants.DB_USER)); login.setPassword(ApplicationConfiguration.get (ConfigurationConstants.DB_ENCRYPTED_PASSWORD));
// 設置數據庫參數
login.setUsesNativeSequencing(true);
login.setSequencePreallocationSize(1);
login.setShouldBindAllParameters(false);
login.setShouldCacheAllStatements(false);
login.setUsesByteArrayBinding(true);
login.setUsesStringBinding(false);
if (login.shouldUseByteArrayBinding()) { // Can only be used with binding.
login.setUsesStreamsForBinding(false);
}
login.setShouldForceFieldNamesToUpperCase(false);
login.setShouldOptimizeDataConversion(true);
login.setShouldTrimStrings(true);
login.setUsesBatchWriting(false);
if (login.shouldUseBatchWriting()) { // Can only be used with batch writing.
login.setUsesJDBCBatchWriting(true);
}
login.setUsesExternalConnectionPooling(false);
login.setUsesExternalTransactionController(false);
setLogin(login);
}
建立映射關系
Toplink通過類似於builXXXDescriptor方法來建立Java對象與數據庫表字段 之間的對應關系,示范代碼如下:
public Descriptor buildAccountDescriptor() {
Descriptor descriptor = new Descriptor();
descriptor.descriptorIsAggregate();
descriptor.setJavaClass (com.routease.db.vo.user.Account.class);
descriptor.setAlias("Account");
// Mappings.
//建立Account 對象的deposit屬性與數據庫表的DEPOSIT字段的對應關系
DirectToFieldMapping depositMapping = new DirectToFieldMapping ();
depositMapping.setAttributeName("deposit");
depositMapping.setFieldName("DEPOSIT");
descriptor.addMapping(depositMapping);
…
return descriptor;
}
3.定制後台Action
根據MVC的精神,View和Model設計好之後應該是將開發重點轉移到控制器的 開發上。控制器是根據用戶行為進行響應的處理模塊,比如用戶通過首頁的搜索 條對服務信息進行檢索,這時,web服務中的SearchToTradeEntityAction(對應 SearchToTradeEntityAction.java文件)會對用戶這一動作進行處理。以下對這 一Action進行詳細分析:
package com.routease.action.totradeentity;
import java.util.Collection;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import com.routease.action.PagingAction;
import com.routease.action.helper.UserHelper;
import com.routease.db.dao.DataSource;
import com.routease.db.dao.totradeentity.SearchingCriteria;
import com.routease.db.dao.totradeentity.ToTradeEntityDAO;
import com.routease.db.util.Constants;
import com.routease.db.util.Page;
public class SearchToTradeEntityAction extends PagingAction {
public SearchToTradeEntityAction()
{
super();
}
// executeWithDataSource方法為該Action默認執行的方法
public ActionForward executeWithDataSource(ActionMapping actionMapping, ActionForm actionForm, HttpServletRequest request, HttpServletResponse response, DataSource ds) throws Exception {
//首先接受用戶提交的表單數據
String objective = (String) PropertyUtils.getSimpleProperty (actionForm, "objective");
String keyWords = (String) PropertyUtils.getSimpleProperty (actionForm, "keyWords");
String keyWordsRange = (String) PropertyUtils.getSimpleProperty(actionForm, "keyWordsRange");
if (StringUtils.isEmpty(keyWordsRange)) {
keyWordsRange = SearchingCriteria.KEY_WORDS_RANGE_NAME;
}
String industryLevel1 = (String) PropertyUtils.getSimpleProperty(actionForm, "industryLevel1");
String industryLevel2 = (String) PropertyUtils.getSimpleProperty(actionForm, "industryLevel2");
String startingPrice = (String) PropertyUtils.getSimpleProperty(actionForm, "startingPrice");
String endingPrice = (String) PropertyUtils.getSimpleProperty (actionForm, "endingPrice");
String city = (String) PropertyUtils.getSimpleProperty (actionForm, "city");
String province = (String) PropertyUtils.getSimpleProperty (actionForm, "province");
String startNoStr = (String) PropertyUtils.getSimpleProperty (actionForm, "startNumber");
String lengthStr = (String) PropertyUtils.getSimpleProperty (actionForm, "length");
if (StringUtils.isEmpty(startNoStr)) {
startNoStr = "1";
}
//根據用戶提交的數據,創建查詢表達式對象SC
int startNumber = Integer.parseInt(startNoStr);
int length = UserHelper.getPagingLength(ds, request);
ToTradeEntityDAO serviceDAO = new ToTradeEntityDAO(ds);
SearchingCriteria sc = new SearchingCriteria();
sc.setCity(city);
sc.setProvince(province);
sc.setEndingPrice(endingPrice);
sc.setIndustryLevel1(industryLevel1);
sc.setIndustryLevel2(industryLevel2);
sc.setKeyWords(keyWords);
sc.setKeyWordsRange(keyWordsRange);
sc.setObjective(objective);
sc.setStartingPrice(startingPrice);
if (Constants.IS_TEST) {
System.out.println("start of page:" + startNumber);
}
//提交查詢對象SC,並獲得查詢結果集,將其結果集放入Request對象中 ,便於返回
Page result = serviceDAO.searchToTradeEntities(sc, startNumber, length);
Collection industries = serviceDAO.findIndustryDistribution (sc);
result.setSizePerPage(length);
request.setAttribute(Constants.TO_TRADE_ENTITY, result);
request.setAttribute("MY_INDUSTRIES",industries);
request.setAttribute("MY_PAGE", result);
//業務邏輯處理完畢之後,返回成功頁面
return actionMapping.findForward("SUCCESS_PAGE");
}
}
SearchToTradeEntityAction是一個典型的Action,由前面注解不難看出,一 般Action分為三部分:
a. 接受用戶表單數據
b. 處理用戶表單數據
c. 返回處理結果及頁面
4.修改頁面為JSP文件
凡是涉及到與用戶狀態相關的頁面均應改造為動態頁面(JSP文件),改造是 在前面靜態文件的基礎上進行的,用服務器端返回的數據(存放在Request對象 裡)替換靜態文本,由於這部分相對技術性不強,所以不再詳細贅述了。
通過前面四部分的介紹,基本概述了如易網技術實施的主要過程,在下面的 一章裡介紹網站技術中的幾個重要技巧。
《完結篇》
本篇主要介紹網站實施過程中的幾個重要技巧和思路,最後還將介紹網站維 護方面的內容。
一. 加快網站速度
盡量以靜態html文件為主,由於靜態文件不需要WEB服務器解析而直接返回給 客戶端,所以速度更快。
對網站實時性不強的動態文件可以采用後台定期刷新的機制來轉化為靜態文 件或者js文件,如易網首頁中的"推薦服務"欄目實際就是采用這種機制,但是對 實時性要求較強的交易環節是不適合用這種方式的。
另外一種加快網站速度的方法就是將頻繁訪問數據庫的信息放在內存中,在 web服務器啟動的時候加載進來,這種以為空間換時間的思路也值得借鑒。
二.服務器監測管理流程
一般企業級服務器都是采用獨立服務器,需要專人維護,但是這樣成本較高 ,有必要開發一套後台監控程序來對系統資源,數據/程序備份做監測,用技術 手段來降低成本。
如易網後台監控程序實現思路是:分兩個線程,一個進程監測服務器的內存 、磁盤資源以及數據庫、Web服務等相關應用的狀態,一旦發現有異常,將以 Email或短信的形式通知系統管理員;另外一個進程主要對數據進行周期性的備 份,並將備份通過ftp上傳至指定備用服務器。
本系統管理程序在網站運行期間起到重要的安全保障作用,而且也基本不需 要人工干預,減少了人力成本,值得中小企業借鑒。
三.自助營銷平台
對企業電子商務平台,營銷尤為重要。通過不同路徑收集營銷數據庫,並定 期給用戶發送企業產品信息,這一切過程采用程序的方式實現,方便,省事。
最後,簡要介紹一下網站維護的事宜。網站一旦運營起來,必須保證其7*24 小時的全天候正常運行。所以,網站後期的維護極為重要。根據筆者經驗,主要 需要做好以下幾個方面:
1.定期做好數據備份和程序備份。
2.做好網站安全防護工作,對重要文件和目錄設置訪問權限,架設防火牆, 關閉不用的端口。定期更改服務器的密碼,防止黑客入侵。
3.任何程序級的修改必須經過測試環境的驗證之後才能發布到生產環境,要 有套嚴格的發布流程。
做好以上三個方面,網站的正常運行基本可以保證。
結束語:
本文重點介紹了網站實施過程中的技術框架和實現方法,並結合實例分析了 其中運用到的相關技術。實踐證明,這套思路建立起來的網站架構穩定高效,具 有很高的應用價值。