摘要:本文以实例来详细分析运用J2EE技术架构来搭建企业级电子商务网站的全过程,并对关键部件的实现以及相关技术进行具体剖析,同时结合笔者经验,提出很多有价值的思路和方法。
一. 前言: 近年来,随着互联网业务的迅猛发展,企业间、企业与消费者间实现电子商务已经成为可能,建立企业级的电子商务平台不仅可以拓宽企业的营销渠道,而且对提升企业品牌形象等方面有重要的战略意义。
本文将以目前国内最大服务交易平台如易网(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. 任何程序级的修改必须经过测试环境的验证之后才能发布到生产环境,要有套严格的发布流程。
做好以上三个方面,网站的正常运行基本可以保证。
结束语: 本文重点介绍了网站实施过程中的技术框架和实现方法,并结合实例分析了其中运用到的相关技术。实践证明,这套思路建立起来的网站架构稳定高效,具有很高的应用价值。