1.6 典型MVC框架Struts及其应用
随着Java Web开发技术的不断成熟,越来越多的开发人员开始使用Web应用框架,框架为Java Web应用提供了预备的软件架构和相关的软件包,它能够大大提高开发Java Web应用的速度和效率。在众多的Java Web开发框架中,最典型的、应用最为广泛的莫过于Struts框架。
Struts框架是最早发布的基于MVC设计模式的Java Web框架,它是由Apache软件组织提供的一项开源项目,尤其适用于开发大型可扩展的Java Web应用。Struts为Java Web应用提供了一个通用的框架,使得开发人员可以将精力集中在如何解决实际业务问题上。
下面简单介绍一下Struts的基本框架。
1.6.1 Struts框架
Struts框架以ActionServlet作为核心控制器,整个应用由客户端请求驱动。在Web应用启动时加载并初始化核心控制器ActionServlet,它将从配置文件struts-config.xml中读取配置信息,并将它们存放在各种配置对象中,如Action的映射信息存放在ActionMapping对象中。
当客户端向Web应用发送请求时,由Struts的核心控制器ActionServlet拦截请求,检索和用户请求匹配的ActionMapping实例,如果该实例不存在,就返回用户请求路径无效的信息。接着创建一个与请求对应的ActionForm Bean对象,并把客户提交的表单数据保存在其中,根据配置信息决定是否需要进行表单数据校验以及属性重新设置。然后根据ActionMapping实例包含的映射信息决定是否需要调用那个业务逻辑控制器(Action)处理用户请求,通过Action调用业务逻辑模型组件中方法,更新模型数据状态,并返回一个ActionForward对象,决定应用请求转发流程,呈现给用户JSP视图。
在Struts框架中,控制器就是它的核心,Struts控制器由两部分组成:核心控制器和业务逻辑控制器。其中核心控制器就是ActionServlet,由Struts框架提供;业务逻辑控制器就是用户自定义的Action,由应用开发者提供。
Struts是典型的MVC框架,如图1-40显示了实现MVC的Struts框架流程。
图1-40 Struts框架流程
● 视图:就是一组JSP文件,在这些JSP文件中没有业务逻辑,也没有模型信息,只有标签,这些标签可以是标准的JSP标签或客户自定义标签,也可以是Struts标签库中的标签。通常情况下,ActionFrom Bean也是属于视图模块,它被利用来进行视图和控制器之间表单数据的传递:一方面Struts框架把用户输入的表单数据保存在ActionForm Bean中,把它传递给控制器;另一方面控制器可以对ActionForm Bean中的数据进行修改,JSP文件使用Struts标签读取修改后的ActionForm Bean的信息,重新设置HTML表单。
● 模型:表示应用程序的状态和业务逻辑。主要由底层的业务逻辑组件充当,这些业务逻辑组件封装了底层数据库访问、业务逻辑方法实现。对于企业级Java EE应用来说,模型不仅仅是简单的JavaBean,可能是一个或多个EJB组件,也可能是一个WebService服务。Struts框架没有为实现Model组件提供任何支持。
● 控制器:由ActionServlet类和Action类实现。ActionServlet是Struts框架中的核心组件,继承了javax.servlet.http.HttpServlet类,是一个标准的Servlet,它在MVC模型中扮演中央控制器角色,该控制器负责拦截所有HTTP请求,然后根据用户请求决定是否需要调用业务逻辑控制器,如果需要调用业务逻辑控制器,则将请求转发给Action处理,否则直接转向请求的JSP页面。业务逻辑控制器负责处理用户请求,但它本身并不具有处理业务逻辑能力,而是调用Model来完成处理。
1.6.2 Struts框架应用:电子商务网站—购物车应用
为了帮助获得使用Struts开发Java Web应用的经验,讲解一个简单的Struts应用例子——电子商务网站中的购物车应用实现。
1.分析电子商务网站—购物车应用需求
在开发应用时,首先从分析需求入手,列举该应用的各种功能,以及限制条件。电子商务网站—购物车应用的需求比较简单,其包括如下需求:
● 客户浏览商品。
● 添加商品到购物车。
● 显示购物车中商品数量。
● 客户可能更新商品数量、删除购物车中的商品。
● 客户可以结账。
购物车应用处理流程如图1-41所示。
图1-41 购物车应用处理流程
2.数据分析设计
通过分析电子商务网站中购物车应用需求,可以抽取购物车应用中所涉及的实体对象包括产品信息(cart_product)、产品类别(cart_category)、商品信息(cart_item)、商品库存(cart_inventory)、商品供应商(cart_supplier)。产品信息与产品类别是一对多的关系,即某个产品类别下有多个产品;产品信息与商品信息时一对多的关系;商品信息与商品库存是一对一的关系;商品信息与商品供应商存在一对多关系。如图1-42所示是购物车应用中各数据实体关系图。
图1-42 购物车应用中各数据实体关系图
仍然采用电子商务网站—用户注册登录应用所构建数据,创建购物车应用相关数据表,创建数据表的sql语句如下。
● 商品供应商信息表:
create table eb_c_supplier ( supplierid char(10) not null, suppliername varchar(80), status char(2) not null, addr1 varchar(80), addr2 varchar(80), city varchar(80), state varchar(80), zip varchar(6), phone varchar(80), constraint pk_supplier primary key (supplierid) );
● 产品类别信息表:
create table eb_c_category ( cateid char(10) not null, name varchar(80), descn varchar(255), constraint pk_category primary key (cateid) );
● 产品信息表:
create table eb_c_product ( productid char(10) not null, cateid char(10) not null, name varchar(80) , descn varchar(255), constraint pk_product primary key (productid), constraint fk_product_1 foreign key (cateid) references eb_c_category (cateid) );
● 商品信息表:
create table eb_c_item ( itemid char(10) not null, productid char(10) not null, listprice float, unitcost float, supplierid char(10) not null, status varchar(2), attr1 varchar(80), attr2 varchar(80), attr3 varchar(80), attr4 varchar(80), attr5 varchar(80), constraint pk_item primary key (itemid), constraint fk_item_1 foreign key (productid) references eb_c_product (productid), constraint fk_item_2 foreign key (supplierid) references eb_c_supplier (supplierid) );
● 商品库存表:
create table eb_c_inventory ( invenid char(10) not null, itemid char(10) not null, itemqty int not null, constraint pk_inventory primary key (invenid), constraint fk_inven_1 foreign key (itemid) references eb_c_item (itemid) );
3.运用Struts框架
将Struts框架运用于电子商务网站的购物车应用中。Struts框架可以方便迅速地把一个复杂的应用划分为模型、视图和控制器组件,而Struts的配置文件struts-config.xml则可以灵活地组装这些组件,从而简化开发过程。
在电子商务网站项目TopEBus中添加Struts框架支持:
● 加载jar包。
要想获得Struts框架支持,必须下载Struts框架jar包,可以到http://struts.apache.org/下载Struts,下载后直接解压缩到指定目录,然后找到lib目录,将其中的jar包添加TopEBus构建路径中即可。
实际上这里只需要几个jar包即可:antlr.jar,commons-beanutils.jar,commons-digester.jar, commons-fileupload.jar,commons-logging.jar,commons-validator.jar,jakarta-oro.jar,struts.jar。将这几个jar包直接复制到TopEBus项目下WebRoot\WEB-INF\lib目录下即可。
● 配置web.xml。
由于在项目中添加了Struts框架支持,所以需要在Web应用部署描述符web.xml文件中添加Struts配置,配置如【代码1-15】所示。
【代码1-15】 web.xml文件Struts配置片段。
<!-- 配置struts --> <servlet> <servlet-name>struts</servlet-name> <servlet-class>org.apache.struts.action.ActionServlet</servlet-class> <init-param> <param-name>config</param-name> <param-value>/WEB-INF/struts-config.xml</param-value> </init-param> <load-on-startup>0</load-on-startup> </servlet> <servlet-mapping> <servlet-name>struts</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping>
● 配置struts-config.xml。
Struts框架允许把应用划分为多个组件,从而提高开发效率。而Struts框架的配置文件struts-config.xml可以把这些组件组装起来,决定如何使用它们。这里仅仅只是添加struts-config.xml文件,暂时没有在里面添加组件,如【代码1-16】所示。
【代码1-16】 struts-config.xml代码片段。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation //DTD Struts Configuration 1.1//EN" "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd"> <struts-config> <!-- =========Form Bean Definitions=========================--> <form-beans> <!—这里配置视图组件ActionForm Bean --></form-beans> <!--=========Action Mapping Definitions=============--> <action-mappings><!—这里配置控制器组件action --></action-mappings> <!-- ========Message Resource Definitions==============--> <message-resources parameter=" " /><!—这里配置资源文件 --> </struts-config>
4.构建模型组件
在购物车应用中,主要包含一些对应数据表构建的域对象、专门为购物车构建的域对象和对应域对象操作的数据访问JavaBean对象。
数据表域对象包括有对应供应商信息的域对象EB_C_Supplier.java、对应产品信息的域对象EB_C_Product.java、对应产品种类信息的域对象EB_C_Category.java、对应商品项信息的域对象EB_C_Item.java、对应商品库存信息的域对象EB_C_Inventory.java等,因这些是最简单的POJO类,这里略去对应代码。
专门为购物车构建的域对象包括EB_C_CartItem和EB_C_Cart。
其中EB_C_CartItem域对象描述的是购物车中每一项商品(EB_C_Item),包括有该项商品的相关信息、数量以及总价格。此外,定义calculateTotal()方法用于计算该域对象内所有商品的总金额(单价*数量),当然,在设置EB_C_Item的数量时,需要计算总价格,这样才能保持数据一致。其详情如【代码1-17】所示。
【代码1-17】 EB_C_CartItem.java文件。
public class EB_C_CartItem { private EB_C_Item item; // 私有字段 private int quantity; private BigDecimal total; public EB_C_Item getItem() {return item;} // JavaBeans方法 public int getQuantity() {return quantity;} public BigDecimal getTotal() {return total;} private void calculateTotal() {// 计算总价格 if (item != null && item.getListprice() != 0.0f) { total=new BigDecimal(item.getListprice()).multiply(new BigDecimal (quantity)); } else {total = null;} } public void setItem(EB_C_Item item) {// 设置Item,同时计算总价格 this.item = item; calculateTotal(); } public void setQuantity(int quantity) {// 设置数量,同时计算总价格 this.quantity = quantity; calculateTotal(); } public void incrementQuantity(){//增加Item数量,同时计算总价格 quantity++; calculateTotal(); } }
EB_C_Cart代表了购物车域对象,购物车用来保存客户所选择的商品,其中提供了添加商品、修改商品数量、删除购物车中商品、查看购物车中商品总金额等方法。在增加EB_C_Item到EB_C_CartItem中后,需要调用cartItem.incrementQuantity()方法使EB_C_Item数量加1,当然也可以使用cartItem.setQuantity(1)方法使cartItem的初始数量为1。计算购物车中所有物品总金额的方法为getSubTotal(),需要注意的是,BigDecimal是一个对象,所以不能直接调用:subTotal=subtotal+listprice*quantity之类的方法,BigDecimal为数据运算提供一系列方法,例如, add(BigDecimal val)表示加;subtract(BigDecimal val)表示减;multiply(BigDecimal val)表示乘;divide(BigDecimal val,int roundingMode)表示除。其详情如【代码1-18】所示。
【代码1-18】 EB_C_Cart.java文件。
public class EB_C_Cart { /** 购物车中所有的商品信息都保存在Map中* */ private final Map<String, EB_C_CartItem> itemMap = Collections .synchronizedMap(new HashMap<String, EB_C_CartItem>()); public Iterator<EB_C_CartItem>getCartItems(){/** 获得所有cartItem**/ return itemMap.values().iterator(); } public int getNumberOfItems() {/** 获得EB_C_Item的总数* */ return itemMap.size(); } public boolean containsItemId(String itemId) {/**是否包含某个EB_C_Item* */ return itemMap.containsKey(itemId); } public void addItem(EB_C_Item item) {/** 增加EB_C_Item* */ EB_C_CartItem cartItem = itemMap.get(item.getItemid()); if (cartItem == null) { cartItem = new EB_C_CartItem(); cartItem.setItem(item); cartItem.setQuantity(0); itemMap.put(item.getItemid(), cartItem); } cartItem.incrementQuantity(); } public EB_C_Item removeItemById(String itemId) {/** 删除EB_C_Item* */ EB_C_CartItem cartItem = itemMap.remove(itemId); if (cartItem == null) {return null; } else { return cartItem.getItem(); } } public void incrementQuantityByItemId(String itemId) {/** 增 加 EB_C_Item的数量 */ EB_C_CartItem cartItem = itemMap.get(itemId); cartItem.incrementQuantity(); } public void setQuantityByItemId(String itemId, int quantity) {/** 设 置EB_C_Item的数量* */ EB_C_CartItem cartItem = itemMap.get(itemId); cartItem.setQuantity(quantity); } public BigDecimal getSubTotal() { /** 计算价格* */ BigDecimal subTotal = new BigDecimal("0"); Iterator<EB_C_CartItem> items = getCartItems(); while (items.hasNext()) { EB_C_CartItem cartItem = (EB_C_CartItem) items.next(); EB_C_Item item = cartItem.getItem(); BigDecimal listPrice = new BigDecimal(item.getListprice()); BigDecimal quantity = new BigDecimal(String.valueOf(cartItem. getQuantity())); subTotal = subTotal.add(listPrice.multiply(quantity)); } return subTotal; } }
在购物车应用中,对应域对象操作的数据访问JavaBean对象包括有EB_C_ProductDAO对象、EB_C_CategoryDAO对象以及EB_C_ItemDAO对象等。
其中EB_C_ProductDAO实现对产品信息访问,其中定义两个接口方法:getProductByID(String productid),用于根据产品ID查找对应产品详细信息;getAllProduct(),用于查找所有产品。EB_C_ProductDAO.java源代码详见【代码1-19】。
【代码1-19】 EB_C_ProductDAO.java文件。
public class EB_C_ProductDAO { //略去数据库操作属性定义 public EB_C_Product getProductByID(String productid){ //根据ID查找产品信息 EB_C_Product product=null; conn = new EBusDb().getCon(); //获取数据库连接对象 //构建SQL语句:根据产品ID查找产品信息 String sqlbyid="select * from eb_c_product where productid=?"; try { pstmt=conn.prepareStatement(sqlbyid); ////建立预处理对象 pstmt.setString(1,productid); //给占位符赋值 rs=pstmt.executeQuery(); //执行查询 if(rs.next()){//迭代结果,是否存在符合条件记录 product=new EB_C_Product(); product.setProductid(rs.getString("productid")); product.setName(rs.getString("name")); product.setDescn(rs.getString("descn")); product.setCateid(rs.getString("cateid")); } } catch (SQLException e) { e.printStackTrace(); } finally{ ebusdb.closedb(rs, stmt, pstmt, conn); } return product; } public Collection<EB_C_Product> getAllProduct(){//查找所有的产品 Collection<EB_C_Product> coll=new ArrayList<EB_C_Product>(); conn = new EBusDb().getCon(); String sqlall="select * from eb_c_product"; try { stmt=conn.createStatement();////建立预处理对象 rs=stmt.executeQuery(sqlall); //执行查询 while(rs.next()){//迭代结果 EB_C_Product product=new EB_C_Product(); product.setCateid(rs.getString("cateid")); product.setName(rs.getString("name")); product.setDescn(rs.getString("descn")); product.setProductid(rs.getString("productid")); coll.add(product); } } catch (SQLException e) { e.printStackTrace(); } finally{ ebusdb.closedb(rs, stmt, pstmt, conn); } return coll; } }
EB_C_CategoryDAO实现对产品种类信息访问,其中定义两个接口方法:getCategoryByID (String cateid),用于根据产品种类ID查找对应产品种类详细信息;getAllCategory(),用于查找所有产品种类。EB_C_CategoryDAO.java源代码详见【代码1-20】。
【代码1-20】 EB_C_CategoryDAO.java文件。
public class EB_C_CategoryDAO { //略去数据库操作属性定义 public EB_C_Category getCategoryByID(String cateid){ //根据ID查找产品 种类信息 EB_C_Category category=null; conn = new EBusDb().getCon(); String sqlbyid="select * from eb_c_category where cateid=?"; try { pstmt=conn.prepareStatement(sqlbyid); ////建立预处理对象 pstmt.setString(1,cateid); //给占位符赋值 rs=pstmt.executeQuery(); //执行查询 if(rs.next()){//迭代结果,是否存在符合条件记录 category=new EB_C_Category(); category.setCateid(rs.getString("cateid")); category.setName(rs.getString("name")); category.setDescn(rs.getString("descn")); } } catch (SQLException e) { e.printStackTrace(); }finally{ ebusdb.closedb(rs, stmt, pstmt, conn); } return category; } public Collection<EB_C_Category> getAllCategory(){//查找所有的产品种类 Collection<EB_C_Category> coll=new ArrayList<EB_C_Category>(); conn = new EBusDb().getCon(); String sqlall="select * from eb_c_category"; try { stmt=conn.createStatement(); ////建立预处理对象 rs=stmt.executeQuery(sqlall); //执行查询 while(rs.next()){//迭代结果 EB_C_Category category=new EB_C_Category(); category.setCateid(rs.getString("cateid")); category.setName(rs.getString("name")); category.setDescn(rs.getString("descn")); coll.add(category); } } catch (SQLException e) { e.printStackTrace(); }finally{ ebusdb.closedb(rs, stmt, pstmt, conn); } return coll; } }
EB_C_ItemDAO实现对商品信息访问,其中定义两个接口方法:getItemByID(String itemid),用于根据商品ID查找对应商品详细信息;getItemByProductID(String productid),用于根据产品ID查找该产品对应的所有商品。EB_C_ItemDAO.java源代码详见【代码1-21】。
【代码1-21】 EB_C_ItemDAO.java文件。
public class EB_C_ItemDAO { //略去数据库操作属性定义 public EB_C_Item getItemByID(String itemid){ //根据ID查找商品信息 EB_C_Item item=null; conn = new EBusDb().getCon(); String sqlbyid="select * from eb_c_item where itemid=?"; try { pstmt=conn.prepareStatement(sqlbyid); ////建立预处理对象 pstmt.setString(1,itemid); //给占位符赋值 rs=pstmt.executeQuery(); //执行查询 if(rs.next()){//迭代结果,是否存在符合条件记录 item=new EB_C_Item(); item.setItemid(rs.getString("itemid")); //略去部分代码,从数据库表字段获取值封装在item对象中 } } catch (SQLException e) { e.printStackTrace(); }finally{ ebusdb.closedb(rs, stmt, pstmt, conn); } } return item; //根据产品ID查找所有商品信息 public Collection<EB_C_Item> getItemByProductID(String productid){ Collection<EB_C_Item> coll=new ArrayList<EB_C_Item>(); onn = new EBusDb().getCon(); String sqlallbyproductid="select * from eb_c_item where productid=?"; try { pstmt=conn.prepareStatement(sqlallbyproductid); ////建立预处理 对象 pstmt.setString(1,productid); //给占位符赋值 rs=pstmt.executeQuery(); //执行查询 while(rs.next()){//迭代结果 EB_C_Item item=new EB_C_Item(); item.setItemid(rs.getString("itemid")); //略去部分代码,从数据库表字段获取值封装在item对象中 coll.add(item);//将item对象添加到集合容器中 } } catch (SQLException e) { e.printStackTrace(); }finally{ ebusdb.closedb(rs, stmt, pstmt, conn); } return coll; } }
5.创建视图组件
在struts应用中,通常会把FormBean对象作为视图组件,在本节购物车应用中,需要构建FormBean对象EB_C_CartForm,用来表示购物车,该对象包含了购物车域对象EB_C_Cart,而EB_C_Cart域对象又包含了EB_C_CartItem域对象。EB_C_CartForm、EB_C_Cart、EB_C_CartItem三者之间的关系如图1-43所示。
图1-43 EB_C_CartForm、EB_C_Cart、EB_C_CartItem关系图
EB_C_CartForm和EB_C_Cart是一种组合关系,EB_C_CartForm对象直接由EB_C_Cart对象组合而成;同样,EB_C_Cart对象与EB_C_CartItem对象也是一种组合关系,EB_C_Cart对象由多个EB_C_CartItem对象组合而成。
在整个在线购物的B2C电子商务网站实现中,由于有很多地方会构建FormBean对象,而这些对象中都需要进行表单校验,因此将基础的表单校验方法单独定义在一个基本的FormBean中,这样其他的FromBean可以继承它从而减少代码的重复。定义基本的FormBean为EB_BasicForm对象,其详情如【代码1-22】所示。
【代码1-22】 EB_BasicForm.java文件。
public class EB_BasicForm extends ActionForm { public ActionErrors validate(ActionMapping mapping,HttpServletRequest request) { ActionErrors actionErrors = null; ArrayList<String> errorList = new ArrayList<String>(); doValidate(mapping, request, errorList); request.setAttribute("errors", errorList); if (!errorList.isEmpty()) { actionErrors = new ActionErrors(); actionErrors.add(ActionErrors.GLOBAL_ERROR, new ActionError ("global.error")); } return actionErrors; } public void doValidate(ActionMapping mapping,HttpServletRequest request, List<String> errors) {} protected void addErrorIfStringEmpty(List<String>errors,String message, String value) { if (value == null || value.trim().length() < 1)errors.add(message); } }
EB_C_CartForm继承了EB_BasicForm,它有一个属性,就是EB_C_Cart。其详情如【代码1-23】所示。
【代码1-23】 EB_C_CartForm.java文件。
public class EB_C_CartForm extends EB_BasicForm { private EB_C_Cart cart = new EB_C_Cart(); private String theItemId; // 当前正在操作的ItemId //属性对应的getter、setter方法 public void reset(ActionMapping mapping, HttpServletRequest request) { super.reset(mapping, request); theItemId = null; } }
将FormBean配置在Struts核心文件struts-config.xml中,配置代码如【代码1-24】所示。
【代码1-24】 struts-config.xml中的FormBean配置片段。
<!-- 表单 Bean --> <form-beans> <form-bean name="basicForm" type="com.etop.topebus.view.forms.EB_ BasicForm"></form-bean> <form-bean name="cartForm" type="com.etop.topebus.view.forms.EB_C_ CartForm"> </form-bean> </form-beans>
购物车应用是基于Struts实现的,为了更好地表现Struts的应用,我们在页面文件中采用Struts标签,用以替代原来在JSP页面中出现的Java Script。为此,需要在使用Struts标签之前将Struts系列标签库引入到页面文件中。
我们首先需要将Struts标签库的描述文件放到WEB-INF目录下,这里包括struts-logic.tld、struts-html.tld、struts-bena.tld、struts-tiles.tld、struts-nested.tld共5个文件。然后修改web.xml配置文件,在其中加入这些tld文件的定位代码:
<!-- Struts Tag Library Descriptors --> <taglib> <taglib-uri>/WEB-INF/struts-bean.tld</taglib-uri> <taglib-location>/WEB-INF/struts-bean.tld</taglib-location> </taglib> <taglib> <taglib-uri>/WEB-INF/struts-html.tld</taglib-uri> <taglib-location>/WEB-INF/struts-html.tld</taglib-location> </taglib> <taglib> <taglib-uri>/WEB-INF/struts-logic.tld</taglib-uri> <taglib-location>/WEB-INF/struts-logic.tld</taglib-location> </taglib>
为了通用性,我们将Struts标签库在公共的页头文件中导入,因此,需要修改header.jsp内容,在其中添加如下代码:
<%@ taglib uri="../WEB-INF/struts-logic.tld" prefix="logic" %> <%@ taglib uri="../WEB-INF/struts-html.tld" prefix="html" %> <%@ taglib uri="../WEB-INF/struts-bean.tld" prefix="bean" %>
视图组件除了必要的FormBean组件以外,还包括一系列的JSP页面文件,这里我们构建购物车应用页面文件包括有eb_c_cart.jsp和eb_c_checkout.jsp。
eb_c_cart.jsp主要用来显示购物车的状态,并且提供了更新购物车状态的操作。页面中采用Struts的Logic标签库包含的逻辑运算标签如equal、notEqual运算来对EB_C_CartForm中的cart.numberOfItems进行判断,如果结果和0相当,那么就在浏览器中显示标记包含中的代码,否则,不显示这段代码。
在eb_c_cart.jsp中采用Struts的Logic标签库中包含的迭代器标签iterate,用来将购物车中所有的商品项信息显示出来:
<logic:iterate id="cartItem" name="cartForm" property="cart.cartItems">
它相当于:
<% Iterator<EB_C_CartItem> cartItems=cartForm.getCart().getCartItems(); EB_C_CartItem cartItem; while(cartItems.hasNext()){cartItem=cartsItems.next(); %> <td><%=cartItem.getItem().getProduct().getName() %></td> <td><%=cartItem.getItem().getAttr1() %></td> <%}%>
eb_c_cart.jsp页面文件详情如【代码1-25】所示。
【代码1-25】 eb_c_cart.jsp文件。
<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%> <!-- 包含头部JSP --> <%@ include file="/include/header.jsp"%><br> <table border="0" width="1050" cellspacing="0" cellpadding="0" align= "center"> <tr><td valign="top" align="center"><h2 align="center">购物车</h2> <html:form action="/cart/updateItem.do" method="post"> <TABLE class=font1 cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width="99%" align=center bgColor=#ffffff borderColorLight=#003686 border=1> <tr bgcolor="#C4E669"> <td><b>项目 ID</b></td><td><b>产品 ID</b></td> <td><b>说明</b></td><td><b>数量</b></td> <td><b>价格</b></td><td> </td> </tr> <!-- 如果cart.numberOfItems是0,表示还没有往购物车中添加商品 --> <logic:equal name="cartForm" property="cart.numberOfItems" value="0"> <tr bgcolor="#fffdd7"> <td colspan="6"><b>购物车中没有货物.</b></td> </tr> </logic:equal> <!-- 利用struts迭代器标签显示商品信息 --> <logic:iterate id="cartItem" name="cartForm" property= "cart.cartItems"> <tr bgcolor="#fffdd7"><td><b> <html:link paramId="itemId" paramName="cartItem" paramProperty="item.itemid" page="/product/viewItem. jsp"> <bean:write name="cartItem" property="item. itemid" /> </html:link></b> </td> <td><bean:write name="cartItem" property="item. productid" /></td> <td> <!-- 输出商品的信息 --> <bean:write name="cartItem" property="item. product.name" /> <bean:write name="cartItem" property="item. attr1" /> <bean:write name="cartItem" property="item. attr2" /> <bean:write name="cartItem" property="item. attr3" /> <bean:write name="cartItem" property="item. attr5" /> </td> <td align="center"> <!-- 更新商品数量的输入框 --> <input type="text" size="3" name="<bean:write name="cartItem" property="item.itemid"/>" value="<bean:write name="cartItem" property="quantity"/>" /> </td> <td align="right"> <bean:write name="cartItem" property="item. listprice" /> </td> <td> <html:link paramId="theItemId" paramName= "cartItem" paramProperty="item.itemid" page="/cart/ removeItem.do"> <img border="0" src="../images/button_ remove.gif" /> </html:link> </td> </tr> </logic:iterate> <tr bgcolor="#fffdd7"> <td colspan="5" align="right"> <b>小计: <bean:write name="cartForm" property="cart. subTotal" /></b> <br /><input type="image" border="0" src="../images/ button_update _cart.gif" name="update" /> </td> <td> </td> </tr> </table> </html:form> <!-- 如果cart.numberOfItems是0,那么不显示结账链接 --> <logic:notEqual name="cartForm" property="cart.numberOfItems" value="0"> <br /> <center> <html:link page="/cart/eb_c_checkout.jsp"> <img border="0" src="../images/button_checkout.gif" /> </html:link> </center> </logic:notEqual> </td> </tr> </table><br> <!-- 包含尾部JSP --> <%@ include file="/include/footer.jsp"%>
eb_c_checkout.jsp表示对购物车中的购物信息进行统计展示,并提供返回购物车和进行结账操作的入口,其页面如【代码1-26】所示。
【代码1-26】 eb_c_checkout.jsp文件。
<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%> <!-- 包含头部JSP --> <%@ include file="/include/header.jsp"%><br> <table border="0" width="1050" cellspacing="0" cellpadding="0" align= "center"> <tr> <td valign="top" align="center"> <h2 align="center"> <html:link page="/cart/eb_c_cart.jsp"><< 购物车</html: link> ::购物统计 </h2> <TABLE class=font1 cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width="99%" align=center bgColor=#ffffff borderColorLight=#003686 border=1> <tr bgcolor="#C4E669"> <td><b>项目 ID</b></td><td><b>产品 ID</b></td> <td><b> 介 绍 </b></td><td><b> 数 量 </b></td><td><b> 价 格 </b></td> </tr> <logic:iterate id="cartItem" name="cartForm" property="cart.cartItems"> <tr bgcolor="#fffdd7"> <td> <b><html:link paramId="itemid"paramName="cartItem" paramProperty="item.itemid" page="/product/ viewItem.jsp"> <bean:write name="cartItem" property= "item.itemid" /> </html:link></b> </td> <td> <bean:write name="cartItem" property="item. productid" /> </td> <td> <bean:write name="cartItem" property="item.product. name" /> <bean:write name="cartItem"property="item.attr1"/> <bean:write name="cartItem"property="item.attr2"/> <bean:write name="cartItem"property="item.attr3"/> <bean:write name="cartItem"property="item.attr5"/> </td> <td align="center"> <bean:write name="cartItem" property="quantity" /> </td> <td align="right"> <bean:write name="cartItem" property="item. listprice" /> </td> </tr> </logic:iterate> <tr bgcolor="#fffdd7"> <td colspan="5" align="right"> <b>小计: <bean:write name="cartForm" property="cart. subTotal" /></b><br /> </td> </tr> </table><br /> <center> <!—购物车统计后,可以进行结账处理,此处为结账入口 --> <html:link page="/order/createOrderForm.do"> <img border="0" src="../images/button_continue.gif" /> </html:link> </center> </td> <td valign="top" width="20%" align="right"> </td> </tr> </table><br> <!-- 包含尾部JSP --> <%@ include file="/include/footer.jsp"%>
6.创建控制器组件
购物车应用中,包括向购物车中添加商品,更新购物车中某项商品的数量以及从购物车中删除某项商品,因此我们在实现过程中定义多个控制器组件来实现上述功能,包括AddItemAction组件用以处理添加商品项、RemoveItemAction组件用于处理删除商品项以及UpdateItemAction组件用以更新商品项的数量。
AddItemAction核心方法execute,它操作处理客户请求的过程,如果EB_C_Cart中已经存在有商品项EB_C_Item,那么就增加这个EB_C_Item的数量;否则,在EB_C_Cart中添加这个商品项EB_C_Item。AddItemAction.java文件源代码如【代码1-27】所示。
【代码1-27】 AddItemAction.java文件。
public class AddItemAction extends Action { public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { EB_C_CartForm cartForm = (EB_C_CartForm) form; EB_C_Cart cart = cartForm.getCart(); String theItemId = cartForm.getTheItemId(); <!—如果购物车中已经存在该商品项,则直接添加该商品项的数量;否则,添加这个商品项 --> if (cart.containsItemId(theItemId)) { cart.incrementQuantityByItemId(theItemId); } else { EB_C_ItemDAO dao = new EB_C_ItemDAO(); EB_C_Item item = dao.getItemByID(theItemId); cartForm.getCart().addItem(item); } <!—添加商品项处理成功,页面转发到“success”所代表的逻辑视图 --> return mapping.findForward("success"); } }
由于向购物车中添加商品项或增加商品项的数量成功后,页面会被转发到success所代表的逻辑视图,而逻辑视图则在struts-config.xml文件中配置,其对应有物理视图页面cart/eb_c_cart.jsp。该控制器组件在struts-config.xml文件中配置片段为:
<action name="cartForm" path="/cart/addItem" scope="session" type="com.etop.topebus.control.action.AddItemAction"> <forward name="success" path="/cart/eb_c_cart.jsp"></forward> </action>
其中,name属性对应FormBean组件EB_C_CartForm配置逻辑名;path属性对应页面请求路径,即页面form表单中action属性所对应值;type属性对应控制器组件的完整限定名称类;scope属性表示所配置的控制器作用范围。
UpdateItemAction组件更新购物车中商品数量,UpdateItemAction.java文件源代码如【代码1-28】所示。
【代码1-28】 UpdateItemAction.java文件。
public class UpdateItemAction extends Action { public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { EB_C_CartForm cartForm = (EB_C_CartForm) form; Iterator<EB_C_CartItem>cartItems=cartForm.getCart().getCartItems(); while (cartItems.hasNext()) { EB_C_CartItem cartItem = cartItems.next(); String itemId = cartItem.getItem().getItemid(); try { int quantity = Integer.parseInt(request.getParameter(itemid)); cartForm.getCart().setQuantityByItemId(itemId, quantity); if (quantity < 1) cartItems.remove(); } catch (Exception e) {} } return mapping.findForward("success"); } }
相应的,该控制器组件在struts-config.xml文件中配置片段为:
<action name="cartForm" path="/cart/updateItem" scope="session" type="com.etop.topebus.control.action.UpdateItemAction"> <forward name="success" path="/cart/eb_c_cart.jsp"></forward> </action>
RemoveItemAction组件用于删除购物车中商品,RemoveItemAction.java文件源代码如【代码1-29】所示。
【代码1-29】 RemoveItemAction.java文件。
public class RemoveItemAction extends Action { public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { EB_C_CartForm cartForm = (EB_C_CartForm) form; EB_C_Item item = cartForm.getCart().removeItemById(cartForm. getTheItemId()); if (item == null) { request.setAttribute("message","Attempted to remove null CartItem from Cart."); return mapping.findForward("error"); } else { return mapping.findForward("success"); } } }
相应的,该控制器组件在struts-config.xml文件中配置片段为:
<action name="cartForm" path="/cart/removeItem" scope="session" type="com.etop.topebus.control.action.RemoveItemAction"> <forward name="success" path="/cart/eb_c_cart.jsp"></forward> </action>
在RemoveItemAction组件中,当从购物车中删除商品发生问题的时候,请求将被转发到一个叫error的逻辑视图中,该逻辑视图同样在struts-config.xml文件中配置,其物理视图对应整个应用的全局异常页面。配置信息如下:
<!-- 全局转发 --> <global-forwards> <forward name="error" path="/include/error.jsp"></forward> </global-forwards>
7.部署运行购物车应用
至此,已经将在线购物的B2C电子商务网站中的购物车应用完全实现。结合注册登录应用,重新部署一下项目TopEBus即可完成请求。