笔记43 Spring Web Flow——重写订购披萨应用

一、项目的目录结构

二、订购流程总体设计

三、订购流程的详细设计

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" /> &nbsp;&nbsp;&nbsp;&nbsp;
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                         &nbsp;&nbsp;&nbsp;&nbsp; <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

猜你喜欢

转载自www.cnblogs.com/lyj-gyq/p/9141671.html