2

构建Spring Web应用程序

  1. 映射请求到Spring控制器
  2. 透明地绑定表单参数
  3. 校验表单提交

Spring MVC 基于模型-视图-控制器(Model-View-Controller,MVC)模式实现,帮助构建灵活和松耦合的Web应用程序

Spring MVC起步

Spring MVC 请求流程

Spring MVC框架将请求在调度Servler、处理器映射(handler mapping)、控制器以及视图解析器(view resolver)之间移动

clipboard.png

1:请求访问前端控制器(DispatcherServlet);
2:DispatcherServlet查询一个或多个处理器映射(handler mapping),通过请求路径确定控制器(Controller);
3:DispatcherServlet将请求发送给控制器(Controller);
4:控制器对请求进行处理返回模型和视图名(ModelAndView);
5:DispatcherServlet查询视图解析器(view resolver),通过视图名匹配一个特定的视图实现;
6:DispatcherServlet将模型发送给视图;
7:视图通过模型数据渲染输出,通过响应对象传递给客户端;

搭建Spring MVC

配置DisPatcherServlet

DisPatcherServlet是Spring MVC的核心。负责将请求路由到指定控制器处理。
传统方式,DispathcherServlet会配置在web.xml文件中;
借助于Servlet 3规范和Spring3.1功能增强,可实现Java配置DispatcherServlet

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

public class DemoWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected String[] getServletMappings() {
        return new String[]{ "/" };                 //将DispatcherServlet 映射到 "/"
    }

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class<?>[]{ RootConfig.class };   //指定Spring配置类
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[]{ WebConfig.class };    //指定Spring MVC配置类
    }
}

原理:
1:在Serlvet 3环境中,Servlet容器会在类路径中查找实现javax.servlet.ServletContainerInitializer接口的类,用来配置Servlet容器;
2:Spring通过SpringServletContainerInitializer实现了该接口,SpringServletContainerInitializer又会在容器中查找实现WebApplicationInitializer接口的类并将配置任务交给它们来完成;
3:Spring3.2引入一个便利的WebApplicationInitializer基础实现,即AbstractAnnotationConfigDispatcherServletIni-tializer,通过继承该类,当部署到Servler3.0容器中时,容器就会自动发现它,并用它来配置Servlet上下文。

继承AbstractAnnotationConfigDispatcherServletInitializer需重写三个方法:
1:getServletMappings() 设置DispatcherServlet映射、

在讲另外两个方法之前,先了解一下Dispatcher和一个Servler监听器(ContextLoaderListener)的关系
关于两个应用上下文的关系
DispatcherServlet启动的时候,会创建Spring应用上下文容器,并加载配置文件或配置类中所声明的bean。
Spring Web应用中,通常还有会另外一个应用上下文,由ContextLoaderListener创建。

我们通常希望DispathcerServlet加载包含Web组件的bean,如控制器、视图解析器以及处理器映射。
而ContextLoaderListener加载应用中其他bean,这些bean通常是加载驱动应用后端的中间层和数据层组件。

2:getServletConfigClasses() 返回@Configuration注解类并定义DispatcherServlet应用上下文中的bean。
3:getRootConfigClasses() 返回@Configuration注解类并定义ContextLoaderListener应用上下文中的bean。

启用Spring MVC

完成配置DispatcherServlet后,需启用Spring MVC。

传统方式:使用XML配置,使用<mvn:annotation-driven>启用注解驱动的Spring MVC
使用Java配置:@EnableWebMvc注解 启用Spring MVC

package demo;
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "demo.web")    // 控制器、视图解析器以及处理器映射。
public void WebConfig extends WebMvcConfigurerAdapter {
    @Bean
    public ViewResolver viewResolver () {    //设置视图解析器
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();
        resolver.setPrefix("/WEB-INF/views/");
        resolver.setSuffix(".jsp");
        resolver.setExposeContextBeansAsAttributes(true);
        return resolver;
    }
    
    @Override    //继承自WebMvcConfigurerAdapter 用于配置静态资源的处理
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }
}

package demo;
@Configuration
@ComponentScan(basePackages = "demo",    // 加载驱动应用后端的中间层和数据层组件
    excludeFilters = {
        @Filter(type = FilterType.ANNOTATION, value = EnableWebMvc.class)
    }
)
public class RootConfig {

}

编写基本的控制器(Controller)

package demo.web;

@Controller    //声明为一个控制器,与@Component作用相同,语义不同,更具有可读性
public class HomeController {
    
    @RequestMapping(value = "/", method = RequestMethod.GET)     // 处理对"/"的GET请求
    public String home() {
        return "home";    //视图名为home
    }
}

基于以上配置,我们可以使用一个简单的Spring MVC访问页面,
为助于读者理解,提供一份开发部署流程,仅供参考:
1 配置DispatcherServlet,编写DemoWebAppInitializer(src.main.java.demo包)
2 开启Spring MVC,编写WebConfig 和 RootConfig (src.main.java.demo包)
3 编写控制器,HomeController(src.main.java.demo.web包)
4 编写页面,home.jsp(src.main.webapp.WEB-INF.views)
4 打war包(此过程应该会提示缺少web.xml,可在WEB-INF下添加web.xml 或 配置maven-war-plugin插件忽略web.xml)
5 部署war包至tomcat即可(注意Tomcat版本。理论上至少需要支持Servlet 3的Tomcat容器)

测试控制器

//关于断言
import static org.junit.Assert.assertEquals;    //此处通过静态引入assertEquals静态方法
import org.junit.Test;
import demo.web.HomeController;
public class HomeControllerTest {

    @Test
    public void testHomePage() throws Exception {
        HelloController controller = new HelloController();
        assertEquals("home",controller.home());    //通过断言判断controller.home()返回的值是否为"home",不是则报错。
    }

上述测试仅调用home(),并断言返回包含"home"值的String,而不是站在Spring MVC控制器的视角进行测试。
正确的测试应该是发送"/"的GET请求会调用home()方法,并真正判断"home"是视图的名称。
Spring3.2开始支持第控制器视角的测试。通过mock Spring MVC并针对控制器执行HTTP请求的测试机制。

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.*;
import org.junit.Test;
import org.springframework.test.web.servlet.MockMvc;
import demo.web.HomeController;
public class HomeControllerTest {

    @Test
    public void testHomePage() throws Exception {
        HelloController controller = new HelloController();
       
        MockMvc mockMvc = standaloneSetup(controller).build();    //构建MockMvc

        mockMvc.perform(get("/"))                    // 执行 "/"GET请求
                .andExpect(view().name("home"));     // 预期得到home视图
    }

请求映射处理

@Controller
@RequestMapping("demo")    \\当控制器在类级别上添加该注解,这个注解会应用到控制器的所有处理器方法上。
public void DemoController {
    
    @RequestMapping("demo")    \\此方法路径为\demo\demo
    public String demo () {
        return "demo";
    }
}

@RequestMapping({"/","/demo"}) 支持多个路径映射

传递模型数据到视图

有三种方式

//方式1 Model(本质是一个Map,即 key-value 对的集合)
public String demo(Model model) {
    //未指定key时,key依据值类型推断得出(List<String>:stringList,String:string等)
    model.addAttribute(createList());
    return "demo";
}

//方式2 Map
public String demo(Map model) {
    model.put("list",createList());
    return "demo";
}

//方式3 此方式即没返回视图名称,也没显式设定模型
public List<String> demo() {
    //(视图名称由请求路径决定,返回的对象会添加到模型中,key由其类型推断,类似方式1)
    return createList();
}

public List<String> createList() {
    List<String> list = new ArrayList<String>();
    list.add("A");
    list.add("B");
    return list;
}

接受请求的输入

Spring MVC允许以多种方式传递参数
1:查询参数(Query Parameter)
2:表单参数(Form Parameter)
3:路径变量(Path Variable)

处理查询参数

1基本类型 2 包装类型 3 java bean
默认以参数名接参
@RequestParam
value、name 设置参数名(与请求参数名匹配)
required 设置参数是否必传
defaultValue 设置默认值

通过路径参数接受输入

1 设置路径参数占位符
@RequestMapping("/book/{id}")

2 接受路径参数
public String getBook(@PathVariable Long id) {...}

@PathVariable
value、name 设置参数名(与路径参数名匹配)
required 设置参数是否必传

传输少量数据时,查询参数 和 路径参数都很适合
传递很多数据时,通常是表单提交的数据

处理表单

接受表单数据

表单的数据往往较多,可使用java Bean接受参数,Spring MVC会将请求参数中与bean属性同名的进行赋值,并返回bean实例。

检验表单数据

避免校验逻辑弄乱处理器代码,可使用Spring对Java校验API(Java Validation API,又称JSR-303)的支持。
Spring3.0开始,Spring MVC中提供了对Java校验API的支持。只需导入该Java API的实现即可,如Hibernate Validator

将注解定义到Java Bean字段属性上,在接受参数处对待检验的参数添加 @Valid 即可

@AssertFalse 所注解的元素必须为Boolean类型,且值为false
@AssertTrue 所注解的元素必须为Boolean类型,且值为true
@DecimalMax 所注解的元素必须为数字,且值要小于或等于给定的BigDecimalString的值
@DecimalMin 所注解的元素必须为数字,且值要大于或等于给定的BigDecimalString的值
@Max 所注解的元素必须为数字,并且它的值要小于或等于给定的值
@Min 所注解的元素必须为数字,并且它的值要大于或等于给定的值
@Digits 所注解的元素必须为数字,且值必须有指定的位数
@Future 所注解的元素必须为一个将来的日期
@Past 所注解元素必须为一个已过去的日期
@NotNull 所注解元素的值必须不能为null
@Null 所注解元素的值必须为null
@Pattern 所注解元素必须匹配给定的正则表达式
@Size 所注解的元素的值必须是String、集合或数组,并且它的长度要符合给定的范围

校验出错时,可以通过Errors对象进行访问,此对象已作为processRegistration()方法参数。(需注意,Errors参数要紧跟在带有@Valid注解的参数后面)
processRegistration()方法所做的第一件事就是调用Errors.hasErrors()来检查是否有错误。

请求转发 forward:
请求重定向 redirect:


roylion
204 声望25 粉丝

读书破万卷


引用和评论

0 条评论