一、简单使用
1、搭建Spring MVC
1.1 配置DispatcherServlet并让Servlet容器发现他
(1)说明
DispatcherServlet是Spring MVC的核心。在这里请求request第一次接触到框架,它要负责将请求路由到其他的组件之中
(2)配置方式
1)方式一(传统方式):配置在web.xml文件中,这个文件会放到应用的WAR包里面。
2)方式二(Servlet 3规范和Spring 3.1的功能增强):以编写Java代码的方式,将DispatcherServlet配置在Servlet容器中,不会再使用web.xml文件
如果按照方式二配置DispatcherServlet,而不是使用web.xml的话,那唯一问题在于它只能部署到支持Servlet 3.0的服务器中才能正常工作,如Tomcat 7或更高版本;
本文主要介绍方式二,具体做法是自定义一个类,继承自AbstractAnnotationConfigDispatcherServletInitializer
方式二原理:
|
AbstractAnnotationConfigDispatcherServletInitializer抽象类中有三个需要实现的方法(这三个方法就是围绕上面的原理中流程需要而定义的):
方法一:getServletMappings()
@Override
protected String[] getServletMappings() {
return new String[]{"/"};//将DispatcherServlet映射到“/”
}
方法二:getServletConfigClasses()
getServletConfigClasses()方法返回的带有@Configuration注解的类将会用来定义DispatcherServlet负责创建的Spring应用上下文中的bean。
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{WebConfig.class};
}
WebConfig.java见下文
方法三:getRootConfigClasses()
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{RootConfig.class};
}
RootConfig.java见下文
1.2 启用Spring MVC
1.2.1 配置getServletConfigClasses()中的WebConfig.java
方式一:使用spring配置文件方式启动:可以使用<mvc:annotation-driven>启用注解驱动的Spring MVC
方式二:使用JavaConfig配置方式启动:使用@EnableWebMvc注解修饰一个JavaConfig类(已经使用@Configuration注解的类)
本文主要介绍方式二:
步骤1:@EnableWebMvc注解
步骤2:选择是否启动组件扫描
步骤3:配置视图解析器
如果没有配置,spring将使用默认的视图解析器,行为是:默认会使用BeanNameViewResolver,这个视图解析器会查找ID与视图名称匹配的bean,并且查找的bean要实现View接口,它以这样的方式来解析视图
package com.mzj.springmvc.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.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
@Configuration
@EnableWebMvc//启用Spring MVC
@ComponentScan("com.mzj.springmvc.spittr.webbean")//指定组件扫描范围,可以扫描到带有@Controller注解的bean(spring mvc中的Controller)
public class WebConfig extends WebMvcConfigurerAdapter {
/**
* 添加视图解析器bean
*
* 本实现:
*
* 会查找.JSP文件,在查找的时候,它会在视图名称上加一个特定的前缀和后缀(例如,名为home的视图将会解析为/WEB-INF/views/home.jsp)。
*
* @return
*/
@Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
return resolver;
}
/**
* 通过继承WebMvcConfigurerAdapter
* 复写configureDefaultServletHandling
* 调用 configurer.enable();
* 达到的效果是:要求DispatcherServlet将对静态资源的请求转发到Servlet容 器中默认的Servlet上,而不是使用DispatcherServlet本身来处理 此类请求。
* @param configurer
*/
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
1.2.2 配置getRootConfigClasses()中的RootConfig.java
com.mzj.springmvc.spittr
这样的话,我们会将很多组件(非Web的组件)来应用到RootConfig中,来充实完善RootConfig。
package com.mzj.springmvc.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={"com.mzj.springmvc.spittr"},
excludeFilters={
@Filter(type=FilterType.ANNOTATION, value= EnableWebMvc.class)
})
public class RootConfig {
}
2、编写简单的控制器
步骤1:使用@Controller注解声明一个类是一个控制器
package com.mzj.springmvc.spittr.web;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import static org.springframework.web.bind.annotation.RequestMethod.GET;
/**
* 编写基本的控制器
*
* @Auther: mazhongjia
* @Date: 2020/3/30 12:44
* @Version: 1.0
*/
@Controller//1、声明此类是一个控制器
public class HomeController {
@RequestMapping(value="/",method = GET)//2、处理对“/”的GET请求
public String home(){
return"home";//3、视图名为”home“
}
}
步骤5:测试这个Controller
测试controller的方式
方式一:传统的方式测试控制器最直接的办法可能就是构建并部署应用,然后通过浏览器对其进行访问
方式二:spring3.2开始,可以通过mock的方式测试controller,spring包含了一种mock Spring MVC并针对控制器执行HTTP请求的机制。这样的话,在测试控制器的时候,就没有必要再启动Web服务器和Web浏览器了。
package com.mzj.springmvc.spittr;
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 com.mzj.springmvc.spittr.web.HomeController;
import org.junit.Test;
import org.springframework.test.web.servlet.MockMvc;
public class HomeControllerTest {
@Test
public void testHomePage() throws Exception {
HomeController controller = new HomeController();
/**
* 从Spring 3.2开始,我们可以按照控制器的方式来测试Spring MVC 中的控制器了,
* Spring现在包含了一 种mock Spring MVC并针对控制器执行HTTP请求的机制
* 这样的话, 在测试控制器的时候,就没有必要再启动Web服务器和Web浏览器了:
*
*/
MockMvc mockMvc = standaloneSetup(controller).build();
mockMvc.perform(get("/"))
.andExpect(view().name("home"));//这种测试发起了对“/”的GET请求,并断言结果视图的名称为 home。
}
}
运行测试:成功
@RequestMapping注解
参考测试用例:com.mzj.springmvc.spittr.HomeControllerTest2 |
3、Controller中传递模型数据到视图
步骤1:创建一个实现业务的查询数据库Repository接口及实现
package com.mzj.springmvc.spittr.data;
import com.mzj.springmvc.spittr.Spittle;
import java.util.List;
/**
* @Auther: mazhongjia
* @Date: 2020/3/31 12:55
* @Version: 1.0
*/
public interface SpittleRepository {
/**
*
* @param max Spittle列表中Spittle的ID最大值
* @param count 要返回的列表中Spittle数量
* @return
*/
List<Spittle> findSpittles(long max, int count);
}
接口实现略。
步骤2:在Controller中的声明的Controller方法参数列表增加org.springframework.ui.Model作为参数,Controller方法就能将Repository中获取到的 Spittle列表填充到模型中。Model实际上就是一个Map(也就是 key-value对的集合),它会传递给视图,这样数据就能渲染到客户端了
步骤3:Controller方法返回对应的视图名,这个视图会渲染模型(在视图中可以获取模型中数据)
Controller代码:
package com.mzj.springmvc.spittr.web;
import com.mzj.springmvc.spittr.data.SpittleRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import static org.springframework.web.bind.annotation.RequestMethod.GET;
import java.util.List;
import java.util.Map;
/**
* @Auther: mazhongjia
* @Date: 2020/3/31 13:06
* @Version: 1.0
*/
@Controller(value = "spittleController")
@RequestMapping("/spittles")
public class SpittleController {
private SpittleRepository spittleRepository;
@Autowired
public SpittleController(SpittleRepository spittleRepository) {
this.spittleRepository = spittleRepository;
}
/**
* 在spittles()方法中给定了一个Model作为参 数。这样,spittles()方法就能将Repository中获取到的 Spittle列表填充到模型中。Model实际上就是一个Map(也就是 key-value对的集合),它会传递给视图,这样数据就能渲染到客户端了。
* <p>
* 以下两个注释方法,与本方法达到的效果一致
*
* @param model
* @return
*/
@RequestMapping(method = GET)
public String spittles(Model model) {//如果你希望使用非Spring类型的话,那么可以用java.util.Map来 代替Model。实际上Model就是Map
//当调用addAttribute()方法并且不指定key的时候,那么key会 根据值的对象类型推断确定。在本例中,因为它是一 个List<Spittle>,因此,键将会推断为spittleList。
model.addAttribute("spittleList", spittleRepository.findSpittles(Long.MAX_VALUE, 20));//将spittle添加到模型中
return "spittles";//返回视图名,spittles()方法所做的最后一件事是返回spittles作为视图的名字,这个视图会渲染模型。
}
// public String spittles(Map model){/
// model.put("spittleList",spittleRepository.findSpittles(Long.MAX_VALUE,20));
// return "spittles";
// }
// public List<Spittle> spittles() {//这个版本与其他的版本有些差别。它并没有返回视图名称,也没有显 式地设定模型,这个方法返回的是Spittle列表。当处理器方法像 这样返回对象或集合时,这个值会放到模型中,模型的key会根据其 类型推断得出(在本例中,也就是spittleList),而逻辑视图的名称将会根据请求路径推断得出。因为这个方法处理针 对“/spittles”的GET请求,因此视图的名称将会是spittles(去掉开 头的斜线)。
// return spittleRepository.findSpittles(Long.MAX_VALUE, 20);
// }
}
步骤4:测试
方式1:部署到tomcat中运行
方式2:通过测试用例mock的方式(可以不创建SpittleRepository接口的实现)
package com.mzj.springmvc.spittr;
import static org.hamcrest.Matchers.*;
import static org.mockito.Mockito.*;
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 java.util.ArrayList;
import java.util.Date;
import java.util.List;
import com.mzj.springmvc.spittr.data.SpittleRepository;
import com.mzj.springmvc.spittr.web.SpittleController;
import org.junit.Test;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.web.servlet.view.InternalResourceView;
public class SpittleControllerTest {
@Test
public void houldShowRecentSpittles() throws Exception {
//1、这个测试首先会创建SpittleRepository接口的mock实现,这个 实现会从它的findSpittles()方法中返回20个Spittle对象。
List<Spittle> expectedSpittles = createSpittleList(20);
SpittleRepository mockRepository = mock(SpittleRepository.class);//mock SpittleRepository接口的实现
when(mockRepository.findSpittles(Long.MAX_VALUE, 20))
.thenReturn(expectedSpittles);
//2、它将这个Repository注入到一个新的SpittleController 实例中,然后创建MockMvc并使用这个控制器
SpittleController controller = new SpittleController(mockRepository);
MockMvc mockMvc = standaloneSetup(controller)//mock spring mvc
.setSingleView(new InternalResourceView("/WEB-INF/views/spittles.jsp"))//这里设置的路径其实没有太大意义,只是与在WebConfig中定义的viewResolver保持一致
//调用了setSingleView()。这样的话,mock框架就不用解析 控制器中的视图名了,之所以使用这种直接set视图名的方式,是因为视图名与请求路径是非常相似的(spittles),这样按照默认 的视图解析规则时,MockMvc就会发生失败,因为无法区分视图路 径和控制器的路径。
.build();
//3、开始断言:测试对“/spittles”发起GET请求,然后断言视图的名称为spittles并 且模型中包含名为spittleList的属性,在spittleList中包含 预期的内容。
mockMvc.perform(get("/spittles"))//对/spittles发起GET请求
.andExpect(view().name("spittles"))//断言期望值
.andExpect(model().attributeExists("spittleList"))
.andExpect(model().attribute("spittleList",
hasItems(expectedSpittles.toArray())));
}
private List<Spittle> createSpittleList(int count) {
List<Spittle> spittles = new ArrayList<Spittle>();
for (int i=0; i < count; i++) {
spittles.add(new Spittle("Spittle " + i, new Date()));
}
return spittles;
}
}
4、Controller中接收请求的输入
- 请求参数(请求少量数据)
- 路径变量(请求少量数据,restful风格)
- 表单参数(传递很多数据时使用)
4.1 通过【请求参数】接收输入
步骤1:controller方法
-
@RequestParam注解:使用此注解修饰controller方法的参数,其中
-
value属性对应请求参数中属性名
-
defaultValue属性用于不传递此参数时默认取值
-
这样,在controller中就获取了通过请求参数方式接收客户端的输入
package com.mzj.springmvc.spittr.web.param;
import com.mzj.springmvc.spittr.Spittle;
import com.mzj.springmvc.spittr.data.SpittleRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.List;
import static org.springframework.web.bind.annotation.RequestMethod.GET;
/**
* @Auther: mazhongjia
* @Date: 2020/3/31 13:06
* @Version: 1.0
*/
@Controller
@RequestMapping("/spittles2")
public class SpittleController2 {
private static final String MAX_LONG_AS_STRING = "9223372036854775807";
private SpittleRepository spittleRepository;
@Autowired
public SpittleController2(SpittleRepository spittleRepository) {
this.spittleRepository = spittleRepository;
}
// @RequestMapping(method = GET)
// public List<Spittle> spittles(@RequestParam(value = "max", defaultValue = MAX_LONG_AS_STRING) long max,
// @RequestParam(value = "count", defaultValue = "20") int count) {
// return spittleRepository.findSpittles(max, count);
// }
@RequestMapping(method = GET)
public String spittles(Model model,@RequestParam(value = "max", defaultValue = MAX_LONG_AS_STRING) long max,
@RequestParam(value = "count", defaultValue = "20") int count) {
System.out.println("controller收到:count = " + count + ",max = " + max);
model.addAttribute("spittleList", spittleRepository.findSpittles(max,count));
return "spittles";
}
}
步骤2:测试
package com.mzj.springmvc.spittr.param;
import com.mzj.springmvc.spittr.Spittle;
import com.mzj.springmvc.spittr.data.SpittleRepository;
import com.mzj.springmvc.spittr.web.param.SpittleController2;
import org.junit.Test;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.web.servlet.view.InternalResourceView;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import static org.hamcrest.Matchers.hasItems;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup;
public class SpittleControllerTest2 {
@Test
public void shouldShowPagedSpittles() throws Exception {
List<Spittle> expectedSpittles = createSpittleList(50);
SpittleRepository mockRepository = mock(SpittleRepository.class);
when(mockRepository.findSpittles(238900, 50))//预期的max和count参数
.thenReturn(expectedSpittles);
SpittleController2 controller = new SpittleController2(mockRepository);
MockMvc mockMvc = standaloneSetup(controller)//mock spring mvc
.setSingleView(new InternalResourceView("/WEB-INF/views/spittles.jsp"))
.build();
mockMvc.perform(get("/spittles2?max=238900&count=50"))//对/spittles发起GET请求,同时传入max和count参数,它测试 了这些参数存在时的处理器方法
.andExpect(view().name("spittles"))
.andExpect(model().attributeExists("spittleList"))
.andExpect(model().attribute("spittleList",
hasItems(expectedSpittles.toArray())));
}
private List<Spittle> createSpittleList(int count) {
List<Spittle> spittles = new ArrayList<Spittle>();
for (int i=0; i < count; i++) {
spittles.add(new Spittle("Spittle " + i, new Date()));
}
return spittles;
}
}
4.2 通过【路径参数】接收输入
说明:此种传递参数的方式常用在构建面向资源的控制器时(restful风格),这种方式就是将传递参数作为请求路径的一部分。
步骤1:controller方法的@RequestMapping注解中增加代表路径参数的占位符,比如这里的"/{spittleId}"中spittleId,其中spittleId是URL路径中的占位符,占位符的名称要用大括号(“{”和“}”)括起来。 路径中的其他部分要与所处理的请求完全匹配,占位符部分可以是任意的值。
package com.mzj.springmvc.spittr.web.restful;
import com.mzj.springmvc.spittr.data.SpittleRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import static org.springframework.web.bind.annotation.RequestMethod.GET;
/**
* @Auther: mazhongjia
* @Date: 2020/3/31 13:06
* @Version: 1.0
*/
@Controller
@RequestMapping("/spittles3")
public class SpittleController3 {
private SpittleRepository spittleRepository;
@Autowired
public SpittleController3(SpittleRepository spittleRepository) {
this.spittleRepository = spittleRepository;
}
@RequestMapping(value = "/{spittleId}",method = GET)//其中spittleId是URL路径中的占位符,占位符的名称要用大括号(“{”和“}”)括起来。 路径中的其他部分要与所处理的请求完全匹配,但是占位符部分可以 是任意的值。
// public String spittles(Model model, @PathVariable("spittleId") long spittleId) {//这里的spittleId与RequestMapping中/{spittleId}对应
// 需要注意的是:代码中spittleId这个词出现了好几次:先是 在@RequestMapping的路径中,然后作为@PathVariable属性的 值,最后又作为方法的参数名称。因为方法的参数名碰巧与占位符的 名称相同,因此我们可以去掉@PathVariable中的value属性:
public String spittles(Model model, @PathVariable long spittleId) {//这里参数名spittleId与RequestMapping中请求占位符/{spittleId}对应
System.out.println("controller收到:spittleId = " + spittleId);
model.addAttribute(spittleRepository.findOne(spittleId));//将查询到的模型放入model中,以便在视图中拿到,模型的key将会是spittle,这是根据传递 到addAttribute()方法中的类型推断得到的
return "spittles";
}
}
步骤3:测试
package com.mzj.springmvc.spittr.restful;
import com.mzj.springmvc.spittr.Spittle;
import com.mzj.springmvc.spittr.data.SpittleRepository;
import com.mzj.springmvc.spittr.web.SpittleController;
import com.mzj.springmvc.spittr.web.param.SpittleController2;
import com.mzj.springmvc.spittr.web.restful.SpittleController3;
import org.junit.Test;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.web.servlet.view.InternalResourceView;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import static org.hamcrest.Matchers.hasItems;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup;
public class SpittleControllerTest3 {
@Test
public void testSpittle() throws Exception {
Spittle expectedSpittle = new Spittle("Hello", new Date());
SpittleRepository mockRepository = mock(SpittleRepository.class);//构建了一个mock Repository
when(mockRepository.findOne(12345)).thenReturn(expectedSpittle);
SpittleController3 controller = new SpittleController3(mockRepository);//构建了一个控制器
MockMvc mockMvc = standaloneSetup(controller).build();
mockMvc.perform(get("/spittles3/12345"))//构建了一个mock mvc,对“/spittles/12345”发起GET请求
.andExpect(view().name("spittles"))
.andExpect(model().attributeExists("spittle"))
.andExpect(model().attribute("spittle", expectedSpittle));
}
}
4.3 表单参数
/**
* 展现表单控制器
* @return
*/
@RequestMapping(value="/register", method=GET)
public String showRegistrationForm() {
return "registerForm";
}
- 这个JSP必须要包含一个HTML<form>标签
- 这里的<form>标签中并没有设置action属性。在这种情况下,当表单提交时,它会提交到与展现时相同的URL路径上。也就是说,它会提交到“/spitter/register”
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>
<html>
<head>
<title>Spitter</title>
<link rel="stylesheet" type="text/css"
href="<c:url value="/resources/style.css" />" >
</head>
<body>
<h1>Register</h1>
<%--这里的<form>标签中并没有设置action属性。在 这种情况下,当表单提交时,它会提交到与展现时相同的URL路径 上。也就是说,它会提交到“/spitter/register”上--%>
<form method="POST">
First Name: <input type="text" name="firstName" /><br/>
Last Name: <input type="text" name="lastName" /><br/>
Email: <input type="email" name="email" /><br/>
Username: <input type="text" name="username" /><br/>
Password: <input type="password" name="password" /><br/>
<input type="submit" value="Register" />
</form>
</body>
</html>
3)处理表单的控制器SpitterController
-
处理注册表单的 POST 请求时,控制器需要接受表单数据并将表单数据保存为Spitter 对象。
-
为了防止重复提交(用户点击浏览器的刷新按钮有可能会发生这种情况),将浏览器重定向到新创建用户的基本信息页面。
-
其他细节关注下面代码注释
/**
* 处理表单提交控制器
* @param spitter
* @return
*/
@RequestMapping(value="/register", method=POST)
public String processRegistration(Spitter spitter) {
/**
* 这个控制器方法直接声明接收一个Spittler对象参数,需要依赖发送请求的客户端:在请求中使用与Spittler对象中属性同名的参数,这样Spittler对象的属性会自动进行填充,对于本测试来说,是SpittlerControllerTest的shouldProcessRegistration中的下面代码起到的作用:
*
* mockMvc.perform(post("/spitter/register")//)对“/spitter/ register”发起了一个POST请求。
* .param("firstName", "Jack")
* .param("lastName", "Bauer")
* .param("username", "jbauer")
* .param("password", "24hours")
* .param("email", "jbauer@ctu.gov"))
*/
System.out.println(spitter);
spitterRepository.save(spitter);//当使用Spitter对象调用processRegistration()方法时,它会进而调用SpitterRepository的save()方法,SpitterRepository是在Spitter-Controller的构造器中注入进来的。
//返回的值还带有重定向的格式
//请求重定向:当InternalResourceViewResolver看到视图格式中 的“redirect:”前缀时,它就知道要将其解析为重定向的规则,而不是视图的名称。
//请求转发:除了“redirect:”,InternalResourceViewResolver还能识 别“forward:”前缀。当它发现视图格式中以“forward:”作为前缀时,请求将会前往(forward)指定的URL路径,而不再是重定向。
return "redirect:/spitter/" + spitter.getUsername();//在处理POST类型的请求时,在请求处理完成后,最好进行一下重定向,这样浏览器的刷新就不会重复提交表单了。在在这个测试中,预期 请求会重定向到“/spitter/jbauer”,也就是新建用户的基本信息页面
}
4)表单提交后重定向请求(基本信息页面的控制器)SpitterController
/**
* 处理提交表单后处理完重定向请求的跳转,也就是基本信息页面的请求
* @param username
* @param model
* @return
*/
@RequestMapping(value="/{username}", method=GET)
public String showSpitterProfile(@PathVariable String username, Model model) {
/**
* SpitterRepository通过用户名获取一个Spitter对 象,showSpitter-Profile()得到这个对象并将其添加到模型 中,然后返回profile,也就是基本信息页面的逻辑视图名。
*/
Spitter spitter = spitterRepository.findByUsername(username);
model.addAttribute(spitter);
return "profile";
}
5)基本信息页面profile.jsp
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>
<html>
<head>
<title>Spitter</title>
<link rel="stylesheet" type="text/css" href="<c:url value="/resources/style.css" />" >
</head>
<body>
<h1>Your Profile</h1>
<c:out value="${spitter.username}" /><br/>
<c:out value="${spitter.firstName}" /> <c:out value="${spitter.lastName}" /><br/>
<c:out value="${spitter.email}" />
</body>
</html>
4.4 表单校验
1、作用:用来验证表单提交内容是否为空,提交内容是否合法等。
2、方式
1)方式一:直接再业务控制器方法中加入if代码进行校验
缺点:校验逻辑弄脏了业务处理器方法。
2)方式二:Java校验API(Java Validation API,又称JSR-303)(Spring3.0以上对其进行了支持)
3、使用(这里介绍方式二使用)
1)准备:在Spring MVC中要使用Java校验API的话,并不需要什么额外的配置。只要保证在类路径下包含这个Java校验API的实现即可,比如Hibernate Validator。
2)说明:Java校验API定义了多个注解,这些注解可以放到属性上,从而限制这些属性的值。
所有的注解都位于javax.validation.constraints包中,常用的见下表
注解 | 描述 |
---|---|
@AssertFalse
|
所注解的元素必须是
Boolean
类型,并且值为
false
|
@AssertTrue
|
所注解的元素必须是
Boolean
类型,并且值为
true
|
@DecimalMax
|
所注解的元素必须是数字,并且它的值要小于或等于给定的BigDecimalString值
|
@DecimalMin
|
所注解的元素必须是数字,并且它的值要大于或等于给定的BigDecimalString值
|
@Digits
|
所注解的元素必须是数字,并且它的值必须有指定的位数
|
@Future
|
所注解的元素的值必须是一个将来的日期
|
@Max
|
所注解的元素必须是数字,并且它的值要小于或等于给定的值
|
@Min
|
所注解的元素必须是数字,并且它的值要大于或等于给定的值
|
@NotNull
|
所注解元素的值必须不能为
null
|
@Null
|
所注解元素的值必须为
null
|
@Past
|
所注解的元素的值必须是一个已过去的日期
|
@Pattern
|
所注解的元素的值必须匹配给定的正则表达式
|
@Size
|
所注解的元素的值必须是
String
、集合或数组,并且它的长度要符合给定的范围
|
自定义限制条件 | 。。。。 |
3)在bean的属性上增加上述校验注解进行限制
代码见:com.mzj.springmvc.spittr.Spitter
package com.mzj.springmvc.spittr;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.hibernate.validator.constraints.Email;
public class Spitter {
private Long id;
@NotNull
@Size(min=5, max=16)
private String username;
@NotNull
@Size(min=5, max=25)
private String password;
@NotNull
@Size(min=2, max=30)
private String firstName;
@NotNull
@Size(min=2, max=30)
private String lastName;
@NotNull
@Email
private String email;
public Spitter() {}
public Spitter(String username, String password, String firstName, String lastName, String email) {
this(null, username, password, firstName, lastName, email);
}
public Spitter(Long id, String username, String password, String firstName, String lastName, String email) {
this.id = id;
this.username = username;
this.password = password;
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public boolean equals(Object that) {
return EqualsBuilder.reflectionEquals(this, that, "firstName", "lastName", "username", "password", "email");
}
@Override
public int hashCode() {
return HashCodeBuilder.reflectionHashCode(this, "firstName", "lastName", "username", "password", "email");
}
}
4)在控制器方法的对应参数上增加@Valid注解,以启用校验功能(这会告知Spring,需要确保这个对象满足校验限制)
注意@Valid注解是Java校验API中的注解,而不是spring的,spring是对这个API进行了实现
5)增加Errors参数
!!很重要一点需要注意,Errors参数要紧跟在带有@Valid注解的参数后面,@Valid注解所标注的就是要检验的参数。)
以上两点代码实现:com.mzj.springmvc.spittr.web.form.SpitterController,关键代码如下:
/**
* 处理表单提交控制器
* @param spitter
* @return
*/
@RequestMapping(value="/register", method=POST)
public String processRegistration(@Valid Spitter spitter,Errors errors) {
if(errors.hasErrors()){//如果验证失败,重新返回表单
return "registerForm";
}
...
}