第16步 – 添加一些业务逻辑的类
目前位置我们的应用还不是很有用。我想添加一些业务逻辑,一个产品类Product和一个管理所有产品的类。我把管理类命名为ProductManager。为了能分离依赖Web的逻辑和业务逻辑,我将在Java源代码重创建两个单独的包——web和bus。如果这个应用程序是为一个真实的公司开发的,我可能会把包命名成像com.mycompany.web和com.mycompany.bus之类的名字,不过这只是一个演示而已我就让包的名称简短一些。Product类是实现为一个JavaBean——它有一个默认的构造器(如果我们没有指明任何构造器,会自动给出),两个实例变量description和price的获取器(getter)和设制器(setter)。我还把它设为Serializable,这对我们的应用不是必需的,不过以后我们很可能要把这个类在不同的应用层中传递的时候,那时就可以直接使用了。
src/bus/Product.java
package bus;
 
import java.io.Serializable;
 
public class Product implements Serializable {
 
    private String description;
    private Double price;
 
    public void setDescription(String s) {
        description = s;
    }
 
    public String getDescription() {
        return description;
    }
 
    public void setPrice(Double d) {
        price = d;
    }
 
    public Double getPrice() {
        return price;
    }
}
ProductManager中有一个Product的列表List,同样的,这个类也是实现为一个JavaBean。
bus/ProductManager.java
package bus;
 
import java.io.Serializable;
import java.util.List;
 
public class ProductManager implements Serializable {
    private List products;
 
    public void setProducts(List p) {
        products = p;
    }
 
    public List getProducts() {
        return products;
    }
}
下面,我修改了SpringappController来存放一个指向ProductManager类的引用。正如你所见,它现在在一个单独的web的包中——记得把代码放到这个新位置中。我还要添加让控制器将产品信息传送到视图的代码。getModelAndView现在返回一个Map,同时包含了时间日期和产品管理的引用。
src/web/SpringappController.java
package web;
 
import org.springframework.web.servlet.mvc.Controller;
import org.springframework.web.servlet.ModelAndView;
 
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import java.io.IOException;
 
import java.util.Map;
import java.util.HashMap;
 
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
 
import bus.Product;
import bus.ProductManager;
 
public class SpringappController implements Controller {
 
    /** Logger for this class and subclasses */
    protected final Log logger = LogFactory.getLog(getClass());
 
    private ProductManager prodMan;
 
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
 
        String now = (new java.util.Date()).toString();
        logger.info("returning hello view with " + now);
 
        Map myModel = new HashMap();
        myModel.put("now", now);
        myModel.put("products", getProductManager().getProducts());
 
        return new ModelAndView("hello", "model", myModel);
    }
 
    public void setProductManager(ProductManager pm) {
        prodMan = pm;
    }
 
    public ProductManager getProductManager() {
        return prodMan;
    }
}
第 17 步 – 修改视图用于现实业务数据并且添加消息绑定的支持
我使用了JSTL<c:forEach>标签来添加了一个显示产品信息的部分。我还用JSTL<fmt:message>标记替换了标题和欢迎文本,这样可以从给定的“message”源中读取文本并显示——在后面的步骤中我会显示这个方法。 
WEB-INF/jsp/hello.jsp
<%@ include file="/WEB-INF/jsp/include.jsp" %>
 
 
<html>
<head><title><fmt:message key="title"/></title></head>
<body>
<h1><fmt:message key="heading"/></h1>
<p><fmt:message key="greeting"/> <c:out value="${model.now}"/>
</p>
 
<h3>Products</h3>
<c:forEach items="${model.products}" var="prod">
  <c:out value="${prod.description}"/> <i>$<c:out value="${prod.price}"/></i><br><br>
</c:forEach>
</body>
</html>第18步 – 添加一些测试数据来自动组装一些业务对象
我不会添加任何用于从数据库中载入业务对象的代码。然和,我们可以使用Spring的bean和应用程序上下文的支持来牵线到实例的引用。我只要简单地把握需要的数据作为bean之间的耦合条目写入springapp-servlet.xml。我还要添加messageSource条目来引入消息资源绑定(“messages.properties”),在下一步我将创建它。
WEB-INF/springapp-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
 
<!--
  - Application context definition for "springapp" DispatcherServlet.
  -->
 
 
<beans>
    <bean id="springappController" class="web.SpringappController">
        <property name="productManager">
            <ref bean="prodMan"/>
        </property>
    </bean>
 
    <bean id="prodMan" class="bus.ProductManager">
        <property name="products">
            <list>
                <ref bean="product1"/>
                <ref bean="product2"/>
                <ref bean="product3"/>
            </list>
        </property>
    </bean>
 
    <bean id="product1" class="bus.Product">
        <property name="description"><value>Lamp</value></property>
        <property name="price"><value>5.75</value></property>
    </bean>
        
    <bean id="product2" class="bus.Product">
        <property name="description"><value>Table</value></property>
        <property name="price"><value>75.25</value></property>
    </bean>
 
    <bean id="product3" class="bus.Product">
        <property name="description"><value>Chair</value></property>
        <property name="price"><value>22.79</value></property>
    </bean>
 
    <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
        <property name="basename"><value>messages</value></property>
    </bean>
 
    <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        <property name="mappings">
            <props>
                <prop key="/hello.htm">springappController</prop>
            </props>
        </property>
    </bean>
 
    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="viewClass">
<value>org.springframework.web.servlet.view.JstlView</value>
</property>
        <property name="prefix"><value>/WEB-INF/jsp/</value></property>
        <property name="suffix"><value>.jsp</value></property>
    </bean>
</beans>       

解决方案 »

  1.   

    第19步 – 添加消息绑定以及给build.xml添加“clean”目标
    我在WEB-INF/classes目录中创建了一个“messages.properties”文件。这个属性绑定文件目前有3个条目可以匹配在<fmt:message>标记中指定的键。
    WEB-INF/classes/messages.properties
    title=SpringApp
     
    heading=Hello :: SpringApp
     
    greeting=Greetings, it is nowrebuild项目并运行:
    我在做这例子的时候犯了一个低级错误,messages.properties 命名成了message.properties, 与springapp-servlet.xml中定义的不一致,导致最后的结果页面出现乱码第3部分 -为应用程序添加单元测试和表单
    第20步 – 为SpringappController添加单元测试
    在我们创建单元测试之前,我们需要把junit.jar导入项目中。我使用了Spring分发包中自带的spring-framework-1.2.6-with-dependencies/spring-framework-1.2.6/lib /junit/junit.jar。参照上面的步骤,把junit.jar添加到项目中。
    现在我在src目录中添加了一个新的子目录叫做tests。相信大家也都猜到了,这个目录将包含所有的单元测试。 
    这些工作结束之后,我们准备开始写我们的第一个单元测试。SpringappController要依赖于HttpServletRequest和HttpServletResponse以及我们的应用程序上下文。由于控制器并没有使用请求和响应,我们直接传送null。如果不是这样,我们要使用EasyMock创建一些模仿对象mock object,这样就可以在测试用使用了。使用某个类,可以在Web Server环境之外载入应用程序上下文。有好几个类可以使用,针对当前的任务我们将使用FileSystemXmlApplicationContext。
    tests/TestSpringappController.java
    package tests;
     
    import java.util.Map;
    import java.util.List;
    import java.io.IOException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.ServletException;
    import junit.framework.TestCase;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.FileSystemXmlApplicationContext;
    import org.springframework.web.servlet.ModelAndView;
    import web.SpringappController;
    import bus.ProductManager;
    import bus.Product;
     
    public class TestSpringappController extends TestCase {
     
        private ApplicationContext ac;
     
        public void setUp() throws IOException {
            ac = new FileSystemXmlApplicationContext("src/tests/WEB-INF/springapp-servlet.xml");
        }
     
        public void testHandleRequest() throws ServletException, IOException {
            SpringappController sc = (SpringappController) ac.getBean("springappController");
            ModelAndView mav = sc.handleRequest((HttpServletRequest) null, (HttpServletResponse) null);
            Map m = mav.getModel();
            List pl = (List) ((Map) m.get("model")).get("products");
            Product p1 = (Product) pl.get(0);
            assertEquals("Lamp", p1.getDescription());
            Product p2 = (Product) pl.get(1);
            assertEquals("Table", p2.getDescription());
            Product p3 = (Product) pl.get(2);
            assertEquals("Chair", p3.getDescription());
        }
    }唯一的测试就是调用SpringappController的handleRequest,我们检测从模型中返回的产品。在setUp方法中,我们载入应用程序上下文,之前我已经复制到了tests中的WEB-INF目录中。我创建了一个副本这样这个文件可以在测试中以“messageSource”所需的bean的最小集来运行。这样,复制WEB-INF/springapp-servlet.xml 到springapp/src/tests/WEB-INF目录中。你可以删除“messageSource”、“urlMapping”和“viewResolver”bean条目,因为这个测试不需要他们。
    当你运行这个测试的时候,你应该看到载入应用程序上下文时有很多日志信息。
    第21步 – 为ProductManager添加单元测试和新的功能
    接下来我为ProductManager添加一个测试案例,同时我打算给ProductManager添加一个用于增加价格的新方法,并添加一个测试。
    tests/TestProductManager .java
    package tests;
     
    import java.util.List;
    import java.util.ArrayList;
    import junit.framework.TestCase;
    import bus.ProductManager;
    import bus.Product;
     
    public class TestProductManager extends TestCase {
        private ProductManager pm;
     
        public void setUp() {
            pm = new ProductManager();
            Product p = new Product();
            p.setDescription("Chair");
            p.setPrice(new Double("20.50"));
            ArrayList al = new ArrayList();
            al.add(p);
            p = new Product();
            p.setDescription("Table");
            p.setPrice(new Double("150.10"));
            al.add(p);
            pm.setProducts(al);
        }
     
        public void testGetProducs() {
            List l = pm.getProducts();
            Product p1 = (Product) l.get(0);
            assertEquals("Chair", p1.getDescription());
            Product p2 = (Product) l.get(1);
            assertEquals("Table", p2.getDescription());
        }
     
     
        public void testIncreasePrice() {
            pm.increasePrice(10);
            List l = pm.getProducts();
            Product p = (Product) l.get(0);
            assertEquals(new Double("22.55"), p.getPrice());
            p = (Product) l.get(1);
            assertEquals(new Double("165.11"), p.getPrice());
        }
     
    }
    对于这个测试,没有必要创建一个应用程序上下文。我只在setUp方法中建立了产品信息并且把他们添加到了产品管理对象中。我还给getProducts和increasePrice添加了测试。increasePrice方法根据传给它的百分比对价格进行增加。我修改了ProductManager类来实现这个新方法。
    bus/ProductManager.java
    package bus;
     
    import java.io.Serializable;
    import java.util.ListIterator;
    import java.util.List;
     
    public class ProductManager implements Serializable {
        private List products;
     
        public void setProducts(List p) {
            products = p;
        }
     
        public List getProducts() {
            return products;
        }
     
        public void increasePrice(int pct) {
            ListIterator li = products.listIterator();
            while (li.hasNext()) {
                Product p = (Product) li.next();
                double newPrice = p.getPrice().doubleValue() * (100 + pct)/100;
                p.setPrice(new Double(newPrice));
            }
            
        }
     
    }下面我构建并运行这些测试。正如你所见,这些测试就像一般的测试一样——业务类不依赖于任何servlet类,所以这些类测试起来很方便。