《spring实战》学习笔记-第五章:构建springweb应用程序

     2022-03-26     495

关键词:

5.1 Spring MVC起步

5.1.1 跟踪Spring MVC的请求
  每当用户在Web浏览器中点击链接或提交表单的时候,请求就开始工作了。对请求的工作描述就像是快递投送员。与邮局投递员或FedEx投送员一样,请求会将信息从一个地方带到另 一个地方。

技术分享图片

  在请求离开浏览器时 ①,会带有用户所请求内容的信息,例如用户提交的表单信息。
  请求旅程的第一站是Spring的前端控制器DispatcherServlet,在这里一个单实例的Servlet将请求委托给应用程序的其他组件来执行实际的处理。
DispatcherServlet的任务是将请求发送给Spring MVC控制器(controller)。DispatcherServlet会查询一个或多个处理器映射(handler mapping)② 来确定请求应该将请求发送给哪个控制器。处理器映射会根据请求所携带的URL信息来进行决策。
  一旦选择了合适的控制器,DispatcherServlet会将请求发送给选中的控制器 。到了控制器③,请求会卸下其负载(用户提交的信息)并耐心等待控制器处理这些信息。(实际上, 设计良好的控制器本身只处理很少甚至不处理工作,而是将业务逻辑委托给一个或多个服务 对象进行处理。)
  控制器在完成逻辑处理后,通常会产生一些信息,这些信息需要返回给用户并在浏览器上显 示。这些信息被称为模型(model)。不过仅仅给用户返回原始的信息是不够的——这些信 息需要以用户友好的方式进行格式化,一般会是HTML。所以,信息需要发送给一个视图 (view),通常会是JSP。
  控制器所做的最后一件事就是将模型数据打包,并且标示出用于渲染输出的视图名。它接下来会将请求连同模型和视图名发送回DispatcherServlet④ 。
  这样,控制器就不会与特定的视图相耦合,传递给DispatcherServlet的视图名并不直接表示某个特定的JSP。实际上,它甚至并不能确定视图就是JSP。相反,它仅仅传递了一个 逻辑名称,这个名字将会用来查找产生结果的真正视图。DispatcherServlet将会使用视图解析器(view resolver) ⑤来将逻辑视图名匹配为一个特定的视图实现,它可能是也可能 不是JSP。 既然DispatcherServlet已经知道由哪个视图渲染结果,那请求的任务基本上也就完成 了。它的最后一站是视图的实现(可能是JSP)⑥ ,在这里它交付模型数据。请求的任务就完 成了。视图将使用模型数据渲染输出,这个输出会通过响应对象传递给客户端(不会像听上 去那样硬编码)⑦ 。
  可以看到,请求要经过很多的步骤,最终才能形成返回给客户端的响应。大多数的步骤都是 在Spring框架内部完成的,也就是图5.1所示的组件中。尽管本章的主要内容都关注于如何编 写控制器,但在此之前我们首先看一下如何搭建Spring MVC的基础组件。

5.1.2 搭建Spring MVC
  基于图5.1,看上去我们需要配置很多的组成部分。幸好,借助于最近几个Spring新版本的功能增强,开始使用Spring MVC变得非常简单了。现在,我们要使用最简单的方式来配置 Spring MVC:所要实现的功能仅限于运行我们所创建的控制器。在第7章中,我们会看一些其他的配置选项。
配置DispatcherServlet
  DispatcherServlet是Spring MVC的核心。在这里请求会第一次接触到框架,它要负责将请求路由到其他的组件之中。 按照传统的方式,像DispatcherServlet这样的Servlet会配置在web.xml文件中,这个文件会放到应用的WAR包里面。当然,这是配置DispatcherServlet的方法之一。

  但是, 借助于Servlet 3规范和Spring 3.1的功能增强,这种方式已经不是唯一的方案了,这也不是我们本章所使用的配置方法。 我们会使用Java将DispatcherServlet配置在Servlet容器中,而不会再使用web.xml文件。如下的程序清单展示了所需的Java类。
程序清单5.1 配置DispatcherServlet

package spittr.config;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class SpittrWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
    @Override
    protected String[] getServletMappings() {
        return new String[] { "/" };//将DispatcherServlet映射到"/"
    }
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class<?>[] { RootConfig.class };
    }
    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[] { WebConfig.class };//指定配置类
    }
}

  在程序清单5.1中,SpittrWebAppInitializer重写了三个方法。
  第一个方法是getServletMappings(),它会将一个或多个路径映射到DispatcherServlet上。在本例中,它映射的是“/”,这表示它会是应用的默认Servlet。它会处理进入应用的所有请求。
  为了理解其他的两个方法,我们首先要理解DispatcherServlet和一个Servlet监听器(也就是ContextLoaderListener)的关系。
  当DispatcherServlet启动的时候,它会创建Spring应用上下文,并加载配置文件或配置类中所声明的bean。在程序清单5.1的getServletConfigClasses()方法中,我们要求DispatcherServlet加载应用上下文时,使用定义在WebConfig配置类(使用Java配置)中的bean。
  但是在Spring Web应用中,通常还会有另外一个应用上下文。另外的这个应用上下文是由ContextLoaderListener创建的。
  我们希望DispatcherServlet加载包含Web组件的bean,如控制器、视图解析器以及处理器映射,而ContextLoaderListener要加载应用中的其他bean。这些bean通常是驱动应后端的中间层和数据层组件。
  实际上,AbstractAnnotationConfigDispatcherServletInitializer会同时创建DispatcherServlet和ContextLoaderListener。GetServletConfigClasses()方法返回的带有@Configuration注解的类将会用来定义DispatcherServlet应用上下文中的bean。getRootConfigClasses()方法返回的带有@Configuration注解的类将会用来配置ContextLoaderListener创建的应用上下文中的bean。
启用Spring MVC
  正如有多种方式可以配置DispatcherServlet,激活Spring MVC组件也有不止一种方法。一般的,都会通过XML配置文件的方式来配置Spring,例如可以通过<mvc:annotation-driven>来激活基于注解的Spring MVC。
  我们所能创建的最简单的Spring MVC配置就是一个带有@EnableWebMvc注解的类:

package spittr.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
 
@Configuration
@EnableWebMvc
public class WebConfig {
}

  这可以运行起来,它的确能够启用Spring MVC,但还有不少问题要解决:
  ● 没有配置视图解析器。如果这样的话,Spring默认会使用BeanNameView-Resolver,这个视图解析器会查找ID与视图名称匹配的bean,并且查找的bean要实现View接口,它以这样的方式来解析视图。
  ● 没有启用组件扫描。这样的结果就是,Spring只能找到显式声明在配置类中的控制器。
  ● 这样配置的话,DispatcherServlet会映射为应用的默认Servlet,所以它会处理所有的请求,包括对静态资源的请求,如图片和样式表(在大多数情况下,这可能并不是你想要的效果)。
  因此,我们需要在WebConfig这个最小的Spring MVC配置上再加一些内容,从而让它变得真正有用。如下程序清单中的WebConfig解决了上面所述的问题。
程序清单5.2 最小但可用的Spring MVC配置

package spittr.config;
 
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
 
@Configuration
@EnableWebMvc   //启动spring mvc
@ComponentScan("spitter.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;
    }
 
    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();//配置静态资源的处理
    }
}

  在程序清单5.2中第一件需要注意的事情是WebConfig现在添加了@ComponentScan注解,因此将会扫描spitter.web包来查找组件。稍后你就会看到,我们所编写的控制器将会带有@Controller注解,这会使其成为组件扫描时的候选bean。因此,我们不需要在配置类中显式声明任何的控制器。
  接下来,我们添加了一个ViewResolver bean。更具体来讲,是InternalResourceViewResolver。我们将会在第6章更为详细地讨论视图解析器。我们只需要知道它会查找JSP文件,在查找的时候,它会在视图名称上加一个特定的前缀和后缀(例如,名为home的视图将会解析为WEB-INFviews/home.jsp)。
  最后,新的WebConfig类还扩展了WebMvcConfigurerAdapter并重写了其configureDefaultServletHandling()方法。通过调用DefaultServletHandlerConfigurer的enable()方法,我们要求DispatcherServlet将对静态资源的请求转发到Servlet容器中默认的Servlet上,而不是使用DispatcherServlet本身来处理此类请求
  WebConfig已经就绪,那RootConfig呢?因为本章聚焦于Web开发,而Web相关的配置通过DispatcherServlet创建的应用上下文都已经配置好了,因此现在的RootConfig相对很简单:

package spittr.config;
 
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
 
@Configuration
@ComponentScan(basePackages = { "spitter" }, excludeFilters = {
        @Filter(type = FilterType.ANNOTATION, value = EnableWebMvc.class) })
public class RootConfig {
}

  唯一需要注意的是RootConfig使用了@ComponentScan注解。这样的话,在本书中,我们就有很多机会用非Web的组件来充实完善RootConfig。
5.1.3 Spittr应用介绍
  这一章要用的例子应用,从Twitter获取了一些灵感,因此最开始叫Spitter;然后又借鉴了最近比较流行的网站Flickr,因此我们也把e去掉,最终形成Spittr这个名字。这也有利于区分领域名称(类似于twitter,这里用spring实现,因此叫spitter)和应用名称。
  Spittr类似于Twitter,用户可以通过它添加一些推文。Spittr有两个重要的概念:spitter(应用的用户)和spittle(用户发布简单状态)。本章将会构建该应用的web层、创建用于展示spittle的控制器以及用户注册流程。

5.2 编写基本的控制器

  在Spring MVC中,控制器只是方法上添加了@RequestMapping注解的类,这个注解声明了它们所要处理的请求。
  开始的时候,我们尽可能简单,假设控制器类要处理对“/”的请求,并渲染应用的首页。程序清单5.3所示的HomeController可能是最简单的Spring MVC控制器类了。

package spittr.web;
 
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
 
@Controller // 声明一个控制器
public class HomeController {
 
    @RequestMapping(value = "/", method = RequestMethod.GET) // 处理GET请求
    public String home() {
        return "home";
    }
 
}

  你可能注意到的第一件事情就是HomeController带有@Controller注解。很显然这个注解是用来声明控制器的,但实际上这个注解对Spring MVC本身的影响并不大。            HomeController是一个构造型(stereotype)的注解,它基于@Component注解。在这里,它的目的就是辅助实现组件扫描。因为HomeController带有@Controller注解,因此组件扫描器会自动找到HomeController,并将其声明为Spring应用上下文中的一个bean。
  其实,你也可以让HomeController带有@Component注解,它所实现的效果是一样的,但是在表意性上可能会差一些,无法确定HomeController是什么组件类型。
  HomeController唯一的一个方法,也就是home()方法,带有@RequestMapping注解。它的value属性指定了这个方法所要处理的请求路径,method属性细化了它所处理的HTTP方法。在本例中,当收到对“/”的HTTP GET请求时,就会调用home()方法。
  你可以看到,home()方法其实并没有做太多的事情:它返回了一个String类型的“home”。这个String将会被Spring MVC解读为要渲染的视图名称。DispatcherServlet会要求视图解析器将这个逻辑名称解析为实际的视图。
  鉴于我们配置InternalResourceViewResolver的方式,视图名“home”将会解析为“WEB-INFviews/home.jsp”路径的JSP。现在,我们会让Spittr应用的首页相当简单,如下所示。
程序清单5.4 Spittr应用的首页,定义为一个简单的JSP

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
 
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
 
<html>
<head>
<meta charset="utf-8">
<title>Spittr</title>
<link rel="stylesheet" type="text/css"
    href="<c:url value="/resources/style.css" />">
</head>
<body>
    <h1>Welcome to Spittr</h1>
    <a href="<c:url value="/spittles" />">Spittles</a> |
    <a href="<c:url value="/spitter/register" />">Register</a>
</body>
</html>

  这个JSP并没有太多需要注意的地方。它只是欢迎应用的用户,并提供了两个链接:一个是查看Spittle列表,另一个是在应用中进行注册。下图展现了此时的首页是什么样子的。

技术分享图片

5.2.1 测试控制器
  下面程序阐述了如何测试Spring MVC的控制器。
程序清单5.5 改进HomeControllerTest

package spittr.web;
 
import org.junit.Test;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
 
public class HomeControllerTest {
 
    @Test
    public void testHomePage() throws Exception {
        HomeController controller = new HomeController();
        // 设置MockMvc
        MockMvc mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
        mockMvc.perform(MockMvcRequestBuilders.get("/")).andExpect(MockMvcResultMatchers.view().name("home"));
    }
 
}

  它首先传递一个HomeController实例到MockMvcBuilders.standaloneSetup()并调用build()来构建MockMvc实例。然后它使用MockMvc实例来执行针对“/”的GET请求并设置期望得到的视图名称。
5.2.2 定义类级别的请求处理
  我们可以做的一件事就是拆分@RequestMapping,并将其路径映射部分放到类级别上。程序清单5.7展示了这个过程。
程序清单5.7 拆分HomeController中的@RequestMapping

package spittr.web;
 
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
 
@Controller // 声明一个控制器
@RequestMapping("/") // 控制器匹配路径
public class HomeController {
 
    @RequestMapping(method = RequestMethod.GET) // 处理GET请求
    public String home() {
        return "home";// 视图名称
    }
 
}

  在这个新版本的HomeController中,路径现在被转移到类级别的@RequestMapping上,而HTTP方法依然映射在方法级别上。当控制器在类级别上添加@RequestMapping注解时,这个注解会应用到控制器的所有处理器方法上。处理器方法上的@RequestMapping注解会对类级别上的@RequestMapping的声明进行补充。
  就HomeController而言,这里只有一个控制器方法。与类级别的@RequestMapping合并之后,这个方法的@RequestMapping表明home()将会处理对“/”路径的GET请求。
换言之,我们其实没有改变任何功能,只是将一些代码换了个地方,但是HomeController所做的事情和以前是一样的。因为我们现在有了测试,所以可以确保在这个过程中,没有对原有的功能造成破坏。
  当我们在修改@RequestMapping时,还可以对HomeController做另外一个变更。@RequestMapping的value属性能够接受一个String类型的数组。到目前为止,我们给它设置的都是一个String类型的“/”。但是,我们还可以将它映射到对“/homepage”的请求,只需将类级别的@RequestMapping改为如下所示:

@RequestMapping("/","homepage") // 控制器匹配路径
public class HomeController {
//......
}

  现在,HomeController的home()方法能够映射到对“”和“homepage”的GET请求。

  本文代码链接:https://github.com/Gugibv/spring/tree/master/spittr

 

spring实战第四版第五章pom.xml

<projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0http://maven.apache.org/maven-v4_0_0.xsd"& 查看详情

安卓权威编程指南-第五章学习笔记(两个activity)

学习安卓编程权威指南第五章的时候自己写了个简单的Demo来加深理解两个Activity互相传递数据的问题,然后将自己的学习笔记贴上来,如有错误还请指正。 IntentActivityDemo学习笔记题目:ActivityA登录界面(用户名、密码、登陆... 查看详情

构建之法第五章学习

今天我学习了《构建之法》第五章团队和流程。首先我了解了写了再改模式(Code-and-Fix)史蒂夫·迈克康奈尔(SteveMcConnell)在这里提到了不少开发流程。第一个提到的开发流程。这个流程也有好处,不需要太多其他准备或相关... 查看详情

javascript高级程序设计学习笔记第五章上

 第五章 引用类型的值(对象)是引用类型的一个实例。在ECMAScript中,引用类型是一种数据结构,用于将数据和功能组织在一起。它也常被称为类,但这种称呼并不妥当。尽管ECMAScript从技术上讲是一门面向对象的语言,但它不具... 查看详情

《构建之法》第五章读书笔记

第5章团队和流程 一、非团队和团队团队的共同特点:1、团队有一致的集体目标,团队要一起完成这目标。一个团队的成员不一定要同时工作,例如接力跑。2、团队成员有各自的分工,互相依赖合作,共同完成任务。二、软件... 查看详情

构建之法学习(第五章团队和流程)

第五章团队和流程 本章主要讲了一些典型的软件团队模式和开发流程以及它们的优缺点1.团队的共同特点:   —应该有一致的集体目标,团队要一起完成这目标   —团队成员有各自的分工,互相依赖合作,共同完... 查看详情

java第五章循环结构学习笔记

1.for语法:  for(1参数初始化,2,条件,3,参数叠加){4.循环操作}2.break:改变程序的控制流,多用于循环中,结合条件机构和分支结构一起使用。  作用,跳出整个循环; continue:只能用于循环中,跳过循环中剩... 查看详情

《python深度学习》第五章-1(cnn简介)读书笔记(代码片段)

第五章深度学习用于计算机视觉5.1 卷积神经网络简介5.1.1卷积神经网络对MNIST分类使用卷积神经网络对MNIST数字进行分类,在第2章用密集连接网络做过(当时的测试精度为97.8%)。它是Conv2D层和MaxPooling2D层的堆叠。实... 查看详情

神经网络和深度学习笔记-第五章深度神经网络学习过程中的梯度消失问题

之前的章节,我们利用一个仅包含一层隐藏层的简单神经网络就在MNIST识别问题上获得了98%左右的准确率。我们于是本能会想到用更多的隐藏层,构建更复杂的神经网络将会为我们带来更好的结果。就如同在进行图像模式识别的... 查看详情

第五章笔记

...                  循环结构(一)学习本章会用到的单词:while:循环,当...的时候do:做,执行,干index:索引,指标,指出bug:漏洞,缺陷,计算机程序中的故障debug:调试,除错,改正有毛病的部分equa... 查看详情

增强学习笔记第五章蒙特卡洛方法

5.1蒙特卡洛预测分为两种:First-VisitMC和Every-VisitMC,前者用的更多。后者用于函数近似和EligibilityTraces5.2蒙特卡洛评估actionvalue如果没有模型(即不知道每个a会得到什么样的s),则应该使用actionvalue而不是statevalue5.3蒙特卡洛控制... 查看详情

《python深度学习》第五章-6(可视化类激活图)读书笔记(代码片段)

《Python深度学习》第五章-6(可视化类激活图)读书笔记卷积神经网络学到的表示非常适合可视化,很大程度上是因为它们是视觉概念的表示\\colorred视觉概念的表示视觉概念的表示。接下来介绍3种可视化方法。事中\\... 查看详情

第五章笔记

循环结构(一)学习本章会用到的单词:while:循环,当...的时候do:做,执行,干index:索引,指标,指出bug:漏洞,缺陷,计算机程序中的故障debug:调试,除错,改正有毛病的部分equal:等于,相等step:步骤,一步error:误... 查看详情

《domscripting》学习笔记-——第五章第六章案列改进

第四章的案例代码可以得到更好的改进。例如:预留退路、向后兼容性和分离js。原html代码:1<!DOCTYPEhtml>2<htmllang="en">3<head>4<metacharset="UTF-8">5<title>Document</title>6</head>7<body>8<h1& 查看详情

o'reill的svg精髓(第二版)学习笔记——第五章

第五章文档结构 5.1结构与表现XML的目标之一便是提供一种能将结构从视觉表示中独立出来的方法。但是不幸的是,关于XML的很多讨论都强调结构而非表现。我们将通过详细讨论如何在SVG中指定表现来纠正这一错误。5.2在SVG中... 查看详情

《python深度学习》第五章-3(预训练)读书笔记(代码片段)

5.3 使用预训练的卷积神经网络预训练网络(pretrainednetwork):是一个之前已在大型数据集(通常是大规模图像分类任务)上训练好、保存好的网络\\colorred训练好、保存好的网络训练好、保存好的网络。预训... 查看详情

tensorflow实战google深度学习框架第五章5.2.1minister数字识别源代码(代码片段)

1importos2importtab3importtensorflowastf45print"tensorflow5.2"67fromtensorflow.examples.tutorials.mnistimportinput_data89‘‘‘10mnist=input_data.read_data_sets("/asky/tensorflow/mnist_data",one_hot=True 查看详情

前方高能预警!阿里大佬出品“spring实战学习笔记”震撼来袭

Spring框架已经成为Java开发人员的必备知识,而且Spring3引入了强大的新特性,例如SpEL、Spring表达式语言、loC容器的新注解以及用户急需的对REST的支持。无论你是刚刚接触Spring还是被Spring3.0的新特性所吸引,这份笔记... 查看详情