图片来源网络,如有侵权请联系删除
文章目录
Spring MVC 框架介绍
什么是 MVC
MVC
是一种软件架构的思想,将软件按照模型、视图、控制器来划分,主要的作用是降低了视图与业务逻辑的双向耦合。
Model
(模型层):指工程中的JavaBean
,作用是处理数据。
JavaBean
分为两类:
1)实体类 Bean:专门存储业务数据的,如Student
、User
等。
2)业务处理 Bean:指Service
或Dao
对象,专门用于处理业务逻辑和数据访问。View
(视图层):指工程中的html或jsp等页面,作用是与用户进行交互,展示数据。Controller
(控制器层):指工程中的servlet,作用是接收请求和响应浏览器。
SpringMVC 的特点
三层架构分为表述层(或表示层)、业务逻辑层、数据访问层,表述层表示前台页面和后台 Servlet。
- Spring 家族原生产品,与
IOC
容器等基础设施无缝对接。 - 基于原生的 Servlet,通过了功能强大的前端控制器
DispatcherServlet
,对请求和响应进行统一处理。 - 表述层各细分领域需要解决的问题全方位覆盖,提供全面解决方案。
- 代码清新简洁,大幅度提升开发效率。
- 内部组件化程度高,可插拔式组件即插即用,想要什么功能配置相应组件即可。
- 性能卓著,尤其适合现代大型、超大型互联网项目要求。
SpringMVC 架构原理解析
- 发起请求到中央调度器 DispatcherServlet。
- 中央调度器接收到请求后,调用 HandlerMapping 映射器查找 Handler。
- HandlerMapping 映射器根据xml配置、注解进行查找具体的处理器 Handler,生成处理器对象及连接器一并向中央调度器返回 Handler。
- 中央调度器调用 HandlerAdapter 设配器执行Handler。
- HandlerAdapter 适配器经过适配调用具体的处理器(Controller 也称:控制器)进行业务逻辑操作。
- Handler执行完成给适配器返回ModelAndView。
- HandlerAdapter 将 Handler 执行结果 ModelAndView 返回给中央调度器(ModelAndView是springmvc框架的一个底层对象,包括 Model和view)。
- 中央调度器将 ModelAndView 传给 ViewResolver 视图解析器进行视图解析,根据逻辑视图名解析成真正的视图(jsp)。
- ViewResolver 视图解析器向中央调度器返回View。
- 中央调度器进行视图渲染,视图渲染将模型数据(在ModelAndView对象中)填充到request域。
- 前端控制器向用户响应结果。
- 前端控制器 DispatcherServlet(不需要程序员开发)
作用:接收请求,响应结果,相当于转发器,中央处理器。有了DispatcherServlet减少了其它组件之间的耦合度。- 处理器映射器 HandlerMapping(不需要程序员开发)
作用:根据请求的url查找Handler。- 处理器适配器 HandlerAdapter
作用:按照特定规则(HandlerAdapter要求的规则)去执行Handler。- 处理器 Handler(需要程序员开发)
注意:编写Handler时按照HandlerAdapter的要求去做,这样适配器才可以去正确执行Handler。- 视图解析器 ViewResolver(不需要程序员开发)
作用:进行视图解析,根据逻辑视图名解析成真正的视图(view)- 视图View(需要程序员开发jsp) View是一个接口,实现类支持不同的View类型(jsp、freemarker、pdf…)
SpringMVC 配置
pom.xml 文件引入依赖
<!-- 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>
<scope>provided</scope>
</dependency>
<!-- Spring5 和 Thymeleaf整合包 -->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
<version>3.0.12.RELEASE</version>
</dependency>
web.xml 文件头部信息格式
注意此处的web-app版本是4.0。
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
配置 DispatcherServlet 中央调度器
1)在web.xml
配置文件中注册 Spring MVC 框架的核心对象 DispatcherServlet
。
<!-- 配置SpringMVC的前端控制器,对浏览器发送的请求统一进行处理 -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<!-- 设置 springmvc 的核心控制器所能处理的请求的请求路径 -->
<url-pattern>/</url-pattern>
</servlet-mapping>
<url-pattern>
标签中使用/
和/*
的区别:
/
所匹配的请求可以是/login
、.html
、.js
、.css
方式的请求路径。但是/
不能匹配.jsp
请求路径的请求,因此就可以避免在访问jsp
页面时,该请求被DispatcherServlet
处理,从而找不到相应的页面。/*
则能够匹配所有请求,例如在使用过滤器时,若需要对所有请求进行过滤,就需要使用/*
的写法。
2)扩展配置方式:用<init-param>
设置 SpringMVC 配置文件的位置和名称,通过load-on-startup
标签设置 SpringMVC 前端控制器DispatcherServlet
的初始化时间。
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 通过初始化参数指定 SpringMVC 配置文件的位置和名称 -->
<init-param>
<!-- contextConfigLocation为固定值 -->
<param-name>contextConfigLocation</param-name>
<!-- 使用classpath:表示从类路径查找配置文件,例如 maven 工程中的 src/main/resources -->
<param-value>classpath:applicationContext.xml</param-value>
</init-param>
<!--
作为框架的核心组件,在启动过程中有大量的初始化操作要做,而这些操作放在第一次请求时才执行会严重影响访问速度,
因此需要通过此标签将启动控制 DispatcherServlet 的初始化时间提前到服务器启动时。
-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
中央调度器 <url-pattern />
-
在没有特殊要求的情况下,springmvc 的中央调度器
DispatcherServlet
的< url-pattern/ >
常使用后缀匹配方式,如写为*.do
、*.action
或者*.mvc
等。 -
也可以使用
/
代替。当我们发起静态资源请求时,DispatcherServlet
会将此请求最为普通的 Controller。中央调度器会调用控制器映射器为其查找相应的控制器。在这种情况下所有静态资源请求会报错404。 -
一般服务器所返回的静态资源都是由 Tomcat 自身处理的。
-
Tomcat 的
web.xml
配置文件里有一个名为default
的 Servlet,当运行 Tomcat 时会创建DefaultServlet
对象,用于处理静态资源与为映射的请求。<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>
-
创建请求控制器
由于前端控制器对浏览器发送的请求进行了统一的处理,但是具体的请求有不同的处理过程,因此需要创建处理具体请求的类,即请求控制器(Controller)。
SpringMVC 的控制器是由一个POJO
(普通的Java类)担任,因此需要通过@Controller
注解将其标识为一个控制层组件。由 Spring 的 IoC 容器管理,此时 SpringMVC 才能将相应的请求交给对应的控制器(Controller)进行处理。
@Controller
public class HelloController {
}
创建 springmvc 的配置文件
在resources
文件中创建 Spring 配置文件applicationContext.xml
。
- 配置 JSP 视图解析器:用于处理 jsp 文件。
<!-- 开启组件扫描:如果扫描多个包,多个包之间使用逗号隔开或者直接写这些包共有的上层目录 -->
<context:component-scan base-package="com.springmvc.controller" />
<!-- 配置 JSP 视图解析器,设置视图文件的路径 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 文件路径前缀 -->
<property name="prefix" value="/WEB-INF/view/"/>
<!-- 文件后缀 -->
<property name="suffix" value=".jsp"/>
</bean>
<!--处理静态资源-->
<mvc:default-servlet-handler/>
<!--mvc注释驱动开启-->
<mvc:annotation-driven/>
2)配置 Thymeleaf 视图解析器:用于处理 html 模板文件。
<!-- 开启组件扫描:如果扫描多个包,多个包之间使用逗号隔开或者直接写这些包共有的上层目录 -->
<context:component-scan base-package="com.springmvc.controller" />
<!-- 配置Thymeleaf视图解析器 -->
<bean id="viewResolver" class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
<!-- 视图解析器优先级 -->
<property name="order" value="1"/>
<property name="characterEncoding" value="UTF-8"/>
<property name="templateEngine">
<bean class="org.thymeleaf.spring5.SpringTemplateEngine">
<property name="templateResolver">
<bean class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
<!-- 视图前缀 -->
<property name="prefix" value="/WEB-INF/templates/"/>
<!-- 视图后缀 -->
<property name="suffix" value=".html"/>
<property name="templateMode" value="HTML5"/>
<property name="characterEncoding" value="UTF-8" />
</bean>
</property>
</bean>
</property>
</bean>
<!--处理静态资源-->
<mvc:default-servlet-handler/>
<!-- 开启mvc注解驱动 -->
<mvc:annotation-driven>
<mvc:message-converters>
<!-- 处理响应中文内容乱码 -->
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="defaultCharset" value="UTF-8" />
<property name="supportedMediaTypes">
<list>
<value>text/html</value>
<value>application/json</value>
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
浏览器发送请求,若请求地址符合前端控制器的
<url-pattern>
,该请求就会被前端控制器DispatcherServlet
处理。前端控制器会读取 SpringMVC 的核心配置文件,通过扫描组件找到控制器,将请求地址和控制器中@RequestMapping
注解的 value 属性值进行匹配,若匹配成功,该注解所标识的控制器方法就是处理请求的方法。处理请求的方法需要返回一个字符串类型的视图名称,该视图名称会被视图解析器解析,加上前缀和后缀组成视图的路径,通过 Thymeleaf 对视图进行渲染,最终转发到视图所对应页面。
如何解决访问静态资源
1)第一种:在 springmvc 配置文件里加入<mvc:default-servlet-handler>
标签:框架创建控制器对象,把接受到的请求转发给 Tomcat。
<mvc:default-servlet-handler/>
2)第二种:在 springmvc 配置文件里加入<mvc:resources>
标签:框架创建 ResourceHttpRequestHandler
控制器对象,让这个对象处理静态资源的访问,不依赖 Tomcat 服务器。
location
属性:静态资源在你的项目中的目录位置。mapping
属性:访问静态资源的 URL 地址,使用通配符**
。
<mvc:resources location="/static/" mapping="/static/**"/>
注解
@Controller 注解
创建一个 MyController 类并使用@Controller
注解标识此类为控制器对象,由 Spring 的 IoC 容器管理。
@Controller
public class MyController {
@RequestMapping("/doSome")
public ModelAndView doSome() {
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("msg","smg数据");
modelAndView.setViewName("show");
return modelAndView;
}
}
@RequestMapping 注解
-
@RequestMapping注解:可以设置在方法上或是一个类中。
-
@RequestMapping注解:请求映射,作用是把请求地址与方法绑定,一个请求指定一个方法处理。
-
@RequestMapping注解:被修饰的方法叫做处理器方法或者控制器方法可以处理请求,类似servlet中的doGet、doPost。
1)@RequestMapping
注解的作用就是将请求和处理请求的控制器方法关联起来,建立映射关系。
2)SpringMVC 接收到指定的请求,就会来找到在映射关系中对应的控制器方法来处理这个请求。
-
在类上定义注解称父级接口。
@RequestMapping("/test") public class MyController { }
-
在方法上定义注解称子级级接口。
@RequestMapping(value = "/doSome") public ModelAndView doSome() { }
接口访问方式:http://xxxx.xxxx/test/doSome
-
在注解中定义请求方式,
method
属性表示请求方式,它的值是RequestMethod
类枚举值。@RequestMapping("/test") public class MyController { @RequestMapping(value = "/doSome", method = RequestMethod.GET) public ModelAndView doSome() { ModelAndView model = new ModelAndView(); model.setViewName("index"); return model; } }
@RequestMapping 派生注解
对于处理指定请求方式的控制器方法,SpringMVC 中提供了@RequestMapping
的派生注解:
@GetMapping
:处理GET
请求的映射。@PostMapping
:处理POST
请求的映射。@PutMapping
:处理PUT
请求的映射。@DeleteMapping
:处理DELETE
请求的映射。
SpringMVC 支持 ANT 风格的路径。
通配符 | 说明 |
---|---|
? | 匹配任何字符。 |
* | 匹配 0 或者任意数量的字符。 |
** | 匹配 0 或者更多的目录。 |
注意:在使用
**
时,只能以/**/list
这种方式使用。
SpringMVC 支持路径中的占位符。
SpringMVC 路径中的占位符常用于 RESTful 风格中,当请求路径中将某些数据通过路径的方式传输到服务器中,就可以在相应的@RequestMapping
注解的 value 属性中通过占位符{xxx}
表示传输的数据,在通过@PathVariable
注解,将占位符所表示的数据赋值给控制器方法的形参。
// 请求:http://192.168.1.111/doSome/12/admin
@RequestMapping(value = "/doSome/{id}/{name}")
public String doSome(@PathVariable("id") Integer id, @PathVariable("name") String name) {
return "success";
}
控制器方法请求参数获取
被@RequestMapping
注解所表示的方法有三种参数:请求、响应、会话。这些参数在系统被调用时由系统自动赋值。
@RequestMapping(value = "/doSome",method = RequestMethod.GET)
public ModelAndView doSome(HttpServletRequest request, HttpServletResponse response, HttpSession session) {
}
逐个参数接收
在控制器方法的形参位置,设置和请求参数同名的形参,当浏览器发送请求,匹配到请求映射时,在DispatcherServlet
中就会将请求参数赋值给相应的形参。
@RequestMapping(value = "/login",method = RequestMethod.POST)
public ModelAndView otherSome(String username, String password) {
}
形参注解 @RequestParam
@RequestParam
是将请求的参数与控制器方法的形参创建映射关系。
@RequestParam
注解一共有三个参数:
value
:指定为形参负责的请求参数名称。required
:设置是否必传参数。默认:true
defaultValue
:不管required
属性值是true
或false
,当value
所指定的请求参数没有传输或者为空时,则使用默认为形参赋予。
// 请求:http://192.168.1.111/doSome/12/admin?id=1&name=admin
@RequestMapping(value = "/index",method = RequestMethod.GET)
public ModelAndView otherSome(@RequestParam("id") Integer id, @RequestParam("name") String name) {
}
形参注解 @RequestHeader
@RequestHeader
是将请求头信息和控制器方法的形参创建映射关系。
@RequestHeader
属性与@RequestParam
相同。
@RequestMapping(value = "/index",method = RequestMethod.GET)
public ModelAndView otherSome(@RequestHeader("token") String token) {
}
形参注解 @CookieValue
@CookieValue
是将cookie数据和控制器方法的形参创建映射关系。
@CookieValue
属性与@RequestParam
相同。
@RequestMapping(value = "/index",method = RequestMethod.GET)
public ModelAndView otherSome(@CookieValue("token") String token) {
}
POJO 获取请求参数
@RequestMapping(value = "/index",method = RequestMethod.POST)
public ModelAndView otherSome(User user) {
}
CharacterEncodingFilter 解决中文乱码
-
在
web.xml
配置文件中注册过滤器,过滤所有接口解决中文乱码。<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> <!-- 强制请求对象(HttpServletRequest)使用encoding编码的值,默认值为false --> <init-param> <param-name>forceRequestEncoding</param-name> <param-value>true</param-value> </init-param> <!-- 强制响应对象(HttpServletResponse)使用encoding编码的值,默认值为false --> <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>
域对象共享数据
使用 ServletApi 向域对象共享数据
@RequestMapping(value = "/index",method = RequestMethod.GET)
public String otherSome(HttpServletRequest request) {
request.setAttribute("name","admin");
return "success";
}
使用 ModelAndView 向 Request 域对象共享数据
ModelAndView
对象有 Model 和 View 的功能:
1)Model 主要用于向请求域共享数据。
2)View 主要用于设置视图,实现页面跳转功能。
ModelAndView
对象方法:
addObject(String attributeName, @Nullable Object attributeValue)
:添加数据,框架在请求的最后把数据放入到request作用域中。setViewName(@Nullable String viewName)
:指定视图,框架对视图执行request.getRequestDispatcher("/show.jsp").forward(req,resp)
操作。
框架会使用视图解析器拼接路径:前缀 + 文件名 + 后缀。
@RequestMapping(value = "/index",method = RequestMethod.GET)
public ModelAndView otherSome() {
ModelAndView model = new ModelAndView();
// 共享数据
model.addObject("name","admin");
// 设置视图
model.setViewName("success")
return model;
}
使用 Model 向 Request 域对象共享数据
@RequestMapping(value = "/index",method = RequestMethod.GET)
public String otherSome(Model model) {
model.addAttribute("name","admin");
return "success";
}
使用 Map 向 Request 域对象共享数据
@RequestMapping(value = "/index",method = RequestMethod.GET)
public String otherSome(Map<String, Object> map) {
map.put("name","admin");
return "success";
}
使用 ModelMap 向 Request 域对象共享数据
@RequestMapping(value = "/index",method = RequestMethod.GET)
public String otherSome(ModelMap modelMap) {
modelMap.addAttribute("name","admin");
return "success";
}
Model、ModelMap、Map的关系
Model、ModelMap、Map 类型的参数其实本质上都是 BindingAwareModelMap 类型的
public interface Model {}
public class ModelMap extends LinkedHashMap<String, Object> {}
public class ExtendedModelMap extends ModelMap implements Model {}
public class BindingAwareModelMap extends ExtendedModelMap {}
Session 域共享数据
@RequestMapping(value = "/index",method = RequestMethod.GET)
public String otherSome(HttpSession session) {
session.setAttribute("name","admin");
return "success";
}
application 域共享数据
@RequestMapping(value = "/index",method = RequestMethod.GET)
public String otherSome(HttpSession session) {
ServletContext application = session.getServletContext();
application.setAttribute("name","admin");
return "success";
}
控制器方法返回值
String 返回值
直接return 要跳转页面的名称,框架会用视图解析器作拼接。
数据的返回可以在形参中加入 HttpServletRequest 对象,并设置对象域在页面接收即可。
@RequestMapping(value = "/returnString-view",method = RequestMethod.POST)
public String doReturnStringView(HttpServletRequest req,String username, String password) {
req.setAttribute("username",username);
req.setAttribute("password",password);
return "show";
}
void 返回值:Ajax 响应
在处理 Ajax 的时候,可以使用 void
返回值。
Ajax 请求服务器端放回的就是数据和视图无关。
使用 HttpServletResponse
输出数据响应Ajax请求,Ajax 请求所接受的数据个格式为 JSON,需要使用 Jackson
依赖使对象转换为 JSON 字符串,而 Ajax 会把 JSON 字符串解析为 JSON。
@RequestMapping(value = "/returnVoid-ajax")
public void doReturnAjax(HttpServletResponse resp, String username, String password) throws IOException {
Student student = new Student();
student.setUsername(username);
student.setPassword(password);
//对象转换为json结果
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(student);
//响应ajax请求
resp.setContentType("application/json; charset=utf-8");
resp.getWriter().write(json);
}
$(function(){
$("#btn").click(function() {
$.post("user/returnVoid-ajax",{username:"tuoyingtao",password:"123456"},
function fun(response) {
alert(JSON.stringify(response))
},"json")
})
})
Object 返回值:@ResponseBody
注解 Ajax 响应
1)springmvc 控制器方法返回 Object,转换 JSON 输出到浏览器,响应原理:
<mvc:annotaction-driven>
注解驱动:完成 Java 对象到 JOSN、xml、text、二进制等数据格式的转换。<mvc:annotaction-driven>
在加入到 springmvc 配置后会自动创建 HttpMessageConverter 接口的实现类,包括MappingJackson2HttpMessageConverter。 (内部使用 Jackson 工具库中的 ObjectMapper 实现 Java 对象转为 JSON 字符串)
2)HttpMessageConverter 接口两个方法:
canWrite(Class<?> clazz, @Nullable MediaType mediaType)
:检查控制器方法返回值,是否可以转换为 MediaType 的数据格式。write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)
:把控制器方法的返回对象,调用 Jackson 中的 ObjectMapper 转为 JSON 格式。
3)通过@ResponseBody
把数据结果输出到浏览器,Ajax 处理响应。
- 把处理器返回的对象转换为 JSON 后,通过 HttpServletResponse 输出给浏览器。
- 引入 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>
- 在 spring 配置文件注册
annotation-driver
驱动:(注意:包路径是 mvc 下的)
<mvc:annotation-driven/>
- 使用
@ResponseBody
注解将数据响应给 Ajax 请求:
//返回对象
@ResponseBody
@RequestMapping(value = "/returnStudentJson",method = RequestMethod.POST)
public Student doReturnStudentJson(String username, String password) {
Student student = new Student();
student.setUsername("托马斯·克里斯特");
student.setPassword("123456");
return student;
}
//返回List数组
@ResponseBody
@RequestMapping(value = "/returnStudentList",method = RequestMethod.POST)
public List<Student> doReturnStudentList(String username, String password) {
List<Student> list = new ArrayList<Student>();
Student student = new Student();
student.setUsername("托马斯·克里斯特");
student.setPassword("123456");
Student student1 = new Student();
student1.setUsername("玛丽·莫利亚");
student1.setPassword("123456");
list.add(student);
list.add(student1);
return list;
}
注解 Ajax 响应文本
1)控制器方法返回 String 的数据,不是视图。
2)如果加上@ResponseBody
注解,则表示返回的就是文本数据,反之就是视图。
3)返回文本数据时默认使用 text/plain; charset=ISO-8859-1
作为Content-Type
。可能出现中文乱码。
解决:在 @RequestMapping
中添加 produces
属性来指定Content-Type
类型。
@ResponseBody
@RequestMapping(value = "/returnStringData", produces = "text/plain;charset=utf-8")
public String doStringData(String username,String password) {
return "杰克·罗伯塔";
}
SpringMVC 视图
转发视图
SpringMVC 中默认的转发视图是InternalResourceView
。
SpringMVC 中创建转发视图的情况:
当控制器方法中所设置的视图名称以forward:
为前缀时,创建InternalResourceView
视图,此图的视图名称不会被 SpringMVC 配置文件中所配置的视图解析器解析,而是会将前缀forward:
去掉,剩余部分分作为最终路径通过转发的方式实现跳转。
如:forward:/
、forward:/employee
@RequestMapping(value = "/doForward")
public String doForward() {
return "forward:/testHello";
}
重定向视图
SpringMVC 中默认的重定向视图时 RedirectView
。
当控制器方法中所设置的视图名称以redirect:
为前缀时,创建RedirectView
视图,此时的视图名称不会被 SpringMVC 配置文件中所配置的视图解析器解析,而是会将前缀redirect:
去掉,剩余部分作为最终路径通过重定向的方式实现跳转。
注意:重定向视图在解析时,会先将
redirect:
前缀去掉,然后会判断剩余部分是否以/开头,若是则会自动拼接上下文路径
如:redirect:/
、redirect:/employee
@RequestMapping(value = "/doRedirect")
public String doRedirect() {
return "redirect:/testHello";
}
RESTful API
什么是 API
- API 是应用程序接口,可理解为程序与程序之间通信的桥梁,其本质就是一个函数而已。
- API 把 web app 的功能全部封装了,所以,通过 API 操作数据,可以极大的把前端和后端的代码分离,使得后端代码易于测试,前端代码编写更简单。
什么是 RESTful
RESTful 是一种设计 API 的模式。REST 的全称是 Representational State Transfer,翻译过来就是 “资源”在网络传输中以某种“表现形式”进行“状态转移” 。
- 资源(Resource):资源是一种看待服务器的方式,即,将服务器看作是由很多离散的资源组成。每个资源是服务器上一个可命名的抽象概念。因为资源是一个抽象的概念,所以它不仅仅能代表服务器文件系统中的一个文件、数据库中的一张表等等具体的东西,可以将资源设计的要多抽象有多抽象,只要想象力允许而且客户端应用开发者能够理解。与面向对象设计类似,资源是以名词为核心来组织的,首先关注的是名词。一个资源可以由一个或多个 URI 来标识。URI 既是资源的名称,也是资源在 Web 上的地址。对某个资源感兴趣的客户端应用,可以通过资源的 URI 与其进行交互。
- 资源表述(Representational):资源的表述是一段对于资源在某个特定时刻的状态的描述。可以在客户端-服务器端之间转移(交换)。资源的表述可以有多种格式,例如
HTML
、XML
、JSON
、纯文本、图片、视频、音频等等。资源的表述格式可以通过协商机制来确定。请求-响应方向的表述通常使用不同的格式。 - 状态转移(State Transfer):在客户端和服务器端之间转移(Transfer)代表资源状态的表述。通过转移和操作资源的表述,来间接实现操作资源的目的。
RESTful 的实现
具体说就是 HTTP 协议里面,四个表示操作方式的动词:GET
、POST
、PUT
、DELETE
。
它们分别对应四种基本操作:GET
用来获取资源,POST
用来新建资源,PUT
用来更新资源,DELETE
用来删除资源。
REST 风格提倡 URL 地址使用统一的风格设计,从前到后各个单词使用斜杠分开,不使用问号键值对方式携带请求参数,而是将要发送给服务器的数据作为 URL 地址的一部分,以保证整体风格的一致性。
操作 | 传统方式 | REST风格 |
---|---|---|
查询操作 | getUserById?id=1 | user/1–>get请求方式 |
保存操作 | saveUser | user–>post请求方式 |
删除操作 | deleteUser?id=1 | user/1–>delete请求方式 |
更新操作 | updateUser | user–>put请求方式 |
HiddenHttpMethodFilter
1)由于浏览器只支持发送GET
和POST
方式的请求,那么该如何发送PUT
和DELETE
请求呢?
SpringMVC 提供了 HiddenHttpMethodFilter
帮助我们将 POST 请求转换为 DELETE 或 PUT 请求。
2)HiddenHttpMethodFilter
处理PUT
和DELETE
请求的条件:
-
请求方式必须为
POST
。 -
请求必须传输请求参数
_method
。满足以上条件,
HiddenHttpMethodFilter
过滤器就会将当前请求的请求方式转换为请求参数_method
的值,因此请求参数_method
的值才是最终的请求方式。 -
在
web.xml
中注册HiddenHttpMethodFilter
:<filter> <filter-name>hiddenHttpMethodFilter</filter-name> <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class> </filter> <filter-mapping> <filter-name>hiddenHttpMethodFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
CharacterEncodingFilter
1)目前为止,SpringMVC 中提供了两个过滤器:CharacterEncodingFilter
和HiddenHttpMethodFilter
。
2)在web.xml
中注册时,必须先注册CharacterEncodingFilter
,再注册HiddenHttpMethodFilter
。
-
在
CharacterEncodingFilter
中通过request.setCharacterEncoding(encoding)
方法设置字符集的。 -
request.setCharacterEncoding(encoding)
方法要求前面不能有任何获取请求参数的操作。
而HiddenHttpMethodFilter
恰恰有一个获取请求方式的操作:String paramValue = request.getParameter(this.methodParam);
<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>
<!-- 强制请求对象(HttpServletRequest)使用encoding编码的值,默认值为false -->
<init-param>
<param-name>forceRequestEncoding</param-name>
<param-value>true</param-value>
</init-param>
<!-- 强制响应对象(HttpServletResponse)使用encoding编码的值,默认值为false -->
<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>
RESTful 案例
实体类:
public class Employee {
private Integer id;
private String lastName;
private String email;
private Integer gender;
// 省略get、set等方法
}
模拟 DAO 数据:
@Repository
public class EmployeeDao {
private static Map<Integer, Employee> employees = null;
static{
employees = new HashMap<Integer, Employee>();
employees.put(1001, new Employee(1001, "E-AA", "aa@163.com", 1));
employees.put(1002, new Employee(1002, "E-BB", "bb@163.com", 1));
employees.put(1003, new Employee(1003, "E-CC", "cc@163.com", 0));
employees.put(1004, new Employee(1004, "E-DD", "dd@163.com", 0));
employees.put(1005, new Employee(1005, "E-EE", "ee@163.com", 1));
}
private static Integer initId = 1006;
// 保存
public void save(Employee employee){
if(employee.getId() == null){
employee.setId(initId++);
}
employees.put(employee.getId(), employee);
}
// 列表
public Collection<Employee> getAll(){
return employees.values();
}
// 查询
public Employee get(Integer id){
return employees.get(id);
}
// 删除
public void delete(Integer id){
employees.remove(id);
}
}
功能清单
功能 | URL 地址 | 请求方式 |
---|---|---|
访问首页√ | / | GET |
查询全部数据√ | /employee | GET |
删除√ | /employee/2 | DELETE |
跳转到添加数据页面√ | /toAdd | GET |
执行保存√ | /employee | POST |
跳转到更新数据页面√ | /employee/2 | GET |
执行更新√ | /employee | PUT |
访问首页
-
在 Spring 配置文件中配置
view-controller
标签。<mvc:view-controller path="/" view-name="index"/>
-
创建页面
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8" > <title>Title</title> </head> <body> <h1>首页</h1> <a th:href="@{/employee}">访问员工信息</a> </body> </html>
查询所有员工数据
-
Controller 层。
@RequestMapping(value = "/employee", method = RequestMethod.GET) public String getEmployeeList(Model model){ Collection<Employee> employeeList = employeeDao.getAll(); model.addAttribute("employeeList", employeeList); return "employee_list"; }
-
创建
employee_list.html
。<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Employee Info</title> <script type="text/javascript" th:src="@{/static/js/vue.js}"></script> </head> <body> <table border="1" cellpadding="0" cellspacing="0" style="text-align: center;" id="dataTable"> <tr> <th colspan="5">Employee Info</th> </tr> <tr> <th>id</th> <th>lastName</th> <th>email</th> <th>gender</th> <th>options(<a th:href="@{/toAdd}">add</a>)</th> </tr> <tr th:each="employee : ${employeeList}"> <td th:text="${employee.id}"></td> <td th:text="${employee.lastName}"></td> <td th:text="${employee.email}"></td> <td th:text="${employee.gender}"></td> <td> <a class="deleteA" @click="deleteEmployee" th:href="@{'/employee/'+${employee.id}}">delete</a> <a th:href="@{'/employee/'+${employee.id}}">update</a> </td> </tr> </table> </body> </html>
删除功能
-
创建处理
DELETE
请求方式的表单。<!-- 作用:通过超链接控制表单的提交,将POST请求转换为DELETE请求 --> <form id="delete_form" method="post"> <!-- HiddenHttpMethodFilter 要求:必须传输 _method 请求参数,并且值为最终的请求方式 --> <input type="hidden" name="_method" value="delete"/> </form>
-
删除超链接绑定点击事件
-
引入vue.js
<script type="text/javascript" th:src="@{/static/js/vue.js}"></script>
-
删除超链接
<a class="deleteA" @click="deleteEmployee" th:href="@{'/employee/'+${employee.id}}">delete</a>
-
通过 Vue 处理点击事件
<script type="text/javascript"> var vue = new Vue({ el:"#dataTable", methods:{ //event表示当前事件 deleteEmployee:function (event) { //通过id获取表单标签 var delete_form = document.getElementById("delete_form"); //将触发事件的超链接的href属性为表单的action属性赋值 delete_form.action = event.target.href; //提交表单 delete_form.submit(); //阻止超链接的默认跳转行为 event.preventDefault(); } } }); </script>
-
Controller 层
@RequestMapping(value = "/employee/{id}", method = RequestMethod.DELETE) public String deleteEmployee(@PathVariable("id") Integer id){ employeeDao.delete(id); return "redirect:/employee"; }
-
跳转到添加数据页面
-
在 Spring 配置文件中配置
view-controller
<mvc:view-controller path="/toAdd" view-name="employee_add"></mvc:view-controller>
-
创建
employee_add.html
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Add Employee</title> </head> <body> <form th:action="@{/employee}" method="post"> lastName:<input type="text" name="lastName"><br> email:<input type="text" name="email"><br> gender:<input type="radio" name="gender" value="1">male <input type="radio" name="gender" value="0">female<br> <input type="submit" value="add"><br> </form> </body> </html>
执行保存
-
Controller 层
@RequestMapping(value = "/employee", method = RequestMethod.POST) public String addEmployee(Employee employee){ employeeDao.save(employee); return "redirect:/employee"; }
跳转到更新数据页面
-
修改超链接
<a th:href="@{'/employee/'+${employee.id}}">update</a>
-
控制器方法
@RequestMapping(value = "/employee/{id}", method = RequestMethod.GET) public String getEmployeeById(@PathVariable("id") Integer id, Model model){ Employee employee = employeeDao.get(id); model.addAttribute("employee", employee); return "employee_update"; }
-
创建
employee_update.html
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Update Employee</title> </head> <body> <form th:action="@{/employee}" method="post"> <input type="hidden" name="_method" value="put"> <input type="hidden" name="id" th:value="${employee.id}"> lastName:<input type="text" name="lastName" th:value="${employee.lastName}"><br> email:<input type="text" name="email" th:value="${employee.email}"><br> <!-- th:field="${employee.gender}"可用于单选框或复选框的回显 若单选框的value和employee.gender的值一致,则添加checked="checked"属性 --> gender:<input type="radio" name="gender" value="1" th:field="${employee.gender}">male <input type="radio" name="gender" value="0" th:field="${employee.gender}">female<br> <input type="submit" value="update"><br> </form> </body> </html>
执行更新
-
Controller 层
@RequestMapping(value = "/employee", method = RequestMethod.PUT) public String updateEmployee(Employee employee){ employeeDao.save(employee); return "redirect:/employee"; }
HttpMessageConverter 文本转换器
HttpMessageConverter
文本转换器,将请求报文转换为 JAVA 对象,或将 JAVA 对象转换为响应文本。
httpMessageConverter
提供两个注解和两个类型:@RequestBody
、@ResponseBody
、RequestEntity
、@ResponseEntity
。
@RequestBody
@RequestBody
可以获取请求体,需要在控制器方法设置一个形参,使用@RequestBody
进行标识,当前请求的请求体就会为当前注解所标识的形参赋值。
<form th:action="@{/testRequestBody}" method="post">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
<input type="submit">
</form>
@RequestMapping("/testRequestBody")
public String testRequestBody(@RequestBody String requestBody){
System.out.println("requestBody:"+requestBody);
return "success";
}
@ResponseBody
@ResponseBody
用于标识一个控制器方法,可以将该方法的返回值直接作为响应报文的响应体响应到浏览器。
@RequestMapping("/testResponseBody")
@ResponseBody
public String testResponseBody(){
return "success";
}
RequestEntity
RequestEntity
封装请求报文的一种类型,需要在控制器方法的形参中设置该类型的形参,当前请求的请求报文就会赋值给该形参,可以通过getHeaders()
获取请求头信息,通过getBody()
获取请求体信息。
@RequestMapping("/testRequestEntity")
public String testRequestEntity(RequestEntity<String> requestEntity){
System.out.println("requestHeader:"+requestEntity.getHeaders());
System.out.println("requestBody:"+requestEntity.getBody());
return "success";
}
请求响应处理 JSON
@ResponseBody
处理 JSON 的步骤:
-
导入
jackson
的依赖。<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.12.1</version> </dependency>
-
在 SpringMVC 的核心配置文件中开启
<mvc:annotation-driven/>
的注解驱动,此时在HandlerAdaptor
中会自动装配一个消息转换器:MappingJackson2HttpMessageConverter
,可以将响应到浏览器的 JAVA 对象转换为 JSON 格式的字符串。 -
在处理器方法上使用
@ResponseBody
注解进行标识。 -
将 JAVA 对象直接作为控制器方法的返回值返回,就会自动转换为 JSON 格式的字符串。
@RequestMapping("/testResponseUser") @ResponseBody public User testResponseUser(){ return new User(1001,"admin","123456",23,"男"); }
请求响应处理 Ajax
-
通过 Vue 点击事件来处理 axios 请求。
<script type="text/javascript" th:src="@{/static/js/vue.js}"></script> <script type="text/javascript" th:src="@{/static/js/axios.min.js}"></script> <div id="app"> <a th:href="@{/testAjax}" @click="testAjax">testAjax</a><br> </div> <script type="text/javascript"> var vue = new Vue({ el:"#app", methods:{ testAjax:function (event) { axios({ method:"post", url:event.target.href, params:{ username:"admin", password:"123456" } }).then(function (response) { alert(response.data); }); event.preventDefault(); } } }); </script>
-
控制器方法
@RequestMapping("/testAjax") @ResponseBody public String testAjax(String username, String password){ System.out.println("username:"+username+",password:"+password); return "hello,ajax"; }
@RestController 注解
@RestController
注解是 SpringMVC 提供的一个复合注解,标识在控制器的类上,就相当于为类添加了@Controller
注解,并且为其中的每个方法添加了@ResponseBody
注解。
ResponseEntity
ResponseEntity
用于控制器方法的返回值类型,该控制器方法的返回值就是响应到浏览器的响应文本。
文件上传和下载
文件下载
使用 ResponseEntity 实现下载文件的功能。
@RequestMapping("/testDown")
public ResponseEntity<byte[]> testResponseEntity(HttpSession session) throws IOException {
//获取ServletContext对象
ServletContext servletContext = session.getServletContext();
//获取服务器中文件的真实路径
String realPath = servletContext.getRealPath("/static/images/1.jpg");
//创建输入流
InputStream is = new FileInputStream(realPath);
//创建字节数组
byte[] bytes = new byte[is.available()];
//将流读到字节数组中
is.read(bytes);
//创建HttpHeaders对象设置响应头信息
MultiValueMap<String, String> headers = new HttpHeaders();
//设置要下载方式以及下载文件的名字
headers.add("Content-Disposition", "attachment;filename=1.jpg");
//设置响应状态码
HttpStatus statusCode = HttpStatus.OK;
//创建ResponseEntity对象
ResponseEntity<byte[]> responseEntity = new ResponseEntity<>(bytes, headers, statusCode);
//关闭输入流
is.close();
return responseEntity;
}
文件上传
文件上传要求 Form 表单的请求方式必须为 POST,并且添加属性enctype="multipart/form-data"
,SpringMVC 中将上传的文件封装到MultipartFile
对象中,通过此对象可以获取文件相关信息。
-
添加依赖
<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload --> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.1</version> </dependency>
-
在 SpringMVC 的配置文件中添加配置
<!--必须通过文件解析器的解析才能将文件转换为MultipartFile对象--> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"></bean>
-
控制器方法
@RequestMapping("/testUp") public String testUp(MultipartFile photo, HttpSession session) throws IOException { //获取上传的文件的文件名 String fileName = photo.getOriginalFilename(); //处理文件重名问题 String hzName = fileName.substring(fileName.lastIndexOf(".")); fileName = UUID.randomUUID().toString() + hzName; //获取服务器中photo目录的路径 ServletContext servletContext = session.getServletContext(); String photoPath = servletContext.getRealPath("static/images"); File file = new File(photoPath); if(!file.exists()){ file.mkdir(); } String finalPath = photoPath + File.separator + fileName; //实现上传功能 photo.transferTo(new File(finalPath)); return "success"; }
异常处理器
SpringMVC 框架采用的是统一,全局的异常处理。把 Controller 中的所有异常处理都集中到一个地方,采用 AOP 思想,把业务逻辑和异常处理代码分开。也称解耦合。
使用两个注解:
ControllerAdvice:控制器增强 ,必须让框架知道这个注解所在的包名,需要在 SpringMVC 配置文件声明组件扫描器指定 @ControllerAdvice
所在的全局异常处理类包名。
ExceptionHandler:表示异常的类型,当发生此类型的异常时,由当前方法处理。处理异常的方法和控制器方法的定义一样,可以有多个参数。可以有ModelAndView
、String
、void
对象类型的返回值, 形参:Exception,表示 Controller 中抛出的异常对象,通过形参可以获取发送的异常信息。
-
自定义异常类
MyUserException.java
,继承Exception
类。public class MyUserException extends Exception{ public MyUserException() { super(); } public MyUserException(String message) { super(message); } }
-
创建
MyUserException
的子类NameException.java
、PasswordException.java
。public class UsernameException extends MyUserException{ public UsernameException() { super(); } public UsernameException(String message) { super(message); } } public class PasswordException extends MyUserException { public PasswordException() { super(); } public PasswordException(String message) { super(message); } }
-
在控制器方法中,捕获
NameException
、PasswordException
异常,并抛出父类异常MyUserException
。@Controller public class MyController { @ResponseBody @RequestMapping(value = "/errorData",method = RequestMethod.POST) public ModelAndView doErrorData(String username,String password) throws MyUserException { System.out.println(username + password); ModelAndView mv = new ModelAndView(); //更具请求参数抛出异常 if (!"托马斯·克里斯特".equals(username)) { throw new UsernameException("姓名有误!!!"); } if (!"123456".equals(password)) { throw new PasswordException("密码有误!!!"); } mv.addObject("username",username); mv.addObject("password",password); mv.setViewName("show"); return mv; } }
-
创建
GlobalExceptionHandler
类,使用@ControllerAdvice
注解来标识这个类为全局异常处理类。在方法上标注@ExceptionHandler
注解表示所有的异常都会有当前方法进行处理。若给定 value 值,则指针对某一种类型进行处理。@ControllerAdvice public class GlobalExceptionHandler { //定义处理异常方法 @ExceptionHandler(value = UsernameException.class) public ModelAndView doUsernameException(Exception e) { //处理账号异常 ModelAndView mv = new ModelAndView(); mv.addObject("message","姓名必须为托马斯·克里斯特!!!"); mv.addObject("error",e); mv.setViewName("uesrnameError"); return mv; } @ExceptionHandler(value = PasswordException.class) public ModelAndView doPasswordException(Exception e) { //处理账号异常 ModelAndView mv = new ModelAndView(); mv.addObject("message","密码必须为123456!!!"); mv.addObject("error",e); mv.setViewName("passwordError"); return mv; } //处理未知异常 @ExceptionHandler public ModelAndView doOtherException(Exception e) { ModelAndView mv= new ModelAndView(); mv.addObject("message","用户或密码错误我!"); mv.addObject("error",e);java mv.setViewName("defaultError"); return mv; } }
-
SpringMVC 配置文件扫描异常处理程序与注册驱动。
<context:component-scan base-package="com.springmvc.handler"/> <!-- 添加注解驱动 --> <mvc:annotation-driven/>
拦截器
什么是拦截器
1)拦截器是 SpringMVC 中的一种,需要实现 HandlerInterceptor
接口。
2)拦截器与过滤器类似,处理的应用场景不同。过滤器是用来过滤请求参数,设置字符集编码等,而拦截器则是对用户的请求做拦截,对请求做判断处理。
3)拦截器是全局的,可以对多个 Controller 做拦截。一个项目可以有多个拦截器。常用在:用户登录处理、权限检查、记录日志等。
拦截器的执行
定义一个拦截器类实现 HandlerInterceptor 接口,重写以下三个方法:preHandle()、postHandle()、afterCompletion()
。
1)preHandle(HttpServletRequest req, HttpServletResponse resp, Object handler)
:在请求处理之前,也就是 Controller 类中的方法之前先被拦截,如果返回值为true
就继续执行下一步,如果为false
就终止,在这个方法中可以获取用户请求信息,验证请求是否符合要求。
2)preHandle()
:返回true
执行 Controller 类中的方法。
3)postHandle(HttpServletRequest req, HttpServletResponse resp, Object handler, ModelAndView mv)
:在控制器方法执行之后执行拦截器,能够获取控制器方法的返回值 ModelAndView
,可以修改ModelAndView
中的数据和视图,可以影响最后的执行结果,主要对原来的执行结果做二次修改。
4)afterCompletion(HttpServletRequest req, HttpServletResponse resp, Object handler, Exception ex)
:在请求处理完后执行拦截器,在框架中规定是当你的视图处理完成后,对视图执行了forward
,就认为请求处理完成,一般做资源回收工作的,程序请求过程中创建了一些对象,在这里可以删除,把占用的内存回收。
拦截器的使用
-
定义类实现
HandlerInterceptor
接口。public class MyInterceptor implements HandlerInterceptor { /** * @param request * @param response * @param handler 被拦截的控制器对象 * @return Boolean * @throws Exception */ public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("拦截器MyInterceptor的preHandle方法"); return true; } /** * @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("拦截器MyInterceptor的postHandle方法"); } /** * @param request * @param response * @param handler 被拦截的控制器对象 * @param ex 程序中发生的异常 * @throws Exception */ public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("拦截器MyInterceptor的afterCompletion方法"); } }
-
在 SpringMVC 配置文件中声明拦截器,在框架中多个拦截器是按照顺序存放到
ArrayList
中的。<mvc:interceptors> <!-- 声明第一个拦截器 --> <mvc:interceptor> <!-- 指定拦截器的请求url地址 拦截/**所有接口 --> <mvc:mapping path="/**"/> <!-- 声明拦截器对象 --> <bean class="com.springmvc.handler.MyInterceptor"/> </mvc:interceptor> </mvc:interceptors>
执行顺序
多个拦截器的执行
-
MyInterceptor
拦截器类一public class MyInterceptor implements HandlerInterceptor { public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("拦截器MyInterceptor的preHandle方法"); return true; } public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("拦截器MyInterceptor的postHandle方法"); } public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("拦截器MyInterceptor的afterCompletion方法"); } }
-
MyInterceptor2
拦截器类二public class MyInterceptor2 implements HandlerInterceptor { public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("拦截器MyInterceptor的preHandle方法22222222"); return true; } public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("拦截器MyInterceptor的postHandle方法2222222222"); } public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("拦截器MyInterceptor的afterCompletion方法2222222"); } }
-
SpringMVC 配置文件
<mvc:interceptors> <!-- 声明第一个拦截器 --> <mvc:interceptor> <mvc:mapping path="/**"/> <bean class="com.springmvc.handler.MyInterceptor"/> </mvc:interceptor> <!-- 声明第二个拦截器 --> <mvc:interceptor> <mvc:mapping path="/**"/> <bean class="com.springmvc.handler.MyInterceptor2"/> </mvc:interceptor> </mvc:interceptors>
情况一:当MyInterceptor
和MyInterceptor2
拦截器中的preHandle()
返回 都为true
时执行结果。
情况二:当MyInterceptor2
拦截器中的preHandle()
返回false
时执行结果。
情况三:当MyInterceptor
拦截器中的preHandle()
返回false
时执行结果。
拦截器与过滤去的区别
- 过滤器是 Servlet 中的对象。
拦截器是框架中的对象。 - 过滤器是实现 Filte 接口的对象。
拦截器是实现 Handlerinterceptor 接口。 - 过滤器是用来设置 Request、Response 的参数,是对数据的过滤。
拦截器是用来验证请求的,能截断请求。 - 过滤器是在拦截器之前先执行的。
拦截器是 SpringMVC 容器中的对象。 - 过滤器是一个执行时间点。
拦截器有三个执行时间点。 - 过滤器可以处理 jsp、js、html 等。
拦截器是对 Controller 的对象拦截,如果你的请求不能被 DispatcherServlet 接受,这个请求不会执行拦截器。 - 过滤器过滤 Servlet 请求响应。
拦截器拦截普通方法的执行。
注解配置 SpringMVC
使用配置类和注解代替web.xml
和 SpringMVC 配置文件的功能。
创建初始化类来代替 web.xml
在 Servlet3.0 环境中,容器会在类路径中查找实现javax.servlet.ServletContainerInitializer
接口的类,如果找到的话就用它来配置 Servlet 容器。
Spring 提供了这个接口的实现,名为SpringServletContainerInitializer
,这个类反过来又会查找实现WebApplicationInitializer
的类并将配置的任务交给它们来完成。Spring3.2 引入了一个便利的WebApplicationInitializer
基础实现,名为AbstractAnnotationConfigDispatcherServletInitializer
,当我们的类扩展了AbstractAnnotationConfigDispatcherServletInitializer
并将其部署到 Servlet3.0 容器的时候,容器会自动发现它,并用它来配置 Servlet 上下文。
public class WebInit extends AbstractAnnotationConfigDispatcherServletInitializer {
/**
* 指定spring的配置类
* @return
*/
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{SpringConfig.class};
}
/**
* 指定SpringMVC的配置类
* @return
*/
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{WebConfig.class};
}
/**
* 指定DispatcherServlet的映射规则,即url-pattern
* @return
*/
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
/**
* 添加过滤器
* @return
*/
@Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter encodingFilter = new CharacterEncodingFilter();
encodingFilter.setEncoding("UTF-8");
encodingFilter.setForceRequestEncoding(true);
HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();
return new Filter[]{encodingFilter, hiddenHttpMethodFilter};
}
}
创建 SpringConfig 配置类,代替 Spring 的配置文件
@Configuration
public class SpringConfig {
//ssm整合之后,spring的配置信息写在此类中
}
创建 WebConfig 配置类,代替 SpringMVC 的配置文件
@Configuration
//扫描组件
@ComponentScan("com.atguigu.mvc.controller")
//开启MVC注解驱动
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
//使用默认的servlet处理静态资源
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
//配置文件上传解析器
@Bean
public CommonsMultipartResolver multipartResolver(){
return new CommonsMultipartResolver();
}
//配置拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
FirstInterceptor firstInterceptor = new FirstInterceptor();
registry.addInterceptor(firstInterceptor).addPathPatterns("/**");
}
/**
//配置视图控制
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
}
*/
/**
//配置异常映射
@Override
public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
SimpleMappingExceptionResolver exceptionResolver = new SimpleMappingExceptionResolver();
Properties prop = new Properties();
prop.setProperty("java.lang.ArithmeticException", "error");
//设置异常映射
exceptionResolver.setExceptionMappings(prop);
//设置共享异常信息的键
exceptionResolver.setExceptionAttribute("ex");
resolvers.add(exceptionResolver);
}
*/
//配置生成模板解析器
@Bean
public ITemplateResolver templateResolver() {
WebApplicationContext webApplicationContext = ContextLoader.getCurrentWebApplicationContext();
// ServletContextTemplateResolver需要一个ServletContext作为构造参数,可通过WebApplicationContext 的方法获得
ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver(
webApplicationContext.getServletContext());
templateResolver.setPrefix("/WEB-INF/templates/");
templateResolver.setSuffix(".html");
templateResolver.setCharacterEncoding("UTF-8");
templateResolver.setTemplateMode(TemplateMode.HTML);
return templateResolver;
}
//生成模板引擎并为模板引擎注入模板解析器
@Bean
public SpringTemplateEngine templateEngine(ITemplateResolver templateResolver) {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(templateResolver);
return templateEngine;
}
//生成视图解析器并未解析器注入模板引擎
@Bean
public ViewResolver viewResolver(SpringTemplateEngine templateEngine) {
ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
viewResolver.setCharacterEncoding("UTF-8");
viewResolver.setTemplateEngine(templateEngine);
return viewResolver;
}
}