Spring In Action 04 ---构建SpringWeb应用程序

对于Java开发者来说,基于Web的应用程序是他们主要的关注点。如果在这方面有经验的话,你会意识到这种系统所面临的挑战。具体来讲,状态管理、工作流以及验证都是需要解决的重要特性。HTTP协议的无状态性决定了这些问题都不那么容易解决。

Spring的Web框架就是为了帮你解决这些关注点而设计的。Spring MVC基于模型-视图-控制器(MVC)模式实现,他能够帮你构建像Spring框架那样灵活和松耦合的Web应用程序。

Spring MVC起步配置

Spring MVC请求跟踪

每当用户在Web浏览器中点击链接或者提交表单的时候,请求就开始工作了。请求是一个十分繁忙的家伙,从离开浏览器开始到获取响应返回,他会经历好多站,在每站都会留下一些信息同时也会带上一些信息。下图展示了请求使用Spring MVC所经历的过程。

请求离开浏览器开始(1),会带有用户所请求内容的信息,至少会包含请求的URL,还可能带有用户提交的表单信息。请求的第一站将会来到DispatcherServlet。与大多数的基于Java的Web框架一样,Spring MVC所有的请求都会通过一个前端控制器Servlet,前端控制器是常用的Web应用程序模式,在这里一个单实例的Servlet将请求委托给应用程序的其他组件来执行实际的处理。在Spring MVC中,DispatcherServlet就是前端控制器。
DispatcherServlet的任务是将请求发送给Spring MVC控制器,控制器是一个用于处理请求的Spring组件,但是在典型的应用程序中会有多个控制器,DispatcherServlet需要知道将请求发送给哪个控制器。所以DispatcherServlet会查询一个或多个处理器映射(2)来确定请求的下一站在哪里。处理器映射会根据请求所携带的的URL信息来进行决策。一旦选择了合适的控制器,DispatcherServlet会将请求发送给选中的控制器(3)。到了控制器,请求会卸下其负载(用户提交的信息)并耐心等待控制器处理信息。实际上设计良好的控制器本身只处理很少或者不处理工作,而是将业务逻辑委托给其他的服务对象进行处理。
控制器在完成逻辑处理后,通常会产生一些信息,这些信息需要返回给用户并在浏览器上显示。这些信息被称为模型,不过仅仅仅仅给用户返回原始信息是不可取的,我们需要对这些信息以用户友好的方式进行格式化。所以信息需要发送给一个视图。控制器所做的最后一件事情就是将模型数据打包,并且标示出用于渲染输出的视图名。接下来将请求连同模型和视图名发送回DispatcherServlet(4)。
控制器仅仅只是传递了一个逻辑名称,这个名字将会用来查找产生结果的真正视图。DispatcherServlet将会使用视图解析器(5)来将逻辑视图名匹配为一个特定的视图实现。请求的最后一站是视图的实现(6)在这里他交付模型数据,请求的任务就完成了。最后响应给用户(7)。

配置DispatcherServlet

DispatcherServlet是Spring MVC的核心,在这里请求回第一次接触到框架,他要负责将请求路由到其他的组件之中。传统的配置方式是在web.xm文件中配置DispatcherServlet,但是接触Servlet3规范和Spring的新功能增强,我们可以使用Java将DispatcherServlet配置在Servlet容器中。
/**
 * 任何扩展了AbstractAnnotationConfigDispatcherServletInitializer的类都会自动配置
 * DispatcherServlet和Spring应用上下文,Spring应用上下文会为与应用程序的Servlet上下文之中
 * 与此同时他还会创建ContextLoaderListener。
 * 我们希望在DispatcherServlet中加载web组件的bean(控制器、视图解析器、处理器映射等)
 * 在ContextLoaderListener中加载其他的组件的bean(后端中间层和数据层组件)
 */
public class SpittrWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer{

    /**
     * getServletMappings作用是将一个或者多个路径映射到DispatcherServlet上
     * 本例中映射的是"/",这表示他会是默认的Servlet,他会处理进入应用的所有请求,因为所有的请求都是从根发起的
     * 当DispatcherServlet启动的时候,他会创建Spring应用上下文并加载配置文件中声明的bean
     */

    @Override
    protected String[] getServletMappings() {
        return new String[] {"/"};
    }

    /**
     * getRootConfigClasses方法返回带有@Configuration注解的类
     * 用来配置ContextLoaderListener创建的应用上下文中(另外一种应用上下文)的bean
     */

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class<?>[] {RootConfig.class};
    }

    /**
     * 我们使用DispatcherServlet加载上下文时,使用定义在WebConfig配置类中的bean
     * getServletConfigClasses方法返回带有@Configuration注解的类
     * 将会用来配置定义在DispatcherServlet创建的应用上下文中(也就是Spring应用上下文)的bean
     */

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[] {WebConfig.class};
    }
}
按照这种方式配置DispatcherServlet唯一的问题在于他只能部署到支持Servlet3.0d的服务器才能正常工作,如Tomcat7或者更高的版本。

启动Spring MVC

启动Spring MVC 组件的方式也不仅是一种,这里我们使用基于Java的注解简单配置,使用@EnableWebMvc,这样就可以启动Spring MVC,但是这样还有几个问题要解决
  • 没有配置视图解析器
  • 没有启动组件扫描
  • DispatcherServlet会映射所有的请求包括对静态资源的请求,如图片和样式表。
所以我们还需要在这个最小的Spring MVC配置上在加一些内容。
/**Spring MVC配置
 * Created by Wung on 2016/8/29.
 * "@Configuration"这是一个配置类
 * "@EnableWebMvc"启用Spring MVC
 * "@ComponentScan"启动组件扫描(稍后我们会编写带有@Controller注解的控制器)
 */


@Configuration
@EnableWebMvc
@ComponentScan("spittr.web")
public class WebConfig extends WebMvcConfigurerAdapter{

    /**
     * 配置JSP视图解析器
     */

    @Bean
    public ViewResolver viewResolver(){
        InternalResourceViewResolver resolver =
                new InternalResourceViewResolver();
        resolver.setPrefix("/WEB-INF/views");//前缀
        resolver.setSuffix(".jsp");//后缀
        resolver.setExposeContextBeansAsAttributes(true);
        return resolver;
    }

    /**
     * 配置静态资源的处理
     * enable()方法要求DispatcherServlet将会静态的资源的请求转发到
     * Servlet容器默认的Servlet上而不是使用DispatcherServlet本身来处理此类请求
     */

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }
}

基本的控制器

在Spring MVC中,控制器只是在方法上添加@RequestMapping注解的类,这个注解声明了他们所要处理的请求。
/**声明为一个控制器带有@Contorller注解的类会被自动扫描声明为Spring应用上下文的bean
 * @author Wung
 * RequestMapping注解代表会处理对"/"发起的GET请求
 * 最后返回视图名为"home"
 */
@Controller
public class HomeController {

  @RequestMapping(value="/",method = GET)
  public String home(Model model) {
    return "home";
  }

}
除此之外我们还可以将@RequestMapping注解定义在类级别上,这样注解就可以应用到控制器的所有处理器上,同时可以在处理器方法上补充@RequestMapping。另外还有一点@RequestMapping中映射的路径可以匹配多个,也就是可以在他的value中写多个路径,比如@RequestMapping({"/","/home"})。

接受请求的输入

Spring MVC允许以多种方式将客户端中的数据传送到控制器的处理方法中,包括
  • 查询参数
  • 表单参数
  • 路径变量

处理查询参数

作为开始我们先看一下如何处理带有查询参数的请求,这也是客户端往服务器发送数据时,最简单和最直接的方式。
mockMvc.perform(get("/spittles?max=238900&count=50"))
      .andExpect(view().name("spittles"))
      .andExpect(model().attributeExists("spittleList"))
      .andExpect(model().attribute("spittleList", 
       hasItems(expectedSpittles.toArray())));
传入了max和count参数作为查询参数。请求中的查询参数是往控制器中传递信息的常用手段,另外一种方式也很流行尤其是在构建面向资源的控制器的时候,这种方式就是将传递参数作为直接作为请求路径的一部分。

路径参数

假设我们现在需要根据给定的ID来查找Spittle记录,其实一种方法是我们可以在编写控制器的时候,通过使用@RequestParam注解让他接受ID作为查询参数
@RequestMapping(value="/show",method=RequestMapping.GET)
public String showSpittle(@RequestParam("spittle_id") long spittleId,Model model){
	model.addAttribute(spittleRepository,findOne(spittleId));
	return "spittle";
}
这个处理器方法会处理形如"/spittles/show?spittle_id=123"这样的请求,这个可以正常工作,但是从面向资源的角度来讲并不理想。在理想的情况下,要识别的资源应该通过URL路径进行标示而不是通过查询参数,对"/spittles/123"发起GET请求要优于上面请求。为了实现这种路径变量,Spring MVC允许我们在@RequestMapping路径中添加占位符,在路径中的其他部分要与处理的请求完全匹配,但是占位符可以是任意的值。
@RequestMapping(value="/{spittleId}",method=RequestMapping.GET)
public String showSpittle(@RequestParam("spittle_id") long spittleId,Model model){
	model.addAttribute(spittleRepository,findOne(spittleId));
	return "spittle";
}
其中的@PathVariable("spittleId")注解表明在请求路径中,不管占位符的值是什么都会传递到处理器方法的spittleId参数中,假设对"/splittles/12345"发送GET请求,那么就会将12345传递进来并作为spittleId的值。需要注意的是如果方法的参数名和占位符的名称相同的话,可以去掉@PathVariable中的value属性。

处理表单

Web应用的功能通常不局限于为用户推送内容,大多数的应用允许用户填充表单并将数据提交回应用中,通过这种方式实现于用户的交互,Spring MVC的控制器也为表单处理提供了良好的支持。

结束

Spring有一个强大灵活的Web框架,借助于注解,Spring MVC提供了近似于POJO的开发模式,这使得开发处理请求的控制器变得非常简单同时也容易测试。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值