一、项目的目录结构
二、订购流程总体设计
三、订购流程的详细设计
1.定义基本流程pizza-flow.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <flow xmlns="http://www.springframework.org/schema/webflow" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://www.springframework.org/schema/webflow 5 http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd"> 6 7 <var name="order" class="main.java.com.springinaction.pizza.domain.Order"/> 8 9 <!-- Customer --> 10 <subflow-state id="customer" subflow="customer-flow"> 11 <input name="order" value="order"/> 12 <transition on="customerReady" to="order" /> 13 </subflow-state> 14 15 <!-- Order --> 16 <subflow-state id="order" subflow="order-flow"> 17 <input name="order" value="order"/> 18 <transition on="orderCreated" to="payment" /> 19 </subflow-state> 20 21 <!-- Payment --> 22 <subflow-state id="payment" subflow="payment-flow"> 23 <input name="order" value="order"/> 24 <transition on="paymentTaken" to="saveOrder"/> 25 </subflow-state> 26 27 <action-state id="saveOrder"> 28 <evaluate expression="pizzaFlowActions.saveOrder(order)" /> 29 <transition to="thankCustomer" /> 30 </action-state> 31 32 <view-state id="thankCustomer"> 33 <transition on="end" to="endState" /> 34 </view-state> 35 36 <!-- End state --> 37 <end-state id="endState" /> 38 39 <global-transitions> 40 <transition on="cancel" to="endState" /> 41 </global-transitions> 42 </flow>
在进入主流程时,必须先新建一个Order的实例,Order类会带有关于订单的所有信息,包含顾客信息、订购的披萨列表以及支付详情。然后进入Customer流程,对应的子流程为customer-flow,而且在进入子流程前,必须将订单对象作为子流程的输入进行传递,如果子流程结束的<end-state>状态ID为customerReady,那么当执行完子流程后就会跳转到名为order的状态。接下来,先介绍customer子流程。
2.子流程customer-flow.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <flow xmlns="http://www.springframework.org/schema/webflow" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://www.springframework.org/schema/webflow 5 http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd"> 6 7 <input name="order" required="true"/> 8 9 <!-- Customer --> 10 <view-state id="welcome"> 11 <transition on="phoneEntered" to="lookupCustomer"/> 12 <transition on="cancel" to="cancel"/> 13 </view-state> 14 15 <action-state id="lookupCustomer"> 16 <evaluate result="order.customer" expression= 17 "pizzaFlowActions.lookupCustomer(requestParameters.phoneNumber)" /> 18 <transition to="registrationForm" on-exception= 19 "main.java.com.springinaction.pizza.service.CustomerNotFoundException" /> 20 <transition to="customerReady" /> 21 </action-state> 22 23 <view-state id="registrationForm" model="order" popup="true" > 24 <on-entry> 25 <evaluate expression= 26 "order.customer.phoneNumber = requestParameters.phoneNumber" /> 27 </on-entry> 28 <transition on="submit" to="checkDeliveryArea" /> 29 <transition on="cancel" to="cancel" /> 30 </view-state> 31 32 <decision-state id="checkDeliveryArea"> 33 <if test="pizzaFlowActions.checkDeliveryArea(order.customer.zipCode)" 34 then="addCustomer" 35 else="deliveryWarning"/> 36 </decision-state> 37 38 <view-state id="deliveryWarning"> 39 <transition on="accept" to="addCustomer" /> 40 <transition on="cancel" to="cancel" /> 41 </view-state> 42 43 <action-state id="addCustomer"> 44 <evaluate expression="pizzaFlowActions.addCustomer(order.customer)" /> 45 <transition to="customerReady" /> 46 </action-state> 47 48 <!-- End state --> 49 <end-state id="cancel" /> 50 <end-state id="customerReady" /> 51 </flow>
第一个进入的流程是由<view-state>定义的“welcome”视图状态,即用户欢迎界面,需要用户输入电话号码,具体如下图所示,
在welcome状态中有两个用<transition>定义的转移,如果触发了phoneEntered事件,即用户输入电话号码,然后点击Lookup Customer按钮后,会跳转到由<action-state>定义的lookupCustomer行为状态,对用户输入的电话号码进行查询。进入到lookupCustomer状态后,首先使用<evaluate>元素计算了一个表达式(SpEL表达式),将计算结果放在order对象的customer变量中。计算过程就是调用pizzaFlowActions(调用的时候首字母小写)类中的lookupCustomer方法,输入参数就是用户输入的电话号码,通过requestParameters.phoneNumber得到。在lookupCustomer中状态的转移是通过抛出异常触发的,因为如果通过电话号码找不到顾客,说明这个顾客是新客户,需要进行信息登记,所以就会抛出自定义异常CustomerNotFoundException,用来触发下一个流程——用户注册。相反,如果通过电话号码找到了用户,则说明是老顾客,那么就直接跳转到customerReady状态,即第一个Customer流程随之结束,跳转到下一个流程——Order。进入由<view-state>定义的registrationForm视图状态后,首先通过设置<view-state>的model属性为表单绑定order对象,具体如下图所示,
然后使用<on-entry>进行切入,即进入registrationForm状态后,先获取前一个页面用户输入的电话号码,通过requestParameters.phoneNumber得到,然后赋值给order.customer.phoneNumber。当用户输入完全部信息后,点击Submit按钮就会通过<transition>进行状态转移,转移到checkDeliveryArea状态中。如果点击Cancel就会返回首页。checkDeliveryArea是一个由<decision-state>定义的决策状态,通过表达式的值确定下一步的转移方向,表达式是通过调用pizzaFlowActions类中checkDeliveryArea方法对邮编进行判断。如果表达式结果为ture则转移到then属性指定的addCustomer状态中,如果为false,则转移到else属性指定的deliveryWarning状态中。deliveryWarning状态是由于用户所填写的地址超出配送范围,需要用户到店自取,判断用户是否接受这个请求,具体如下图所示。
如果用户点击Accept则会跳转到addCustomer状态中,点击cancel就会返回首页。addCustomer状态是一个行为状态,使用表达式将刚注册的用户信息进行保存,然后跳转到customerReady状态,即子流程的结束状态,最后返回到主流程中,第一个customer流程执行完毕,跳转到order流程。
同样进入到order流程后,会跳转到子流程order-flow,也会需要一个订单对象作为输入。如果子流程结束的<end-state>状态ID为orderCreated,那么子流程执行完毕会跳转到payment状态。具体的流程下面进行详细介绍。
3.子流程order-flow.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <flow xmlns="http://www.springframework.org/schema/webflow" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://www.springframework.org/schema/webflow 5 http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd"> 6 7 <input name="order" required="true" /> <!-- 接收order作为输入 --> 8 9 <!-- Order --> 10 <view-state id="showOrder"><!-- 展现order的状态 --> 11 <transition on="createPizza" to="createPizza" /> 12 <transition on="checkout" to="orderCreated" /> 13 <transition on="cancel" to="cancel" /> 14 </view-state> 15 16 <view-state id="createPizza" model="flowScope.pizza"> <!-- 创建披萨的状态 --> 17 <on-entry> 18 <set name="flowScope.pizza" 19 value="new main.java.com.springinaction.pizza.domain.Pizza()" /> 20 <evaluate result="viewScope.toppingsList" 21 expression="T(main.java.com.springinaction.pizza.domain.Topping).asList()" /> 22 </on-entry> 23 <transition on="addPizza" to="showOrder"> 24 <evaluate expression="order.addPizza(flowScope.pizza)" /> 25 </transition> 26 <transition on="cancel" to="showOrder" /> 27 </view-state> 28 29 30 <!-- End state --> 31 <end-state id="cancel" /> <!-- 取消的结束状态 --> 32 <end-state id="orderCreated" /> <!-- 创建订单的结束状态 --> 33 </flow>
首先进入showOrder状态,其中包含三个可以进行转移的状态:createPizza,orderCreated,cancel。showOrder是一个视图状态,对应的页面如下图所示:
当用户点击Create Pizza按钮后,触发createPizza状态,因为createPizza页面中也有表单,所以先设置model属性进行表单与pizza对象进行绑定。需要注意的是这里的pizza对象的作用域范围是flow,即当流程开始时创建,在流程结束时销毁。只有在创建它的流程中是可见的。在进入createPizza流程后,先设置两个变量。第一个变量是pizza,作用域是flow,通过<set>设置,用于保存pizza信息,当表单提交时,表单的内容会填充到该对象中。需要注意的是,这个视图状态引用的model是流程作用域内的同一个Pizza对象。第二个是toppingsList,作用域是view,即当进入视图状态时创建,当这个状态退出时销毁,只在视图状态内时可见的,所以flow>view。toppingsList是用来保存披萨的种类的。具体的createPizza对应的页面如下所示:
当用户选择完披萨的大小和种类后,点击Continue按钮后,通过触发addPizza进入到视图showOrder,在重新进入showOrder视图的时候,将刚才填充完毕的pizza对象加入到order对象当中,现在的order就不为空了,具体如下图所示:
当用户点击Checkout按钮时,转移到orderCreated状态,意味着order子流程的结束,需要跳转到payment流程。下面再对payment流程进行介绍。
4.子流程payment-flow.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <flow xmlns="http://www.springframework.org/schema/webflow" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://www.springframework.org/schema/webflow 5 http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd"> 6 7 <input name="order" required="true"/> 8 9 <view-state id="takePayment" model="flowScope.paymentDetails"> 10 <on-entry> 11 <set name="flowScope.paymentDetails" 12 value="new main.java.com.springinaction.pizza.domain.PaymentDetails()" /> 13 <evaluate result="viewScope.paymentTypeList" 14 expression="T(main.java.com.springinaction.pizza.domain.PaymentType).asList()" /> 15 </on-entry> 16 <transition on="paymentSubmitted" to="verifyPayment" /> 17 <transition on="cancel" to="cancel" /> 18 </view-state> 19 20 <action-state id="verifyPayment"> 21 <evaluate result="order.payment" expression= 22 "pizzaFlowActions.verifyPayment(flowScope.paymentDetails)" /> 23 <transition to="paymentTaken" /> 24 </action-state> 25 26 <!-- End state --> 27 <end-state id="cancel" /> 28 <end-state id="paymentTaken" /> 29 </flow>
首先进入takePayment流程当中,先初始化两个变量。一个是flow级作用域的paymentDetails,另一个是view级的paymentTypeList。takePayment对应的页面如下所示:
当用户点击Submit触发paymentSubmitted,然后转移到verifyPayment行为状态。verifyPayment主要是通过调用pizzaFlowActions类的verifyPayment方法对用户的支付类型进行判断。最后转移到paymentTaken状态,意味着子流程结束,返回到主流程中。
主流程中和payment相关的流程还有一个行为状态saveOrder,顾名思义将上述流程创建的订单进行保存,然后转移到thankCustomer状态。thankCustomer界面如下所示:
当点击finish就又跳转到首页,重新开始整个流程。
四、具体实现
Spring Web Flow 就是 Spring Web MVC 的一个扩展,如果粗略一些来讲,所谓 flow 就相当于 Spring Web MVC 中一种特殊的 controller ,这种 controller 可通过 XML 文件加以配置。所以必须对Spring Web MVC进行配置,然后再定义相应的flow。
(一)通过Java来配置SpringMVC,但是flow不能通过java配置,必须通过xml来进行配置。使用Java配置的目的是显示欢迎页面。
1.在src/main/java目录下分别创建两个包,一个是spizza.config用来配置SpringMVC,另一个是spizza.controller用来配置控制器。目录结构如下所示:
RootConfig.java
1 package spizza.config; 2 3 import org.springframework.context.annotation.ComponentScan; 4 import org.springframework.context.annotation.ComponentScan.Filter; 5 import org.springframework.context.annotation.Configuration; 6 import org.springframework.context.annotation.FilterType; 7 import org.springframework.web.servlet.config.annotation.EnableWebMvc; 8 9 @Configuration("RootConfig") 10 @ComponentScan(basePackages = { "spizza" }, excludeFilters = { 11 @Filter(type = FilterType.ANNOTATION, value = EnableWebMvc.class) }) 12 public class RootConfig { 13 14 }
SpizzaWebApplnitializer.java
1 package spizza.config; 2 3 import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; 4 5 public class SpizzaWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { 6 7 @Override 8 protected Class<?>[] getRootConfigClasses() { 9 // TODO Auto-generated method stub 10 return new Class<?>[] { RootConfig.class }; 11 } 12 13 @Override 14 protected Class<?>[] getServletConfigClasses() { 15 // TODO Auto-generated method stub 16 return new Class<?>[] { WebConfig.class }; 17 } 18 19 @Override 20 protected String[] getServletMappings() { 21 // TODO Auto-generated method stub 22 return new String[] { "/" }; 23 } 24 }
WebConfig.java
1 package spizza.config; 2 3 import org.springframework.context.annotation.Bean; 4 import org.springframework.context.annotation.ComponentScan; 5 import org.springframework.context.annotation.Configuration; 6 import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer; 7 import org.springframework.web.servlet.config.annotation.EnableWebMvc; 8 import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; 9 import org.springframework.web.servlet.view.InternalResourceViewResolver; 10 11 @Configuration("WebConfig") 12 @EnableWebMvc 13 @ComponentScan("spizza.controller") 14 public class WebConfig extends WebMvcConfigurerAdapter { 15 @Bean 16 public InternalResourceViewResolver viewResolver() { 17 InternalResourceViewResolver resolver = new InternalResourceViewResolver(); 18 resolver.setPrefix("/WEB-INF/view/"); 19 resolver.setSuffix(".jsp"); 20 resolver.setViewClass(org.springframework.web.servlet.view.JstlView.class); 21 resolver.setExposeContextBeansAsAttributes(true); 22 return resolver; 23 } 24 25 @Override 26 public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { 27 // TODO Auto-generated method stub 28 configurer.enable(); 29 } 30 31 }
HomeController.java
1 package spizza.controller; 2 3 import org.springframework.stereotype.Controller; 4 import org.springframework.web.bind.annotation.RequestMapping; 5 import org.springframework.web.bind.annotation.RequestMethod; 6 7 @Controller 8 public class HomeController { 9 public HomeController() { 10 } 11 12 @RequestMapping(value = "welcome", method = RequestMethod.GET) 13 public String home() { 14 return "index"; 15 } 16 }
通过Java来配置SpringMVC也是一种复习回顾,Spring实战中推荐使用Java配置。
2.运行结果:
(二)配置Spring Web Flow
在WEB-IN目录下创建spring文件夹,用来存放有关flow的配置,在WEB-IN目录下创建flows文件夹,用来存放具体的flow流程。具体目录结构如下所示:
1.基础配置,将不同作用的配置文件分开,然后在root-config.xml中进行导入。
flow.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:flow="http://www.springframework.org/schema/webflow-config" 4 xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" 5 xsi:schemaLocation=" 6 http://www.springframework.org/schema/beans 7 http://www.springframework.org/schema/beans/spring-beans-4.0.xsd 8 http://www.springframework.org/schema/context 9 http://www.springframework.org/schema/context/spring-context-3.0.xsd 10 http://www.springframework.org/schema/webflow-config 11 http://www.springframework.org/schema/webflow-config/spring-webflow-config.xsd"> 12 13 <context:component-scan base-package="main.java.com.springinaction.pizza.flow" /> 14 15 <!-- 执行流程:进入Spring Web Flow系统的入口点 --> 16 <flow:flow-executor id="flowExecutor" /> 17 18 <!-- 所有 flow 定义文件位置在此配置, flow-builder-services 用于配置 flow 的特性 --> 19 <flow:flow-registry id="flowRegistry" 20 flow-builder-services="flowBuilderServices"> 21 <flow:flow-location path="/WEB-INF/flows/pizza/pizza-flow.xml" 22 id="pizza-flow" /> 23 <flow:flow-location path="/WEB-INF/flows/pizza/customer/customer-flow.xml" 24 id="customer-flow" /> 25 <flow:flow-location path="/WEB-INF/flows/pizza/order/order-flow.xml" 26 id="order-flow" /> 27 <flow:flow-location path="/WEB-INF/flows/pizza/payment/payment-flow.xml" 28 id="payment-flow" /> 29 </flow:flow-registry> 30 <!--Web Flow 中的视图通过 MVC 框架的视图技术来呈现 --> 31 <flow:flow-builder-services id="flowBuilderServices" 32 view-factory-creator="mvcViewFactoryCreator" /> 33 <!-- 指明 MVC 框架的 view resolver ,用于通过 view 名查找资源 --> 34 <bean id="mvcViewFactoryCreator" 35 class="org.springframework.webflow.mvc.builder.MvcViewFactoryCreator"> 36 <property name="viewResolvers" ref="viewResolver" /> 37 </bean> 38 39 </beans>
mvc.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://www.springframework.org/schema/beans 5 http://www.springframework.org/schema/beans/spring-beans.xsd"> 6 <bean id="viewResolver" 7 class="org.springframework.web.servlet.view.InternalResourceViewResolver"> 8 <property name="viewClass" 9 value="org.springframework.web.servlet.view.JstlView"> 10 </property> 11 <property name="prefix" value="/WEB-INF/view/"> 12 </property> 13 <property name="suffix" value=".jsp"> 14 </property> 15 </bean> 16 <bean id="viewMappings" 17 class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> 18 <!-- /pizza-flow.do 请求由 flowController 来处理 --> 19 <property name="mappings"> 20 <value> /pizza-flow.do=flowController </value> 21 </property> 22 </bean> 23 <bean id="flowController" class="org.springframework.webflow.mvc.servlet.FlowController"> 24 <property name="flowExecutor" ref="flowExecutor" /> 25 </bean> 26 </beans>
domain.xml,加载流程中需要的bean。
1 <?xml version="1.0" encoding="UTF-8"?> 2 3 <beans xmlns="http://www.springframework.org/schema/beans" 4 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 5 xmlns:context="http://www.springframework.org/schema/context" 6 xsi:schemaLocation="http://www.springframework.org/schema/beans 7 http://www.springframework.org/schema/beans/spring-beans-4.0.xsd 8 http://www.springframework.org/schema/context 9 http://www.springframework.org/schema/context/spring-context-3.1.xsd"> 10 11 <context:spring-configured /> 12 13 <bean id="order" class="main.java.com.springinaction.pizza.domain.Order" abstract="true"> 14 <property name="pricingEngine" ref="pricingEngine" /> 15 </bean> 16 17 </beans>
services.xml,加载流程中需要的bean。
1 <?xml version="1.0" encoding="UTF-8"?> 2 3 <beans xmlns="http://www.springframework.org/schema/beans" 4 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 5 xsi:schemaLocation="http://www.springframework.org/schema/beans 6 http://www.springframework.org/schema/beans/spring-beans-2.0.xsd"> 7 8 <bean id="pricingEngine" 9 class="main.java.com.springinaction.pizza.service.PricingEngineImpl" /> 10 11 <!-- 12 <lang:groovy id="pricingEngineGroovy" 13 script-source="classpath:scripts/PricingEngineImpl.groovy" /> 14 --> 15 16 <bean id="customerService" 17 class="main.java.com.springinaction.pizza.service.CustomerServiceImpl" /> 18 19 <!-- Payment processing bean, as discussed on page 606 --> 20 <bean id="paymentProcessor" 21 class="main.java.com.springinaction.pizza.service.PaymentProcessor" /> 22 23 <bean id="orderService" 24 class="main.java.com.springinaction.pizza.service.OrderServiceImpl" /> 25 26 </beans>
root-config.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" 4 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd 5 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd"> 6 7 <context:component-scan base-package="main.java.com.springinaction.pizza" /> 8 <import resource="domain.xml" /> 9 <import resource="flow.xml" /> 10 <import resource="mvc.xml" /> 11 <import resource="services.xml" /> 12 13 </beans>
web.xml,虽然以及使用Java配置过Servlet,参考SpizzaWebAppInitializer.java,但是为了执行flow,还需要在web.xml中进行相应的配置。
1 <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 2 xmlns="http://xmlns.jcp.org/xml/ns/javaee" 3 xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" 4 id="WebApp_ID" version="3.1"> 5 <servlet> 6 <servlet-name>CartServlet</servlet-name> 7 <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> 8 <init-param> 9 <param-name>contextConfigLocation</param-name> 10 <param-value> 11 /WEB-INF/spring/root-config.xml 12 </param-value> 13 </init-param> 14 <load-on-startup>1</load-on-startup> 15 </servlet> 16 <servlet-mapping> 17 <servlet-name>CartServlet</servlet-name> 18 <url-pattern>*.do</url-pattern> 19 </servlet-mapping> 20 </web-app>
2.Flow的定义
整个顾客订购披萨的过程可以分为三个部分,第一部分就是顾客相关信息验证以及查询,第二个部分是订单的创建,第三个部分是支付。所以不应该将全部的流程都放在一个文件中进行定义,应该分别定义为子流程,然后在主流程中进行调用。具体内容参照第三小节。
(三)视图,只给出了主要内容。
1.index.jsp
1 <h1>Hello!</h1><br/> 2 <a href="pizza-flow.do">Spizza</a>
2.welcome.jsp
1 <h2 align="center">Welcome to Spizza!!!</h2> 2 <form:form> 3 <table align="center"> 4 <tr> 5 <td>phoneNumber</td> 6 <td><input type="hidden" name="_flowExecutionKey" 7 value="${flowExecutionKey}" /> <input type="text" 8 name="phoneNumber" /><br /></td> 9 </tr> 10 <tr height="80px"></tr> 11 <tr> 12 <td colspan="2" align="center"><input type="submit" name="_eventId_phoneEntered" 13 value="Lookup Customer" /></td> 14 </tr> 15 </table> 16 </form:form> 17 <!-- 首先要注意的是隐藏的“_flowExecutionKey”输入域。 18 当进入视图 状态时,流程暂停并等待用户采取一些行为。 19 赋予视图的流程执行 key(flow execution key)就是一种返回流程的“回程票”(claim ticket)。 20 当用户提交表单时,流程执行key会 在“_flowExecutionKey”输入域中返回并在流程暂停的位置进行恢 复。 21 -->
3.registrationForm.jsp
1 <h2 align="center">Customer Registration</h2> 2 <form:form commandName="order"> 3 <table align="center"> 4 <tr> 5 <td><input type="hidden" name="_flowExecutionKey" 6 value="${flowExecutionKey}" /></td> 7 </tr> 8 <tr> 9 <td align="center">Phone number:</td> 10 <td><form:input path="customer.phoneNumber" /></td> 11 </tr> 12 <tr> 13 <td align="center">Name:</td> 14 <td><form:input path="customer.name" /></td> 15 </tr> 16 <tr> 17 <td align="center">Address:</td> 18 <td><form:input path="customer.address" /></td> 19 </tr> 20 <tr> 21 <td align="center">State:</td> 22 <td><form:input path="customer.state" /></td> 23 </tr> 24 <tr> 25 <td align="center">Zip Code:</td> 26 <td><form:input path="customer.zipCode" /></td> 27 </tr> 28 <tr height="80px"></tr> 29 <tr> 30 <td colspan="2" align="center"><input type="submit" 31 name="_eventId_submit" value="Submit" /> 32 <input type="submit" name="_eventId_cancel" value="Cancel" /></td> 33 </tr> 34 </table> 35 </form:form>
4.deliveryWarning.jsp
1 <h2>Delivery Unavailable</h2> 2 3 <p>The address is outside of our delivery area. The order may still be taken for carry-out.</p> 4 5 <a href="${flowExecutionUrl}&_eventId=accept">Accept</a> | 6 <a href="${flowExecutionUrl}&_eventId=cancel">Cancel</a>
5.showOrder.jsp
1 <div align="center"> 2 <h2>Your order</h2> 3 4 <h3>Deliver to:</h3> 5 <table> 6 <tr> 7 <td>name</td> 8 <td>${order.customer.name}</td> 9 </tr> 10 <tr> 11 <td>address</td> 12 <td>${order.customer.address}</td> 13 </tr> 14 <tr> 15 <td>city</td> 16 <td>${order.customer.city}</td> 17 </tr> 18 <tr> 19 <td>state</td> 20 <td>${order.customer.state}</td> 21 </tr> 22 <tr> 23 <td>zipCode</td> 24 <td>${order.customer.zipCode}</td> 25 </tr> 26 <tr> 27 <td>phoneNumber</td> 28 <td>${order.customer.phoneNumber}</td> 29 </tr> 30 </table> 31 <hr /> 32 <h3> 33 Order total: 34 <fmt:formatNumber type="currency" value="${order.total}"></fmt:formatNumber> 35 36 </h3> 37 <hr /> 38 <h3>Pizzas:</h3> 39 40 <c:if test="${fn:length(order.pizzas) eq 0}"> 41 <b>No pizzas in this order.</b> 42 </c:if> 43 44 <br /> 45 <table border="1"> 46 <tr> 47 <th>Size</th> 48 <th>Toppings</th> 49 <th>IsCombo</th> 50 <th>Price</th> 51 </tr> 52 <c:forEach items="${order.pizzas }" var="pizza"> 53 <tr> 54 <td align="center">${pizza.size}</td> 55 <td align="center"> 56 <c:forEach items="${pizza.toppings}" var="topping"> 57 <c:out value="${topping}" /> 58 </c:forEach> 59 </td> 60 <td align="center">${pizza.isCombo }</td> 61 62 <td>${pizza.price }</td> 63 </tr> 64 </c:forEach> 65 </table> 66 67 <form:form> 68 <input type="hidden" name="_flowExecutionKey" 69 value="${flowExecutionKey}" /> 70 <input type="submit" name="_eventId_createPizza" value="Create Pizza" /> 71 <c:if test="${fn:length(order.pizzas) gt 0}"> 72 <input type="submit" name="_eventId_checkout" value="Checkout" /> 73 </c:if> 74 <input type="submit" name="_eventId_cancel" value="Cancel" /> 75 </form:form> 76 </div>
6.createPizza.jsp
1 <div align="center"> 2 3 <h2>Create Pizza</h2> 4 <form:form commandName="pizza" > 5 <input type="hidden" name="_flowExecutionKey" 6 value="${flowExecutionKey}" /> 7 8 <b>Size: </b> 9 <br /> 10 <table> 11 <tr> 12 <td><form:radiobutton path="size" label="Small (12-inch)——————¥6.99" 13 value="SMALL" /></td> 14 </tr> 15 <tr> 16 <td><form:radiobutton path="size" label="Medium (14-inch)——————¥7.99" 17 value="MEDIUM" /></td> 18 </tr> 19 <tr> 20 <td><form:radiobutton path="size" label="Large (16-inch)——————¥8.99" 21 value="LARGE" /></td> 22 </tr> 23 <tr> 24 <td><form:radiobutton path="size" label="Ginormous (20-inch)——————¥9.99" 25 value="GINORMOUS" /></td> 26 </tr> 27 </table> 28 <br /> 29 <br /> 30 <b>Toppings: PRICE_PER_TOPPING 0.20¥</b> 31 <br /> 32 <table> 33 <tr> 34 <td> 35 <form:checkboxes name="topping" path="toppings" items="${toppingsList}" 36 delimiter="<br/>" /> 37 </td> 38 </tr> 39 </table> 40 <br /> 41 <br /> 42 <b>Hyperchannel</b> 43 <table> 44 <tr> 45 <td><form:checkbox path="specialPizza" label="MEAT" 46 value="MEAT" /></td> 47 </tr> 48 <tr> 49 <td><form:checkbox path="specialPizza" label="VEGGIE" 50 value="VEGGIE" /></td> 51 </tr> 52 <tr> 53 <td><form:checkbox path="specialPizza" label="THEWORKS" 54 value="THEWORKS" /></td> 55 </tr> 56 </table> 57 <input type="submit" class="button" name="_eventId_addPizza" 58 value="Continue" /> 59 <input type="submit" class="button" name="_eventId_cancel" 60 value="Cancel" /> 61 </form:form> 62 </div>
7.takePayment.jsp
1 <div align="center"> 2 3 <script> 4 function showCreditCardField() { 5 var ccNumberStyle = document.paymentForm.creditCardNumber.style; 6 ccNumberStyle.visibility = 'visible'; 7 } 8 9 function hideCreditCardField() { 10 var ccNumberStyle = document.paymentForm.creditCardNumber.style; 11 ccNumberStyle.visibility = 'hidden'; 12 } 13 </script> 14 15 <h2>Take Payment</h2> 16 <form:form commandName="paymentDetails" name="paymentForm"> 17 <table> 18 <tr> 19 <td><input type="hidden" name="_flowExecutionKey" 20 value="${flowExecutionKey}" /></td> 21 </tr> 22 <tr> 23 <td><form:radiobutton path="paymentType" value="CASH" 24 label="Cash (taken at delivery)" onclick="hideCreditCardField()" /></td> 25 </tr> 26 <tr> 27 <td><form:radiobutton path="paymentType" value="CHECK" 28 label="Check (taken at delivery)" onclick="hideCreditCardField()" /></td> 29 </tr> 30 <tr> 31 <td><form:radiobutton path="paymentType" value="CREDIT_CARD" 32 label="Credit Card" onclick="showCreditCardField()" /></td> 33 <td><form:input path="creditCardNumber" 34 cssStyle="visibility:hidden;" /></td> 35 </tr> 36 <tr height="80px"></tr> 37 <tr> 38 <td colspan="2" align="center"><input type="submit" 39 class="button" name="_eventId_paymentSubmitted" value="Submit" /> 40 <input type="submit" class="button" 41 name="_eventId_cancel" value="Cancel" /></td> 42 </tr> 43 </table> 44 45 </form:form> 46 </div>
8.thankCustomer.jsp
1 <h2>Thank you for your order!</h2> 2 <h3>付款方式</h3> 3 ${order.payment} 4 <br> 5 <a href="${flowExecutionUrl}&_eventId=end">Finish</a>
(四)后台
1.用于保存信息的类Customer,Pizza,PaymentDetails,Order。
Customer保存用户的姓名、地址、城市、详细地址、邮政编码、电话号码等。
1 package main.java.com.springinaction.pizza.domain; 2 3 import java.io.Serializable; 4 5 @SuppressWarnings("serial") 6 public class Customer implements Serializable { 7 private Integer id; 8 private String name; 9 private String address; 10 private String city; 11 private String state; 12 private String zipCode; 13 private String phoneNumber; 14 15 public Customer() { 16 } 17 18 public Customer(String phoneNumber) { 19 this.phoneNumber = phoneNumber; 20 } 21 22 public String getCity() { 23 return city; 24 } 25 26 public void setCity(String city) { 27 this.city = city; 28 } 29 30 public Integer getId() { 31 return id; 32 } 33 34 public void setId(Integer id) { 35 this.id = id; 36 } 37 38 public String getName() { 39 return name; 40 } 41 42 public void setName(String name) { 43 this.name = name; 44 } 45 46 public String getPhoneNumber() { 47 return phoneNumber; 48 } 49 50 public void setPhoneNumber(String phoneNumber) { 51 this.phoneNumber = phoneNumber; 52 } 53 54 public String getState() { 55 return state; 56 } 57 58 public void setState(String state) { 59 this.state = state; 60 } 61 62 public String getAddress() { 63 return address; 64 } 65 66 public void setAddress(String address) { 67 this.address = address; 68 } 69 70 public String getZipCode() { 71 return zipCode; 72 } 73 74 public void setZipCode(String zipCode) { 75 this.zipCode = zipCode; 76 } 77 }
Pizza保存披萨的大小、添加的配料、价格等信息。
1 package main.java.com.springinaction.pizza.domain; 2 3 import java.io.Serializable; 4 import java.util.List; 5 6 @SuppressWarnings("serial") 7 public class Pizza implements Serializable { 8 private PizzaSize size; 9 private List<Topping> toppings; 10 private List<String> specialPizza; 11 private String isCombo; 12 private float price; 13 14 public Pizza() { 15 size = PizzaSize.LARGE; 16 } 17 18 public float getPrice() { 19 return price; 20 } 21 22 public void setPrice(float price) { 23 this.price = price; 24 } 25 26 public String getIsCombo() { 27 return isCombo; 28 } 29 30 public void setIsCombo(String isCombo) { 31 this.isCombo = isCombo; 32 } 33 34 public List<String> getSpecialPizza() { 35 return specialPizza; 36 } 37 38 public void setSpecialPizza(List<String> specialPizza) { 39 this.specialPizza = specialPizza; 40 } 41 42 public PizzaSize getSize() { 43 return size; 44 } 45 46 public void setSize(PizzaSize size) { 47 this.size = size; 48 } 49 50 public void setSize(String sizeString) { 51 this.size = PizzaSize.valueOf(sizeString); 52 } 53 54 public List<Topping> getToppings() { 55 return toppings; 56 } 57 58 public void setToppings(List<Topping> toppings) { 59 this.toppings = toppings; 60 } 61 62 public void setToppings(String[] toppingStrings) { 63 for (int i = 0; i < toppingStrings.length; i++) { 64 toppings.add(Topping.valueOf(toppingStrings[i])); 65 } 66 } 67 }
因为在披萨选择页面顾客可以自行选择配料,也可以直接选择已经提供好的套餐,用户可以自由选择。所以在Pizza类中会有specialPizza字段用来存放顾客是否选择了套餐。
其中PizzaSize和Topping都是自定义的枚举类型的类,用来存放披萨大小和配料种类,具体如下所示:
PizzaSize.java
1 package main.java.com.springinaction.pizza.domain; 2 3 import java.io.Serializable; 4 5 public enum PizzaSize implements Serializable { 6 SMALL, MEDIUM, LARGE, GINORMOUS; 7 }
Topping.java
1 package main.java.com.springinaction.pizza.domain; 2 3 import java.io.Serializable; 4 import java.util.Arrays; 5 import java.util.List; 6 7 import org.apache.commons.lang3.text.WordUtils; 8 9 public enum Topping implements Serializable { 10 PEPPERONI, SAUSAGE, HAMBURGER, MUSHROOM, CANADIAN_BACON, PINEAPPLE, GREEN_PEPPER, JALAPENO, TOMATO, ONION, EXTRA_CHEESE; 11 12 public static List<Topping> asList() { 13 Topping[] all = Topping.values(); 14 return Arrays.asList(all); 15 } 16 17 @Override 18 public String toString() { 19 return WordUtils.capitalizeFully(name().replace('_', ' ')); 20 } 21 }
PaymentDetails保存顾客的付款信息,其中主要包括付款类型,以及卡号(如果使用信用卡)。
1 package main.java.com.springinaction.pizza.domain; 2 3 import java.io.Serializable; 4 5 public class PaymentDetails implements Serializable { 6 private static final long serialVersionUID = 1L; 7 8 private PaymentType paymentType; 9 private String creditCardNumber; 10 11 public PaymentType getPaymentType() { 12 return paymentType; 13 } 14 15 public void setPaymentType(PaymentType paymentType) { 16 this.paymentType = paymentType; 17 } 18 19 public String getCreditCardNumber() { 20 return creditCardNumber; 21 } 22 23 public void setCreditCardNumber(String creditCardNumber) { 24 this.creditCardNumber = creditCardNumber; 25 } 26 }
其中PaymentType是自定义的一个枚举类型的类,用来存放支付的方式,具体如下所示:
1 package main.java.com.springinaction.pizza.domain; 2 3 import java.util.Arrays; 4 import java.util.List; 5 6 import org.apache.commons.lang3.text.WordUtils; 7 8 public enum PaymentType { 9 CASH, CHECK, CREDIT_CARD; 10 11 public static List<PaymentType> asList() { 12 PaymentType[] all = PaymentType.values(); 13 return Arrays.asList(all); 14 } 15 16 @Override 17 public String toString() { 18 return WordUtils.capitalizeFully(name().replace('_', ' ')); 19 } 20 }
Order类中保存整个订单的信息。
1 package main.java.com.springinaction.pizza.domain; 2 3 import java.io.Serializable; 4 import java.util.ArrayList; 5 import java.util.List; 6 7 import org.springframework.beans.factory.annotation.Configurable; 8 import org.springframework.util.StringUtils; 9 10 import main.java.com.springinaction.pizza.flow.SpecialtyPizza; 11 import main.java.com.springinaction.pizza.service.PricingEngineImpl; 12 13 @Configurable("order") 14 public class Order implements Serializable { 15 private static final long serialVersionUID = 1L; 16 private Customer customer; 17 private List<Pizza> pizzas; 18 private Payment payment; 19 20 public Order() { 21 pizzas = new ArrayList<Pizza>(); 22 customer = new Customer(); 23 } 24 25 public Customer getCustomer() { 26 return customer; 27 } 28 29 public void setCustomer(Customer customer) { 30 this.customer = customer; 31 } 32 33 public List<Pizza> getPizzas() { 34 return pizzas; 35 } 36 37 public void setPizzas(List<Pizza> pizzas) { 38 this.pizzas = pizzas; 39 } 40 41 public void addPizza(Pizza pizza) { 42 // System.out.println(StringUtils.isEmpty(pizza.getToppings())); 43 // System.out.println(StringUtils.isEmpty(pizza.getSpecialPizza())); 44 45 Boolean pizza1 = StringUtils.isEmpty(pizza.getToppings()); 46 Boolean pizza2 = StringUtils.isEmpty(pizza.getSpecialPizza()); 47 48 if (pizza1 == false && pizza2 == true) { 49 pizza.setIsCombo("—"); 50 pizzas.add(pizza); 51 } else if (pizza1 == true && pizza2 == false) { 52 SpecialtyPizza specialtyPizza = new SpecialtyPizza(); 53 List<Pizza> newPizzas = specialtyPizza.getPizza(pizza); 54 for (Pizza temp : newPizzas) { 55 pizzas.add(temp); 56 } 57 } else if (pizza1 == false && pizza2 == false) { 58 pizza.setIsCombo("—"); 59 pizzas.add(pizza); 60 SpecialtyPizza specialtyPizza = new SpecialtyPizza(); 61 List<Pizza> newPizzas = specialtyPizza.getPizza(pizza); 62 for (Pizza temp : newPizzas) { 63 pizzas.add(temp); 64 } 65 } 66 } 67 68 public float getTotal() { 69 PricingEngineImpl pricingEngineImpl = new PricingEngineImpl(); 70 List<Pizza> pizzas = this.getPizzas(); 71 72 return pricingEngineImpl.calculateOrderTotal(pizzas); 73 } 74 75 public Payment getPayment() { 76 return payment; 77 } 78 79 public void setPayment(Payment payment) { 80 this.payment = payment; 81 payment.setAmount(this.getTotal()); 82 } 83 84 }
<1>需要注意的是在添加披萨到订单当中时,即方法addPizza,其参数是一个Pizza类型的列表。因为在表单中直接绑定了pizza的三个对象,size、topping和specialPizza,而其中topping与specialPizza可以不用同时赋值,所以再添加pizza对象到订单中的时候要对这两个对象判断是否为空,然后根据实际情况添加相应的pizza对象到订单当中。所以还需要一个SpecialtyPizza类返回特殊的pizza对象,具体如下所示:
1 package main.java.com.springinaction.pizza.flow; 2 3 import java.util.ArrayList; 4 import java.util.List; 5 6 import main.java.com.springinaction.pizza.domain.Pizza; 7 import main.java.com.springinaction.pizza.domain.Topping; 8 9 public class SpecialtyPizza { 10 11 public List<Pizza> getPizza(Pizza pizza) { 12 List<Pizza> newPizza = new ArrayList<Pizza>(); 13 if (pizza.getSpecialPizza().size() != 0) { 14 for (String type : pizza.getSpecialPizza()) { 15 if ("MEAT".equals(type)) { 16 17 List<Topping> meats = new ArrayList<Topping>(); 18 19 meats.add(Topping.CANADIAN_BACON); 20 meats.add(Topping.HAMBURGER); 21 meats.add(Topping.PEPPERONI); 22 meats.add(Topping.SAUSAGE); 23 Pizza tempt = new Pizza(); 24 tempt.setSize(pizza.getSize()); 25 tempt.setToppings(meats); 26 tempt.setIsCombo("MEAT"); 27 newPizza.add(tempt); 28 } else if ("VEGGIE".equals(type)) { 29 30 List<Topping> meats = new ArrayList<Topping>(); 31 32 meats.add(Topping.GREEN_PEPPER); 33 meats.add(Topping.MUSHROOM); 34 meats.add(Topping.PINEAPPLE); 35 meats.add(Topping.TOMATO); 36 37 Pizza tempt = new Pizza(); 38 tempt.setSize(pizza.getSize()); 39 tempt.setToppings(meats); 40 tempt.setIsCombo("VEGGIE"); 41 newPizza.add(tempt); 42 } else if ("THEWORKS".equals(type)) { 43 44 List<Topping> meats = new ArrayList<Topping>(); 45 System.out.println("THE WORKS!"); 46 47 meats.add(Topping.CANADIAN_BACON); 48 meats.add(Topping.HAMBURGER); 49 meats.add(Topping.PEPPERONI); 50 meats.add(Topping.SAUSAGE); 51 meats.add(Topping.GREEN_PEPPER); 52 meats.add(Topping.MUSHROOM); 53 meats.add(Topping.PINEAPPLE); 54 meats.add(Topping.TOMATO); 55 meats.add(Topping.EXTRA_CHEESE); 56 meats.add(Topping.ONION); 57 meats.add(Topping.JALAPENO); 58 59 Pizza tempt = new Pizza(); 60 tempt.setSize(pizza.getSize()); 61 tempt.setToppings(meats); 62 tempt.setIsCombo("THEWORKS"); 63 newPizza.add(tempt); 64 } 65 } 66 return newPizza; 67 } else { 68 return newPizza; 69 } 70 } 71 }
<2>在初始化payment对象时,同时用订单总金额来初始化payment对象中amount变量,其中的payment是一个抽象类的实例,代码如下所示:
Payment.java
1 package main.java.com.springinaction.pizza.domain; 2 3 import java.io.Serializable; 4 5 public abstract class Payment implements Serializable { 6 private static final long serialVersionUID = 1L; 7 8 private float amount; 9 private String creditCardNumber; 10 11 public String getCreditCardNumber() { 12 return creditCardNumber; 13 } 14 15 public void setCreditCardNumber(String creditCardNumber) { 16 this.creditCardNumber = creditCardNumber; 17 } 18 19 public void setAmount(float amount) { 20 this.amount = amount; 21 } 22 23 public float getAmount() { 24 return amount; 25 } 26 }
这个抽象类主要包含两个成员变量:amount(需要付款的总金额),creditCardNumber(使用信用卡付款时的卡号)。然后添加了两个类CshOrCheckPayment和CreditCardPayment,它们都继承Payment。
CshOrCheckPayment
1 package main.java.com.springinaction.pizza.domain; 2 3 public class CashOrCheckPayment extends Payment { 4 public CashOrCheckPayment() { 5 } 6 7 public String toString() { 8 return "CASH or CHECK: ¥" + getAmount(); 9 } 10 }
CreditCardPayment
1 package main.java.com.springinaction.pizza.domain; 2 3 public class CreditCardPayment extends Payment { 4 public CreditCardPayment() { 5 } 6 7 public String toString() { 8 return "CREDIT: ¥" + getAmount() + " ; AUTH: " + this.getCreditCardNumber(); //调用父类的成员变量 9 } 10 }
2.流程执行过程中需要用到的方法。
<1>CustomerServiceImpl,实现CustomerService接口,接口中只有一个方法,lookupCustomer。
1 package main.java.com.springinaction.pizza.service; 2 3 import main.java.com.springinaction.pizza.domain.Customer; 4 5 public class CustomerServiceImpl implements CustomerService { 6 public CustomerServiceImpl() { 7 } 8 9 public Customer lookupCustomer(String phoneNumber) throws CustomerNotFoundException { 10 if ("9725551234".equals(phoneNumber)) { 11 Customer customer = new Customer(); 12 customer.setId(123); 13 customer.setName("Craig Walls"); 14 customer.setAddress("3700 Dunlavy Rd"); 15 customer.setCity("Denton"); 16 customer.setState("TX"); 17 customer.setZipCode("76210"); 18 customer.setPhoneNumber(phoneNumber); 19 return customer; 20 } else { 21 throw new CustomerNotFoundException(); 22 } 23 } 24 }
<2>OrderServiceImpl将用户订单保存到日志当中,因为没有连接数据库。
1 package main.java.com.springinaction.pizza.service; 2 3 import org.apache.log4j.Logger; 4 5 import main.java.com.springinaction.pizza.domain.Order; 6 7 public class OrderServiceImpl { 8 private static final Logger LOGGER = Logger.getLogger(OrderServiceImpl.class); 9 10 public OrderServiceImpl() { 11 } 12 13 public void saveOrder(Order order) { 14 LOGGER.debug("SAVING ORDER: "); 15 LOGGER.debug(" Customer: " + order.getCustomer().getName()); 16 LOGGER.debug(" # of Pizzas: " + order.getPizzas().size()); 17 LOGGER.debug(" Payment: " + order.getPayment()); 18 } 19 }
<3>PricingEngineImpl实现PricingEngine接口,接口中只有一个方法calculateOrderTotal,计算每个披萨的价格,以及订单中披萨的总价。
1 package main.java.com.springinaction.pizza.service; 2 3 import java.io.Serializable; 4 import java.util.HashMap; 5 import java.util.List; 6 import java.util.Map; 7 8 import org.apache.log4j.Logger; 9 10 import main.java.com.springinaction.pizza.domain.Pizza; 11 import main.java.com.springinaction.pizza.domain.PizzaSize; 12 13 @SuppressWarnings("serial") 14 public class PricingEngineImpl implements PricingEngine, Serializable { 15 private static final Logger LOGGER = Logger.getLogger(PricingEngineImpl.class); 16 17 private static Map<PizzaSize, Float> SIZE_PRICES; 18 static { 19 SIZE_PRICES = new HashMap<PizzaSize, Float>(); 20 SIZE_PRICES.put(PizzaSize.SMALL, 7.00f); 21 SIZE_PRICES.put(PizzaSize.MEDIUM, 8.00f); 22 SIZE_PRICES.put(PizzaSize.LARGE, 9.00f); 23 SIZE_PRICES.put(PizzaSize.GINORMOUS, 10.00f); 24 } 25 private static float PRICE_PER_TOPPING = 2.00f; 26 27 public PricingEngineImpl() { 28 } 29 30 public float calculateOrderTotal(List<Pizza> pizzas) { 31 32 float total = 0.0f; 33 if (pizzas.size() == 0) { 34 return total; 35 } else { 36 for (Pizza pizza : pizzas) { 37 float pizzaPrice = SIZE_PRICES.get(pizza.getSize()); 38 if (pizza.getToppings().size() > 0) { 39 pizzaPrice += (pizza.getToppings().size() * PRICE_PER_TOPPING); 40 } 41 pizza.setPrice(pizzaPrice); 42 total += pizzaPrice; 43 } 44 45 return total; 46 } 47 } 48 49 }
3.在执行总流程中调用所需的方法时通过PizzaFlowActions类。
1 package main.java.com.springinaction.pizza.flow; 2 3 import org.apache.log4j.Logger; 4 import org.springframework.beans.factory.annotation.Autowired; 5 import org.springframework.stereotype.Service; 6 7 import main.java.com.springinaction.pizza.domain.CashOrCheckPayment; 8 import main.java.com.springinaction.pizza.domain.CreditCardPayment; 9 import main.java.com.springinaction.pizza.domain.Customer; 10 import main.java.com.springinaction.pizza.domain.Order; 11 import main.java.com.springinaction.pizza.domain.Payment; 12 import main.java.com.springinaction.pizza.domain.PaymentDetails; 13 import main.java.com.springinaction.pizza.domain.PaymentType; 14 import main.java.com.springinaction.pizza.service.CustomerNotFoundException; 15 import main.java.com.springinaction.pizza.service.CustomerService; 16 17 @Service 18 public class PizzaFlowActions { 19 20 private static final Logger LOGGER = Logger.getLogger(PizzaFlowActions.class); 21 22 public Customer lookupCustomer(String phoneNumber) throws CustomerNotFoundException { 23 Customer customer = customerService.lookupCustomer(phoneNumber); 24 if (customer != null) { 25 return customer; 26 } else { 27 throw new CustomerNotFoundException(); 28 } 29 } 30 31 public void addCustomer(Customer customer) { 32 LOGGER.warn("TODO: Flesh out the addCustomer() method."); 33 } 34 35 public Payment verifyPayment(PaymentDetails paymentDetails) { 36 Payment payment = null; 37 if (paymentDetails.getPaymentType() == PaymentType.CREDIT_CARD) { 38 payment = new CreditCardPayment(); 39 } else { 40 payment = new CashOrCheckPayment(); 41 } 42 43 return payment; 44 } 45 46 public void saveOrder(Order order) { 47 LOGGER.warn("TODO: Flesh out the saveOrder() method."); 48 } 49 50 public boolean checkDeliveryArea(String zipCode) { 51 LOGGER.warn("TODO: Flesh out the checkDeliveryArea() method."); 52 return "75075".equals(zipCode); 53 } 54 55 @Autowired 56 CustomerService customerService; 57 }
五、程序源码
https://github.com/lyj8330328/Spizza.git