使用Spring设计和实现Web应用程序 开发指引(四)

翻译自 http://spring.io/guides/tutorials/web/3/

首发于 http://my.oschina.net/u/179755/blog/260918


现在我们已经编写和测试了一个Web控制器,并很自豪地添加到我们的救生圈,如下图所示。现在是时候将整个应用程序搭建起来了。

204702_z8Vs_179755.png


3步:配置一个基本的应用程序

此刻我们已经做好了如下准备:

1.    配置我们的应用程序核心

2.    配置我们的Web组件

3.    初始化我们的基础结构来创建可以工作的WAR文件

4.    Web容器中执行我们的Web应用程序

为了完成这些工作,我们需要一个新域,配置域。

204721_IgjZ_179755.png


使用Spring JavaConfig为我们应用程序核心和持久域创建配置

Yummy面馆应用程序包含了一个核心组件集(域类和服务)。它也包含了一个跟核心整合在一起的内存持久存储。

我们可以为这些组件创建配置;但是,就如前面步骤所做的,我们使用测试驱动方法来构建我们的配置。


测试我们的核心和持久配置

首先,构建集成测试如下。

src/test/java/com/yummynoodlebar/config/CoreDomainIntegraionTest.java

package   com.yummynoodlebar.config;

 

import   com.yummynoodlebar.core.services.MenuService;

import   com.yummynoodlebar.core.services.OrderService;

import   com.yummynoodlebar.events.menu.AllMenuItemsEvent;

import   com.yummynoodlebar.events.menu.RequestAllMenuItemsEvent;

import   com.yummynoodlebar.events.orders.AllOrdersEvent;

import   com.yummynoodlebar.events.orders.CreateOrderEvent;

import   com.yummynoodlebar.events.orders.OrderDetails;

import   com.yummynoodlebar.events.orders.RequestAllOrdersEvent;

import   org.junit.Test;

import   org.junit.runner.RunWith;

import   org.springframework.beans.factory.annotation.Autowired;

import   org.springframework.test.context.ContextConfiguration;

import   org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

 

import   static junit.framework.TestCase.assertEquals;

 

 

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration(classes   = {PersistenceConfig.class, CoreConfig.class})

public   class CoreDomainIntegrationTest {

 

    @Autowired

    MenuService menuService;

 

  @Autowired

  OrderService orderService;

 

    @Test

    public void thatAllMenuItemsReturned() {

 

    AllMenuItemsEvent allMenuItems =   menuService.requestAllMenuItems(new RequestAllMenuItemsEvent());

 

    assertEquals(3,   allMenuItems.getMenuItemDetails().size());

 

    }

 

  @Test

  public void addANewOrderToTheSystem() {

 

    CreateOrderEvent ev = new   CreateOrderEvent(new OrderDetails());

 

    orderService.createOrder(ev);

 

    AllOrdersEvent allOrders =   orderService.requestAllOrders(new RequestAllOrdersEvent());

 

    assertEquals(1,   allOrders.getOrdersDetails().size());

  }

 

}

 

这个集成测试使用JavaConfig通过标注@ContextConfiguration构建了一个ApplicationContext。核心域配置使用CoreConfig创建。持久域配置使用PersistenceConfig创建。

ApplicationContext构建后,这个测试可以自动注入MenuServiceOrderService,为测试方法做好准备。

最后,我们有两个测试方法来断言menuServieorderService依赖已经被提供,并且正确工作。

接下来,我们来创建核心和持久域配置。


实现我们的核心域配置

Yummy面馆应用程序的核心域配置只包含两个服务。它依赖即将配置的持久域来提供依赖。

下面的代码显示了完整的配置类:

src/main/java/com/yummynoodlebar/config/CoreConfig.java

package   com.yummynoodlebar.config;

 

import   com.yummynoodlebar.core.services.OrderEventHandler;

import   com.yummynoodlebar.core.services.OrderService;

import   com.yummynoodlebar.persistence.services.OrderPersistenceService;

import   org.springframework.context.annotation.Bean;

import   org.springframework.context.annotation.Configuration;

 

import   com.yummynoodlebar.core.services.MenuEventHandler;

import   com.yummynoodlebar.core.services.MenuService;

import   com.yummynoodlebar.persistence.services.MenuPersistenceService;

 

@Configuration

public   class CoreConfig {

    @Bean

    public MenuService   menuService(MenuPersistenceService menuPersistenceService) {

        return new   MenuEventHandler(menuPersistenceService);

    }

  @Bean

  public OrderService   orderService(OrderPersistenceService orderPersistenceService) {

    return new   OrderEventHandler(orderPersistenceService);

  }

 

}

 

核心事件处理器将分发事件给持久域来进行真实的持久化。目前我们使用了内存库包装的HashMaps

src/main/java/com/yummynoodlebar/config/PersistenceConfig.java

package   com.yummynoodlebar.config;

 

import   com.yummynoodlebar.persistence.domain.MenuItem;

import   com.yummynoodlebar.persistence.domain.Order;

import   com.yummynoodlebar.persistence.repository.*;

import   com.yummynoodlebar.persistence.services.MenuPersistenceEventHandler;

import   com.yummynoodlebar.persistence.services.MenuPersistenceService;

import   com.yummynoodlebar.persistence.services.OrderPersistenceEventHandler;

import   com.yummynoodlebar.persistence.services.OrderPersistenceService;

import   org.springframework.context.annotation.Bean;

import   org.springframework.context.annotation.Configuration;

 

import   java.math.BigDecimal;

import   java.util.HashMap;

import   java.util.Map;

import   java.util.UUID;

 

@Configuration

public   class PersistenceConfig {

 

  @Bean

  public OrdersRepository ordersRepo() {

    return new OrdersMemoryRepository(new   HashMap<UUID, Order>());

  }

 

  @Bean

  public OrderStatusRepository   orderStatusRepo() {

    return new OrderStatusMemoryRepository();

  }

 

  @Bean

  public OrderPersistenceService   orderPersistenceService() {

    return new   OrderPersistenceEventHandler(ordersRepo(), orderStatusRepo());

  }

 

    @Bean

    public MenuItemRepository   menuItemRepository() {

        return new   MenuItemMemoryRepository(defaultMenu());

    }

 

    @Bean

    public MenuPersistenceService   menuPersistenceService(MenuItemRepository menuItemRepository) {

        return new   MenuPersistenceEventHandler(menuItemRepository);

    }

 

    private Map<String, MenuItem>   defaultMenu() {

        Map<String, MenuItem> items =   new HashMap<String, MenuItem>();

        items.put("YM1",   menuItem("YM1", new BigDecimal("1.99"), 11, "Yummy   Noodles"));

        items.put("YM2",   menuItem("YM2", new BigDecimal("2.99"), 12, "Special   Yummy Noodles"));

        items.put("YM3", menuItem("YM3",   new BigDecimal("3.99"), 13, "Low cal Yummy Noodles"));

        return items;

    }

 

    private MenuItem menuItem(String id,   BigDecimal cost, int minutesToPrepare, String name) {

        MenuItem item = new MenuItem();

        item.setId(id);

        item.setCost(cost);

          item.setMinutesToPrepare(minutesToPrepare);

        item.setName(name);

        return item;

    }

 

}

 

Spring JavaConfig将会检查每一个@Bean 注解的方法作为产生Spring Bean的方法。

执行com.yummynoodlebar.config测试包中的CoreDomainIntegrationTest,将验证我们的核心域配置是可以工作的。


为我们的Web组件创建配置

配置我们新的控制器是相当直白的,因为我们已经在每个控制器类上使用@Controller

为了初始化我们的Web域组件,我们只需要打开组件扫描,这样Spring就可以找到和初始化这些Spring Bean


实现我们的Web域配置

我们可以创建如下Spring JavaConfig来执行组件扫描。

src/main/java/com/yummynoodlebar/config/WebCofig.java

package   com.yummynoodlebar.config;

 

import   org.springframework.context.annotation.ComponentScan;

import   org.springframework.context.annotation.Configuration;

import   org.springframework.web.servlet.config.annotation.EnableWebMvc;

 

@Configuration

@EnableWebMvc

@ComponentScan(basePackages   = {"com.yummynoodlebar.web.controller"})

public   class WebConfig {

 

}

 

JavaConfig中的@ComponentScan属性指明我们的组件可以在com.yummynoodlebar.web.controllers下找到。

 

注意:当我们定义组件扫描的位置时,越具体越好,这样我们才不会意外初始化我们不需要的组件。


测试我们的Web域配置

只有通过相关的测试,我们才可以相信这些配置。下面的测试保证了Web配置正确输出。

src/test/java/com/yummynoodlebar/config/WebDomainIntegrationTest.java

package   com.yummynoodlebar.config;

 

import   static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;

import   static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*;

import   static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

import   static org.hamcrest.Matchers.*;

 

import   org.junit.Before;

import   org.junit.Test;

import   org.junit.runner.RunWith;

 

import   org.springframework.beans.factory.annotation.Autowired;

import   org.springframework.test.context.ContextConfiguration;

import   org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import   org.springframework.test.context.web.WebAppConfiguration;

import   org.springframework.test.web.servlet.MockMvc;

import   org.springframework.test.web.servlet.setup.MockMvcBuilders;

import   org.springframework.web.context.WebApplicationContext;

 

@RunWith(SpringJUnit4ClassRunner.class)

@WebAppConfiguration

@ContextConfiguration(classes   = { PersistenceConfig.class, CoreConfig.class,

        WebConfig.class })

public   class WebDomainIntegrationTest {

 

    private static final String STANDARD =   "Yummy Noodles";

    private static final String CHEF_SPECIAL   = "Special Yummy Noodles";

    private static final String LOW_CAL =   "Low cal Yummy Noodles";

 

    private MockMvc mockMvc;

 

    @Autowired

    WebApplicationContext   webApplicationContext;

 

    @Before

    public void setup() {

        mockMvc =   MockMvcBuilders.webAppContextSetup(webApplicationContext).build();

    }

 

    @Test

    public void thatTextReturned() throws   Exception {

        mockMvc.perform(get("/"))

        .andDo(print())

        .andExpect(content().string(containsString(STANDARD)))

          .andExpect(content().string(containsString(CHEF_SPECIAL)))

          .andExpect(content().string(containsString(LOW_CAL)));

 

    }

 

}

 

我们已经断言了控制器和核心域的服务组件之间的协作的正确性。

这个测试确保一旦所有的对象被注入后,WebConfig的注入正确,对应的控制器就位。

这个测试通过模拟执行处理器映射的请求处理来验证WebConfig。完整的答复也确认正确。更多的测试可以执行,最重要的是我们在前面的步骤已经断言控制器可以正常运行。这个测试简单的表明我们已经正确地通过Spring JavaConfig配置了这些组件。


初始化Web服务网页基础结构

Spring3.2开始,如果我们使用了支持Servlet3的容器,例如Tomcat7+,我们可以不用写哪怕是一行xml来初始化整个我网页基础结构。

我们准备用WebApplicationInitializer来设置我们的应用程序的Web应用上下文参数,快速搭建我们的应用程序网页基础结构。

首先我们通过类com.yummynoodlebar.config.WebAppInitializer创建一段配置,这个类从AbstractAnnotationConfigDispatcherServletInitializer

src/main/java/com/yummynoodlebar/config/WebAppInitializer.java

public   class WebAppInitializer extends

    AbstractAnnotationConfigDispatcherServletInitializer   {

 

接下来我们重载getRootConfigClasses方法来提供一类Spring配置类来构建根应用上下文。这个上下文将被整个应用程序共享,包括Servlets,FiltersContext Listeners。它包含了我们的主要组件,包括核心和持久域。

src/main/java/com/yummynoodlebar/config/WebAppInitializer.java

  @Override

  protected Class<?>[]   getRootConfigClasses() {

    return new Class<?>[] {   PersistenceConfig.class, CoreConfig.class };

  }

 

在根应用上下文初始化后,重载getServletConfigClasses。同样返回一系列Spring配置类,这里只返回一个。

src/main/java/com/yummynoodlebar/config/WebAppInitializer.java

  @Override

  protected Class<?>[]   getServletConfigClasses() {

    return new Class<?>[] {   WebConfig.class };

  }

 

最后,增加一些额外的配置来映射Servlet URL context和增加一个标准的filter

src/main/java/com/yummynoodlebar/config/WebAppInitializer.java

  @Override

  protected String[] getServletMappings() {

    return new String[] { "/" };

  }

 

  @Override

  protected Filter[] getServletFilters() {

 

    CharacterEncodingFilter   characterEncodingFilter = new CharacterEncodingFilter();

      characterEncodingFilter.setEncoding("UTF-8");

    return new Filter[] {   characterEncodingFilter};

  }

 

AbstractAnnotationConfigDispatcherServletInitializer完成Spring DispatcherServletContextLoader的建立,这些是Spring Web应用程序的标准部分。

DispatcherServlet是前控制器Servlet,它接收所有请求,这些请求将被多个注册的控制器处理。它还负责将这些请求派给适当的控制器方法来处理。

整个WebAppInitializer源代码如下:

src/main/java/com/yummynoodlebar/config/WebAppInitializer.java

package   com.yummynoodlebar.config;

 

import   org.springframework.web.filter.CharacterEncodingFilter;

import   org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

 

import   javax.servlet.Filter;

 

public   class WebAppInitializer extends

      AbstractAnnotationConfigDispatcherServletInitializer {

 

  @Override

  protected Class<?>[]   getRootConfigClasses() {

    return new Class<?>[] {   PersistenceConfig.class, CoreConfig.class };

  }

 

  @Override

  protected Class<?>[]   getServletConfigClasses() {

    return new Class<?>[] {   WebConfig.class };

  }

 

  @Override

  protected String[] getServletMappings() {

    return new String[] { "/" };

  }

 

  @Override

  protected Filter[] getServletFilters() {

 

    CharacterEncodingFilter   characterEncodingFilter = new CharacterEncodingFilter();

      characterEncodingFilter.setEncoding("UTF-8");

    return new Filter[] {   characterEncodingFilter};

  }

}

 


Web容器中运行我们的Web服务

我们能否执行我们的Web应用?

首先告诉Gradle我们将使用Tomcat。更新我们的build.gradle文件。

build.gradle

apply   plugin: 'war'

apply   plugin: 'tomcat'

apply   plugin: 'java'

apply   plugin: 'propdeps'

apply   plugin: 'propdeps-maven'

apply   plugin: 'propdeps-idea'

apply   plugin: 'propdeps-eclipse'

apply   plugin: 'eclipse-wtp'

apply   plugin: 'idea'

 

println   "PROJECT=" + project.name

 

buildscript   {

  repositories {

    mavenCentral()

    maven {

      url   "http://download.java.net/maven/2"

    }

    maven { url   'http://repo.spring.io/plugins-release' }

  }

 

  dependencies {

    classpath   'org.gradle.api.plugins:gradle-tomcat-plugin:0.9.8'

    classpath   'org.springframework.build.gradle:propdeps-plugin:0.0.1'

  }

}

 

 

repositories   { mavenCentral() }

 

dependencies   {

    def tomcatVersion = '7.0.42'

    tomcat   "org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}",

              "org.apache.tomcat.embed:tomcat-embed-logging-juli:${tomcatVersion}"

      tomcat("org.apache.tomcat.embed:tomcat-embed-jasper:${tomcatVersion}")   {

      exclude group:   'org.eclipse.jdt.core.compiler', module: 'ecj'

    }

 

    compile   'org.springframework:spring-core:3.2.3.RELEASE'

    compile   'org.springframework:spring-webmvc:3.2.3.RELEASE'

 

    compile 'org.slf4j:slf4j-api:1.7.5'

    runtime 'org.slf4j:slf4j-log4j12:1.7.5'

 

    testCompile   'org.springframework:spring-test:3.2.3.RELEASE'

 

    testCompile 'junit:junit:4.11'

    testCompile   "org.mockito:mockito-all:1.9.5"

    testCompile   "org.hamcrest:hamcrest-library:1.3"

 

    provided   'javax.servlet:javax.servlet-api:3.0.1'

}

 

test {

  testLogging {

    // Show that tests are run in the   command-line output

    events 'started', 'passed'

  }

}

 

task   wrapper(type: Wrapper) { gradleVersion = '1.6' }

 

tomcatRunWar.contextPath   = ''

 

我们也许留意到build文件的最后一行保证应用在根上下文运行。

tomcatRunWar.contextPath   = ''

 

现在我们可以运行网页了,端口默认8080.

$   ./gradlew tomcatRunWar

 

如果我们访问http://localhost:8080/,我们将得到文本回应,这个回应正是PersitenceConfig中返回的初始菜单。

Yummy   Noodles,Special Yummy Noodles,Low cal Yummy Noodles

 

如果我们想运行于不同的端口或者其他配置,可以参考https://github.com/bmuschko/gradle-tomcat-plugin/


总结

我们走过了很长的路,现在我们得到了一个完整配置的Web前端,它运行在Tomcat,可以打包成War来分发。

我们在配置域中增加了三个新组建,CoreConfigPersistenceConfigWebConfig

204749_agxw_179755.png

整体看起来:

204805_h7Ap_179755.png

我们的Web前端并不美,甚至没有什么功能。在后面的教程,我们将美化它,增加更多的功能。

 

下一步:用Thymeleaf创建好看的HTML视图。

 


转载于:https://my.oschina.net/u/179755/blog/260918

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值