SpringMVC
第一章 SpringMVC概述
1.1 SpringMVC基本说明
-
SpringMVC是基于Spring的,是Spring中的一个模块,做web开发使用的。
-
SpringMVC叫做Spring web mvc,说明他是Spring的核心技术,做web开发的,SpringMVC内部是使用mvc架构模式。
-
SpringMVC是一个容器,管理对象的,使用IoC核心技术。
-
SpringMVC是管理界面层中的控制器对象。
-
SpringMVC底层也是Servlet。以Servlet为核心,接收请求、处理请求、显示处理结果给用户。
-
处理用户的请求:
- 用户发起请求—SpringMVC—Spring—MyBatis—Mysql数据库
1.2 SpringMVC中的核心Servlet–DispatcherServlet
-
DispatcherServlet是框架中的一个Servlet对象。负责接收请求,响应处理结果
-
DispatcherServlet的父类是HttpServlet
-
DispatcherServlet也叫做前端控制器(front controller)
-
SpringMVC是管理控制器对象。原来没有SpringMVC之前使用Servlet作为控制器对象使用,现在通过SpringMVC容器创建一种叫做控制器的对象,代替Servlet行驶控制器的功能。
-
SpringMVC主要使用注解的方式,创建控制器对象,处理请求。
1.3 第一个注解的SpringMVC程序
- 需求:用户发起一个请求,SpringMVC接收请求,显示请求的处理结果
- 步骤:
1. 新建maven web项目
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mDbEO9Qk-1628590589963)(C:\Users\naizhi\AppData\Roaming\Typora\typora-user-images\image-20210728204208945.png)]
2. pom.xml
- 加入web依赖:
- spring-webmvc依赖(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.kgc</groupId>
<artifactId>ch01-springmvc</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<!--springMVC-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<!--servlet-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
<!--单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
3. 注册中央调度器
- 声明springmvc核心对象DispatcherServlet
- DispatcherServlet是一个Servlet对象
- DispatcherServlet叫做前端控制器(front controller)
- DispatcherServlet作用:
- 在servlet的init()方法中,创建springmvc中的容器对象
- 作为servlet,接收请求
<?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的核心对象-->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--自定义配置文件的位置-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!--
表示服务器tomcat创建对象的顺序,是个整数值,大于等于0
数值越小,创建对象的时间越早
-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<!--
url-pattern 作用:把一些请求交给指定的servlet处理
使用中央调度器(DispatcherServlet)
1.使用扩展名,格式*.xxx,xxx是自定义的扩展名。
例如:*.do,*.action,*.mvc等等,不能使用*.jsp
2.使用斜杠"/"
-->
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>
4. 创建一个jsp,发起请求
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>第一个SpringMVC</title>
</head>
<body>
<a href="some.do">发起some.do的请求</a>
</body>
</html>
5. 创建控制器
- 创建一个普通的类,作为控制器使用(代替之前的servlet)
- 在类的上面加入@Controller注解
- 在类中定义方法,方法的上面加入@RequestMapper注解方法处理请求的,相当于servlet中的doGet、doPost
/**
* @Controller: 创建控制器(处理器)对象
* 控制器:叫做后端控制器(back controller),自定义的类处理请求的
* 位置:在类的上面,表示创建此类对象的,对象放在springmvc的容器中
*/
@Controller
public class MyController {
/**
* springmvc框架,使用控制器类中的方法,处理请求
* 方法的特点:
* 1.方法的形参,表示请求中的参数
* 2.方法的返回值,表示本次请求的处理结果
*/
/**
* @RequestMapper: 请求映射
* 属性:value请求中的url地址,唯一值,以/开头
* 位置:1.在方法的上面 (必须的)2.在类定义的上面(可选)
* 作用:把指定的请求,交给指定的方法处理,等同于url-pattern
* 返回值ModelAndView:表示本次请求的处理结果(数据和视图)
* model:表示数据
* view:表示视图
*/
@RequestMapping(value = "/some.do")
//定义方法,处理请求。public void doGet(){}
public ModelAndView doSome(){//相当于doGet()
//使用这个方法处理请求,能处理请求的方法叫做控制器方法
//调用service对象,处理请求,返回数据
ModelAndView mv = new ModelAndView();
//添加数据
mv.addObject("msg","处理了some.do请求");
mv.addObject("fun","执行了doSome方法");
//指定视图mv.setViewName("视图的完整路径");
mv.setViewName("/show.jsp");
//返回结果
return mv;
}
/**
* 当框架调用完doSome()方法后,得到返回中ModelAndView
* 框架会在后续的处理逻辑值,处理mv对象里面的数据和视图
* 对数据执行request.setAttribute("msg","处理了some.do请求");把数据放入request作用域
* 对视图执行forward转发操作。等同于request.getRequestDispather(/show.do).forward(..)
*/
}
6. 结果页面
- 创建作为结果的jsp页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>show</title>
</head>
<body>
/show.jsp,显示request作用域中的数据 <br>
<h3>msg数据:${msg}</h3>
<h3>fun数据:${fun}</h3>
</body>
</html>
7. 创建SpringMVC配置文件
- 创建SpringMVC的配置文件(spring的配置文件一样)
- 声明组件扫描器,指定@Controller注解所在的包名
<?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.kgc.controller"/>
</beans>
1.4 SpringMVC请求的处理过程
- 简单的处理过程:
- 用户发起请求some.do->Tomcat接收了请求->DispatcherServlet->分配MyController(doSome()返回mv对象)->mx显示给用户
- 省略tomcat
- 用户some.do-------------->DispatcherServlet------------->MyController
- servlet处理请求
- 用户发起请求----------------------------->Servlet
1.5 视图解析器
声明视图解析器
<!--声明视图解析器:帮助处理视图-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--前缀,指定视图文件的路径-->
<property name="prefix" value="/"/>
<!--后缀,指定视图文件的扩展名-->
<property name="suffix" value=".jsp"/>
</bean>
使用视图解析器
@Controller
public class MyController {
@RequestMapping(value = "/some.do")
public ModelAndView doSome(){
ModelAndView mv = new ModelAndView();
mv.addObject("msg","处理了some.do请求");
mv.addObject("fun","执行了doSome方法");
//mv.setViewName("/show.jsp");
//当配置了视图解析器,使用文件名称作为视图名使用,叫做视图逻辑名称
//使用逻辑名称,框架使用配置文件中视图解析器的前缀和后缀,拼接为完整的视图路径
// / + show + .jsp
mv.setViewName("show");
return mv;
}
}
1.6 web开发中配置文件分说明
- web.xml:部署描述符文件,给服务器(tomcat)
- 作用:服务器在启动的时候,读取web.xml,根据文件中的声明创建各种对象,并且根据声明知道请求和servlet等对象的关系
- 框架的配置文件:springmvc的配置文件
- 作用:声明框架创建的项目中的各种对象,主要是创建Controller对象
-
配置文件的加载顺序和功能:
- tomcat服务器启动,读取web.xml,根据web.xml文件中的说明,创建对象
- 创建DispatcjerServlet对象,会执行init()方法,在init()方法中会执行springmvc容器对象的创建
- ApplicationContext ctx = new ClassPathXmlApplicationContext(“classpath:springmvc.xml”);
<!--声明springmvc的核心对象--> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!--自定义配置文件的位置--> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springmvc.xml</param-value> </init-param> <!-- 表示服务器tomcat创建对象的顺序,是个整数值,大于等于0 数值越小,创建对象的时间越早 --> <load-on-startup>1</load-on-startup> </servlet>
- springmvc框架,new ClassPathXmlApplicationContext() 读取springmvc的配置文件
- 使用组件扫描器base-package=“com.kgc.controller”,遍历controller包中的所有类,MyController类,找到这个类中的@Controller,@RequestMapper注解,就能创建MyController对象。知道some.do的请求是执行doSome()方法
<context:component-scan base-package="com.kgc.controller"/> <!--声明视图解析器:帮助处理视图--> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!--前缀,指定视图文件的路径--> <property name="prefix" value="/"/> <!--后缀,指定视图文件的扩展名--> <property name="suffix" value=".jsp"/> </bean>
- 用户发起请求some.do----DispatcherServlet
- DispatcherServlet里面有WebApplicationContext,WebApplicationContext里面有MyController对象
- 请求some.do,DispatcherServlet就知道是MyController处理的
- tomcat服务器启动,读取web.xml,根据web.xml文件中的说明,创建对象
1.7 SpringMVC内部的执行流程
- springmvc内部请求的处理过程:
- 用户发起请求给DispatcherServlet。
- DispatcherServlet把请求(request)交给了处理器映射器。
- 处理器映射器:springmvc框架中的对象,需要实现HandleMapping接口
- 映射器作用:从springmvc容器中,获取控制器对象(MyController),把找到的控制器和拦截器对象都放到处理器执行链对象中,保存,并返回给中央调度器(MyController controller = ApplicationContext.getBean())。
- DispatcherServlet把获取到的处理器执行链中的控制器对象,交给处理器适配器。
- 处理器适配器:是springmvc框架中的对象,实现HandleAdapter接口
- 适配器作用:执行控制器的方法,也就是执行MyController.doSome()方法,得到结果ModelAndView
- DispatcherServlet把控制器执行结果mv交给了视图解析器。
- 视图解析器:是springmvc框架中的对象,需要实现ViewResolver接口
- 视图解析器作用:处理视图的,组成视图的完整路径,能创建View类型的对象
- DispatcherServlet调用View类的方法,把Model中的数据放入到request作用域,执行request.setAttrbute(),对视图执行forward()转发行为,request.getRequstDispather("/show.jsp")。forward(request,response)
第二章 SpringMVC注解式开发
2.1 RequestMapping注解的使用
/**
* @RequestMapping
* 属性:value表示所有请求地址的公共前缀,相当于是模块名称
* 位置:在类的上面
*
*/
@Controller
@RequestMapping("/test")
public class MyController {
/**
* @RequestMapper: 请求映射
* 属性:value请求中的url地址,唯一值,以/开头
* method请求的方式,使用RequestMethod类的枚举值。
* 例如get方式,RequestMethod.GET
* 位置:1.在方法的上面 (必须的)2.在类定义的上面(可选)
* 作用:把指定的请求,交给指定的方法处理,等同于url-pattern
* 返回值ModelAndView:表示本次请求的处理结果(数据和视图)
* model:表示数据
* view:表示视图
*/
//请求方式不一样,错误码是405,请求方式不支持
//指定some.do使用get请求方式
@RequestMapping(value = "/some.do",method = RequestMethod.GET)
public ModelAndView doSome(){
ModelAndView mv = new ModelAndView();
mv.addObject("msg","处理了some.do请求");
mv.addObject("fun","执行了doSome方法");
mv.setViewName("show");
return mv;
}
//other.do使用post请求方式
@RequestMapping(value = "/other.do",method = RequestMethod.POST)
public ModelAndView doOther(){
ModelAndView mv = new ModelAndView();
mv.addObject("msg","处理了other.do请求");
mv.addObject("fun","执行了doOther方法");
mv.setViewName("show");
return mv;
}
//不指定method属性,请求方式没有限制
@RequestMapping(value = "/first.do")
public ModelAndView doFirst(){
ModelAndView mv = new ModelAndView();
mv.addObject("msg","处理了first.do请求");
mv.addObject("fun","执行了doFirst方法");
mv.setViewName("show");
return mv;
}
}
2.2 接收请求中的参数
- 对应HttpServletRequest、HttpServletResponse、HttpSession只需要在控制器方法的形参列表中,定义就可以了。框架会给参数赋值,在控制器方法内部可以直接使用request、response、session参数。
- 400:http status,表示客户端异常。主要是发生在用户提交参数过程中
- 接收请求中的参数:逐个接收、对象接收
2.2.1 逐个接收
- 逐个接收:请求中的参数名和控制器方法的形参名一样,按照名称对应接收参数
发起请求
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>请求方式</title>
</head>
<body>
<p>逐个接收请求参数</p>
<form action="receive-property.do" method="post">
姓名:<input type="text" name="name"><br>
年龄:<input type="text" name="age"><br>
<input type="submit" value="提交参数">
</form>
</body>
</html>
控制器接收参数
@Controller
public class MyController {
/**
* 逐个接收请求参数
* 要求: 请求中的参数名和控制器方法的形参名一样。按名称对应接收请求参数
* 参数接收:
* 1.框架使用request对象,接收参数
* String name = request.getParameter("name")
* String age = request.getParameter("age")
* 2.正在中央调度器的内部调用doProperty方法时,按名称对应传递参数
* doProperty(name,Integer.valueOf(age))
* 框架可以实现请求参数String 到int、long、float、double等类型的转换
*/
@RequestMapping(value = "/receive-property.do")
public ModelAndView doProperty(String name,Integer age){
ModelAndView mv = new ModelAndView();
mv.addObject("myname",name);
mv.addObject("myage",age);
mv.setViewName("show");
return mv;
}
}
响应结果
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>show</title>
</head>
<body>
/show.jsp,显示request作用域中的数据 <br>
<h3>myname数据:${myname}</h3>
<h3>myage数据:${myage}</h3>
</body>
</html>
- 接收参数的问题:
- 参数最好使用包装类型。例如Integer,能接收空值情况,接收的是null
- 框架可以实现String到int、long、float、double等类型转换
- post请求中有乱码的问题,使用字符集过滤器
2.2.2 CharacterEncodingFilter
CharacterEncodingFilter使用
<!--声明过滤器,框架提供的,解决post请求中乱码的问题-->
<filter>
<filter-name>CharacterEncodingFilter</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>
<!--强制请求(request)对象使用encoding的编码方式-->
<init-param>
<param-name>forceRequestEncoding</param-name>
<param-value>true</param-value>
</init-param>
<!--强制响应(response)对象使用encoding的编码方式-->
<init-param>
<param-name>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<!--强制所有请求,先经过过滤器处理-->
<url-pattern>/*</url-pattern>
</filter-mapping>
2.2.3 @RequestParam
- 请求中的参数名和形参名不一样,使用@RequestParam
@RequestParam使用
- jsp页面:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>请求方式</title>
</head>
<body>
<p>逐个接收请求参数:请求中参数名和形参名不一样</p>
<form action="receive-property.do" method="post">
姓名:<input type="text" name="rname"><br>
年龄:<input type="text" name="rage"><br>
<input type="submit" value="提交参数">
</form>
</body>
</html>
- 控制器页面:
@Controller
public class MyController {
/**
* 逐个接收请求参数,请求中的参数名和形参名不一样
* @RequestParam: 解决名称不一样的问题
* 属性:value 请求中的参数名称
* required:boolean类型的,默认是true
* true:请求中必须有此参数,没有报错
* false:请求中可以没有此参数。
* defaultValue:参数默认值,若参数为空参数等于默认值
* 位置:在形参定义的前面
*/
@RequestMapping(value = "/receive-property.do")
public ModelAndView doProperty(
@RequestParam(value = "rname",required = false,defaultValue = "张三") String name,
@RequestParam(value = "rage",required = false,defaultValue = "23") Integer age){
ModelAndView mv = new ModelAndView();
mv.addObject("myname",name);
mv.addObject("myage",age);
mv.setViewName("show");
return mv;
}
}
2.2.4 对象接收
- 对象接收:在控制器方法的形参是java对象,使用java对象的属性接收请求中的参数值
对象接收使用
- 创建实体类:
/**
* 实体类
*/
public class Student {
//属性名和请求中参数名一样
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
- jsp页面:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>请求方式</title>
</head>
<body>
<p>逐个接收请求参数:请求中参数名和对象的属性名一样</p>
<form action="receive-property.do" method="post">
姓名:<input type="text" name="name"><br>
年龄:<input type="text" name="age"><br>
<input type="submit" value="提交参数">
</form>
</body>
</html>
- 控制器页面:
@Controllerpublic class MyController { /** * 使用对象接收请求中的参数 * 要求:java对象的属性名和请求中参数名一样 * java类需要一个无参数构造方法,属性有set方法 *框架的处理: * 1.调用Student类的无参数构造方法,创建对象 * 2.调用对象set方法,同名的参数,调用对应的set方法 * 参数是name,调用setName(参数值) */ @RequestMapping("/receive-property.do") public ModelAndView doProperty(Student student){ ModelAndView mv = new ModelAndView(); mv.addObject("myname",student.getName()); mv.addObject("myage",student.getAge()); mv.setViewName("show"); return mv; }}
2.3 控制器方法的返回值
- 控制器方法的返回值表示本次请求的处理结果,返回值有ModelAndView,String,void,Object
- 请求的处理结果包含:数据和视图
2.3.1 ModelAndView 数据和视图
- 请求的结果有数据和视图,使用ModelAndView最方便
- 数据:存放request作用域
- 视图:执行forward转发操作
2.3.2 String 视图
- 框架对返回值是String,执行的是forward转发操作
- 视图可以表示为完整视图路径,或者视图的逻辑名称
视图逻辑名称
- jsp页面:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>返回值</title>
</head>
<body>
<p>控制器方法返回String-逻辑视图名称</p>
<form action="return-string.do" method="post">
姓名:<input type="text" name="name"><br>
年龄:<input type="text" name="age"><br>
<input type="submit" value="提交参数">
</form>
</body>
</html>
- 控制器页面:
@Controller
public class MyController {
/**
* 控制器方法返回String,表示逻辑名称,需要项目中配置视图解析器
*/
@RequestMapping("/return-string.do")
public String doProperty(HttpServletRequest request,String name, Integer age){
//处理数据,
request.setAttribute("myname",name);
request.setAttribute("myage",age);
//返回结果,forward转发到show.jsp
return "show";
}
}
完整视图路径
- jsp页面:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>返回值</title>
</head>
<body>
<p>控制器方法返回String-完整视图路径</p>
<form action="return-string.do" method="post">
姓名:<input type="text" name="name"><br>
年龄:<input type="text" name="age"><br>
<input type="submit" value="提交参数">
</form>
</body>
</html>
- 控制器页面:
@Controllerpublic class MyController { /** * 控制器方法返回String,表示完整视图路径,项目中不能使用视图解析器 */ @RequestMapping("/return-string.do") public String doProperty(HttpServletRequest request,String name, Integer age){ //处理数据, request.setAttribute("myname",name); request.setAttribute("myage",age); //返回结果,forward转发到show.jsp return "/show.jsp"; }}
2.3.3 void 没有数据和视图
- void:没有数据和视图,可以使用HttpServletResponse对象输出数据,响应ajax请求
Ajax操作
- json依赖:
<!--jackson依赖-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
- jsp页面:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>返回值</title>
<script src="js/jquery-1.8.3.min.js"></script>
<script>
$(function () {
//绑定事件
$("#btnAjax").on("click",function () {
$.ajax({
url:"return-void-ajax.do",
data:{name:"李四",age:18},
dataType:"json",
success:function (result) {
alert(result.name+result.age)
}
})
})
})
</script>
</head>
<body>
<button id="btnAjax">发起ajax请求</button>
</body>
</html>
- 控制器页面:
@Controller
public class MyController {
/**
* 控制器方法返回是void,响应ajax请求。使用HttpServletResponse删除数据
*/
@RequestMapping("/return-void-ajax.do")
public void returnVoidAjax(HttpServletResponse response,String name, Integer age) throws IOException {
//调用service得到结果对象
Student student = new Student();
student.setName(name);
student.setAge(age);
//把对象转为json
ObjectMapper om = new ObjectMapper();
String json = om.writeValueAsString(student);
//输出json,响应ajax
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
out.print(json);
out.flush();
out.close();
}
}
2.3.4 Object 数据
- 返回Student表示数据还是视图?控制器方法返回对象Object,用来响应ajax请求
- 返回对象Object,可以是List、Student、Map、String、Integer……这些都是数据,而ajax请求需要的就是数据。在ajax请求中,一般需要从服务器返回的是json格式的数据,经常要处理java对象到json的转换,而且还需要输出数据响应ajax请求。框架提供了处理java对象到json转换,还有数据输出工作
HttpMessageConverter 消息转换器
- HttpMessageConverter 是接口,作用是:
- 实现请求的数据转为java对象,
- 把控制器方法返回的对象转为json、xml、text,二进制等不同格式的数据
public interface HttpMessageConverter<T> { /** 作用:检查clazz这个类型的对象,能否转为mediaType表示的数据格式 如果能转为mediaType表示的类型,返回true,若返回true调用read() */ boolean canRead(Class<?> var1, @Nullable MediaType var2); /** 作用:接收请求中的数据,把数据转为clazz表示的对象 */ T read(Class<? extends T> var1, HttpInputMessage var2) throws IOException,HttpMessageNotReadableException; /** 作用:检查clazz这种数据类型,能否转为mediaType表示的数据格式 如果能转为mediaType表示的类型,返回true,若返回true调用write() */ boolean canWrite(Class<?> var1, @Nullable MediaType var2); /** 作用:把t对象,按照contextType说明的格式,把对象转为json或者xml */ void write(T var1, @Nullable MediaType var2, HttpOutputMessage var3) throws IOException, HttpMessageNotWritableException;}
-
mediaType:媒体类型,表示互联网中数据的格式。例如application/json,text/html,image/gif
-
HttpMessageConverter 接口的实现类:
MappingJackson2HttpMessageConverter//使用jackson工具库中的ObjectMapper把java对象转为json数据格式StringHttpMessageConverter//把字符串类型的数据,进行格式转换和编码
- 如何使用实现类:
- 框架根据控制器方法的返回类型,自动查找使用的实现类
@RequestMapping("/return-object.do") public Student returnObject(String name, Integer age){ Student student = new Student(); student.setName(name); student.setAge(age); return student; }
- 默认情况下:springmvc使用了HttpMessageConverter接口的4个实现类。包括了StringHttpMessageConverter
- 要想使用json需要在springmvc的配置文件,加入注解驱动的标签mvc:annotation-driven。加入这个标签后,springmvc项目启动后,会创建HttpMessageConverter接口的7个实现类,包括StringHttpMessageConverter和MappingJackson2HttpMessageConverter
<!--声明注解驱动:创建HttpMessageConverter接口的7个实现类对象--><mvc:annotation-driven/>
@ResponseBody
- @ResponseBody注解的作用:就是把student转换后的json通过HttpServletResponse对象输出给浏览器
response.setContentType("application/json;charset=utf-8");PrintWriter out = response.getWriter();out.print(json);out.flush();out.close();//@ResponseBody注解作用就相当于上面代码的实现
控制器方法返回对象转为json的步骤
- pom.xml加入jackson依赖,springmvc框架默认处理json就是使用jackson
- 在springmvc的配置文件中,加入注解驱动的标签mvc:annotation-driven
- 在控制器方法的上面加入@ResponseBody注解,表示返回值数据,输出到浏览器
返回student-json数据
@Controllerpublic class MyController { /** * 控制器方法返回student-json * 框架的处理模式: * 1.框架根据控制器方法的返回值类型,找到HttpMessageConverter接口的实现类 * 最后找到的是MappingJackson2HttpMessageConverter * 2.使用实现类MappingJackson2HttpMessageConverter,执行他的write()方法, * 把student对象转换为json格式的数据 * 3.框架使用@ResponseBody注解,把2中的json输出给浏览器 */ @RequestMapping("/doStudentJson.do") @ResponseBody public Student doAjaxJson(String name,Integer age){ Student student = new Student(); student.setName(name); student.setAge(age); return student; }}
返回List-jsonarray
@Controllerpublic class MyController { /** * 控制器方法返回是List<Student>集合--json array */ @RequestMapping("/doListJsonArray.do") @ResponseBody public List<Student> doListJsonArray(String name, Integer age){ Student student = new Student(); student.setName("张三"); student.setAge(20); Student student1 = new Student(); student1.setName("李四"); student1.setAge(23); List<Student> list = new ArrayList<>(); list.add(student); list.add(student1); return list; }}
返回String-数据
@Controllerpublic class MyController { /** * 控制器方法返回是String--数据 * 区分返回值String是数据还是视图 * 1.方法声明有@ResponseBody注解是数据 * 2.方法上面没有@ResponseBody注解是视图 * *解决中文乱码,需要使用@RequestMapping的produces属性 * produces属性:指定context-type的值 * 框架处理String返回值 * 1.框架使用的StringHttpMessageConverter * 2.StringHttpMessageConverter使用的是text/plain;charset=ISO-8859-1 */ @RequestMapping(value = "/doStringData.do",produces = "text/plain;charset=utf-8") @ResponseBody public String doStringData(String name, Integer age){ return "Hello SpringMVC"; }}
2.4 静态资源处理
2.4.1 tomcat的default servlet
- tomcat安装目录/conf/web.xml
<servlet> <servlet-name>default</servlet-name> <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class> <init-param> <param-name>debug</param-name> <param-value>0</param-value> </init-param> <init-param> <param-name>listings</param-name> <param-value>false</param-value> </init-param> <load-on-startup>1</load-on-startup></servlet><servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>/</url-pattern></servlet-mapping>
- default叫做默认servlet,作用:
- 它提供静态资源的处理
- 它处理所有未映射到其他请求的请求处理
2.4.2 中央调度器设置“/”
<servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springmvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup></servlet><servlet-mapping> <servlet-name>springmvc</servlet-name> <!-- url-pattern 作用:把一些请求交给指定的servlet处理 使用中央调度器(DispatcherServlet) 1.使用扩展名,格式*.xxx,xxx是自定义的扩展名。 例如:*.do,*.action,*.mvc等等,不能使用*.jsp 2.使用斜杠"/"导致中央调度器称为l默认的default servlet 需要处理静态资源和其他的未映射的请求。默认中央调度器没有处理 静态资源的控制器对象,所以静态资源都是404.而some.do这个请求 有MyController对象,所以能访问 如果项目中,中央调度器设置了"/",动态资源能访问,静态资源不能访问, 需要处理静态资源的访问工作 --> <url-pattern>/</url-pattern></servlet-mapping>
2.4.3 第一种方式处理静态资源
- 在springmvc的配置文件加入 mvc:default-servlet-handler标签,springmvc框架会在项目运行时,加入DefaultServletHttpRequestHandler对象,让这个对象处理静态资源的访问
<!--声明注解驱动 default-servlet-handler和@RequestMapping使用有冲突--><mvc:annotation-driven/><!--声明静态资源的第一种处理方式 创建DefaultServletHttpRequestHandler处理静态资源 DefaultServletHttpRequestHandler把接收的静态资源的地址,转发给了tomcat的default 优点:解决方式简单 缺点:依赖tomcat服务器提供的能力--><mvc:default-servlet-handler/>
2.4.4 第二种方式处理静态资源
- 在springmvc的配置文件加入 mvc:resources标签,框架会创建ResourcesHttpRequestHandler控制器对象,使用这个对象处理静态资源的访问。不依赖tomcat服务器。推荐使用!
<!--声明注解驱动 resources和@RequestMapping使用有冲突 --><mvc:annotation-driven/><!--声明静态资源的第二种处理方式 mapping:访问静态资源的url地址,可以使用通配符(**) **:表示任意的目录和目录中的资源名称 location:静态资源的位置,编译使用/WEB-INF目录--><!--一句话设置静态资源--><mvc:resources mapping="/statics/**" location="/statics/"/>
第三章 SSM整合开发
3.1 SSM整合思路
-
SSM思路:SpringMVC+Spring+MyBatis(IBatis),所以有人叫做SSI整合。SSM整合是使用三个框架的优势功能,三个框架对应的三层架构的三层。SpringMVC是视图层,Spring是业务层,MyBatis是持久层。
-
SSM整合,需要把对象交给容器管理,让容器去创建项目中要使用的java对象。现在有两个容器:
- 第一个是Spring容器:Spring容器是管理service和dao等对象的,是业务层对象的容器
- 第二个是SpringMVC容器:SpringMVC是管理控制器对象的,是视图层对象的容器
-
SSM整合就是把对象交给容器管理,两个容器共存,各自负责管理不同的对象。把对象声明到配置文件中,让两个容器创建对象。Spring创建service、dao;SpringMVC创建concroller
3.2 容器的创建
-
Spring容器创建:在web.xml声明监听器ContextLoaderListener,这个功能框架写好了。功能是创建spring的容器对象WebApplicationContext,在创建WebApplicationContext对象时,读取spring的配置文件,读取文件时,遇到bean标签或者注解,就能创建service、dao等对象,放到容器中。
-
SpringMVC容器创建:在web.xml声明了中央调度器DispatcherServlet。在这个servlet的init()方法中,创建了容器对象WebApplicationContext,在创建WebApplicationContext对象时,读取springmvc的配置文件,读取文件时,遇到@Controller注解,创建控制器Controller对象,放到容器中。
-
内存中,创建对象:
- WebApplicationContext spring = new WebApplicationContext();//spring–map(service,dao)
- WebApplicationContext springmvc = new WebApplicationContext();//springmvc–map(controller)
-
SpringMVC容器和Spring容器的关系:设计上SpringMVC容器对象是Spring容器的子容器。Spring是父容器、SpringMVC是子容器。相当于java中的继承关系。
3.3 SSM整合开发步骤
1. 使用的Student表(id,name,age)
CREATE TABLE student( id INT PRIMARY KEY AUTO_INCREMENT,#编号 `name` VARCHAR(80) NOT NULL,#姓名 age INT NOT NULL#年龄);#学生表
2. 创建Maven web项目
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rxbcXfcm-1628590589968)(C:\Users\naizhi\AppData\Roaming\Typora\typora-user-images\image-20210728204208945.png)]
3. pom.xml加入依赖
- 包括:spring、springmvc、mybatis、mybatis-spring、mysql驱动、druid、jackson
<?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.kgc</groupId> <artifactId>ch02-SSM</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties> <dependencies> <dependency> <!--单元测试--> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <!--spring依赖--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.5.RELEASE</version> </dependency> <!--springMVC--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.5.RELEASE</version> </dependency> <!--jsp依赖--> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> <version>2.2.1-b03</version> <scope>provided</scope> </dependency> <!--servlet--> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> </dependency> <!--jackson依赖--> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.9.0</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.0</version> </dependency> <!--mybatis依赖--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.1</version> </dependency> <!--mybatis和spring集成--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.3.1</version> </dependency> <!--mysql驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.9</version> </dependency> <!--阿里的连接池--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.12</version> </dependency> <!--监听器依赖--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>5.2.5.RELEASE</version> </dependency> <!--spring事务依赖--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>5.2.5.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.5.RELEASE</version> </dependency> </dependencies> <build> <!--资源插件:处理src/main/java目录下的xml--> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>false</filtering> </resource> </resources> </build></project>
4. 写web.xml
- 声明容器对象:
- 声明spring的监听器ContextLoaderListener:创建spring的容器对象,创建service、dao对象
- 声明springmvc的中央调度器DispatcherServlet:创建springmvc的容器对象,创建controller对象
- 声明字符集的过滤器CharacterEncodingFilter:解决post请求乱码的问题
<?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">
<!--声明spring监听器-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring_mybatis.xml</param-value>
</context-param>
<!--中央调度器-->
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--声明字符集过滤器-->
<filter>
<filter-name>CharacterEncodingFilter</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>
<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>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
5. 创建程序中的包
- 包括dao、service、controller、pojo
6. 配置文件
- 包括spring、springmvc、mybatis
<?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 https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--springmvc的配置文件:声明controller,视图解析器等web开发中的对象-->
<!--组件扫描器-->
<context:component-scan base-package="com.kgc.controller"/>
<!--视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!--注解驱动-->
<mvc:annotation-driven/>
<!--静态资源-->
<mvc:resources mapping="/static/**" location="/static/"/>
</beans>
<?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"> <!--spring的配置文件:声明service、dao、工具类、事务配置--> <!--组件扫描器--> <context:component-scan base-package="com.kgc.service"/> <!--数据源--> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="url" value="jdbc:mysql://localhost:3306/springDb"/> <property name="username" value="root"/> <property name="password" value="ok"/> </bean> <!--声明会话工厂--> <bean id="factory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="configLocation" value="classpath:mybatis.xml"/> </bean> <!--声明dao代理--> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="sqlSessionFactoryBeanName" value="factory"/> <property name="basePackage" value="com.kgc.dao"/> </bean></beans>
<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration> <!--设置日志--> <settings> <setting name="autoMappingBehavior" value="FULL"/> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings> <mappers> <package name="com/kgc/dao"/> </mappers></configuration>
7. java代码
- 包括实现类、dao接口、mapper文件、service类、controller类。使用注解声明对象和赋值
/** * 学生类 */public class Student { private Integer id; private String name; private Integer age; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } @Override public String toString() { return "Student{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + '}'; }}
/** * 学生持久层接口 */public interface StudentDao { //注册学生 int insertStudent(Student student); //查询学生 List<Student> selectStudents();}
<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.kgc.dao.StudentDao"> <!--注册学生--> <insert id="insertStudent"> insert into student values(null,#{name},#{age}) </insert> <!--查询学生--> <select id="selectStudents" resultType="com.kgc.pojo.Student"> select * from student order by id desc </select></mapper>
/**
* 学生业务层接口
*/
public interface StudentService {
//添加学生
int addStudent(Student student);
//查询学生
List<Student> queryStudents();
}
/**
* 学生业务层实现类
*/
@Service
public class StudentServiceImpl implements StudentService {
@Autowired
private StudentDao studentDao;
//实现添加学生
@Override
public int addStudent(Student student) {
return studentDao.insertStudent(student);
}
//实现查询学生
@Override
public List<Student> queryStudents() {
return studentDao.selectStudents();
}
}
/**
* 学生控制器
*/
@Controller
public class StudentController {
@Autowired
private StudentService service;
//添加学生
@RequestMapping("/add")
public ModelAndView addStudent(Student student){
ModelAndView mv = new ModelAndView();
//调用service,处理业务逻辑,把结果返回给用户
int rows = service.addStudent(student);
String msg="注册失败";
if (rows>0){
//注册成功,给用户成功的数据和视图
msg = "注册成功";
}
mv.addObject("msg",student.getName()+msg);
mv.setViewName("show");
return mv;
}
}
8. 创建视图文件
- 即:各种jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %><html><head> <title>Title</title></head><body> <div align="center"> <p>注册学生</p> <form action="/add" method="post"> <table> <tr> <td>姓名:</td> <td><input type="text" name="name"></td> </tr> <tr> <td>年龄:</td> <td><input type="text" name="age"></td> </tr> <tr> <td>操作:</td> <td><input type="submit" value="提交"></td> </tr> </table> </form> </div></body></html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %><html><head> <title>Title</title></head><body> ${msg}</body></html>
3.4 相对路径
- 在页面中,有路径的问题,访问路径有"/“开头的,还有没有”/"。
<a href="text/some.do">没有斜杠开头</a><a href="/text/some.do">有斜杠开头</a><a href="http://www.baidu.com">有协议开头的地址</a>
-
选择看的都是在页面中的地址,地址的区别:
- 有协议开头的,例如:www.baidu.com,称为绝对地址,地址是唯一的,能够直接访问
- 没有协议开头的,例如:text/some.do;/text/some.do称为相对地址,相对地址单独使用不能表示某个资源,不能访问。相对地址必须有参考地址在一起,才能表示一个资源的完整地址,才能访问。
-
有"/“和没有”/"参考地址是不同的
-
没有斜杆开头的地址,参考地址:当前资源的访问路径
-
当前访问的地址:http://localhost:8080/index.jsp
-
资源名称:index.jsp
-
资源路径:http://localhost:8080
-
在index.jsp有访问地址a href=“test/some.do”
-
点击some.do后,地址变成http://localhost:8080/test/some.do
-
此时资源名称:some.do
-
资源路径:http://localhost:8080/test/
-
再此点击some.do后,地址:http://localhost:8080/test/test/some.do
-
没有斜杆开头的地址:参考地址+当前的相对地址组合在一起是最后的访问地址
-
解决方式:
- 使用EL表达式:
<%--使用${pageContext.request.contextPath}。表示访问项目的路径(上下文件context path)--%> <a href="${pageContext.request.contextPath}/test/some.do">发起请求</a> 优点:好理解 缺点:每个链接地址,都需要加el表达式
- 固定当前页面中的没有斜杆开头地址的参考地址,使用html的base标签
<% String basePath = request.getScheme()+"://"+request.getServerName()+":" +request.getServerPort()+request.getContextPath()+"/"; %> <html> <head> <title>Title</title> <base href="<%=basePath%>"> </head>
-
-
有斜杆开头的地址
- 当前访问的地址:http://localhost:8080/ch02/index.jsp
- 访问之后当前访问的地址:http://localhost:8080/test/some.do
- 使用"/"开头的地址,参考地址是服务器地址,也就是从协议开始到端口号位置http://localhost:8080/
- 使用参考地址http://localhost:8080/ +相对地址/test/aome.do 最后就是http://localhost:8080/test/some.do
- 地址缺少项目访问路径:ch02
- 解决问题的方式:在你的路径前面加入el表达式${pageContext.request.contextPath}
-
第四章 SpringMVC核心技术
4.1 转发和重定向
- forward:视图完整路径
- redirect:视图完整路径
@Controller
public class MyController {
/**
* 控制器方法返回是ModelAndView实现转发
* 语法:mv.setViewName("forward:视图完整路径")
* forward特点:不和视图解析器一同工作,就当项目中没有视图解析器
*/
@RequestMapping(value = "/doForward.do")
public ModelAndView doForward(){
ModelAndView mv = new ModelAndView();
//显示使用forward指定转发操作
mv.setViewName("forward:hello.jsp");
return mv;
}
/**
* 当控制器方法返回ModelAndView实现重定向
* 语法:mv.setViewName("redirect:视图完整路径")
* redirect特点:不和视图解析器一同工作,就当项目中没有视图解析器
* 框架提供重定向的功能
* 1.框架可以实现两次请求之间的数据传递,把第一个请求中的model里面
* 简单类型的数据,转为字符串,附加到目标页面的后面,做get参数传递。
* 可以在目标页面中获取参数值使用
* 2.在目标页面中,可以使用${param.参数名}获取参数的值
*/
@RequestMapping(value = "/doRedirect.do")
public ModelAndView doRedirect(String name,Integer age){
ModelAndView mv = new ModelAndView();
mv.addObject("myName",name);
mv.addObject("myAge",age);
mv.setViewName("redirect:other.jsp");
return mv;
}
}
4.2 异常处理
- 框架使用的集中的异常处理,把各个Controller中抛出的异常集中到一个地方处理。处理异常的叫做异常处理器。
- 框架中使用两个注解完成异常的集中处理,这样每个Controller不用单独处理异常了。注解是:
- @ExceptionHandler:放在方法的上面,表示此方法可以处理某个类型的异常。当异常发生时,执行这个方法。
- @ControllerAdvice:放在类的上面,表示这个类中有异常的处理方法,相当于aop中的@Aspect
- @ControllerAdvice看做是控制器增强,就是给Controller类增加异常(切面)的处理功能
创建异常类
- 创建异常类MyUserException,子类NameException、AgeException
public class MyUserException extends Exception {
public MyUserException() {
super();
}
public MyUserException(String message) {
super(message);
}
}
public class NameException extends MyUserException {
public NameException() {
super();
}
public NameException(String message) {
super(message);
}
}
public class AgeException extends MyUserException{
public AgeException() {
super();
}
public AgeException(String message) {
super(message);
}
}
创建控制器
- 在类的上面加入@Controller注解
- 在类中定义方法,根据请求参数name、age的值抛出NameException、AgeException
@Controller
public class MyController {
@RequestMapping(value = "/some.do")
public ModelAndView doForward(String name,Integer age) throws MyUserException {
//抛出异常
if (!"zs".equals(name)){
throw new NameException("姓名不正确");
}
if (age ==null || age.intValue() >80){
throw new AgeException("年龄太大了");
}
ModelAndView mv = new ModelAndView();
mv.addObject("myName",name);
mv.addObject("myAge",age);
mv.setViewName("show");
return mv;
}
}
异常处理
- 创建一个普通类,做为异常的处理类
- 在类的上面加入@ControllerAdvice
- 在类中定义方法,每个方法处理对应的异常,方法的上面加入@ExceptionHandler注解
/**
* @ControllerAdvice: 表示当前类是异常的处理类。给Controller增加功能的
* 位置:在类的上面
*/
@ControllerAdvice
public class GlobalExceptionHandler {
//定义处理异常的方法,当异常发生后,执行这个方法
//方法定义从controller类中方法的定义
/**
* 处理NameException类型的异常
* 参数:
* Exception:表示controller抛出的异常对象
* @ExceptionHandler: 表示此方法处理异常
* 属性:value异常的类型
* 位置:方法的上面
*/
@ExceptionHandler(value = NameException.class)
public ModelAndView doNameException(Exception e){
System.out.println("doNameException"+e);
/**
* 发生异常:
* 1.记录异常,记录到日志文件
* 2.发生通知,邮件,短信等等
* 3.给用户友好的提示
*/
ModelAndView mv = new ModelAndView();
mv.addObject("tips","姓名只能是zs");
mv.setViewName("nameError");
return mv;
}
//处理ageException
@ExceptionHandler(value = AgeException.class)
public ModelAndView doAgeException(Exception e){
System.out.println("doAgeException"+e);
ModelAndView mv = new ModelAndView();
mv.addObject("tips","年龄不能大于");
mv.setViewName("ageError");
return mv;
}
//处理其他异常
@ExceptionHandler
public ModelAndView doOtherException(Exception e){
System.out.println("doOtherException"+e);
ModelAndView mv = new ModelAndView();
mv.addObject("tips","请稍后重试");
mv.setViewName("defaultError");
return mv;
}
}
springmvc配置文件
- 声明组件扫描器,找到@ControllerAdvice注解包名
<?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 https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--springmvc的配置文件:声明controller,视图解析器等web开发中的对象-->
<!--组件扫描器-->
<context:component-scan base-package="com.kgc.controller"/>
<context:component-scan base-package="com.kgc.handler"/>
<!--视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!--注解驱动-->
<mvc:annotation-driven/>
<!--静态资源-->
<mvc:resources mapping="/static/**" location="/static/"/>
</beans>
4.3 拦截器
- 拦截器:是springmvc框架中的一种对象,需要实现接口HandlerInterceptor,拦截用户的请求,拦截到controller的请求。
- 作用:拦截用户的请求,可以预先对请求做处理。根据处理结果,决定是否执行controller。也可以把多个controller中共用的功能定义到拦截器。
- 特点:
- 拦截器可以分为系统拦截器和自定义拦截器。
- 一个项目中可以多个拦截器。0或多个自定义拦截器
- 拦截器侧重拦截用户的请求
- 拦截器是在请求处理之前先执行的
- 拦截器的定义:
- 创建类实现拦截器接口HandlerInterceptor,实现接口中的方法(3个)
- 在springmvc配置文件中,声明拦截器对象,并指定拦截的url地址
4.3.1 第一个拦截器
拦截器的方法
public class MyInterceptor implements HandlerInterceptor {
/**
*preHandle:预先处理请求的方法(总的开关)
* 参数:
* Object handler:被拦截的控制器对象(MyController)
* 返回值:boolean
* true:请求是正确的,可以被controller处理的
* false:请求不能被处理,控制器方法不会执行,请求到此截止
* 特点:
* 1.预处理方法它的执行时间,在控制器方法之前先执行的
* 2.可以对请求做处理,可以做登录的检查,权限的判断,统计数据等等
* 3.绝对请求是否执行
*/
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
System.out.println("MyInterceptor拦截器的preHandler");
return true;
}
/**
* postHandle:后处理方法
* 参数:
* Object handler:被拦截的控制器对象(MyController)
* ModelAndView mv:控制器方法的返回值(请求的执行结果)
* 特点:
* 1.在控制器方法之后执行的
* 2.能获取到控制器方法的执行结果,可以修改原来的执行结果
* 可以修改数据,也可以修改视图
* 3.可以看做对请求的二次处理
*/
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView mv) throws Exception {
System.out.println("MyInterceptor拦截器的postHandle");
}
/**
* afterCompletion:最后执行的方法
* 参数:
* Object handler:被拦截的控制器对象(MyController)
* Exception ex:异常对象
* 特点:
* 1.在请求处理完成后执行的,请求处理完成的标志是视图处理完成,对视图执行forward操作后
* 2.可以做程序最后要做的工作,释放内存,清理临时变量。
* 3.方法的执行条件:
* (1)当前的拦截器它的preHandle()方法必须执行
* (2)preHandle()必须返回true
*/
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) throws Exception {
System.out.println("MyInterceptor拦截器的afterCompletion");
}
}
声明拦截器
- 在springmvc配置文件中:
<!--声明拦截器-->
<mvc:interceptors>
<!--第一个拦截器-->
<mvc:interceptor>
<!--指定拦截器的拦截地址
path:拦截的uri地址,使用**通配符
例如:path="/user/**"
http://localhost:8080/user/listUser
-->
<mvc:mapping path="/**"/>
<!--指定使用的拦截器-->
<bean class="com.kgc.handler.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
执行结果
- 当你的preHandle返回true,执行的结果:
MyInterceptor拦截器的preHandler
执行了MyController的doSome方法
MyInterceptor拦截器的postHandle
MyInterceptor拦截器的afterCompletion
请求的执行顺序:用户some.do---preHandle---doSome---postHandle---afterComplietion
- 当你的preHandle返回false,执行的结果:
MyInterceptor拦截器的preHandler请求的执行顺序:用户some.do---preHandle(被拦截终止)
- postHandle做二次处理:
@Override
public void postHandle(HttpServletRequest request,HttpServletResponse response,
Object handler, ModelAndView mv) throws Exception {
System.out.println("MyInterceptor拦截器的postHandle");
//对请求做二次的处理
if (mv!=null){
//修改数据
mv.addObject("mydate",new Date());
//修改视图
mv.setViewName("other");
}
}
- afterCompletion做请求的收尾工作:
@Override
public void afterCompletion(HttpServletRequest request,HttpServletResponse response,
Object handler, Exception ex) throws Exception {
System.out.println("MyInterceptor拦截器的afterCompletion");
//获取数据
HttpSession session = request.getSession();
Object attr = session.getAttribute("attr");
System.out.println("attr="+attr);
//删除数据
session.removeAttribute("attr");
//确定数据是否删除
attr = session.getAttribute("attr");
System.out.println("删除后,再次检查数据="+attr);
}
4.3.2 多个拦截器
- 使用两个拦截器,主要看拦截器的执行顺序,以及按个方法控制请求的执行。
1. 都是true
- 两个拦截器,第一个preHandle=true,第二个preHandle=true
MyInterceptor拦截器11的preHandler
MyInterceptor拦截器22的preHandler
执行了MyController的doSome方法
MyInterceptor拦截器22的postHandle
MyInterceptor拦截器11的postHandle
MyInterceptor拦截器22的afterCompletion
MyInterceptor拦截器11的afterCompletion
- 请求的执行顺序:
- 用户some.do—拦截器1的preHandle—拦截器2的preHandle—控制器doSome—拦截器2的postHandle—拦截器1的postHandle—拦截器2的afterCompletion—拦截器1的afterCompletion
2. 一个true一个false
- 两个拦截器,第一个preHandle=true,第二个preHandle=false
MyInterceptor拦截器11的preHandler
MyInterceptor拦截器22的preHandler
MyInterceptor拦截器11的afterCompletion
- 请求的执行顺序:
- 用户some.do—拦截器1的preHandle—拦截器2的preHandle—拦截器1的afterCompletion
3. 反过来
- 两个拦截器,第一个preHandle=false,第二个preHandle=true
MyInterceptor拦截器11的preHandler
4. 总结
- 为什么要使用多个拦截器?
- 把验证功能分散到独立的拦截器,每个拦截器做单一的验证处理
- 组合多个拦截器
- 多个拦截器,串在一个链条上的。多个拦截器和一个控制器对象在一个链条上,框架中使用HandlerExecutionChain(处理器执行链),表示这个执行链条
public class HandlerExecutionChain {
private static final Log logger = LogFactory.getLog(HandlerExecutionChain.class);
private final Object handler;//存放控制器对象的,MyController
@Nullable
private HandlerInterceptor[] interceptors;//存放多个拦截器对象的。MyInterceptor1、2
@Nullable
private List<HandlerInterceptor> interceptorList;
- 拦截器怎么实现1,2 ;2,1的执行顺序,遍历HandlerInterceptor[] interceptors数组
HandlerInterceptor[] interceptors = {MyInterceptor1,MyInterceptor2}
//循环调用1-2
for(int i=interceptors.length-1;i>=0;i--){
HandlerInterceptor obj = interceptors[i];
obj.preHandle();
}
handler。doSome();
//2-1
for(int i=0;i<interceptors.length;i++){
HandlerInterceptor obj = interceptors[i];
obj.postHandle()
}
4.3.3 拦截器和过滤器的对比
- 拦截器是springmvc框架中的对象;过滤器是servlet中的对象。
- 拦截器对象是框架容器创建的;过滤器对象是tomcat创建的对象。
- 拦截器是侧重对请求做判断的、处理的,可以截断请求;过滤器是侧重对request、response对象的属性、参数设值的。例如:request.setCharacterEncoding(“utf-8”)。
- 拦截器有三个执行时间,控制器方法之前,之后,请求完成后;过滤器是在请求之前。
- 拦截器是拦截对controller,动态资源请求的;过滤器可以过滤所有请求,动态或静态的。
- 过滤器和拦截器一起执行的,先执行的过滤器,后面是中央调度器,最后才是拦截器,再后面是控制器方法