一、概述
Spring MVC属于SpringFrameWork的后续产品,已经融合在Spring Web Flow里面。Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块。使用 Spring 可插入的 MVC 架构,从而在使用Spring进行WEB开发时,可以选择使用Spring的Spring MVC框架或集成其他MVC开发框架,如Struts1(现在一般不用),Struts 2(一般老项目使用)等等。
M model 模型层 DAO封装 >>> Mybatis
V view 视图层 html css js jsp
C controller 控制层 Servlet封装 >>> springMVC
SpringMVC是spring为展现层提供的基于MVC设计理念的优秀WEB框架,是目前最主流的MVC框架之一
SpringMVC通过一套注解,可以让普通的JAVA类成为contrllor控制器,无需继承Servlet,实现了控制层和Servlet之间的解耦
SpringMVC支持Rest风格的URL写法
SpringMVC采用了松耦合,可热插的主键结构,比其他的框架更具扩展性和灵活性
二、SpringMVC的项目搭建
首先创建一个Maven项目,如下图所示
这里使用自己配置的Maven项目的环境以及仓库,然后选择完毕后再Finish,项目就搭建好了
创建后,需要把main》》java源码文件目录/resource配置文件目录创建好,然后在src下创建test》》java测试目录
导入Springmvc相关架包
<dependencies>
<!--spring核心容器包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.5</version>
</dependency>
<!--spring切面包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.5</version>
</dependency>
<!--aop联盟包-->
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<!--德鲁伊连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.21</version>
</dependency>
<!--springJDBC包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.5</version>
</dependency>
<!--spring事务控制包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.3.5</version>
</dependency>
<!--spring orm 映射依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>5.3.5</version>
</dependency>
<!--Apache Commons日志包-->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<!--log4j2 日志-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.14.0</version>
<scope>test</scope>
</dependency>
<!--lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
<!--spring test测试支持包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.5</version>
<scope>test</scope>
</dependency>
<!--junit5单元测试-->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.7.0</version>
<scope>test</scope>
</dependency>
<!--springMVC支持包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.3.5</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.5</version>
</dependency>
<!--jsp 和Servlet 可选-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
<scope>provided</scope>
</dependency>
</dependencies>
在java源码目录下创建controller文件
package com.xiaohui.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.HttpRequestHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
@Controller
@RequestMapping("/spring")
public class MyController {
@RequestMapping("/firstController.do")
public String firstController(HttpServletRequest request){
System.out.println("firstController");
return "first";
}
}
在webapp》WEB-INF》web.xml文件里进行配置
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--配置DispatcherServlet -->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--初始化参数,指定SpringMVC配置文件的路径-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springMVC.xml</param-value>
</init-param>
<!--如果不用initparam指定springmvc配置文件的路径,那么dispatcherServlet会自动到WEB-INF下找指定名称的配置文件
默认配置文件名为servlet-name>-servlet.xml
-->
<load-on-startup>1</load-on-startup>
</servlet>
<!--配置dispatcherServlet的映射路径为 / 包含全部的servlet, JSP除外-->
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
在webapp》WEB-INF》view》创建first.jsp文件
在resources中创建一个springmvc.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
">
<!--配置spring包扫描-->
<context:component-scan base-package="com.xiaohui"></context:component-scan>
<!--视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--指定路径的前缀和后缀-->
<property name="prefix" value="/WEB-INF/view/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
</beans>
再创建一个log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="DEBUG">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{YYYY-MM-dd HH:mm:ss} [%t] %-5p %c{1}:%L - %msg%n" />
</Console>
</Appenders>
<Loggers>
<Root level="debug">
<AppenderRef ref="Console" />
</Root>
</Loggers>
</Configuration>
然后配置Tomcat服务器,并启动
http://localhost:8080/springmvc_01_war_exploded/spring/firstController.do
三、执行流程和三大组件
-
1 DispatcherServlet:前端控制器
- 用户请求到达前端控制器,它就相当于 mvc 模式中的 c,dispatcherServlet 是整个流程控制的中心,由 它调用其它组件处理用户的请求,dispatcherServlet 的存在降低了组件之间的耦合性。
-
2 HandlerMapping:处理器映射器
- HandlerMapping 负责根据用户请求找到 Handler 即处理器,SpringMVC 提供了不同的映射器实现不同的 映射方式,例如:配置文件方式,实现接口方式,注解方式等。
-
3 Handler:处理器 (自己定义的Controller处理单元)
- 它就是我们开发中要编写的具体业务控制器。由 DispatcherServlet 把用户请求转发到 Handler。由 Handler 对具体的用户请求进行处理。
-
4 HandlAdapter:处理器适配器
- 通过 HandlerAdapter 对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行
-
5 View Resolver:视图解析器
- View Resolver 负责将处理结果生成 View 视图,View Resolver 首先根据逻辑视图名解析成物理视图名 即具体的页面地址,再生成 View 视图对象,最后对 View 进行渲染将处理结果通过页面展示给用户。
-
6 View:视图
- SpringMVC 框架提供了很多的 View 视图类型的支持,包括:jstlView、freemarkerView、pdfView等。我们最常用的视图就是 jsp。 一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由程序员根据业务需求开 发具体的页面。
-
7 < mvc:annotation-driven>说明
- 在 SpringMVC 的各个组件中,处理器映射器、处理器适配器、视图解析器称为 SpringMVC 的三大组件。
- 使 用 < mvc:annotation-driven> 自动加载 RequestMappingHandlerMapping (处理映射器) 和 RequestMappingHandlerAdapter ( 处 理 适 配 器 ) , 可 用 在 SpringMVC.xml 配 置 文 件 中 使 用 < mvc:annotation-driven>替代注解处理器和适配器的配置。
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
try {
ModelAndView mv = null;
Object dispatchException = null;
try {
processedRequest = this.checkMultipart(request);
multipartRequestParsed = processedRequest != request;
mappedHandler = this.getHandler(processedRequest);
if (mappedHandler == null) {
this.noHandlerFound(processedRequest, response);
return;
}
HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
this.applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
} catch (Exception var20) {
dispatchException = var20;
} catch (Throwable var21) {
dispatchException = new NestedServletException("Handler dispatch failed", var21);
}
this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
} catch (Exception var22) {
this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
} catch (Throwable var23) {
this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
}
} finally {
if (asyncManager.isConcurrentHandlingStarted()) {
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
} else if (multipartRequestParsed) {
this.cleanupMultipart(processedRequest);
}
}
}
private void applyDefaultViewName(HttpServletRequest request, @Nullable ModelAndView mv) throws Exception {
if (mv != null && !mv.hasView()) {
String defaultViewName = this.getDefaultViewName(request);
if (defaultViewName != null) {
mv.setViewName(defaultViewName);
}
}
}
HandlerMapping的实现类的作用
实现类RequestMappingHandlerMapping,它会处理@RequestMapping 注解,并将其注册到请求映射表中。
HandlerAdapter的实现类的作用
实现类RequestMappingHandlerAdapter,则是处理请求的适配器,确定调用哪个类的哪个方法,并且构造方法参数,返回值。
当配置了mvc:annotation-driven/后,Spring就知道了我们启用注解驱动。然后Spring通过context:component-scan/标签的配置,会自动为我们将扫描到的@Component,@Controller,@Service,@Repository等注解标记的组件注册到工厂中,来处理我们的请求,这个时候接收返回json数据、参数验证、统一异常等功能。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
">
<!--配置spring包扫描-->
<context:component-scan base-package="com.xiaohui"></context:component-scan>
<!--配置处理器映射器-->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"></bean>
<!--配置处理器适配器-->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"></bean>
<!--一个注解替换上面的两个配置-->
<!--会自动注册RequestMappingHandlerMapping与RequestMappingHandlerAdapter两个Bean-->
<mvc:annotation-driven></mvc:annotation-driven>
<!--视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--指定路径的前缀和后缀-->
<property name="prefix" value="/WEB-INF/view/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
</beans>
静态资源放行
在访问web项目时,有可能会遇到访问静态资源404的情况,比如一些css/js/img等的文件
处理办法是在springmvc.xml中进行静态资源的放行配置
<!--静态资源放行-->
<!--<mvc:resources mapping="/js/**" location="/js/"></mvc:resources>
<mvc:resources mapping="/css/**" location="/css/"></mvc:resources>
<mvc:resources mapping="/img/**" location="/img/"></mvc:resources>-->
<mvc:resources mapping="/static/**" location="/static/"></mvc:resources>
结果:
四、@RequestMapping注解
1、@RequestMapping控制请求方式
method属性可以控制请求的方式,值为RequestMethod的枚举值
@RequestMapping( value = "/***" ,method = RequestMethod.GET)
2、@RequestMapping控制请求参数params和请求头headers
* param:表示请求中必须包含名为param的参数
* !param:表示请求中不能包含名为param的参数
* param = value 表示请求中包含名为param的参数,但是值必须是value
* param != value 表示请求中包含名为param的参数,但是值不能是value
* {"param1","param2=value"},可以将对于多个参数的要求写入数组
package com.xiaohui.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.servlet.http.HttpServletRequest;
@Controller
public class MyController {
@RequestMapping("/firstController.do")
public String firstController(){
System.out.println("firstController");
return "success";
}
/*@RequestMapping控制请求方式*/
@RequestMapping(value ="/testRequest" ,method = {RequestMethod.POST,RequestMethod.GET})
public String testRequest(){
System.out.println("testRequest");
return "success";
}
/*@RequestMapping控制请求方式*/
@RequestMapping(value ="/testRequest2" ,params = {"username!=root","password"})
public String testRequest2(HttpServletRequest request){
String username = request.getParameter("username");
String password = request.getParameter("password");
System.out.println("testRequest2"+",username:"+username+",password:"+password);
return "success";
}
/*@RequestMapping控制请求方式*/
@RequestMapping(value ="/testRequest3" ,headers = {"Accept-Encoding=gzip"})
public String testRequest3(HttpServletRequest request){
String username = request.getParameter("username");
String password = request.getParameter("password");
System.out.println("testRequest2"+",username:"+username+",password:"+password);
return "success";
}
}
3、@PathVariable注解和RESTful风格的支持
普通形式的url
*****/contextPath/aaa.do
*****/contextPath/aaa.jsp
*****/contextPath/aaa.html
*****/contextPath/css/aaa.css
*****/contextPath/js/aaa.js
*****/contextPath/aaa.do?id=10&username=root
restFul风格的url
*****/contextPath/aaa/10/root
*****/contextPath/aaa
package com.xiaohui.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class PathController {
@RequestMapping("/testPathVariable/{id}/{username}")
public String testPathVariable(@PathVariable("id")String id,@PathVariable("username")String username){
System.out.println("id:"+id);
System.out.println("username:"+username);
System.out.println("testPathVariable");
return "success";
}
}
Http协议中,四个表示操作方式的动词"GET POST PUT DELETE",他们对应四种基本操作,GET用来获取资源,POST用来新建资源,PUT用来更新资源,DELETE用来删除资源
简单的说,就是我们在访问资源时,可以通过这四个状态来表示对于资源的不同操作,这四个状态表现为我们请求的四种方式
/controller/1 HTTP GET :得到id为1 的资源
/controller/1 HTTP DELETE :删除id为1的资源
/controller/1 HTTP PUT :更新id为1 的资源
/controller/1 HTTP POST :增加id为1 的资源
package com.xiaohui.controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController//返回参数转换成字符串
public class HiddenHttp {
@RequestMapping(value = "testRest/{id}", method = RequestMethod.GET)
public String testGet(@PathVariable("id")String id) {
System.out.println("testGet,id:"+id);
return "show";
}
@RequestMapping(value = "testRest/{id}", method = RequestMethod.POST)
public String testPOST(@PathVariable("id")String id) {
System.out.println("testPOST,id:"+id);
return "show";
}
@RequestMapping(value = "testRest/{id}", method = RequestMethod.PUT)
public String testPUT(@PathVariable("id")String id) {
System.out.println("testPUT,id:"+id);
return "show";
}
@RequestMapping(value = "testRest/{id}", method = RequestMethod.DELETE)
public String testDELETE(@PathVariable("id")String id) {
System.out.println("testDELETE,id:"+id);
return "show";
}
}
testRest.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="testRest/10" method="get">
<input type="submit" value="Get"/>
</form>
<form action="testRest/10" method="post">
<input type="submit" value="POST"/>
</form>
<form action="testRest/10" method="post">
<input type="hidden" name="_method" value="PUT"/>
<input type="submit" value="PUT"/>
</form>
<form action="testRest/10" method="post">
<input type="hidden" name="_method" value="DELETE"/>
<input type="submit" value="DELETE"/>
</form>
</body>
</html>
web.xml
<!--配置hiddenHttpMethodFilter,将post请求转换为PUT或者DELETE请求-->
<filter>
<filter-name>hiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>hiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
转换原理
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
HttpServletRequest requestToUse = request;
if ("POST".equals(request.getMethod()) && request.getAttribute("javax.servlet.error.exception") == null) {
String paramValue = request.getParameter(this.methodParam);// "_method"
if (StringUtils.hasLength(paramValue)) {
String method = paramValue.toUpperCase(Locale.ENGLISH);
if (ALLOWED_METHODS.contains(method)) {
requestToUse = new HiddenHttpMethodFilter.HttpMethodRequestWrapper(request, method);
}
}
}
filterChain.doFilter((ServletRequest)requestToUse, response);
}
五、获取请求参数(参数注入)
紧耦合方式(了解)
DispatcherServlet中的service方法直接将此次请求的request对象传递给调用的单元方法即可。同时在单元方法上声明形参HttpServletRequest来接收request实参即可。
解耦合方式(熟练)
DispatcherServlet在其service方法中将请求数据根据需求从request对象中获取出来后,将数据直接传递给对应的单元方法使用。同时在单元方法上直接声明对应的形参接收请求数据即可。在单元方法上声明形参来接收请求数据时,形参名必须和请求数据的键名一致,DispatcherServlet会将调用单元方法的形参名作为请求数据的键名获取请求数据,然后传递给单元方法。
package com.xiaohui.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@RestController
public class TestDataController {
/*紧耦方式参数注入
* 使用传统的HttpServletRequest对象获取参数 javax.servlet
* */
@RequestMapping(value = "/getParamByRequest")
public String getParamByRequest(HttpServletRequest request, HttpServletResponse response){
String username = request.getParameter("username");
String password = request.getParameter("password");
System.out.println("username:"+username+",password:"+password);
System.out.println("getParamByRequest");
return "success";
}
/*解耦合方式参数注入
* HttpServletRequest对象获取参数 通过SpringMVC框架功能,自动转换参数
* 处理单元参数中参数名必须和请求中的参数名一致
* 如不一致,可以通过@RequestParam注解进行转换
* */
@RequestMapping(value = "/getParamByArgName")
public String getParamByArgName(String username, @RequestParam("password") String password){
System.out.println("username:"+username+",password:"+password);
System.out.println("getParamByArgName");
return "success";
}
}
dataPage.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
this is dataPage.jsp
<br/>
紧耦方式参数注入:
<form action="getParamByRequest" method="post">
用户名:<input type="text" name="username" /><br/>
密 码:<input type="password" name="password" />
<input type="submit" value="提交" />
</form>
<br/>
解耦合方式参数注入:
<form action="getParamByArgName" method="post">
用户名:<input type="text" name="username" /><br/>
密 码:<input type="password" name="password" />
<input type="submit" value="提交" />
</form>
</body>
</html>
1、POJO接收参数
package com.xiaohui.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person implements Serializable {
private String pname;
private String page;
private String gender;
private String[] hobby;
private String birthdate;
}
package com.xiaohui.controller;
import com.xiaohui.pojo.Person;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ReceiveDataController {
/*
* 使用POJO接收参数时,注意事项
* 提交的参数名必须和POJO的属性名保持一致
* springmvc底层通过反射给参数列表的属性赋值
* 通过set方法设置属性值的,不是直接通过操作属性
* POJO的属性一定要有set方法,要不然就会接收失败
* */
@RequestMapping("/getDataByPojo")
public String getDataByPojo(Person person){
System.out.println(person);
return "succes";
}
}
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<title>index.jsp</title>
<body>
<h2>Hello World!</h2>
<form action="getDataByPojo">
<p>姓名:<input type="text" name="pname"></p>
<p>年龄:<input type="text" name="page"></p>
<p>性别:
<input type="radio" name="gender" value="1" >男
<input type="radio" name="gender" value="0" >女
</p>
<p>爱好:
<input type="checkbox" name="hobby" value="1"> 篮球
<input type="checkbox" name="hobby" value="2"> 足球
<input type="checkbox" name="hobby" value="3"> 羽毛球
</p>
<p>生日:
<input type="text" name="birthdate">
</p>
<input type="submit">
</form>
</body>
</html>
//结果
Person(pname=aaaa, page=12, gender=1, hobby=[1, 2], birthdate=2020-01-01)
2、日期类型转换
方式一:注解方式 推荐使用
@DateTimeFormat(pattern = “yyyy-MM-dd”)可以用于参数列表和实体类的属性上
方法二:配置转换
定义转换器
package com.xiaohui.util;
import org.springframework.core.convert.converter.Converter;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class StringToDateConvert implements Converter<String, Date> {
private SimpleDateFormat simpleDateFormat =new SimpleDateFormat("yyyy-MM-dd");
@Override
public Date convert(String source) {
Date date =null;
try {
date = simpleDateFormat.parse(source);
} catch (ParseException e) {
throw new RuntimeException("类型转换失败");
}
return date;
}
}
在springMVC.xml中配置转换器
<!--数据转换工厂-->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<!--配置类型转换器-->
<property name="converters">
<array>
<!--注入自定义转换对象-->
<bean class="com.xiaohui.util.StringToDateConvert"></bean>
</array>
</property>
</bean>
<!--会自动注册RequestMappingHandlerMapping与RequestMappingHandlerAdapter两个Bean-->
<mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>
3、List和Map集合接收参数
package com.xiaohui.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Pet implements Serializable {
private String petName;
private String petType;
}
package com.xiaohui.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.format.annotation.DateTimeFormat;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
import java.util.Map;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person implements Serializable {
private String pname;
private String page;
private String gender;
private String[] hobby;
// @DateTimeFormat(pattern = "yyyy-MM-dd")
private Date birthdate;
private Pet pet;
private List<Pet> pets;
private Map<String,Pet> petMap;
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<title>index.jsp</title>
<body>
<h2>Hello World!</h2>
<form action="getDataByPojo">
<p>姓名:<input type="text" name="pname"></p>
<p>年龄:<input type="text" name="page"></p>
<p>性别:
<input type="radio" name="gender" value="1" >男
<input type="radio" name="gender" value="0" >女
</p>
<p>爱好:
<input type="checkbox" name="hobby" value="1"> 篮球
<input type="checkbox" name="hobby" value="2"> 足球
<input type="checkbox" name="hobby" value="3"> 羽毛球
</p>
<p>生日:
<input type="text" name="birthdate">
</p>
宠物:
<p>
宠物: 名字:<input type="text" name="pet.petName" >类型:<input type="text" name="pet.petType">
</p>
宠物List:
<p>
宠物1: 名字:<input type="text" name="pets[0].petName" >类型:<input type="text" name="pets[0].petType">
</p>
<p>
宠物2: 名字:<input type="text" name="pets[1].petName" >类型:<input type="text" name="pets[1].petType">
</p>
宠物Map:
<p>
宠物1: 名字:<input type="text" name="petMap['a'].petName" >类型:<input type="text" name="petMap['a'].petType">
</p>
<p>
宠物2: 名字:<input type="text" name="petMap['b'].petName" >类型:<input type="text" name="petMap['b'].petType">
</p>
<input type="submit">
</form>
</body>
</html>
package com.xiaohui.controller;
import com.xiaohui.pojo.Person;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ReceiveDataController {
@RequestMapping("/getDataByPojo")
public String getDataByPojo(Person person){
System.out.println(person);
return "succes";
}
}
后台输出结果:
4、编码问题
GET乱码问题
POST乱码问题
web.xml中配置编码过滤器
<!--Spring 中提供的字符编码过滤器-->
<filter>
<filter-name>encFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
5、常用注解
1、@RequestMapping
- 作用:
- 用于建立请求 URL 和处理请求方法之间的对应关系
- 出现位置:
- 类上: 请求 URL 的第一级访问目录。此处不写的话,就相当于应用的根目录。写的话需要以/开头
- 方法上: 请求 URL 的第二级访问目录
- 属性:
- value:用于指定请求的 URL。它和 path 属性的作用是一样的。
- method:用于指定请求的方式。
- params(了解):用于指定限制请求参数的条件。它支持简单的表达式。要求请求参数的 key 和 value 必须和 配置的一模一样。
- headers(了解):用于指定限制请求消息头的条件。
2、@RequestParam
作用:
把请求中指定名称的参数给控制器中的形参赋值。
属性:
value:请求参数中的名称。
required:请求参数中是否必须提供此参数。默认值:true。表示必须提供,如果不提供将报错。
@RequestMapping("/getDataParam")
public String getDataParam(@RequestParam(value = "pname") String name,@RequestParam(value = "page",required = true) String age){
System.out.println("name:"+name+",age:"+age);
return "success";
}
3、@PathVariable
- Restful的简介 :
- REST(英文:Representational State Transfer,简称 REST)restful是一种软件架构风格、设计风格,而不是标准,只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。
- restful 的优点
- 它结构清晰、符合标准、易于理解、扩展方便,所以正得到越来越多网站的采用。
- 作用:
- 用于绑定 url 中的占位符。例如:请求 url 中 /delete/{id},这个{id}就是 url 占位符。 url 支持占位符是 spring3.0 之后加入的。是 springmvc 支持 rest 风格 URL 的一个重要标志。
- 属性:
- value:用于指定 url 中占位符名称。
- required:是否必须提供占位符。
@RequestMapping("/getDataPathVariable/{id}/{uname}")
public String getDataPathVariable(@PathVariable("id") int id, @PathVariable("uname") String uname){
System.out.println("id:"+id+",uname:"+uname);
return "success";
}
4、@RequestHeader(了解)
- 作用:
- 用于获取请求消息头。
- 属性:
- value:提供消息头名称
- required:是否必须有此消息头
@RequestMapping("/getRequestHeader")
public String getRequestHeader(@RequestHeader(value = "Accept",required = false) String requestHeader){
System.out.println("requestHeader:"+requestHeader);
return "success";
}
5、@CookieValue(了解)
- 作用:
- 用于把指定 cookie 名称的值传入控制器方法参数。
- 属性:
- value:指定 cookie 的名称。
- required:是否必须有此 cookie
@RequestMapping("/getCookieValue")
public String getCookieValue(@CookieValue(value = "JSESSIONID",required = true) String jsessinid){
System.out.println("jsessinid:"+jsessinid);
return "success";
}
六、响应处理
在学习了SpringMVC的配置流程以及单元方法请求数据的获取后,我们可以使用SpringMVC搭建一个项目,在单元方法中使用SpringMVC提供的方式来获取请求信息,然后根据功能需求,声明请求处理的逻辑代码,进行请求的处理。当请求处理完成后,我们需要将此次请求的处理结果响应给浏览器,以前我们是自己在Servlet中使用response对象来完成响应的,那么在SpringMVC中如何响应请求的处理结果呢?
1、转发和重定向ServletAPI 实现
@RequestMapping("getDemo1")
public void getDemo1(HttpServletRequest request, HttpServletResponse response) throws Exception {
//请求转发
// request.getRequestDispatcher("/data.jsp").forward(request,response);
//响应重定向
String contextPath = request.getContextPath();
response.sendRedirect(contextPath + "/data.jsp");
}
单元方法的返回值类型设置void。因为使用response对象在单元方法中直接对此次请求进行了响应,不再通过DispatcherServlet了,既然已经响应了,就不需要再给DispatcherServlet返回值了。在单元方法上声明HttpServletResponse形参,来接收此次请求的response对象。
2、使用forward关键字完成响应
/*
* 返回字符串告诉DispatcherServlet跳转的路径
* 在路径之前放上一个forward: 关键字,就是请求转发
* 如果路径前的关键字是forward,那么可以省略不写
* */
@RequestMapping("demo2")
public String testDemo2() throws Exception {
//return "forward:/forwardPage.jsp";
return "/data.jsp";
}
使用通过单元方法的返回值来告诉DispatcherServlet请求转发指定的资源,如果是请求转发,forward关键字可以省略不写的
3、使用redirect关键字完成响应
/*
* 返回字符串告诉DispatcherServlet跳转的路径
* 在路径之前放上一个redirect: 关键字,就是重定向
* 如果路径前的关键字是redirect,那么不可以省略
* /表示当前项目下.这里不需要项目的上下文路径
* */
@RequestMapping("demo3")
public String testDemo3() throws Exception {
return "redirect:/redirectPage.jsp";
}
使用通过单元方法的返回值来告诉DispatcherServlet重定向指定的资源,注意这个redirect关键字不可以省去
4、使用View视图转发和重定向
@RequestMapping("demo4")
public View testDemo4(HttpServletRequest request) throws Exception {
View view = null;
// 请求转发
// view = new InternalResourceView("/forwardPage.jsp");
//重定向
view = new RedirectView(request.getContextPath()+"/redirectPage.jsp");
return view;
}
RedirectView中所做的操作,最终的实现是在renderMergedOutputModel中完成实现的,简单来说RedirectView实现了链接的重定向,并且将数据保存到FlashMap中,这样在跳转后的链接中可以获取一些数据.
5、使用ModelAndView转发重定向
@RequestMapping("demo5")
public ModelAndView testDemo5(HttpServletRequest request) throws Exception {
ModelAndView modelAndView = new ModelAndView();
//请求转发
// modelAndView.setViewName("forward:/forwardPage.jsp");
// modelAndView.setView(new InternalResourceView("/forwardPage.jsp"));
//重定向
modelAndView.setViewName("redirece:/redirectPage.jsp");
modelAndView.setView(new RedirectView(request.getContextPath()+"/redirectPage.jsp"));
return modelAndView;
}
ModelAndView中的Model代表模型,View代表视图,这个名字就很好地解释了该类的作用。业务处理器调用模型层处理完用户请求后,把结果数据存储在该类的model属性中,把要返回的视图信息存储在该类的view属性中,然后让该ModelAndView返回该Spring MVC框架。
6、ResponseBody 响应 json 数据
当浏览器发起一个ajax请求给服务器,服务器调用对应的单元方法处理ajax请求。而ajax的请求在被处理完成后,其处理结果需要直接响应。而目前我们在单元方 法中响应ajax请求,使用的是response对象,需要我们自己将要响应的数据转换 为json字符串响应,比较麻烦,而我们一直希望在单元方法中无论是否是ajax请求,都使用return语句来完成资源的响应,怎么办?
既然我们希望使用单元方法的返回值来响应ajax请求的处理结果,而目前DispatcherServlet的底层会将单元方法的返回值按照请求转发或者重定向来处理,所以就需要我们告诉DispatcherServlet,单元方法的返回值不要按照请求转发或者重定向处理,而是按照直接响应处理,将单元方法的返回值直接响应给浏览器。
第一步 导入jackson的jar
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.2</version>
</dependency>
第二步 声明单元方法处理ajax请求并,在单元方法上新增注解@ResponseBody
@Controller
public class AjaxController {
@ResponseBody
@RequestMapping("testAjax")
public Pet testAjax(Person p){
System.out.println(p);
Pet pet = new Pet("Tom","cat");
return pet;
}
}
注意:把我们要响应的数据直接return即可,返回值类型为要return的数据类型。
第三步: 在ajax的回调函数中,无需再次使用eval函数将响应数据转换为json对象
直接使用即可。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<script src="js/jquery.min.js"></script>
</head>
<script>
$(function(){
$("#btn").click(function(){
$.get("testAjax",{pname:"晓明",page:"10"},function(data){
console.log(data);
},"json")
})
})
</script>
<body>
<input id="btn" type="button" value="testJson"/>
</body>
</html>
关于 @RestController
相当于@Controller+@ResponseBody两个注解的结合,返回json数据不需要在方法前面加@ResponseBody注解了,但使用@RestController这个注解,就不能返回jsp,html页面,视图解析器无法解析jsp,html页面
@RestController
public class AjaxController {
@RequestMapping("testAjax")
public Pet testAjax(Person p){
System.out.println(p);
Pet pet = new Pet("Tom","cat");
return pet;
}
}