目录
使用ModelAndView向request域存储数据以及实现页面跳转
b>在SpringMVC的核心配置文件中开启mvc的注解驱动
c>在处理器方法上使用@ResponseBody注解进行标识
一、什么是SpringMVC?
它是基于MVC开发模式的框架,用来优化MVC三层中的控制器。它是Spring家族的一员。它也具备IOC和AOP。
SpringMVC 也叫Spring web mvc。是Spring 框架的一部分,是在Spring3.0 后发布的。
什么是MVC?
它是一种开发模式,它是模型视图控制器的简称.所有的web应用都是基于MVC开发.
M:模型层,包含实体类bean,业务逻辑层service,数据访问层mapper
V:视图层,html,javaScript,vue等都是视图层,用来显现数据
C:控制器,它是用来接收客户端的请求,并返回响应到客户端的组件,Servlet就是组件
二、怎么用SpringMVC
SpringMVC执行的流程
执行流程说明:
- 向服务器发送HTTP请求,请求被前端控制器 DispatcherServlet 捕获。
- DispatcherServlet 根据<servlet-name>中的配置对请求的URL进行解析,得到请求资源标识符(URI)。然后根据该URI,调用 HandlerMapping 获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以 HandlerExecutionChain 对象的形式返回。
- DispatcherServlet 根据获得的Handler,选择一个合适的 HandlerAdapter。
- 提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)。在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作:
HttpMessageConveter:将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息。
数据转换:对请求消息进行数据转换。如String转换成Integer、Double等。
数据格式化:对请求消息进行数据格式化。如将字符串转换成格式化数字或格式化日期等。
数据验证:验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中。
- Handler(Controller)执行完成后,向 DispatcherServlet 返回一个 ModelAndView 对象。
- 根据返回的ModelAndView,选择一个适合的 ViewResolver(必须是已经注册到Spring容器中的ViewResolver)返回给DispatcherServlet。
- ViewResolver 结合Model和View,来渲染视图。
- 视图负责将渲染结果返回给客户端
<mvc:annotation-driven/>标签的使用
<mvc:annotation-driven/>会自动注册两个bean,分别为DefaultAnnotationHandlerMapping和AnnotationMethodHandlerAdapter。
这两个bean就是SpringMVC执行流程中的两大帮手HandlerMapping和HandlerAdapter。
这个标签很重要,一般直接在springmvc.xml中直接声明以便后续使用。
是springmvc为@controller分发请求所必须的。除了注册了这两个bean,还提供了很多支持。
1)支持使用ConversionService 实例对表单参数进行类型转换;
2)支持使用 @NumberFormat 、@DateTimeFormat;
3)注解完成数据类型的格式化;
4)支持使用 @RequestBody 和 @ResponseBody 注解;
5)静态资源的分流也使用这个标签;
SpringMVC开发步骤
1、创建一个web的maven工程
2、修改pom.xml文件,添加SpringMVC的依赖,添加Servlet的依赖
<?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>com.lin.spring</groupId>
<artifactId>springmvc_001_demo</artifactId>
<version>1.0-SNAPSHOT</version>
<!--打包方式改为war,因为是web项目-->
<packaging>war</packaging>
<dependencies>
<!--添加springmvc框架的依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<!--添加servlet的依赖,因为我们是web应用开发,编译的时候需要用到servlet-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<!--指明该依赖只在编译和测试的时候用,运行的时候由tomcat自带的servlet-api提供。-->
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
<include>**/*.properties</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.xml</include>
<include>**/*.properties</include>
</includes>
</resource>
</resources>
</build>
</project>x
3、添加SpringMVC的配置文件,指定包扫描,添加视图解析器ViewResolver。
<?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 https://www.springframework.org/schema/context/spring-context.xsd">
<!--添加包扫描-->
<context:component-scan base-package="com.lin.springmvc.controller"></context:component-scan>
<!--添加springmvc中的视图解析器ViewResolver-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--配置前缀-->
<property name="prefix" value="/admin/"></property>
<!--配置后缀-->
<property name="suffix" value=".jsp"></property>
</bean>
</beans>
4、在web.xml文件中注册springMVC框架(所有的web请求都是基于servlet的)
1、用了SpringMVC框架的web项目请求执行的流程
核心处理器
index.jsp<--------------->DispatcherServlet<------------------->SpringMVC的处理器是一个普通的方法
one.jsp <--------------->DispatcherServlet<------------------->SpringMVC的处理器是一个普通的方法前端发送请求到tomcat服务器,请求被SpringMVC的大哥前端控制器 DispatcherServlet 捕获,然后DispatcherServlet将请求转发给控制层controller中对应的普通方法
2、为啥要在web.xml文件中注册DispatcherServlet类?
因为这个类不在我们自己的源码中,所以不能通过@WebServlet注解指定该类的访问路径,所以只能在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">
<!--注册SpringMVC框架中的前端控制器DispatcherServlet,
对前端发送的请求进行拦截统一处理,这个控制器会将请求转发到那个控制器的普通方法上-->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--在这里可以配置Servlet对象的初始化信息-->
<!--这里得给DispatcherServlet说它的SpringMVC是哪个,就是将springmvc的配置文件告诉DispatcherServlet-->
<!--contextConfigLocation参数定义了要装入的 Spring 配置文件-->
<init-param>
<param-name>contextConfigLocation</param-name>
<!--会到后面编译后的target中的类路径中找springmvc.xml-->
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!--在tomcat服务器启动的时候就创建SpringMVC的前端控制器DispatcherServlet的对象-->
<!--因为DispatcherServlet要初始化很多内容,放在服务器启动就创建,第一次访问的时候速度就快点。-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<!--
指定拦截什么样的请求
http://localhost:8080/one
http://localhost:8080/index.jsp
http://localhost:8080/demo.action
<a href="${pageContext.request.contextPath}/demo.action">访问服务器</a>
以 .action 和 .test 结尾的请求路径,都会被DispatcherServlet程序拦截处理。
-->
<url-pattern>*.action</url-pattern>
<url-pattern>*.test</url-pattern>
</servlet-mapping>
</web-app>
5、在webapp目录下新建admin目录,在admin目录下新建main.jsp页面
6、开发控制器(相当于Servlet),它是一个普通的类。
@Controller// 交给SpringMVC创建对象,是一个控制层组件
public class HelloServlet {
/**
* 以前的Servlet的规范
* protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {}
*
* 控制器的普通方法
* action/servlet中所有的功能实现都是由方法来完成的
* action/servlet方法的规范
* 1)访问权限是public
* 2)方法的返回值任意
* 3)方法名称任意
* 4)方法可以没有参数,如果有可是任意类型
* 5)要使用@RequestMapping注解来声明一个访问的路径(名称)
*
*/
// http://localhost:8080/springmvc/demo.action
// 当浏览器发送的请求路径是 /demo 的时候,就会来执行这个/demo注解所对应的方法。
@RequestMapping("/demo")
public String demo(){
System.out.println("服务器被访问到了.......");
return "main"; //可以直接跳到/admin/main.jsp页面上
}
// 当浏览器发送的请求路径是 /my.test 的时候,就会来执行这个注解所对应的方法。
@RequestMapping("/my.test")
public String demo2(){
System.out.println("服务器被访问到了2.......");
// 返回要跳转的视图名称,交给ViewResolver处理解析
return "test"; //可以直接跳到/admin/test.jsp页面上
}
}
7、web目录下的index.jsp页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>index.jsp</title>
</head>
<body>
<h1>index.jsp</h1>
<%--向tomcat服务器发送请求:/项目根路径/demo.action --%>
<%--http://localhost:8080/springmvc/demo.action--%>
<a href="${pageContext.request.contextPath}/demo.action">访问服务器</a>
<a href="${pageContext.request.contextPath}/my.test">访问服务器2</a>
</body>
</html>
个人理解2022.6.17
当前端发送http://localhost:8080/springmvc/my.test给tomcat服务器时,这个请求会被Springmvc的DispatcherResolver拦截,然后DispatcherServlet将请求转发给控制层controller中对应的普通方法,就是@RequestMapping("/my.test")这个注解对应的方法:
public String demo2(){
System.out.println("服务器被访问到了2.......");
return "test"; //可以直接跳到/admin/test.jsp页面上
}这个方法执行后返回"test",会被SpringMVC的视图解析器ViewResolver解析处理,加上前缀和后缀变成 /admin/test.jsp,然后将这个页面渲染后转发到这个页面中。
总结:
浏览器发送请求给服务器,若请求地址符合前端控制器DispatcherServlet的url-pattern,该请求就会被前端控制器DispatcherServlet处理。
前端控制器会读取SpringMVC的核心配置文件,通过扫描组件找到控制器带@Controller注解的类,将请求地址和控制器中@RequestMapping注解的value属性值进行匹配,若匹配成功,该注解所标识的控制器方法就是处理请求的方法。处理请求的方法需要返回一个字符串类型的视图名称,该视图名称会被视图解析器解析,加上前缀和后缀组成视图的路径,对视图进行渲染,最终转发到视图所对应页面
@RequestMapping注解详解
@RequestMapping注解详解
此注解就是来映射服务器访问的路径.
1)此注解可加在方法上,请求路径对应的某个方法
@RequestMapping("/demo")
public String demo(){
System.out.println("服务器被访问到了.......");
return "main"; //可以直接跳到/admin/main.jsp页面上
}
<a href="${pageContext.request.contextPath}/demo.action">访问服务器</a>2)此注解可以加在类上,相当于是包名(虚拟路径),区分不同类中相同的action的名称
@RequestMapping("/user")
public class DemoAction1 {..}
<a href="${pageContext.request.contextPath}/user/demo.action">访问服务器</a>3)此注解可区分get请求和post请求
@Controller
public class ReqAction {
@RequestMapping(value = "/req",method = RequestMethod.GET)
public String req(){
System.out.println("我是处理get请求的........");
return "main";
}
@RequestMapping(value = "/req" ,method = RequestMethod.POST)
public String req1(){
System.out.println("我是处理post请求的........");
return "main";
}
}
@ResponseBody注解
- @ResponseBody的作用其实是将java对象转为json格式的数据响应给浏览器。
- @responseBody注解的作用是将controller的方法返回的对象通过适当的转换器转换为指定的格式之后,写入到response对象的body区,通常用来返回JSON数据或者是XML数据。
- 注意:在使用此注解之后不会再走视图处理器,平时我们controller中的方法返回字符串都是加上视图解析器中的前缀和后缀形成资源的路径,然后将此路径对应的资源响应给浏览器,而加上此注解就不走这套流程。而是直接将数据写入到输入流中,他的效果等同于通过response对象输出指定格式的数据。
SpringMVC要优化的方面
- 客户端提交数据的优化
- 控制器方法的返回值优化
- 资源跳转的优化
- 携带数据进行资源跳转的优化
- 简化返回处理(服务器响应优化)
1、客户端提交数据的5种优化
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>数据提交的方式</title>
</head>
<body>
<p>1.单个提交数据</p>
<br><br>
<form action="${pageContext.request.contextPath}/dataone.action" method="get">
姓名:<input name="myName"><br>
年龄:<input name="age"><br>
<input type="submit" value="提交">
</form>
<br>
<p>2.对象封装提交数据</p>
<form action="${pageContext.request.contextPath}/datatwo.action" method="get">
姓名:<input name="uName"><br>
年龄:<input name="age"><br>
<input type="submit" value="提交">
</form>
<br>
<%--仅限于超链接或地址拦提交数据.它是一杠一值提交数据,一杠一大括号接收对应的数据,使用注解@PathVariable来解析. --%>
<p>3.动态占位符提交数据</p>
<a href="${pageContext.request.contextPath}/datathree/张三/26.action">动态占位符提交数据</a>
<br>
<p>4.请求参数和接收形参名不一致</p>
<form action="${pageContext.request.contextPath}/datafour.action" method="get">
姓名:<input name="name"><br>
年龄:<input name="age"><br>
<input type="submit" value="提交">
</form>
<br>
<p>5.手动提取数据request.getParameter(name)</p>
<form action="${pageContext.request.contextPath}/datafive.action" method="get">
姓名:<input name="name"><br>
年龄:<input name="age"><br>
<input type="submit" value="提交">
</form>
</body>
</html>
package com.lin.springmvc.controller;
import com.lin.springmvc.bean.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import javax.servlet.http.HttpServletRequest;
/**
* @author shkstart
* @create 2022-06-17 15:20
*/
@Controller
public class DataSubmitAction {
/*
* 姓名:<input name="myName"><br>
年龄:<input name="age"><br>
* myName=老同学&age=21
*
* 前端提交的数据会按前端请求参数名称自动赋值给控制层请求对应的方法的形参中,
* 并且类型会自动转换,要求前端请求参数名称和方法的形参名一样。
* */
@RequestMapping("/dataone")
public String dataOne(String myName,int age){
System.out.println("获取到前端数据了,name=" + myName + ",age=" + age);
return "success";
}
/*
* 前端提交的数据:
* 姓名:<input name="myName"><br>
年龄:<input name="age"><br>
*
* 后端用一个实体类接收,
* 在提交请求中,保证请求参数的名称与实体类中成员变量的名称一致,且实体类中必须提供get和set方法用于注入值,
* 则可以自动创建对象,自动类型转换,自动封装数据到对象中,
* 然后将数据对象赋值给请求对应的方法的形参引用中.
*
* */
@RequestMapping("/datatwo")
public String dataTwo(User user){
System.out.println(user);// User{uName='阿吉', age=21}
return "success";
}
/*
* 仅限于超链接或地址拦提交数据.它是一杠一值,一杠一大括号,使用注解@PathVariable来解析.
* @PathVariable指明是路径中的变量value属性标识变量名,用来取出路径中的请求参数。
*
* <a href="${pageContext.request.contextPath}/datathree/张三/26.action">动态占位符提交数据</a>
* */
@RequestMapping("/datathree/{uName}/{age}")
public String dataThree(@PathVariable("uName")String name,@PathVariable int age){
System.out.println("获取到前端数据了,name=" + name + ",age=" + age);
// 获取到前端数据了,name=张三,age=26
return "success";
}
/*
请求参数:
* 姓名:<input name="name"><br>
年龄:<input name="age"><br>
请求参数的名称 和 控制层的方法形参名 不一致,
在控制层的方法形参名前使用@RequestParam注解来指定形参名对应的请求参数名。
* */
@RequestMapping("/datafour")
public String dataFour(@RequestParam("name") String uName,@RequestParam("age") String uAge){
System.out.println("获取到前端数据了,name=" + uName + ",age=" + uAge);
return "success";
}
/*
* 前端提交的数据:
* 姓名:<input name="name"><br>
年龄:<input name="age"><br>
*
* 给控制层的方法提供一个HttpServletRequest,就可以像以前那样获取请求参数
* request.getParameter("name");
* */
@RequestMapping("/datafive")
public String dataFive(HttpServletRequest request){
String name = request.getParameter("name");
Integer age = Integer.valueOf(request.getParameter("age"));
System.out.println("获取到前端数据了,name=" + name + ",age=" + age);
return "success";
}
}
2、控制器方法的返回值优化
- String:服务器端资源的地址,会自动拼接前缀和后缀后然后转发展示。还可以屏蔽自动拼接字符串,可以指定返回的路径.
- Object:返回json格式的对象.自动将对象或集合转为json.使用的jackson工具进行转换,必须要添加jackson依赖.一般用于ajax请求.
- void:无返回值,一般用于ajax请求.
- 基本数据类型,用于ajax请求.
- ModelAndView:返回数据和视图对象,现在用的很少.
3、资源跳转的优化
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
<p>测试资源跳转的四种方式</p>
<br>
<a href="${pageContext.request.contextPath}/one.action">1.请求转发到页面上</a><br>
<a href="${pageContext.request.contextPath}/two.action">2.请求转发到action方法上</a><br>
<a href="${pageContext.request.contextPath}/three.action">3.重定向到页面上</a><br>
<a href="${pageContext.request.contextPath}/four.action">4.重定向到action方法上</a>
</body>
</html>
package com.lin.springmvc.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @author shkstart
* @create 2022-06-18 16:07
*/
@Controller
public class JumpAction {
@RequestMapping("/one")
public String forwardToPage(){
System.out.println("请求转发到target.jsp页面.......");
// 这个返回的字符串会被SpringMVC的视图解析器加上前缀/myPage/,加上后缀.jsp变成/myPage/target.jsp
// 默认使用请求转发的方式转发到这个页面上/myPage/target.jsp
return "target";
}
@RequestMapping("/two")
public String forwardToAction(){
System.out.println("请求转发到action方法中......");
// 倘若返回的字符串中包含 forward:
// 就会被视图解析器解析是要进行转发到action,所以不会进行前缀和后缀的拼接
// forward: 表示我要进行资源的转发了,转发请求路径是 /other.action
return "forward:/other.action";
// 要请求转发这样是错误的,会变成 /myPage//other.action.jsp
// return "/other.action";
}
@RequestMapping("/three")
public String redirectToPage(){
System.out.println("重定向到target.jsp页面.....");
// redirect: 表示我要重定向了,视图解析器会屏蔽前缀后缀的拼接,重定向到 /myPage/target.jsp
return "redirect:/myPage/target.jsp";
}
@RequestMapping("/four")
public String redirectToAction(){
System.out.println("重定向到action方法.....");
return "redirect:/other.action";
}
}
view-controller
@RequestMapping("/login")
public String doLogin(){
// 没有任何业务逻辑代码
return "goLogin";// 跳转到/pages/goLogin.jsp登录页面
}
当控制器方法中,没有任何业务逻辑代码,仅仅只是用来实现页面跳转,即只需要设置视图名称时,可以将处理器方法使用view-controller标签进行表示
注意:
当SpringMVC中设置任何一个view-controller时,其他控制器中的请求映射将全部失效,此时需要在SpringMVC的核心配置文件中设置开启mvc注解驱动的标签:
<mvc:annotation-driven />
<!--
path:设置处理的请求地址
view-name:设置请求地址所对应的视图名称
-->
<mvc:view-controller path="/login" view-name="goLogin"></mvc:view-controller>
SpringMVC中文编码设置
<br>
<p>5.手动提取数据request.getParameter(name)</p>
<form action="${pageContext.request.contextPath}/datafive.action" method="post">
姓名:<input name="name"><br>
年龄:<input name="age"><br>
<input type="submit" value="提交">
</form>
@RequestMapping("/datafive")
public String dataFive(HttpServletRequest request){
String name = request.getParameter("name");
Integer age = Integer.valueOf(request.getParameter("age"));
System.out.println("获取到前端数据了,name=" + name + ",age=" + age);
return "success";
}
乱码:
解决方案
在 web.xml 中注册字符集过滤器,即可解决 Spring 的请求参数的中文乱码问题。不过,最好将该过滤器注册在其它过滤器之前。因为过滤器的执行是按照其注册顺序进行的。
<!--配置字符集编码过滤器不过,最好将该过滤器注册在其它过滤器之前-->
<filter>
<filter-name>endode</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<!--
配置该类对象的初始化信息
private String encoding; 字符集
private boolean forceRequestEncoding; 请求编码是否强制是否设置的字符集
private boolean forceResponseEncoding; 响应编码是否强制是否设置的字符集
-->
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceRequestEncoding</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>endode</filter-name>
<!--所有的请求都拦截下来进行编码设置-->
<url-pattern>/*</url-pattern>
</filter-mapping>
SpringMVC中的Ajax请求
完成ajax请求访问服务器,返回学生集合.
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>测试ajax请求</title>
<%--导入jQuery--%>
<script src="js/jquery-3.3.1.js"></script>
</head>
<body>
<a href="javascript:getStu()">发送请求给服务器,返回学生列表</a>
<div id="stuInfo">等待数据中......</div>
<script type="text/javascript">
function getStu() {
// 发送Ajax请求给服务器,获取学生列表
$.ajax({
url:"${pageContext.request.contextPath}/list.action",
dataType:"json",
/*
指定服务器成功返回后做什么
sutList是服务器端返回的数据
*/
success:function (sutList) {
alert(sutList);
// 用于拼接学生信息
var stuInfo = "";
// 遍历
$.each(sutList,function (index, stu) {
stuInfo += stu.name + "---" + stu.age + "<br>";
})
// 将学生信息回显到界面中
$("#stuInfo").html(stuInfo);
}
})
}
</script>
</body>
</html>
/**
* @author shkstart
* @create 2022-06-17 22:00
*/
@Controller
public class ListStudentAction {
/*
* 控制器返回值类型是Object:
* 返回json格式的对象.自动将对象或集合转为json.
* 使用的jackson工具进行转换,必须要添加jackson依赖.一般用于ajax请求.
* */
@RequestMapping("/list")
@ResponseBody // 此注解专门用于处理解析Ajax请求
public List<Student> getStuList(){
List<Student> stuList = new ArrayList<>();
stuList.add(new Student("张三",3));
stuList.add(new Student("李四",3));
stuList.add(new Student("王五",3));
System.out.println(stuList);
// 调用json转换工具ObjectMapper进行转换
return stuList;// SpringMVC自动将对象转换为json数组
}
}
SpringMVC默认的参数类型(内置对象)
不需要去创建,直接拿来使用即可.
1)HttpServletRequest
2)HttpServletResponse
3)HttpSession
4)Model
5)Map
6)ModelMap注意:Map,Model,ModelMap和request一样,都使用请求作用域进行数据传递。所以服务器端想利用这三个存储数据,然后携带这些数据到别的页面或方法必须使用请求转发。
HttpServletRequest,HttpSession,Model,Map,ModelMap主要都是用来存储数据的,都有类似setAttribute(name,value);方法。只有HttpServletResponse是用来处理响应的相关问题。
<br>
<p>测试SpringMVC默认的参数</p><br>
<a href="${pageContext.request.contextPath}/data.action?name=你好">去存储数据</a>
@Controller
public class DataAction {
@RequestMapping("/data")
public String keepData(HttpServletRequest request, Map map, Model model, ModelMap modelMap, HttpSession session,
HttpServletResponse response) throws IOException {
// 进行存储数据
request.setAttribute("requestString","请求域中的数据");
map.put("mapString","map中的数据");
model.addAttribute("modelString","model中的数据");
modelMap.addAttribute("modelMapString","modelMap中的数据");
session.setAttribute("sessionString","session域中的数据");
PrintWriter out = response.getWriter();
out.println("hello from response");
return "target";
}
}
SpringMVC日期处理
1)日期的提交处理
A.单个日期处理
要使用注解@DateTimeFormat,此注解必须搭配springmvc.xml文件中的<mvc:annotationdriven标签>B.类中全局日期处理
注册一个注解,用来解析本类中所有的日期类型,自动转换.// 以上日期处理有一个问题,就是每涉及一个日期提交和日期接收处理,都要使用@DateTimeFormat,这样比较麻烦 // 可以定义一个全局的日期处理,注册一个注解,用来解析本类中所有的日期类型,自动转换. /** * * @param webDataBinder web数据捆绑器 */ @InitBinder public void initBinder(WebDataBinder webDataBinder){ // 注册一个自定义的编辑器 webDataBinder.registerCustomEditor(Date.class,new CustomDateEditor(sdf,true)); }2)日期的显示处理
使用jstl格式标签库,在要显示日期的页面上格式化后端传来的日期类型的日期。
1)日期的提交处理
package com.lin.springmvc.controller;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.RequestMapping;
import java.text.SimpleDateFormat;
import java.time.format.DateTimeFormatter;
import java.util.Date;
/**
* @author shkstart
* @create 2022-06-18 20:05
*/
@Controller
public class DateAction {
// 用来格式化日期类型
private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
/*
* 日期:<input type="date" name="myDate"><br>
<input type="submit" name="提交日期">
* */
// @RequestMapping("/date")
// java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required type 'java.util.Date': no matching editors or conversion strategy found
// 日期类型和字符串不能自动转换,前端发送过来的字符串不能自动转为日期类型,需要使用@DateTimeFormat转
/* public String receiveDate(@DateTimeFormat(pattern = "yyyy-MM-dd") Date myDate){
System.out.println(myDate);
String beautifulDate = sdf.format(myDate);
System.out.println(beautifulDate);
return "date";
}*/
// 以上日期处理有一个问题,就是每涉及一个日期提交和日期接收处理,都要使用@DateTimeFormat,这样比较麻烦
// 可以定义一个全局的日期处理,注册一个注解,用来解析本类中所有的日期类型,自动转换,就不用每个日期都加上@DateTimeFormat注解.
/**
*
* @param webDataBinder web数据捆绑器
*/
@InitBinder
public void initBinder(WebDataBinder webDataBinder){
// 注册一个自定义的编辑器
webDataBinder.registerCustomEditor(Date.class,new CustomDateEditor(sdf,true));
}
@RequestMapping("/date")
public String receiveDate(Date myDate){
System.out.println(myDate);
String beautifulDate = sdf.format(myDate);
System.out.println(beautifulDate);
return "date";
}
}
2)日期的显示处理
<%--前端发送查询学生信息的请求给后端--%>
<a href="${pageContext.request.contextPath}/listStu.action">查询学生信息</a>
<!--jstl标签库依赖-->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
@Controller
public class StudentAction {
// 用来格式化日期的
private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
@RequestMapping("/listStu")
public String getStuList(HttpServletRequest request) throws ParseException {
// 模拟从数据库中查询出来的学生集合
List<Student> stuList = new ArrayList<>();
stuList.add(new Student("周杰伦",sdf.parse("1993-1-3")));
stuList.add(new Student("昆凌",sdf.parse("1996-1-3")));
stuList.add(new Student("汪峰",sdf.parse("1993-5-3")));
// 将学生集合保存到request请求域中,这里保存的日期是java.util.Date
request.setAttribute("stuList",stuList);
return "stuShow";
}
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%--导入jstl核心标签库--%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%--导入jstl格式化标签库--%>
<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<html>
<head>
<title>学生列表</title>
</head>
<body>
<table id="stuInfo" width="500px" border="1px">
<tr>
<th>姓名</th>
<th>生日</th>
</tr>
<%--遍历输出保存在request域中的学生集合stuList--%>
<c:forEach items="${stuList}" var="stu">
<tr>
<td>${stu.name}</td>
<%--这里的日期类型输出的样子不是我们想要的,可以使用jstl格式化--%>
<%--<fmt:formatDate value="${stu.birthday}" pattern="yyyy-MM-dd"></fmt:formatDate>--%>
<td>${stu.birthday}-- <fmt:formatDate value="${stu.birthday}" pattern="yyyy-MM-dd"></fmt:formatDate> </td>
</tr>
</c:forEach>
</table>
</body>
</html>
资源放在WEB-INF目录下
很多企业会将动态资源放在WEB-INF目录下,这样可以保证资源的安全性,就不可以在外面直接访问。在WEB-INF目录下的动态资源不可以直接访问,必须要通过请求转发的方式进行访问。这样避免了通过地址栏直接对资源的访问。重定向也无法访问WEB-INF目录下de 资源。
@Controller
public class WebINFAction {
// http://localhost:8080/springmvc/webinf1.action
// 有个问题,其他人只要知道以上这个网址,就可以直接访问WEB-INF目录下的资源,还是不安全,
// 所以我们需要拦截器对请求进行拦截预处理,检查权限等.
@RequestMapping("/webinf1.action")
public String forwardToWebInf(){
System.out.println("请求转发到webinf目录中的页面.....");
// WEB-INF目录下的资源只能通过请求转发的方式进行访问
return "forward:/WEB-INF/jsp/webinf.jsp";
}
@RequestMapping("/webinf2.action")
public String redirectToWebInf(){
System.out.println("请求转发到webinf目录中的页面.....");
// WEB-INF目录下的资源无法通过重定向或者地址栏输入网址的方式进行访问
return "redirect:/WEB-INF/jsp/webinf.jsp";
}
}
SpringMVC的拦截器
可以对请求和响应进行的额外的处理。在请求和响应的过程中添加预处理,后处理和最终处理.
拦截器的应用场景:
1、日志记录:记录请求信息的日志
2、权限检查,如登录检查
3、性能检测:检测方法的执行时间
拦截器的执行原理:
拦截器可以有多个,形成一条拦截器链,每个拦截器检查不同的东西。
拦截器的执行时机:
1)preHandle():在请求被处理之前进行操作,请求预处理
2)postHandle():在请求被处理之后,即对应的controller处理完之后执行,然后通知DispatcherServlet将响应内容返回。
3)afterCompletion:DispatcherServlet将响应内容返回,所有视图渲染完后执行,所有的请求响应结束后执行善后工作,清理对象,关闭资源 最终处理
拦截器实现的两种方式:
1)继承HandlerInterceptorAdapter的父类,单一继承
2)实现HandlerInterceptor接口,实现的接口,推荐使用实现接口的方式,把继承的机会留给别的
步骤:
先编写拦截器实现类
/**
* 登录拦截器
*
* @author shkstart
* @create 2022-06-19 14:02
*/
public class LoginInterceptor implements HandlerInterceptor {
// 在请求到达控制层方法前拦截下来做处理,
// 该方法在处理器方法执行之前执行。其返回值为 boolean,若为 true,则紧接着会放行请求执行处理器方法,
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 判断是否登陆过
HttpSession session = request.getSession(false);
if (session == null || session.getAttribute("user") == null){
// 说明没登录过,转发到登陆页面,并不放行请求
request.setAttribute("msg","您还没登录过!");
request.getRequestDispatcher("/WEB-INF/jsp/login.jsp");
return false;
}else {
// 说明登录过,则将请求放行
return true;
}
}
}
再在springmvc.xml文件中注册拦截器
<!--注册拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<!--映射要拦截的请求-->
<mvc:mapping path="/**"/>
<!--不拦截的请求:要去登陆页面的请求 和 检查登录信息的请求-->
<mvc:exclude-mapping path="/login"></mvc:exclude-mapping>
<mvc:exclude-mapping path="/checkLogin"></mvc:exclude-mapping>
<!--具体的拦截器实现类-->
<bean class="com.lin.springmvc.interceptor.LoginInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
SSM整合
SpringMVC通过域对象实现数据共享
通过request域对象共享数据
@RequestMapping("/testServletAPI")
public String testServletAPI(HttpServletRequest request){
request.setAttribute("testScope", "hello,servletAPI");
return "success";
}
使用Model向request域对象共享数据
@RequestMapping("/testModel")
public String testModel(Model model){
model.addAttribute("testScope", "hello,Model");
return "success";
}
Model是SpringMVC提供的类型,当请求到达Dispatcher时,SpringMVC会为我们创建Model的对象传递给控制器方法赋值给model,往model中存储数据和往request域中存储数据一样。可以直接在转发的页面中通过${key}获取值。
使用ModelAndView向request域存储数据以及实现页面跳转
@RequestMapping("/testModelAndView")
public ModelAndView testModelAndView(){
/**
* ModelAndView有Model和View的功能
* Model主要用于向请求域共享数据
* View主要用于设置视图,实现页面跳转
*/
ModelAndView mav = new ModelAndView();
//向请求域共享数据
mav.addObject("testScope", "hello,ModelAndView");
//设置视图,实现页面跳转,跳转到/page/success.jsp
mav.setViewName("success");
return mav;
}
通过session域实现共享数据
@RequestMapping("/testSession")
public String testSession(HttpSession session){
session.setAttribute("testSessionScope", "hello,session");
return "success";
}
通过application域共享数据
application域是应用级别的域
@RequestMapping("/testApplication")
public String testApplication(HttpSession session){
// 通过session对象获取域对象,ServletContext
ServletContext application = session.getServletContext();
application.setAttribute("testApplicationScope", "hello,application");
return "success";
}
HttpMessageConverter,报文信息转换器,将请求报文转换为Java对象,或将Java对象转换为响应报文
HttpMessageConverter提供了两个注解和两个类型:@RequestBody,@ResponseBody,RequestEntity,ResponseEntity
SpringMVC中的@RequestBody注解
用于获取请求报文的请求体
@RequestBody可以获取前端发送请求的请求体,所以要求是POST请求,因为GET请求没有请求体。需要在控制器方法设置一个形参来接受这个请求体,在这个形参前面使用@RequestBody进行标识,当前请求的请求体的内容就会赋值给这个形参。
如果,这个形参类的属性名和请求体中的key相等,则会调用类的setter方法将对应value设置给类,然后就可以获取请求发送过来的值。
<!--post请求,数据是在请求体中提交的。-->
<form th:action="@{/testRequestBody}" method="post">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
<input type="submit">
</form>
@RequestMapping("/testRequestBody")
public String testRequestBody(@RequestBody String requestBody){
System.out.println("requestBody:"+requestBody);
return "success";
}
输出结果:
requestBody:username=admin&password=123456
------------------------------------------
@RequestMapping("/testRequestBody")
public String testRequestBody(@RequestBody User user){
System.out.println("用户账号:"+user.getUsername);
return "success";
}
输出结果:
用户账号:admin
SpringMVC中的@RequestParam注解
作用:将请求参数对应的值赋值给控制器方法的参数,
value
:参数名,前端发送参数的keyrequired
:是否包含该参数,默认为true,表示该请求路径中必须包含该参数,如果不包含就报错。defaultValue
:默认参数值,如果设置了该值,required=true将失效,自动为false,如果没有传该参数,就使用默认值
<!--post请求,数据是在请求体中提交的。-->
<form th:action="@{/testRequestBody}" method="post">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
<input type="submit">
</form>
@RequestMapping("/testRequestBody")
public String testRequestBody(@RequestParam("username") String name){
System.out.println("用户:"+name);
return "success";
}
输出结果:
用户:admin
RequestEntity
RequestEntity是封装请求报文的一种类型,需要在控制器方法的形参中设置该类型的形参,当前请求的请求报文内容就会赋值给该形参,可以通过getHeaders()获取请求头信息,通过getBody()获取请求体信息
@RequestMapping("/testRequestEntity")
public String testRequestEntity(RequestEntity<String> requestEntity){
System.out.println("requestHeader:"+requestEntity.getHeaders());
System.out.println("requestBody:"+requestEntity.getBody());
return "success";
}
输出结果:
requestHeader:[host:"localhost:8080", connection:"keep-alive", content-length:"27", cache-control:"max-age=0", sec-ch-ua:"" Not A;Brand";v="99", "Chromium";v="90", "Google Chrome";v="90"", sec-ch-ua-mobile:"?0", upgrade-insecure-requests:"1", origin:"http://localhost:8080", user-agent:"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36"]
requestBody:username=admin&password=123
SpringMVC中的@ResponseBody注解
通过原生的response对象给浏览器响应数据
@GetMapping("/testResponse)
public String responseData(HttpServletResponse response){
// 原理是将要响应的数据 hello,browser 直接放在响应的响应体中。
response.getWriter.print("hello,browser");
}
SpringMVC提供的响应给浏览器数据方式
@ResponseBody用于标识一个控制器方法,可以将该方法的返回值直接作为响应报文的响应体响应到浏览器,就不会走视图解析器了。
@RequestMapping("/testResponseBody")
@ResponseBody
public String testResponseBody(){
return "success";
}
SpringMVC处理json
如果我们想要响应一个java对象给浏览器,直接响应回去肯定是不行的,因为浏览器只接收文本、字符串、json等数据类型的数据,换句话说,浏览器是识别不了java对象的,用下面的一个例子演示。
@Controller
public class JsonController {
// 想要响应一个java对象给浏览器
@RequestMapping("/user")
@ResponseBody
public User responseUser(){
return new User(1001,"张三",13);
}
}
从访问结果可以看出,浏览器不可接收对象。
正片开始
通过以上的问题,想要向浏览器响应一个对象,就必须将对象转换为json格式的字符串再响应。
a>导入jackson的依赖
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.3</version>
</dependency>
b>在SpringMVC的核心配置文件中开启mvc的注解驱动
在SpringMVC的核心配置文件中开启mvc的注解驱动,此时在HandlerAdaptor中会自动装配一个消息转换器:MappingJackson2HttpMessageConverter,可以将响应到浏览器的Java对象转换为Json格式的字符串
<mvc:annotation-driven />
c>在处理器方法上使用@ResponseBody注解进行标识
表示不走视图解析器,直接方法的返回值作为响应报文的响应体响应。
@Controller
public class JsonController {
// 想要响应一个java对象给浏览器
@RequestMapping("/user")
@ResponseBody
public User responseUser(){
return new User(1001,"张三",13);
}
}
d>将Java对象直接作为控制器方法的返回值
将Java对象直接作为控制器方法的返回值返回,就会自动将对象转换为Json格式的字符串然后作为响应报文的响应体响应。
@Controller
public class JsonController {
// 想要响应一个java对象给浏览器
@RequestMapping("/user")
@ResponseBody
public User responseUser(){
return new User(1001,"张三",13);
}
}