1 SpringMVC详解(起步)

Spring MVC基于模型-视图-控制器(Model-View-Controller,MVC)模式实现,它能够帮你构建像Spring框架那样灵活和松耦合的Web应用程序。

1 跟踪Spring MVC请求

  1. 在请求离开浏览器时,会带有用户所请求内容的信息,至少会包含请求的URL。请求旅程的第一站是DispatcherServlet。DispatcherServlet就是前端控制器。
  2. DispatcherServlet以会查询一个或多个处理器映射(handler mapping) 来确定请求的下一站在哪里。
  3. 一旦选择了合适的控制器,DispatcherServlet会将请求发送给选中的控制器 。到了控制器,请求会卸下其负载(用户提交的信息)并耐心等待控制器处理这些信息。
  4. 控制器在完成逻辑处理后,通常会产生一些信息,这些信息需要返回给用户并在浏览器上显示。这些信息被称为模型(model)。不过仅仅给用户返回原始的信息是不够的——这些信息需要以用户友好的方式进行格式化。所以,信息需要发送给一个视图(view),通常会是JSP。控制器所做的最后一件事就是将模型数据打包,并且标示出用于渲染输出的视图名。它接下来会将请求连同模型和视图名发送回DispatcherServlet。传递给DispatcherServlet的视图名并不直接表示某个特定的JSP。实际上,它甚至并不能确定视图就是JSP。相反,它仅仅传递了一个逻辑名称,这个名字将会用来查找产生结果的真正视图。
  5. DispatcherServlet将会使用视图解析器(view resolver) 来将逻辑视图名匹配为一个特定的视图实现,它可能是也可能不是JSP。
  6. 既然DispatcherServlet已经知道由哪个视图渲染结果,那请求的任务基本上也就完成了。它的最后一站是视图的实现(可能是JSP)。
  7. 在这里它交付模型数据。请求的任务就完成了。视图将使用模型数据渲染输出,这个输出会通过响应对象传递给客户端。

2 通过注解搭建SpringMVC

2.1 配置DispatcherServlet

我们会使用Java将DispatcherServlet配置在Servlet容器中,而不会再使用web.xml文件。如下的程序清单展示了所需的Java类。

public class SpitterWebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

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

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

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

}

要理解程序清单5.1是如何工作的,我们可能只需要知道扩展AbstractAnnotationConfigDispatcherServletInitializer的任意类都会自动地配置DispatcherServlet和Spring应用上下文,Spring的应用上下文会位于应用程序的Servlet上下文之中。当部署到Servlet 3.0容器中的时候,容器会自动发现它,并用它来配置Servlet上下文。

配置DispatcherServlet包括三个方法:

getServletMappings():对哪些请求进行拦截。在本例中,它映射的是“/”,这表示它会是应用的默认Servlet。它会处理进入应用的所有请求。

getServletConfigClasses():”当DispatcherServlet启动的时候,它会创建Spring应用上下文,并加载配置文件或配置类中所声明的bean。在程序清单5.1的getServletConfigClasses()方法中,我们要求DispatcherServlet加载应用上下文时,使用定义在WebConfig配置类(使用Java配置)中的bean。如控制器、视图解析器以及处理器映射。

getRootConfigClasses():我们希望DispatcherServlet加载包含Web组件的bean,如控制器、视图解析器以及处理器映射,而ContextLoaderListener要加载应用中的其他bean。这些bean通常是驱动应用后端的中间层和数据层组件。getRootConfigClasses()方法返回的带有@Configuration注解的类将会用来配置ContextLoaderListener创建的应用上下文中的bean。

在本例中,根配置定义在RootConfig中,DispatcherServlet的配置声明  在WebConfig中。稍后我们将会看到这两个类的内容。

2.2 启用SpringMVC

我们需要配置控制器,处理器映射器,视图解析器

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

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

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

}

(1)一个带有@EnableWebMvc注解的类,能够启用Spring MVC。

(2)没有启用组件扫描。这样的结果就是,Spring只能找到显式声明在配置类中的控制器。因为getServletClasses中配置的是WebConfig.

(3)viewResolver()是视图解析器,它会查找JSP文件,在查找的时候,它会在视图名称上加一个特定的前缀和后缀

(4)configureDefaultServletHandling()会把DispatcherServlet将对静态资源的请求转发到Servlet容器中默认的Servlet上,而不是使用DispatcherServlet本身来处理此类请求。

2.3 配置控制器

@Controller
@RequestMapping("/")
public class HomeController {

    @RequestMapping(method = GET)
    public String home() {
        return "home";
    }

}

(1)@Controller注解。很显然这个注   解是用来声明控制器的。

(2)HomeController唯一的一个方法,也就是home()方法,带有@RequestMapping注解。它的value属性指定了这个方法所要处理的请求路径

(3)home()方法其实并没有做太多的事情:它返回了一个String类型的“home”。这个String将会被Spring MVC解读为要渲染的视图名称。

2.4 测试控制器

public class HomeControllerTest {

    @Test
    public void testHomePage() throws Exception {
        HomeController controller = new HomeController();
      //  assertEquals("home", controller.home());
        MockMvc mockMvc = standaloneSetup(controller).build();
        mockMvc.perform(get("/"))
                .andExpect(view().name("home"));
    }

}

从Spring 3.2开始,我们可以按照控制器的方式来测试Spring MVC中的控制器了,而不仅仅是作为POJO进行测试。Spring现在包含了一种mock Spring MVC并针对控制器执行HTTP请求的机制。这样的话,在测试控制器的时候,就没有必要再启动Web服务器和Web浏览器了。

3 传递模型到视图中

本节使用两个单元测试框架Mockito和mockMVC,下面简要说明一下这两个框架

3.1 Mockito

Mockito是一个模拟框架,基于JAVA的库,用于JAVA应用程序的有效单元测试. Mockito用于模拟接口,以便可以将虚拟功能添加到可用于单元测试的模拟接口。通俗的讲,创建一个替身,替身可以做主角(真实的对象)的动作。

Stubbing大概就是占坑的代码,桩代码给出的实现是临时性的/待编辑的。它使得程序在结构上能够符合标准,又能够使程序员可以暂时不编辑这段代码。通俗的讲,替身做假动作,并验证结果。

3.3.1 创建替身

三种方式

/***
 * How to mock class by Mockito
 * <ul>
 *     <li>use runner</li>
 *     <li>use annotation</li>
 *     <li>use rule</li>
 * </ul>
 */

3.3.2 替身做动作(Stubbing)

  • 对方法设定返回值

when(i.next()).thenReturn("Hello")

  • 对方法设定返回异常

when(i.next()).thenThrow(new RuntimeException())

  • Mockito支持迭代风格的返回值设定

第一种方式
when(i.next()).thenReturn("Hello").thenReturn("World")
第二种方式
when(i.next()).thenReturn("Hello", "World")
上面的设定相当于:
when(i.next()).thenReturn("Hello")
when(i.next()).thenReturn("World")
第一次调用i.next()将返回”Hello”,第二次的调用会返回”World”。

  • Stubbing的另一种语法

doReturn(Object) 设置返回值
doReturn("Hello").when(i).next();
迭代风格
doReturn("Hello").doReturn("World").when(i).next();
返回值的次序为从左至右,第一次调用返回”Hello”,第二次返回”World”。
doThrow(Throwable) 设置返回异常
doThrow(new RuntimeException()).when(i).next();
因为这种语法的可读性不如前者,所以能使用前者的情况下尽量使用前者,当然在后面要介绍的Spy除外。

  • 对void方法进行方法预期设定

void方法的模拟不支持when(mock.someMethod()).thenReturn(value)这样的语法,只支持下面的方式:
doNothing() 模拟不做任何返回(mock对象void方法的默认返回)
doNothing().when(i).remove();
doThrow(Throwable) 模拟返回异常
doThrow(new RuntimeException()).when(i).remove();

3.2 mockMVC

3.2.1 创建mockMVC对象

我们创建的HelloController就可以在@Before函数中创建并传递到MockMvcBuilders.standaloneSetup()函数中,如:

private MockMvc mvc = MockMvcBuilders.standaloneSetUp(new HelloController()).build();

3.2.2 MockMvc方法解析

perform:执行一个RequestBuilder请求,会自动执行SpringMvc的流程并映射到相应的控制器执行处理
get:声明发送一个get请求方法。MockHttpServletRequestBuilder get(Sring urlTemple, Object… urlVariables):根据url模板和url变量值得到一个GET请求方式的。另外提供了其他的请求方法,如:POST、PUT、DELETE等
param:添加request的参数,如上面发送请求的时候戴上了pcode=root的参数。假如使用需要发送json数据格式的时候将不能使用这种方式。
andExpect:添加ResultMatcher验证规则,验证控制器执行完成后结果是否正确(对返回的数据进行的判断)
andDo:添加ResultHandler结果处理器,比如调试时打印结果到控制台(对返回的数据进行的判断)
andReturn:最后返回相应的MvcResult:然后进行自定义验证/进行下一步异常处理(对返回的数据进行的判断)
如:

@Test
public void getHello() throws Exception{
    mvc.perform(MockMvcRequestBuilders.get("/hello").accept(MediaType.APPLICATION_JSON))
            .andExpect(status().isOk())
            .andExpect(content().string(equalTo("Hello World")));
}

3.3 将数据添加到模型中

Model实际上就是一个Map(也就是key-value对的集合),它会传递给视图,这样数据就能渲染到客户端了。当调用addAttribute()方法并且不指定key的时候,那么key会根据值的对象类型推断确定。在本例中,因为它是一个List<Spittle>,因此,键将会推断 为spittleList。

   @RequestMapping(method = RequestMethod.GET)
    public String spittle(Model model) {
        model.addAttribute(spittleRepository.findSpittles(
                Long.MAX_VALUE,20));
        return "spittles";
    }

如果你希望使用非Spring类型的话,那么可以用java.util.Map来代替Model。下面这个版本的spittles()方法与之前的版本在功能上是一样的:

 现在,数据已经放到了模型中,在JSP中该如何访问它呢?实际上,当视图是JSP的时候,模型数据会作为请求属性放到请求(request)之中。因此,在spittles.jsp文件中可以使用JSTL(JavaServer Pages Standard Tag Library)的<c:forEach>标签渲染spittle列表:

 <c:forEach items="${spittleList}" var="spittle" >
     <li id="spittle_<c:out value="spittle.id"/>">
         <div class="spittleMessage"><c:out value="${spittle.message}" /></div>
         <div>
            <span class="spittleTime"><c:out value="${spittle.time}" /></span>
            <span class="spittleLocation">(<c:out value="${spittle.latitude}" />, <c:out value="${spittle.longitude}" />)</span>
         </div>
     </li>
</c:forEach>

4 接受请求的输入

Spring MVC允许以多种方式将客户端中的数据传送到控制器的处理器方法中,包括:

查询参数(Query Parameter)。

路径参数(Path Variable)。

表单参数(Form Parameter)。

4.1 查询参数

这个测试方法与程序清单5.9中的测试方法关键区别在于它针对“/spittles”发送GET请求,同时还传入了max和count参数。

@RequestParam注解表示参数接收传递过来的值,并且defaultValue属性可以给参数默认值:

4.2 路径参数

这个测试中最重要的部分是最后几行,它对“spittles12345”发起GET请求,然后断言视图的名称是spittle,并且预期的Spittle对象放到了模型之中。

在我们编写的控制器中,所有的方法都映射到了(通过@RequestMapping)   静态定义好的路径上。但是,如果想让这个测试通过的话,我们编写的@RequestMapping  要包含变措部分,这部分代表了Spittle ID。

为了实现这种路径变措,Spring MVC允许我们在@RequestMapping路径中添加占位符。占位符的名称要用大括号(“{”和“}”)括起来。路径中的其他部分要与所处理的请求完全匹 配,但是占位符部分可以是任意的值。

4.3 表单参数

4.3.1 对象接收表单数据

模拟表单的提交

Spitter对象作为参数。这个对象有firstName、lastName、username和password属性,这些属性将会使用请求中同名   的参数进行填充。

视图格式中的“redirect:”前缀时,它就知道要将其解析为重定向的规则,而不是视图的名称。在本例中,它将会重定向到用户基本信息的页面。例如,如果Spitter.username属性的值为“jbauer”,那么视图将会重定向到“spitter/jbauer”。

4.3.2 表单数据校验

在接收对象上添加注解,可以校验表单参数的正确性。更多的校验注解,可以查看相关资料。

Spitter的所有属性都添加了@NotNull注解,以确保它们的值不为null。类似 地,属性上也添加了@Size注解以限制它们的长度在最大值和最小值之间。对Spittr应用   来说,这意味着用户必须要填完注册表单,并且值的长度要在给定的范围内。

(1)@Valid注解所标注的就是要检验的参数。

(2)如果有校验出现错误的话,那么这些错误可以通过Errors对象进行访问。

(3)processRegistration()方法所做的第一件事就是调用Errors.hasErrors() 来检查是否有错误。如果有错误的话,Errors.hasErrors()将会返回到registerForm,也就是注册表单的视图。 

 

发布了146 篇原创文章 · 获赞 91 · 访问量 13万+

猜你喜欢

转载自blog.csdn.net/A1342772/article/details/104097419