构建Spring Web应用程序
- 映射请求到Spring控制器
- 透明地绑定表单参数
- 校验表单提交
Spring MVC 基于模型-视图-控制器(Model-View-Controller,MVC)模式实现,帮助构建灵活和松耦合的Web应用程序
Spring MVC起步
Spring MVC 请求流程
Spring MVC框架将请求在调度Servler、处理器映射(handler mapping)、控制器以及视图解析器(view resolver)之间移动
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:
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。