SpringMVC
Spring MVC介绍
Spring MVC是框架Spring的一部分,是基于Java实现的一个轻量级web框架。
SpringMVC的核心
学习Spring MVC框架最核心的就是中央调度器(DispatcherServlet)的设计,掌握好Dispatcher Servlet是掌握SpringMVC的核心关键。
Spring MVC是管理控制器对象,原来没有SpringMVC之前使用的是Servlet作为控制器对象使用。现在通过SpringMVC容器创建一种叫做控制器的对象,代替Servlet行使控制器的角色,功能。
导入依赖
注意:在Tomcat10.x以后,要导入点不是以前的javax.servlet-api了。而是需要换成下面的依赖。web使用的是jakarta.servlet的依赖。
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<!-- https://mvnrepository.com/artifact/jakarta.servlet/jakarta.servlet-api -->
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>5.0.0</version>
<scope>provided</scope>
</dependency>
<!-- springmvc依赖-->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.5</version>
</dependency>
中央调度器的配置
<?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>
<!--spring配置文件位置-->
<!--
配置文件默认是 /WEB-INF/springmvc-servlet.xml 如果不配置文件路径,则默认找这个地方的文件
文件默认的名称是我们配置的DispatcherServlet(中央调度器的名称)加上 -servlet.xml 组成 <servlet-name>-servlet.xml
springmvc 加载配置文件的时机如果不配置,则是在每次访问配置的虚拟映射路径的时候进行加载,
WebApplicationContext ctx = new ClassPathXmlApplicationContext("springmvc-servlet.xml");
创建web容器对象ctx需要读取配置文件,这个配置文件和Spring配置文件的配置一致
-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</init-param>
<!--web容器启动时加载,配置文件只加载一次-->
<!--
web容器Tomcat创建容器的顺序,数值越小,创建时机越早(大于0的整数值)(大于0表示启动时就会创建),数值0表示访问的时候创建
-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<!--拦截(捕获)除了jsp文件以外的所有请求-->
<!--
把一些请求交给servlet进行处理
中央调度器的常用格式:
1. 使用拓展名 *.xx xx是自定义拓展名 *.do *.test
但是要注意: 不能使用 *.jsp 因为以jsp结尾到底是访问这个页面视图,还是要将以jsp结尾的请求交给中央调度器处理呢,有歧义性
2. 使用 / 稍后进行介绍
-->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
<!--注解扫描器-->
<context:component-scan base-package="com.mao.web.controller"/>
<!--视图解析器,帮助解析视图-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--前缀,指定视图文件的路径-->
<property name="prefix" value="/WEB-INF/view/"/>
<!--后缀,指定视图文件的拓展名-->
<property name="suffix" value=".jsp"/>
</bean>
package com.mao.web.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
/**
* @Description
* @Author 毛毛
* @CreateDate 2021/06/01/周二 1:48 下午
*/
@Controller
public class HelloMVC {
// RequestMapping注解,
// 位置:可以放在方法上面,也可以放在类上面(可选)
// 属性:value是一个数组,可以包含多个请求列表,也就是说一个方法可以处理多个请求,数组的值必须唯一,以 / 开头
// 作用:把指定的请求,交给指定的方法进行处理,等同于 url-pattern
@RequestMapping(value= {"/some.do"})
public ModelAndView hello(){
ModelAndView mv = new ModelAndView();
// 向请求域中添加对象,相当于 request.setAttribute("","")
mv.addObject("username","mao");
// 指定视图,参数是视图完整路径
// 但是当配置了视图解析器以后(配置文件中进行配置),参数为视图逻辑名称
// 使用了逻辑名称,框架使用配置文件中视图解析器的前缀和后缀,拼接为完整路径
// 视图执行的请求转发操作。。。
mv.setViewName("hello");
return mv;
}
/**
* 访问other.do,请求转发给other页面
* @return
*/
@RequestMapping(value= {"/test/other.do"})
public ModelAndView doOther(){
ModelAndView mv = new ModelAndView();
// 向请求域中添加对象,相当于 request.setAttribute("","")
mv.addObject("username","other");
mv.setViewName("other");
return mv;
}
}
请求的处理过程
一般情况:
用户发起请求 —》 Tomcat接收到请求。—〉 DispatcherServlet ---- 分派 ---- 》 具体执行的控制器controller — 〉 返回处理结果
SpringMVC的MVC组件
2. springMVC注解式开发
2.1 @RequestMapping注解的使用
属性:value 请求的url地址(虚拟映射)
位置:1. 在方法上,必须的属性 2. 在类的上面作为模块名称
属性method请求的方式,使用RequestMethod类的枚举值,表示请求的方式。
2.1.1 指定模块名称
// RequestMapping注解,
// 位置:可以放在方法上面,也可以放在类上面(可选)
// 属性:value是一个数组,可以包含多个请求列表,也就是说一个方法可以处理多个请求,数组的值必须唯一,以 / 开头
// 作用:把指定的请求,交给指定的方法进行处理,等同于 url-pattern
@RequestMapping(value={"/some.do"})
@Controller
@RequestMapping(value = "/test") // value属性:表示所有请求地址的公共前缀,相当于是模块名称,位置在类的声明上面
public class MyController {
// 两个请求地址都有 /test 前缀,所以可以将其提取出来
// @RequestMapping(value = {"/test/some.do"})
@RequestMapping(value = {"/some.do"})
public ModelAndView doSome() {
ModelAndView mv = new ModelAndView();
mv.addObject("username", "mao");
mv.setViewName("hello");
return mv;
}
// @RequestMapping(value = {"/test/other.do"})
@RequestMapping(value = {"/other.do"})
public ModelAndView doOther() {
ModelAndView mv = new ModelAndView();
mv.addObject("username", "jun");
mv.setViewName("other");
return mv;
}
}
2.1.2 对请求提交方式的定义
注解@RequestMapping有属性method,用于对被注解方法所处理请求的提交方式进行限制,即只有满足该method属性指定的提交方式的请求才会执行该注解方法。
method属性的取值为 RequestMethod枚举常量,常见的是RequestMethod.GET 和 RequestMethod.POST 方式。
如果不指定该属性,则请求方式任意,任何请求都可以。可以处理所有方式的该请求。
/**
* @RequestMapping: 属性method: 请求的方式:使用RequestMethod枚举类的常量进行赋值,如果不指定该属性,则请求方式任意,任何请求都可以
* @return
*/
@RequestMapping(value = {"/show.do"},method = RequestMethod.GET)
public ModelAndView doShow() {
ModelAndView mv = new ModelAndView();
mv.addObject("username", "jun");
mv.setViewName("other");
return mv;
}
2.1.3 处理器方法的参数
参数可以包含四种类型,这些参数会在系统调用时由系统自动进行赋值,程序员在方法内部直接进行调用就好了。
- HttpServletRequest
- HttpServletResponse
- httpSession
- 请求中所携带的参数
@RequestMapping(value = {"/request.do"},method = RequestMethod.GET)
public ModelAndView doRequest(HttpServletRequest request, HttpServletResponse response) {
ModelAndView mv = new ModelAndView();
// 获取请求参数,使用request对象进行获取
System.out.println(request.getRequestURI());
mv.addObject("username", request.getParameter("username"));
mv.setViewName("post");
return mv;
}
2.1.4 接收请求的参数
错误: 状态码 400 表示客户端异常。主要是发生在用户提交参数的过程中。
1. 参数逐个接收
只要保证请求参数名于该请求处理方法的参数名相同就可。
-
参数的接收最好使用包装类型。例如Integer等。能够接收空值等情况,接收的是null
-
框架可以使用String到int,float等类型的自动转换。
-
post请求中有乱码的问题,使用字符集过滤器
/**
* 按照名称接收请求参数,请求参数名和控制器方法名的行参名一样。按照名称对象映射接收请求参数
* @param username 用户名
* @param password 密码
* @param age age必须是一个数字
* 参数接收:框架使用request对象,接收参数
* String username = request.getParameter("");
* 在中央调度器的内部调用 doProperParam方法时,按名称对象传递参数
* doProperParam(username,password,Integer.valueOf(age))
* 框架可以实现请求参数 String 到 int,long,float等类型的转化。
* 如果客户端传递过来的参数有null,或者这个字段没有赋值,建议方法的接收参数类型都使用包装类来接收空值的参数 Integer
*
* @return
*/
@RequestMapping(value = {"/param.do"})
public ModelAndView doParam(String username,String password,Integer age) {
ModelAndView mv = new ModelAndView();
// 获取请求参数,使用request对象进行获取
mv.addObject("username", username);
mv.addObject("password", password);
mv.addObject("age", age);
mv.setViewName("param");
return mv;
}
springmvc解决中文乱码问题的过滤器
springmvc默认提供好的有一个解决乱码的过滤器。 CharavterEncodingFilter类
<!--声明框架提供好的过滤器:框架提供的,解决post请求中文乱码问题-->
<filter>
<filter-name>filter</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>
<!-- 强制请求对象(request)对象使用encoding的编码格式-->
<param-name>forceRequestEncoding</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<!-- 强制响应对象使用encoding的编码格式-->
<param-name>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>filter</filter-name>
<!--强制所有请求先经过过滤器处理-->
<url-pattern>/*</url-pattern>
</filter-mapping>
请求参数名和行参名不一致(@RequestParam)
使用@RequestParam注解解决参数名不一致的情况
/**
* 请求参数和行参不一致
* 当请求当参数名和行参名不一致当时候,我们使用注解 @RequestParam 注解来进行解决
* 属性: value:请求中当参数名称
* required: boolean类型,默认是true
* 表示该请求中必须有此参数,没有则会报错,有此参数不代表该参数必须有值,但是必须将该参数传递过来
* 位置: 在行参定义前
* @param name
* @param password
* @param age
* @return
*/
@RequestMapping(value = {"/param2.do"})
public ModelAndView doParam2(@RequestParam("name") String name,@RequestParam("pwd") String password,@RequestParam("age") Integer age) {
ModelAndView mv = new ModelAndView();
// 获取请求参数,使用request对象进行获取
mv.addObject("username", name);
mv.addObject("password", password);
mv.addObject("age", age);
mv.setViewName("param");
return mv;
}
}
2. 对象接收
多个请求参数时,建议使用对象形式接收。
对象接收:在控制器方法的行参是java对象,使用java对象的属性接收请求参数值。
要求:Java对象的属性名和请求中参数名一样。(这种方式最简单)
/**
* 使用对象接收请求中的参数,
* 要求:这里的请求参数名和对象的属性名一致
* java类有一个无参构造方法,属性有set方法
* 框架的处理:
* 1。 调用user的无参构造方法,创建对象
* 2。 调用对象的set方法,同名的参数,调用对应的set方法
* 参数是username,调用属性username的set方法
* @return
*/
@RequestMapping(value = {"/obj.do"})
public ModelAndView doObj(User user) {
ModelAndView mv = new ModelAndView();
// 获取请求参数,使用request对象进行获取
mv.addObject("username", user.getUsername());
mv.addObject("password", user.getPassword());
mv.addObject("age", user.getAge());
mv.setViewName("obj");
return mv;
}
package com.mao.bean;
/**
* @Description
* @Author 毛毛
* @CreateDate 2021/06/03/周四 11:27 上午
*/
public class User {
private String username;
private String password;
private Integer age;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
2.2 DispatcherServlet作用
- 在init()中创建springmvc的容器对象。 WebApplicationContext wcp = new ClassPathXmlApplicationContext();
创建springmvc配置文件里面所有的java对象。java对象就是controller 对象
- DispatcherServlet本身就是一个servlet,但是不是我们写的,它负责接收请求。
2.3 配置文件说明
- web.xml 部署描述符文件。是给服务器用的(Tomcat),文件的作用是,在tomcat启动的时候,tomcat读取web.xml文件,根据文件的声明创建各种对象,根据文件中的声明,知道请求和servlet等对象的映射关系啊等等。
- 框架的配置文件。springmvc的配置文件。作用:声明框架创建项目中的各种对象,主要是创建Controller对象的。
配置文件加载的顺序和功能
-
tomcat服务器启动,读取web.xml,根据文件的说明,创建对象。
<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>*.do</url-pattern> </servlet-mapping>
创建DispatcherServlet这个类 对象,会执行init()方法。在init方法中会执行springmvc容器对象创建
WebApplicationContext ctx = new ClassPathApplicationContext(’“classpath:springmvc.xml”);
-
springmvc读取配置文件的时候,
<!--注解扫描器--> <context:component-scan base-package="com.mao.web.controller"/> <!--视图解析器,帮助解析视图--> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!--前缀,指定视图文件的路径--> <property name="prefix" value="/WEB-INF/view/"/> <!--后缀,指定视图文件的拓展名--> <property name="suffix" value=".jsp"/> </bean>
使用组件扫描器,遍历controller包中所有的类,找到类上声明的注解@Controller,以及注解@RequestMapping,然后就可以创建相应的对象。就知道那个请求是执行那个方法进行处理请求了。
-
用户发起请求 ----》 中央调度器 ----〉
中央调度器 里面有WebApplicationContext,WebApplicationContext里面有控制器对象。中央调度器就知道请求的执行方法是哪一个了。
2.4 控制器方法的返回值
控制器方法的返回值表示本次请求的处理结果,返回值有 ModelAndView,String,void,Object。四大类
请求的处理结果包含:数据和视图。
2.4.1 ModelAndView 数据和视图
请求的响应结果有数据和视图,使用ModelAndView最方便
数据:存放在request作用域
视图:执行请求转发操作
2.4.2 String 视图
控制器方法的返回值是String,表示执行forward转发操作。响应的结果就是一个视图。
视图可以是完整的视图路径,或者是视图的逻辑名称。
当前还没解决视图解析器和完整视图路径的冲突问题。
/**
* 控制器方法返回String,表示逻辑视图名称。需要项目中配置视图解析器
*
* @return
*/
@RequestMapping(value = {"/returnString.do"})
public String doReturnString(HttpServletRequest request, String username, String password) {
System.out.println(username);
System.out.println(password);
// 处理请求数据,需要自己手动处理了
// 在行参上加上HttpServletRequest参数
request.setAttribute("username", username);
request.setAttribute("password", password);
// 请求转发到 return/returnString.jsp页面
return "return/returnString";
}
/**
* 完整视图路径,项目中不能配置视图解析器
*
* @param request
* @param username
* @param password
* @return
*/
@RequestMapping(value = {"/returnString2.do"})
public String doReturnString2(HttpServletRequest request, String username, String password) {
System.out.println(username);
System.out.println(password);
// 处理请求数据,需要自己手动处理了
// 在行参上加上HttpServletRequest参数
request.setAttribute("username", username);
request.setAttribute("password", password);
// 请求转发到 return/returnString.jsp页面
// 当前配置的有视图解析器,写完整路径的话肯定会报错的。所以完整路径和视图解析器发生了冲突,这个在后面可以进行解决
return "return/returnString";
}
}
2.4.3 没有数据和视图
空返回值void,经常用来作为响应ajax请求的。使用响应对象HttpServletResponse对象输出数据,响应ajax请求。
/**
* 控制器方法返回的是void,响应ajax请求,使用HttpServletResponse输出数据
* @param name
* @param age
*/
@RequestMapping("/returnVoid-ajax.do")
public void doAjax(HttpServletResponse response,String name, Integer age) throws IOException {
System.out.println(name+"---"+age);
// 处理数据
// 响应数据
User user = new User();
user.setAge(age);
user.setUsername(name);
user.setPassword("111");
// 把对象转化为json类型
ObjectMapper om = new ObjectMapper();
String json = om.writeValueAsString(user);
System.out.println(json);
// 响应ajax
response.setContentType("application/json;charset=utf-8");
response.getWriter().write(json);
}
2.4.4 object 对象
返回 Student 表示的是 数据。
所以一般控制器方法返回的是对象Object,大部分都是用来处理Ajax请求。
返回对象Object,可以是List,Student,Map,String,Integer,。。。都是数据(对象)。ajax请求需要的就是数据。
在ajax请求中,一般需要从服务器返回的是json格式的数据,经常需要处理java对象到json数据的转换。而且还需要输出数据,来响应ajax到请求。
框架提供了处理java对象到json数据的转化,以及数据的输出工作。
1. 消息转换器 HttpMessageConverter
这是一个接口。
作用:
- 实现请求的数据转化为java对象
- 把控制器方法返回的对象转为json,xml,text,二进制等不同格式的数据。
HttpMessgaeConverter接口源码:
MediaType: 就是请求过来的数据格式,也叫媒体类型。比如json格式的数据。互联网中数据的格式。(application/json,text/html,image/gif,。。。)
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表示的数据格式
返回 true 表示能,则调用write方法
*/
boolean canWrite(Class<?> var1, @Nullable MediaType var2);
/*
把t对象,按照 var2(contentType)说明的格式,把对象转为json或者xml
T 范型 就是控制器方法返回的数据类型
*/
void write(T var1, @Nullable MediaType var2, HttpOutputMessage var3) throws IOException, HttpMessageNotWritableException;
}
HttpMessageConverter接口的常见实现类:
- MappingJackson2HttpMessageConverter:使用jackson工具库的ObjectMapper把对象转为json数据格式
- StringHttpMessageConverter: 把字符串类型的数据,进行格式的转换和编码。
2. 使用实现类
框架根据控制器方法的返回值,自动查找使用的实现类。
默认情况下:springmvc使用了HttpMessageConverter接口的四个实现类。包括Http MessageConverter。
需要在springmvc加上注解驱动的标签:<mvc:annotation-driver/>
.加上这个标签后,springmvc项目启动后,会创建HttpMessageConverter接口的七个实现类对象,包含了 StringHttpMessageConverter和MappingJackson2HttpMessageConverter。
3. @ResponseBody
@ResponseBody注解的作用就是把User转换后的json通过HttpServletResponse对象输出给浏览器。
4. 控制器方法返回对象转为json的步骤
- pom。xml加入jackson依赖,springmvc框架默认处理json就是使用jackson
- 在springmvc的配置文件中,加入注解驱动标签
- 在控制器方法的上面加入@ResponseBody注解,表示返回值数据,输出到浏览器。
/**
* 控制器方法返回的对象转化为json格式数据响应客户端
*
* @return
* @ResponseBody: 这个注解会自动 把 对象转化的json输出给浏览器,并设置响应头
* 框架的处理模式:
* 1。 框架根据控制器方法的返回值类型,找到HttpMessageConverter接口的实现类。然后找到json的实现类
* 2。 使用实现类MappingJackson2HttpMessageConverter。执行write方法。把student对象转化为json格式的数据
* 3。 框架使用ResponseBody注解,把json数据输出给浏览器
*/
@ResponseBody
@RequestMapping("/ajaxUser.do")
public User doAjaxUser(String name, Integer age) {
User user = new User();
user.setUsername(name);
user.setPassword("123");
user.setAge(age);
return user;
}
/**
* 控制器方法返回的是集合,则对应json 会转换为 array
* @param name
* @param age
* @return
*/
@ResponseBody
@RequestMapping("/ajaxUserList.do")
public List<User> doAjaxUserList(String name, Integer age) {
User user = new User();
user.setUsername(name);
user.setPassword("123");
user.setAge(age);
User user1 = new User("222","222",222);
List<User> list = new ArrayList<User>();
list.add(user);
list.add(user1);
return list;
}
/**
* 控制器方法返回值是String,在方法上加上 @ResponseBody注解,返回值不是表示视图,而是表示数据
*
* 区分String 返回值是数据还是视图,就看方法上是否有 @ResponseBody 注解
*
* 默认响应头: Content-Type: text/plain;charset=ISO-8859-1 发生中文乱码
* 我们配置的过滤器不会起作用,因为ajax请求默认走的是应答对象
*
* 解决中文乱码: 需要使用 @RequestMapping注解的produces属性 ,指定 content-type的值
* 框架处理String返回值:
* 1。 框架使用StringMessageConverter
* 2。 StringHttpMessageConverter 默认使用的是text/plain;charset=ISO-8859-1字符集
* @param name
* @param age
* @return
*/
@ResponseBody
@RequestMapping(value = "/ajaxUserString.do" ,produces = "text/plain;charset=utf-8")
public String doAjaxUserString(String name, Integer age) {
User user = new User();
user.setUsername(name);
user.setPassword("123");
user.setAge(age);
return user.toString();
}
2.5 解读映射标签<url-pattern/>
2.5.1 配置详解
1. *.do
在没有特殊要求的情况下,springmvc的中央调度器DisptcherServlet的<url-pattern/>
常使用后缀匹配方式,如写为*.do或者 *.action等。
2. /
当我们配置了映射地址为 /,这个中央调度器就变成了tomcat默认的servlet的身份了,可以帮我们处理静态资源以及未映射到其他请求的请求。**发现无论是图片,还是js文件,html静态资源文件等,都无法请求成功了。**动态资源(jsp文件还是tomcat帮我们处理的)以及配置了处理的请求的请求可以正常访问。
静态等资源访问失败的原因就是我们没有配置对应的控制器对象
<!--配置中央调度器-->
<servlet>
<servlet-name>servlet</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>servlet</servlet-name>
<!--映射方式二:使用 / -->
<!--
使用 / 导致中央调度器成为了默认的 default servlet
需要处理静态资源和其他未映射的请求。默认中央调度器没有处理静态资源的控制器对象,所以静态资源的请求都是 404
如果项目中 配置 映射路径为 / 动态资源可以正常访问,静态资源不能政策访问。需要处理静态资源的访问工作。
-->
<url-pattern>/</url-pattern>
</servlet-mapping>
3. 静态资源的处理
实际上:无论是静态页面(html),图片,jsp文件,js文件等,实际上都是Tomcat服务器帮我们处理等。只有 some.do 等请求才是我们的中央调度器帮我们处理的。所以实际上tomcat容器肯定也有一个servlet,帮我们处理一些静态的请求了。
4. tomcat 的默认servlet
tomcat的默认的servlet叫做default,帮我们处理所有未映射到其他请求的请求。
<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,提供了静态资源的处理,以及所有未映射到其他请求的请求。根据tomcat默认的servlet的配置映射路径是 /,而这个default是处理我们没有配置的其他请求,所以我们可以得知 / 的优先级一定是比较低的。
所以当我们配置了 / 动态资源正常访问,静态资源不能了。需要处理静态资源的访问工作。
2.5.2 第一种处理静态资源的方式
在springmvc的配置文件中加入 <mvc:default-servlet-handle>
标签,springmvc框架会在项目运行时,加入DefaultServletHttpRequestHandle对象,让这个对象处理静态资源的访问
这个对象会把接收到的静态资源的请求地址转发给tomcat到default对象,让它处理。
优点:解决方式简单
缺点:依赖服务器tomcat到default对象。(该方式现在用的少了)
注意:该注解 <mvc:default-servlet-handle>
和@RequestMapping二者有冲突,解决方案就是在配置文件中声明注解驱动,加上注解驱动的标签。
<!--配置注解驱动-->
<mvc:annotation-driven/>
<!--配置视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="suffix" value=".jsp"/>
<property name="prefix" value="/WEB-INF"/>
</bean>
<!-- 静态资源处理的第一种方式-->
<mvc:default-servlet-handler/>
2.5.3 第二种解决静态资源的方式
在springmvc配置文件中加入一个<mvc:resources/>
标签,框架会创建ResourcesHttpRequestHandle控制器对象,使用这个对象处理静态资源的访问。不依赖Tomcat服务器。推荐使用。
该标签也会和@Requestmapping发生冲突,因此也需要配置注解驱动标签。
<!-- 静态资源处理的第二种方式-->
<!--
mapping: 访问静态资源的uri地址,可以使用通配符 **
** : 表示任意目录和目录下的文件
location: 静态资源在项目中的位置,不要使用 /WEB-INF 目录
-->
<!--该标签可以多次使用-->
<mvc:resources mapping="/html/**" location="html/"/>
Spring MVC的优点
SSM框架整合
1. 整合思路
SSM整合思路: Spring + SpringMVC + Mybatis
SSM整合是使用三个框架的优势功能,三个框架对应的三层架构的三层。视图层 业务层 持久层
ssm整合就是把对象交给容器管理,让容器去创建项目中要使用的java对象。现在有两个容器。
第一个是 Spring容器:Spring容器是管理service和dao等对象的。是业务层对象的容器。
第二个是SpringMVC容器:管理控制器对象的。是视图层对象
SSM整合就是把对象交给容器管理。两个容器共存,各自管理不同的对象。把对象声明到配置文件中,让两个容器创建对象。
2. 容器的创建和使用
Spring容器创建:在web.xml声明监听器ContextLoader Listener,这个功能框架已经写好了。功能是创建Spring的容器对象 WebApplicationContext。在创建webApplicationContext对象时,读取Spring的配置文件,遇到的bean标签或者注解,就能创建service,dao等对象,放到容器中进行管理。
WebApplicationContext spring = new WebApplicationContext(‘配置文件’)
SpringMVC容器:在web.xml声明了中央调度器DispatcherServlet,在这个servlet等init方法中,创建了容器对象WebapplicationContext,在创建该对象时,会读取springmvc的配置文件,读取配置文件的时候,遇到@Controller注解,创建控制器对象,放到容器中。
WebApplicationContext springmvc = new WebApplicationContext(‘配置文件’)
3. 两个容器之间的关系
设计上,SpringMVC容器对象时Spring容器的子容器。可以理解为Java中的继承关系,但是并不是真正的继承。所以子容器可以访问父容器中的对象。
4. 整合步骤
-
创建mavenweb项目
-
加入依赖坐标(spring,springmvc,myabtis,mybatis-spring,mysql驱动,druid连接池,jackson)
-
配置web.xml文件:声明容器对象
- 声明Spring监听器ContextLoaderListener:创建Spring的容器对象,创建service和dao层对象
- 声明springmvc的中央调度器,创建springmvc容器,创建controller层对象
- 声明字符集过滤器(mvc提供的),解决post请求乱码
-
写Spring,springmvc,myabtis的配置文件
-
Java代码,实体类,dao接口,mapper文件,service,controller类,使用注解声明对象和赋值
-
创建视图文件,各种jsp
mybatis Config配置文件
<?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="logImpl" value="STDOUT_LOGGING"/>
</settings>
<typeAliases>
<package name="com.mao.bean"/>
</typeAliases>
<mappers>
<!-- 配置package 则需要保证接口名和配置文件名相同,且在同一个包下-->
<package name="com.mao.dao"/>
</mappers>
</configuration>
Spring配置文件
<?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配置文件-->
<!-- 注解扫描器-->
<context:component-scan base-package="com.mao.service"/>
<!--jdbc配置文件-->
<context:property-placeholder location="classpath:conf/jdbc.properties"/>
<!-- 数据源-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
init-method="init" destroy-method="close">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="maxActive" value="${jdbc.maxActive}"/>
</bean>
<!-- 创建sqlSessionFactoryBean -->
<bean id="factoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!--配置文件路径 下面配置类mapper,则不需要书写myabtis的config配置文件了-->
<property name="configLocation" value="classpath:conf/mybatisConfig.xml"/>
</bean>
<!--创建dao对象,也就是mapper实现类-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="sqlSessionFactoryBeanName" value="factoryBean"/>
<!--dao 层接口-->
<property name="basePackage" value="com.mao.dao"/>
</bean>
<!-- Spring事务的配置-->
</beans>
springmvc配置文件
<?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配置文件-->
<!-- 声明组件扫描器 控制层交给mvc进行管理-->
<context:component-scan base-package="com.mao.web.controller"/>
<!--配置视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 前缀和后缀-->
<property name="prefix" value="/WEB-INF/jsp"/>
<property name="suffix" value=".jsp"/>
</bean>
<!-- 注解驱动
1。 创建HttpMessageConverter接口的七个实现类对象,处理Java对象到json格式的转化
2。 解决静态资源中,动态资源访问失败的问题
-->
<mvc:annotation-driven/>
</beans>
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">
<!--配置Spring启动监听器-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:conf/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- springmvc配置-->
<!--配置中央调度器-->
<servlet>
<servlet-name>servlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--配置初始化参数-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:conf/dispatcherServlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>servlet</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<!--配置过滤器-->
<filter>
<filter-name>filter</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>filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
5. 路径问题
在项目中,有时候请求地址前面有 /,有时候没有 /,但是有时候都可以请求成功,这有问题呢?
相对路径问题
在页面中,有路径的问题,访问路径有 / 开头,也有没有 / 开头的
<a href='test/some.do'>没有斜杠</a>
<a href='/test/some.do'>有斜杠</a>
<a href='http://www.baidu.com'></a>
地址的区别:
- 有协议开头的,是绝对地址,地址是唯一的,可以直接访问。
- 没有协议开头的,是相对路径,单独使用的时候不代表某个资源,不能访问。相对地址必须和参考地址在一起,才能正常访问。
参考地址:有斜杠 / ,没有斜杠 /,两个参考地址是不一样的。
没有斜杠时:http://localhost/ssm/index.jsp
当我们成功进入某一个资源后,地址会发生改变的http://localhost/ssm/test/some.do,那么参考地址也就变了,再次点击就会发现地址发生错误了。http://localhost/ssm/test/test/some.do
没有斜杠开头时,就会把参考地址和当前相对地址组合在一起成为最终的访问地址。
解决方式:
可以使用 el表达式 ${pageContext.request.contextPath} 这个可以获取当前项目的虚拟目录。优点就是好理解,缺点就是每个地方都需要使用。
也可以使用 html的 base 标签,该标签的作用就是 为页面上的所有链接规定默认地址或目标,也就是说当前页面的所有地址的相对地址都是base标签指定的地址
<head>
<base href='http://localhost/ssm/'/>
</head>
<%
String basePath = request.getScheme() +":" + request.getServerName()+":" + request.getServerPort()+":"+
request.getContextPath()+"/";
%>
<head>
<base href='<%=basePath%>'/>
</head>
有斜杠开头(绝对路径)
有斜杠开头的地址,会直接在 http://localhost 加上带斜杠的地址,也就是 直接变成 http://localhost/test/some.do
使用 / 开头的地址,参考地址是服务器地址,也就是协议到端口号的位置。 不会包含项目的虚拟目录/ssm
解决方式就是上面解决方式的第一种,使用 el表达式进行解决。
SpringMVC核心技术
1. 请求重定向和转发
请求转发:
使用 forword进行请求转发,实现原理就是 request.getRequestDispatcher("").forward(req,resp)
@Controller
@RequestMapping("/test")
public class MyController2 {
/**
* 控制器方法返回的是ModelAndView,实现请求转发
* 语法: mv.setViewname("forward:视图的完整路径")
* forward的特点:不和视图解析器一同工作的,就当作项目中没有配置视图解析器
* @return
*/
@GetMapping("/forward.do")
public ModelAndView doForward(){
ModelAndView mv = new ModelAndView();
// 显式的使用 forward 进行转发操作,我们不管加不加 forward,都是进行转发操作,
// 但是,只有我们显式的加上 forward: 这个关键字,才会忽略视图解析器,
// 没有该关键字,也就是省略了forward的时候,我们只需要写加上视图解析器以后的名称
// 意义就是我们的文件可能不在视图解析器所配置的目录中,所以使用该方式可以获取其他目录中的文件视图
mv.setViewName("forward:/WEB-INF/show/forward.jsp");
return mv;
}
}
重定向
使用 redirect进行重定向,实现原理就是 response.sendRedirect("");
/**
* 当控制器方法返回的 ModelAndView 实现重定向时,
* 语法
* mv.setViewName("redirect:完整路径");
* 特点:也会忽略视图解析器
* 框架提供的重定向功能
* 1。 框架可以实现两次请求之间的数据传递,把第一次请求中的Model里面的简单类型的数据,转为字符串,附加到目标页面的后面,做get参数传递
* 可以在目标页面中获取参数值使用
*获取参数的值 可以使用 ${param.xxx} 进行获取参数的值
* @return
*/
@GetMapping("/redirect.do")
public ModelAndView doRedirect() {
ModelAndView mv = new ModelAndView();
// 重定向 无法访问 WEB-INF 里面的内容哦
mv.setViewName("redirect:/html/redirect.jsp");
return mv;
}
2. 异常处理
2.1 springmvc框架处理异常的方式:
使用 @ExceptionHandler注解处理异常。框架处理异常的方式是集中处理。把各个Controller类里面的方法中抛出的异常集中到一个地方处理。处理异常的叫做异常处理器。
- @Exceptionhandler:放在方法上面的,表示此方法可处理某个类型的异常。当异常发生时,执行这个方法。
- @ControllerAdvice: 放在类上面,表示此类中有异常处理的方法,相当于aop中的@Aspect。此注解可以理解为控制器增强,就是给Controller类增强功能的处理功能。
2.2 @ExceptionHandler
使用该注解可以将一个方法指定为异常处理方法。该注解只有一个可选属性value,为一个Class<?> 数组,用于指定该注解的方法所要处理的类,即所要匹配的异常。
而被注解的方法,其返回值可以说 ModelAndView,String,或者 void,方法名随意,方法参数可以说Exception及其子类,HttpServletRequest,等。
public class MyUserException extends Exception{
public MyUserException() {
}
public MyUserException(String message) {
super(message);
}
}
public class NameException extends MyUserException{
public NameException() {
}
public NameException(String message) {
super(message);
}
}
public class AgeException extends MyUserException{
public AgeException() {
}
public AgeException(String message) {
super(message);
}
}
package com.mao.web.handle;
import com.mao.web.exception.AgeException;
import com.mao.web.exception.NameException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;
/**
* @Description @ControllerAdvice 表示当前类是一个异常处理类
* @Author 毛毛
* @CreateDate 2021/06/13/周日 1:35 下午
*/
@ControllerAdvice
public class GlobalExceptionHandler {
// 定义处理异常的方法 和Controller类中的定义方式相同
/**
* Exception 表示Controller类中的方法抛出去的异常对象,被处理的方法抛出什么异常,我们就接收什么异常
*
* @param e
* @return
* @ExceptionHandler: 表示此方法处理异常
* 属性 value : 值是异常的类型
*/
@ExceptionHandler(value = NameException.class) // 表示此方法处理异常
public ModelAndView doNameException(Exception e) {
System.out.println(e);
// 发生了异常以后,我们可以记录异常日志,发送通知给程序员等,还可以给用户友好的提示,等等
ModelAndView mv = new ModelAndView();
mv.addObject("msg", "姓名有误!");
mv.setViewName("redirect:/fail.jsp");
return mv;
}
@ExceptionHandler(value = AgeException.class) // 表示此方法处理异常
public ModelAndView doAgeException(Exception e) {
System.out.println(e);
// 发生了异常以后,我们可以记录异常日志,发送通知给程序员等,还可以给用户友好的提示,等等
ModelAndView mv = new ModelAndView();
mv.addObject("msg", "年龄有误!");
mv.setViewName("redirect:/fail.jsp");
return mv;
}
@ExceptionHandler() // 表示此方法处理未知异常,也就是任何类型的异常 优先级最低,
// 只有错误类型都没有被异常处理方法所捕获,才会将错误方法给该方法执行
public ModelAndView doOtherException(Exception e) {
System.out.println(e);
// 发生了异常以后,我们可以记录异常日志,发送通知给程序员等,还可以给用户友好的提示,等等
ModelAndView mv = new ModelAndView();
mv.addObject("msg", "有误!");
mv.setViewName("redirect:/fail.jsp");
return mv;
}
}
**还是要注意一下,我们框架进行重定向的时候,会把请求域的数据以查询字符串的形式拼接在重定向的地址后面,也就是说发起的说get请求,在新页面我们可以通过el表达式的${param.xxx}**的形式获取参数的值。
3. 拦截器
Spring MVC中的拦截器Interceptor说非常重要和相当有用的,它的主要作用是拦截指定的用户请求,并进行相应的预处理和后处理。
拦截器:是框架中的一种对象,需要实现接口HandlerInterceptor。拦截用户请求的。准确的说是拦截请求到Controoler方法处理之前的请求。
作用: 可以预先对请求作出相应的处理,然后根据处理的结果来决定是否执行Controller。也可以把多个Controller中共用的功能定义到拦截器。
特点:
- 拦截器分为系统拦截器和自定义拦截器。
- 一个项目中可以有多个拦截器。
- 拦截器侧重拦截用户的请求。
- 拦截器的执行时机是在Controller执行相应的请求之前执行的。
3.1 拦截器的定义
- 创建拦截器需要让该类实现接口HandlerInterceptor,实现接口中的三个方法。
- 在配置文件中(mvc的配置文件)声明拦截器对象,并指定拦截的uri地址
拦截器类的定义;
package com.mao.web.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @Description
* @Author 毛毛
* @CreateDate 2021/06/13/周日 3:03 下午
*/
public class MyInterceptor implements HandlerInterceptor {
/**
* preHandle: 预先处理请求的方法 方法参数:三个
*
* @param request
* @param response
* @param handler 该参数指被拦截的控制器对象(controller)方法
* @return 布尔值 true 请求是正确的,表示放行,让controller方法处理,三个拦截器方法都会被执行。
* <br> false 请求被拦截,无法被controller方法执行,也只会执行该拦截器方法,其他的两个也不会执行,请求到此截止。
* 特点:该方法是在控制器方法的执行前执行,可以对请求进行预处理,做登陆检验,权限判断,统计数据等
* 可以决定请求是否执行
* @throws Exception
*/
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle .... 111111111111");
return true;
}
/**
* postHandle:后处理方法
*
* @param request
* @param response
* @param handler 被拦截的控制器对象
* @param modelAndView 控制器方法的返回值,也就是请求的返回值
* 特点:在控制器方法后执行
* 能够获取到控制器方法的执行结果。可以修改原来的执行结果。可以修改数据和视图。
* 作用:可以做对请求的二次处理
* @throws Exception
*/
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle 22222222");
// 对数据进行二次处理
modelAndView.addObject("username", request.getParameter("name") + "mao");
}
/**
* afterCompletion 最后执行的方法
*
* @param request
* @param response
* @param handler 被拦截的控制器对象(controller)
* @param ex 异常对象
* 特点: 该方法在请求完成之后执行的,请求完成处理的标志是 视图处理完成,对视图执行转发操作
* 也就是说用户已经接收到了响应结果。
* 该方法一般是程序最后要做的工作,可以释放内存,清理临时变量,例如删除会话域中的某些值
* 方法的执行条件:
* 当前拦截器的 preHandle()方法必须执行
* 而且 preHandle方法的返回值必须为 true
* @throws Exception
*/
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion ... 33333333");
}
}
拦截器在配置文件的声明;
<!--声明拦截器-->
<mvc:interceptors>
<!--第一个拦截器-->
<mvc:interceptor>
<!--拦截器要拦截的uri地址,可以使用通配符 ** -->
<!-- 拦截以 /ssm/ 开始的请求-->
<mvc:mapping path="/ssm/**"/>
<!--不拦截的地址-->
<mvc:exclude-mapping path="/test/**"/>
<!--指定该拦截器使用的bean类-->
<bean class="com.mao.web.interceptor.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
3.2 多个拦截器
使用两个拦截器,看拦截器的执行顺序,以及那个方法控制请求的执行。
package com.mao.web.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @Description
* @Author 毛毛
* @CreateDate 2021/06/13/周日 3:03 下午
*/
public class MyInterceptor2 implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("2222222222 preHandle .... ");
return true;
}
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("22222222222222 postHandle ");
// 对数据进行二次处理
modelAndView.addObject("username", request.getParameter("name") + "mao22");
}
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("2222222222222222 afterCompletion ... ");
}
}
-
两个拦截器的preHandle的返回值都为true时
11111111111111 preHandle .... 2222222222 preHandle .... doSome .......... 22222222222222 postHandle 11111111111111111postHandle 。。。。。 2222222222222222 afterCompletion ... 11111111111111 afterCompletion ...
-
第一个preHandle为true,第二个为false时
11111111111111 preHandle .... 2222222222 preHandle .... 11111111111111 afterCompletion ...
发现第一个拦截器的afterCompletion方法执行了,这是因为第一个拦截器返回值为true,所以满足了执行的条件。
-
第一个preHandle为false,第二个为true或者false
11111111111111 preHandle ....
3.3 为什么使用多个拦截器
- 把验证功能分散到独立的拦截器。每个拦截器做单一的验证处理
- 组合多个拦截器。
3.4 总结
多个拦截器,是穿在一个链条上的,多个拦截器和一个控制器对象在一个链条上。框架中使用 HandlerExecutionChain(处理器执行链),表示这个执行链条。
public class HandlerExecutionChain {
private final Object handler; // 控制器对象
private Handlerinterceptor[] interceptors;// 存放多个拦截器对象的。MyInterceptor
private final List<HandlerInterceptor> interceptorList;
}
拦截器实现12 21 的执行顺序,是通过遍历数组 Handlerinterceptor[] interceptors 进行实现的
3.5 拦截器和过滤器的对比
- 拦截器是mvc框架中的对象。过滤器是servlet中的对象
- 拦截器对象是框架容器创建的,过滤器对象是tomcat容器创建的。(过滤器是我们手写,但是对象的创建是tomcat创建的,而且只在服务器启动时创建,服务器销毁时自动销毁)
- 拦截器侧重对请求做判断的,处理的,可以截断请求。过滤器是侧重对request,response对象的属性,参数设置值的。例如request.setCharacterEncoding().
- 拦截器的执行时机有三个,控制器方法之前,之后,请求完成后。过滤器是在请求之前执行的。(当然过滤器在放行以后也可以做其他的处理)
- 拦截器是拦截对controller,动态资源请求的。过滤器可以过滤所有请求,动态和静态的都可以。
- 拦截器和过滤器同时存在的话,先执行的是过滤器( 为什么最先执行过滤器,因为在servlet规范中是这样定义的 ),后面是中央调度器(分配请求到指定的controller类中的方法嘛),最后才是拦截器,然后才会执行控制器方法。
4. springmvc内部的执行流程
流程图:
内部处理过程:(忽略tomcat)
-
用户发起请求,交给中央调度器(dispatcherServlet)
-
中央调度器把请求(request)交给处理器映射器(HandleMapping)。
处理器映射器:spirngmvc框架中的对象,需要实现接口HandleMapping。实现该接口的都是映射器。
作用:从springmvc容器中获取控制器对象,把找到的控制器和拦截器对象都放到处理器执行链对象中,保存并返回给中央调度 器。(ApplicationContext.getBean() )
-
中央调度器把获取到的 处理器执行链中的控制器对象,交给了处理器适配器(HandleAdapter)。
处理器适配器:是springmvc框架中的对象,实现HandleAdapter接口
适配器作用:执行控制器的方法,也就是执行MyController.doSome()方法。得到结果ModelAndView,并把结果交给中央调度器。
-
中央调度器把控制器执行的结果再次交给视图解析器,
视图解析器:springmvc框架中的对象,需要实现ViewResolver接口。
视图解析器的作用:处理视图的,组成视图的完整路径,能创建View类型的对象
-
中央调度器调用 View类方法,把Model中的数据放到了request作用域。对视图执行forward操作。