介绍:
1.SpringMVC是基于Spring的一个框架,专门做web开发的,可理解为是servlet的升级。web开发底层是servlet,框架是在servlet的基础上增加一些功能,使web开发更方便。
2.SpringMVC能创建控制器对象放入到SpringMVC容器之中。(使用@Controller创建对象,把创建的对象当作控制器使用,这个控制器对象能接受用户的请求,显示处理结果,就当作是一个servlet使用。
注意!!!创建的对象并不是servlet,而是普通对象当作servlet来使用,SpringMVC赋予其一些额外的功能)
3.web开发底层是servlet,SpringMVC中有一个对象是servlet:DispatcherServlet(中央调度器)
DispatcherServlet:负责接收用户的所有请求,用户把请求给了DispatcherServlet之后,
它会把请求转发给我们的Controller对象进行相应的处理
注册DispatcherServlet
<!-- 注册springmvc的核心对象DispatcherServlet
需要在Tomcat启动时,创建DispatcherServlet实例对象
-->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--自定义springmvc读取的配置文件的位置-->
<init-param>
<!--springmvc配置文件位置的属性-->
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext-springmvc.xml</param-value>
</init-param>
<!--Tomcat启动后,创建servlet对象,值越小,优先级越高-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<!--此处可以设置两种值
1.扩展名方式:*.xxx,xxx是自定义的扩展名
2.使用“/”:当使用此方式时会替代tomcat的defaultServlet(处理所有的静态资源和
未映射的servlet),默认情况下DispatcherServlet没有处理静态资源
文件的能力
-->
<url-pattern>*.do</url-pattern>
</servlet-mapping>
使用"/"无法处理静态资源文件的解决方式
- 在SpringMVC配置文件中加入<mvc:default-servlet-handler>
<!-- 加入这个标签后,框架会自动创建对象DefaultServletHttpRequestHandler,
此对象会把接收的请求转发给tomcat的defaultServlet-->
<mvc:default-servlet-handler />
- 在SpringMVC配置文件中加入<mvc:resources mapping="" location="" />
<!--
加入后会创建ResourceHttpRequestHandler对象。
此对象处理静态资源的访问,不再需要tomcat的defaultServlet
mapping:访问静态资源的uri地址,使用通配符**
location:静态资源在项目中的目录
所有静态资源文件放在static目录下,省下大量重复代码
-->
<mvc:resources mapping="/static/**" location="/static/" />
tips:两种方式和@RequestMapping有冲突,需要加入<mvc:annotation-driven />
为什么要创建DispatcherServlet的实例对象:
因为DispatcherServlet在它的创建过程中,会同时创建springmvc容器对象,
读取springmvc的配置文件并把这个文件中的对象都创建好,当用户发起请求时就可以直接使用对象了。
servlet的初始化会调用init()方法,DispatcherServlet在init()中{
//创建容器,读取配置文件
webApplicationContext context = new ClassPathXmlApplicationContext("springmvc.xml");
//把容器对象放入到ServletContext中
getServletContext().setAttribute(key,context);
}
SpringMVC的处理流程
1.发起一个请求some.do
2.tomcat容器中url-pattern将*.do请求给DispatcherServlet
3.DispatcherServlert读取springmvc配置文件获取与some.do请求绑定的处理方法
4.框架执行dosome()把得到的ModelAndView处理,然后转发到新页面
接收参数
首先配置过滤器,使用Spring自带的过滤器
<!--配置过滤器,使用Spring自带的-->
<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>
<!--强制请求对象使用utf-8-->
<init-param>
<param-name>forceRequestEncoding</param-name>
<param-value>true</param-value>
</init-param>
<!--强制响应对象使用utf-8-->
<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>
1.逐个接收参数
1.1处理器方法的形参名和请求中的参数名相同,请求参数赋值给同名的形参
<form action="test.do">
<input type="text" name="username" />
<input type="password" name="password" />
</form>
@RequestMapping(value="/test.do")
public ModelAndView doSome(String name, String password){
...
}
1.2处理器方法名和参数名不相同,使用@RequestParam
<form action="receiveParam.do" method="post">
用户名<input type="text" name="zName" /><br>
年龄<input type="text" name="zAge" /><br>
<input type="submit" value="提交" />
</form>
/**
* 请求中属性名和参数名不一样时,使用@RequestParam逐个接收请求的参数
* 属性:1.value:请求中的参数名称
* 2.required:布尔类型,默认为true,表示请求中必须包含参数
* 位置:在方法形参前面
* @param name 接收参数
* @param age 接收参数
* @return 数据与视图
*/
@RequestMapping(value = "/receiveParam.do", method = RequestMethod.POST)
public ModelAndView doSome(@RequestParam(value = "zName", required = false) String name,
@RequestParam(value = "zAge", required = false) Integer age) {
System.out.println("name=" + name + ",age=" + age);
ModelAndView mv = new ModelAndView();
mv.addObject("zName",name);
mv.addObject("zAge",age);
mv.setViewName("show");
return mv;
}
2.使用对象接收参数
新建一个vo包存放包装参数数据的类
package com.znq.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private String name;
private int age;
}
页面发起请求
<form action="duixaing.do">
用户名<input type="text" name="name" /><br>
年龄 <input type="text" name="age" /><br>
<input type="submit" value="提交">
</form>
处理
@RequestMapping("/duixaing.do")
public ModelAndView receiveParam(Student student){
ModelAndView mv = new ModelAndView();
mv.addObject("name",student.getName());
mv.addObject("age",student.getAge());
mv.setViewName("show");
return mv;
}
返回值
1.ModelAndView:有数据和视图,对视图执行forword
2.String:表示视图
3.void:无返回值,啥也不表示
在处理AJAX请求时,可以使用void
4.Object对象:表示数据
对象有属性,属性就是数据。
可以使用对象表示的数据响应AJAX请求
SpringMVC实现AJAX
1.导入处理json工具库的依赖,springmvc默认使用jackson。
2.在springmvc配置文件中加入注解驱动<mvc:annotation-driven>
3.在处理器方法上加入注解@ResponseBody
springmvc处理器方法返回Object,可以转为json输出到浏览器响应AJAX的内部原理
1.<mvc:annotation-driven>注解驱动
注解驱动实现的功能是完成java对象到xml、json、text、二进制等数据格式的转换。
由HttpMessageConverter(消息转换器)接口实现
功能:定义了java对象转为其他数据格式的方法,由实现类完成。
下面两方法是控制器类把结果返回给浏览器时使用:
//检查处理器方法的返回值能不能转为var2表示的数据格式
boolean canWrite(Class<T> var1, @Nullable MediaType var2);
//调用jackson中的ObjectMapper把处理器方法的返回值转为json格式
void write(T var1, @Nullable MediaType var2, HttpOutputMessage var3);
<mvc:annotation-driven>在加入到springmvc配置文件之后,会自动创建HttpMessageConverter的8个实现类 ,包括MappingJackson2HttpMessageConverter(使用Jackson工具库中的工具把Java对象转为Json格式)
2.@ResponseBody注解
放在处理器方法的上面,通过HttpServletResponse输出数据响应AJAX
使用Jquery+SpringMVC完成一个简单的AJAX
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>接收参数</title>
<script src="webjars/jquery/3.5.1/jquery.min.js"></script>
<script type="text/javascript">
$(function() {
$("button").click(function () {
//alert("button click")
$.ajax({
url : "returnStudentsArray.do",
data : {
name : "张三",
age : "20"
},
type : "post",
dataType : "json",
success : function (resp) {
//alert(resp)
$.each(resp, function(i, n){
alert(n.name + " " + n.age)
})
}
})
})
})
</script>
</head>
<body>
<button id="button">发起AJAX请求</button>
</body>
</html>
vo.Student.java
package com.znq.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private String name;
private int age;
}
controller.MyController.java
package com.znq.controller;
import com.znq.vo.Student;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
@Controller
public class MyController {
/**
* 返回list集合的Json对象
* @param name 姓名
* @param age 年龄
* @return Json数组
*/
@RequestMapping("/returnStudentsArray.do")
@ResponseBody
public List<Student> returnStudentJsonArray(String name, int age) {
ArrayList<Student> students = new ArrayList<>();
Student student = new Student();
student.setName("张三");
student.setAge(20);
students.add(student);
student = new Student();
student.setName("李四");
student.setAge(21);
students.add(student);
return students;
}
}
结果展示
绝对路径和相对路径
-
请求不带"/".
下图请求对应的处理器方法返回值是发起请求的index.jsp
-
请求带"/"
异常处理
SpringMVC采用的集中统一的异常处理方式,把controller中的所有异常都集中到一个地方。
采用的是aop的思想,解耦合,把业务逻辑和异常处理分开。
需要用到两个注解:@ExceptionHandler 和 @ControllerAdvice
创建异常类
//父类
public class UserInfoException extends Exception{
public UserInfoException() {
}
public UserInfoException(String message) {
super(message);
}
}
//子类NameException
public class NameException extends UserInfoException{
public NameException() {
}
public NameException(String message) {
super(message);
}
}
//子类AgeException
public class AgeException extends UserInfoException{
public AgeException() {
}
public AgeException(String message) {
super(message);
}
}
异常处理类
/**
* @author znq
* @date 2021/5/9 - 17:24
*
* @ControllerAdvice :控制器增强(给控制器类增加异常处理功能)
* 位置:类的上面
* 需要开启注解扫描器扫描异常处理类所在的包
* 特点:必须要让框架知道这个注解所在的包名,需要在springmvc配置文件中声明组件扫描器
*/
@ControllerAdvice
public class GlobalExceptionHandler {
/**
* 此方法解决抛出的姓名异常
* @param exception 表示controller中抛出的异常对象,可获取发生的异常信息
* @return mv
* @ExceptionHandler(异常的class) 表示异常的类型,当发生此类型异常时,由当前方法处理
*/
@ExceptionHandler(NameException.class)
public ModelAndView doNameException(Exception exception) {
/*
异常发生处理逻辑:
1.需要把异常记录下来,记录到数据库、日志文件
记录日志发生的时间、位置、内容
2.发送通知,把异常的信息通过短信、微信等途径发送给开发人员。
3.给用户提示
*/
ModelAndView mv = new ModelAndView();
mv.addObject("msg", "姓名异常");
mv.addObject("exception", exception);
mv.setViewName("nameError");
return mv;
}
/**
* 解决抛出的年龄异常
* @param exception 年龄异常对象
* @return mv
*/
@ExceptionHandler(AgeException.class)
public ModelAndView doAgeException(Exception exception) {
ModelAndView mv = new ModelAndView();
mv.addObject("msg", "年龄异常");
mv.addObject("exception", exception);
mv.setViewName("ageError");
return mv;
}
/**
* 解决抛出的其他异常
* @param exception 其它异常对象
* @return mv
*/
@ExceptionHandler
public ModelAndView doOtherException(Exception exception) {
ModelAndView mv = new ModelAndView();
mv.addObject("msg", "其它异常");
mv.addObject("exception", exception);
mv.setViewName("otherError");
return mv;
}
}
controller层
@Controller
@RequestMapping("student")
public class StudentServlet {
@Resource
private StudentServiceImpl studentService;
@RequestMapping("/insert.do")
public ModelAndView insertStudent(Student student) throws UserInfoException {
//处理异常
if (!"张三".equals(student.getName())) {
throw new NameException("注册用户输入的姓名存在异常");
}
if (student.getAge() < 0 || student.getAge() > 35) {
throw new AgeException("注册用户输入的年龄存在异常");
}
ModelAndView mv = new ModelAndView();
String tips = "添加失败";
//调用service中的方法
int nums = studentService.insertStudent(student);
//添加成功则修改tips
if (nums > 0) {
tips = "小b崽子[" + student.getName() + "]添加成功";
}
//向mv添加数据
mv.addObject("tips", tips);
//设置返回视图
mv.setViewName("result");
return mv;
}
}
拦截器
1.拦截器是SpringMVC中的一种,实现HandlerInterceptor接口
2.拦截器和过滤器类似,功能方向侧重点不同。
过滤器主要用来过滤请求参数,设置编码字符集等
拦截器是拦截用户的请求,对请求做判断处理的
3.拦截器是全局的,可以对多个controller拦截。
一个项目中可以有0或多个拦截器,他们一起拦截用户的请求
4.拦截器常用在:用户登录处理、权限检查、记录日志
拦截器的使用步骤
- 定义一个类实现HandlerInterceptor接口
- 在springmvc配置文件中声明拦截器
拦截器的执行时间
- 在请求处理之前,也就是controller类中的方法执行之前先被拦截
- 在控制器方法执行之后
- 在请求处理完成后
1.创建一个类实现HandlerInterceptor接口
package com.znq.handler;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author znq
* @date 2021/5/10 - 9:48
*/
public class MyInterceptor implements HandlerInterceptor {
/**
* 预处理方法(整个项目的入口)
* 特点:
* 1.方法在控制器方法之前执行,用户请求先到达此处
* 2.在这个方法中可以获取请求的参数,验证请求是否符合要求。
* 可以用在用户登录、判断用户时都有权限访问某个url
* 验证失败则拦截
* 验证成功则放行
* @param request
* @param response
* @param handler 被拦截的控制器对象
* @return true:请求通过拦截器验证,可以执行处理器方法
* false:请求未通过拦截器
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
}
/**
* 后处理方法
* 特点:
* 1.在处理器方法之后执行
* 2.能获取到ModelAndView并进行二次修改
* @param request
* @param response
* @param handler 被拦截的处理器对象
* @param modelAndView 控制器方法返回的数据
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
/**
* 最后执行的方法
* 特点:
* 1.在请求处理完成后执行(框架中认为对视图执行完forward转发之后即为处理完成)
* 2.一般做资源回收工作,程序请求过程中创建了一些对象,在这里可以销毁删除释放内存
* @param request
* @param response
* @param handler 拦截的处理器方法对象
* @param ex 执行过程中抛出的异常
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
2.在springmvc的配置文件中声明拦截器
<!-- 声明拦截器,可以有0个或多个 -->
<mvc:interceptors>
<!-- 第一个拦截器 -->
<mvc:interceptor>
<!-- 指定拦截的uri地址 -->
<mvc:mapping path="/student/**"/>
<bean class="com.znq.handler.MyInterceptor" />
</mvc:interceptor>
</mvc:interceptors>
拦截器和过滤器的区别
- 过滤器是servlet中的对象,
拦截器是框架的对象。 - 过滤器是实现FIlter接口的对象,
拦截器是实现HandlerInterceptor的对象。 - 过滤器是用来设置request,response的参数、属性的,侧重对数据的过滤。
拦截器是用来验证请求的,能截断请求。 - 过滤器是在拦截器之前运行的。
- 过滤器是tomcat容器中创建的,
拦截器是在SpringMVC容器中创建的。 - 过滤器有一个执行点,
拦截器有三个执行点。 - 过滤器可以处理jsp、js、html等,
拦截器是侧重拦截对controller的对象。如果你的请求不能被DispatcherServlet接收,这个请求不会执行拦截器内容。 - 过滤器过滤servlet请求响应,拦截器拦截普通类方法执行。