目录
补充一:通过MockMvc来测试Spring MVC控制器,省去重复启动服务器的麻烦
SpringMVC流程图
- 请求会携带用户所请求内容的信息(至少会包含请求的URL。可能带有其他的信息,例如用户提交的表单信息)到前端控制器(front controller)Servlet,在Spring MVC中就是DispatcherServlet。
- DispatcherServlet会查询一个或多个处理器映射(handler mapping)。处理器映射会根据请求所携带的URL信息来进行决策,决定使用哪个控制器。
- 选择了合适的控制器,DispatcherServlet会将请求发送给选中的控制器。
- 控制器在完成逻辑处理后,是将模型数据打包,并且标示出用于渲染输出的视图名。接下来会将请求连同模型(model)和视图(view)名发送回 DispatcherServlet 。
- DispatcherServlet会使用视图解析器(view resolver) 来将逻辑视图名匹配为一个特定的视图实现。
- 视图的实现。
- 视图将使用模型数据渲染输出,这个输出会通过响应对象传递给客户端。
完整 的目录结构
示例代码
pom.xml,配置项目的支持
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="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.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>Masorl</groupId>
<artifactId>SpringValid</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<springframework.version>4.0.6.RELEASE</springframework.version>
<hibernate.validator.version>5.1.2.Final</hibernate.validator.version>
<javax.validation.version>1.1.0.Final</javax.validation.version>
</properties>
<dependencies>
<!-- Spring dependencies -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${springframework.version}</version>
</dependency>
<!-- jsr303 validation dependencies-->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>${javax.validation.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>${hibernate.validator.version}</version>
</dependency>
<!-- Servlet dependencies -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!--MockMVC dependencies-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.0.6.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.2</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.4</version>
<configuration>
<warSourceDirectory>src/main/webapp</warSourceDirectory>
<warName>Spring4MVCFormValidationExample</warName>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<finalName>Spring4MVCFormValidationExample</finalName>
</build>
</project>
DispatcherServlet创建
通过继承AbstractAnnotationConfigDispatcherServletInitializer类,在Servlet3.0环境中,容器会在类路径查找到继承该类的类,并用它来配置servlet容器。【扩展该类,会自动配置DispatcherServlet和Spring应用上下文,Spring应用上下文会位于应用程序的Servlet上下文之中】
重写getServletConfigClasses: 指定配置文件
重写getServletMappings:将dispatcherservlet映射到"/",也就是说所有的请求都会由dispatcherservlet来处理
package websystique.springmvc.configuration;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class HelloWorldInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return null;
}
//指定配置类
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] { HelloWorldConfiguration.class };
}
//将dispatcherservlet映射到"/"
@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
配置类
通过EnableWebMvc开启Spring MVC
通过@ComponentScan,扫描包下的控制器文件
配置了一个视图解析器,这个解析器是用来解析jsp的
package websystique.springmvc.configuration;
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.EnableWebMvc;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "websystique.springmvc")
public class HelloWorldConfiguration {
//配置视图解析器
@Bean
public ViewResolver viewResolver(){
InternalResourceViewResolver internalResourceViewResolver = new InternalResourceViewResolver();
internalResourceViewResolver.setViewClass(JstlView.class);
internalResourceViewResolver.setPrefix("/WEB-INF/views/");
internalResourceViewResolver.setSuffix(".jsp");
return internalResourceViewResolver;
}
}
控制器
@Controller表明他是一个控制器
@RequestMapping:拦截相关请求
package websystique.springmvc.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
@RequestMapping("/")
public class HelloWorldController {
@RequestMapping(method = RequestMethod.GET)
public String sayHello(ModelMap model){
model.addAttribute("greeting", "Hello World");
return "welcome";
}
@RequestMapping(value = "helloagain", method = RequestMethod.GET)
public String sayHelloAgain(ModelMap modelMap){
modelMap.addAttribute("greeting", "Hello World Again");
return "welcome";
}
}
JSP文件
<%--
Created by IntelliJ IDEA.
User: masorl
Date: 2018/9/14
Time: 11:42
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>HelloWorld page</title>
</head>
<body>
Greeting : ${greeting}
</body>
</html>
结果:
url:http://localhost:8080/helloagain
补充一:通过MockMvc来测试Spring MVC控制器,省去重复启动服务器的麻烦
静态的standaloneSetup方法传入一个控制器实例,再使用build(),即可以构建MockMvc实例。
然后通过MockMvc,就可以执行对请求已经希望得到的视图名称进行判断。
package websystique.springmvc.controller;
import org.junit.Test;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup;
public class HelloWorldControllerTest {
@Test
public void controllTest() throws Exception{
HelloWorldController helloWorldController = new HelloWorldController();
//搭建MockMvc
MockMvc mockMvc = standaloneSetup(helloWorldController).build();
mockMvc.perform(get("/"))
.andExpect(view().name("welcome"));
}
}
注意:再运行时候,出现了以下错误:
Error:(17, 34) java: 无法访问org.hamcrest.Matcher
找不到org.hamcrest.Matcher的类文件
原因是Junit版本过低(当时用的4.0),改为4.11版本后无error
补充二:处理多个请求
@RequestMapping(value = {"helloagain","say"}, method = RequestMethod.GET)
补充三:传递模型数据到视图中
注:helloPOJO是一个bean
@RequestMapping(value = "getModel", method = RequestMethod.GET)
public String setModel(Model model){
model.addAttribute("helloPOJO", helloPOJO);
return "welcome";
}
结果
还有一种写法
返回一个对象,这个对象会直接放到模型里,而返回的视图,根据请求路径,这里是"model",所以返回的视图也是"model"
@RequestMapping(value = "model", method = RequestMethod.GET)
public HelloPOJO setModel2(Model model){
return helloPOJO;
}
补充三:接受路径参数的传入
只需要通过@RequestParam(参数名) 接收即可
//测试接受参数
@RequestMapping(value = "acceptArgs", method = RequestMethod.GET)
public String acceptArgs(@RequestParam("arg") String arg,
Model model){
model.addAttribute("arg", arg);
return "welcome";
}
补充四:处理表单
在控制器添加两个相同请求路径的方法,一个用于处理GET请求,一个用于处理POST请求
GET方式请会跳转到register.jsp页面进行表单注册
POST请求会接受表单数据
@RequestMapping(value = "register", method = RequestMethod.GET)
public String showRegistration(){
return "register";
}
@RequestMapping(value = "register", method = RequestMethod.POST)
public String processRegistration(Spitrr spitrr){
System.out.println(spitrr);
return "register";
}
register.jsp页面
form表单没有写action,它会提交到与先前相同的URL路径上,不过此时提交的方式为POST
<%--
Created by IntelliJ IDEA.
User: masorl
Date: 2018/9/15
Time: 11:07
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>注册</title>
</head>
<body>
<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="注册" />
</form>
</body>
</html>
当点击提交后,会传入处理POST请求的processRegistration(Spitrr spitrr),参数会传入到Spitrr 类中
【自动调用Spitrr的无参构造函数,并调用setter方法将表单值 传入到类的实例中】
Spitrr类:普通的POJO类
package websystique.springmvc.domain;
import org.springframework.stereotype.Component;
public class Spitrr {
private String firstName;
private String lastName;
private String email;
private String username;
private String password;
public Spitrr() {
System.out.println("默认构造器");
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
System.out.println("setFirstName:" + firstName);
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
System.out.println("setLastName:" + firstName);
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
System.out.println("setEmail:" + email);
this.email = email;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
System.out.println("setUsername:" + username);
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
System.out.println("setPassword:" + password);
this.password = password;
}
}
结果:成功接受到了表单传过来的数据
默认构造器
setEmail:3@4
setFirstName:1
setLastName:1
setPassword:6
setUsername:5
websystique.springmvc.domain.Spitrr@40423458
注意:必须要有无参构造器,不然报如下错误
No default constructor found; nested exception is java.lang.NoSuchMethodException: websystique.springmvc.domain.Spitrr.<init>()
*************************************************************************************************************************************************
来自官网给出的实例【HTML】
<!-- freemarker config -->
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<property name="templateLoaderPath" value="/WEB-INF/freemarker/"/>
</bean>
<!--
View resolvers can also be configured with ResourceBundles or XML files. If you need
different view resolving based on Locale, you have to use the resource bundle resolver.
-->
<bean id="viewResolver" class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
<property name="cache" value="true"/>
<property name="prefix" value=""/>
<property name="suffix" value=".ftl"/>
</bean>