SpringMVC 学习笔记
SpringMVC
spring-context 依赖于 spring-core + spring-beans + spring-aop + spring-expression
spring-webmvc 依赖于 spring-context + spring-web
执行流程
基本配置
开启 spring 注解扫描,并启动 MVC 注解驱动
将 DispatherServlet
接入 web.xml 并配置 init-param: contextConfigLocation
<?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">
<!--扫描 Controller-->
<context:component-scan base-package="com.xtyuns.controller"/>
<!--注册 SpringMVC 组件: HandlerMapping、HandlerAdapter、HandlerExceptionResolver...-->
<!--org.springframework.web.servlet.config.AnnotationDrivenBeanDefinitionParser-->
<mvc:annotation-driven/>
<!--将未映射的路径交给 WEB 容器的 default servlet 处理-->
<!--org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler-->
<mvc:default-servlet-handler/>
<!--视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"/>
</bean>
<!--文件上传, 该 bean 的 id 必须为 multipartResolver-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"/>
<!--拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<!--模拟后台接口-->
<mvc:mapping path="/test/**"/>
<!--转发页面-->
<mvc:mapping path="/page/**"/>
<!--用户操作-->
<mvc:mapping path="/user/**"/>
<!--放行: 用户登录页面-->
<mvc:exclude-mapping path="/page/toLogin"/>
<bean class="com.xtyuns.interceptor.LoginInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
</beans>
转发和重定向
拥有 @RequestMapping 注解的方法(该方法上未设置 @ResponseBody 注解)通过其返回值设置请求转发或重定向。
- 转发:
forward:PATH
(forward: 可以省略, 因为默认的解析方案是请求转发, 且只有省略关键字时视图解析器配置的前后缀才会生效) - 重定向:
redirect:PATH
方法的返回值可以为字符串(直接返回上述对应的字符串), 也可以返回 ModelAndView 对象(将上述对应的字符串设置为返回值对象的 viewName 属性)
接收参数
- 使用普通数据类型接收参数(int、Integer、String ...), 形参需要与前端请求的参数名一致,否则需要通过 @RequestParam 注解绑定前端参数
- 使用实体类接收参数, 实体类中的属性名需要与前端请求的参数名一致
- 可以直接使用 String[] 类型的参数接收多选框数据
- 使用 Map 对象接收参数, 需要使用 @RequestParam 注解声明该参数, 否则该 Map 对象将被用于数据传递(类似 HttpServletRequest)
- 使用 List 对象接收参数(如多选框数据), 需要使用 @RequestParam 注解声明该参数
数据传递
- HttpServletRequest#setAttribute()
- Map<String, Object> dataMap
- Model#addObject()
- ModelAndView#addObject()
使用以上 4 种方式传递的数据都可以在 JSP 中通过 ${requestScope.xxx}
获取。
返回 Json 数据
引入 jackson-databind
依赖, 并且在方法上添加 @ResponseBody
注解即可
// 序列化格式处理
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
//反序列化格式处理
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
RestfulAPI
通过在 url-pattern 中以 {param}
的形式定义参数, 并且在形参上添加 @PathVariable()
的注解接收 url 中的参数。
@GetMapping("/user/{id}")
@ResponseBody
public String user(@PathVaiable("id") String userId) {
return "Hello, " + userId;
}
文件上传
文件上传 form 表单的 enctype 需要设置为 multipart/form-data,以二进制的形式传输数据。
实现文件上传,其实就是解析一个 Mutipart 请求。DispatchServlet自己并不负责去解析 Mutipart 请求,而是委托一个实现了MultipartResolver
接口的类来解析 Mutipart 请求。在 Spring3.1 之后 Spring 提供了两个现成的 MultipartResolver 接口的实现类:
CommonMutipartResolver
:通过利用 Jakarta Commons FileUpload
来解析mutipart 请求
StandardServletMutipartResolver
:依赖 Servlet3.0 来解析 Mutipart 请求
所以要实现文件上传功能,只需在我们的项目中配置好这两个 bean 中的 任何一个
即可。其实这两个都很好用,如果我们部署的容器支持 Servlet3.0,我们完全可以使用 StandardServletMutipartResolver。但是如果我们的应用部署的容器不支持 Servlet3.0
或者用到的 Spring 版本是 3.1 以前的
,那么我们就需要用到 CommonMutipartResolver
了。下面就具体介绍一下两种bean的配置,当然也是实现文件上传的两种配置。
StandardServletMutipartResolver
-
在 spring-webmvc.xml 中注册
StandardServletMutipartResolver
<bean id="multipartResolver" class="org.springframework.web.multipart.support.StandardServletMultipartResolver"/>
-
配置 MultipartConfigElement
<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:spring-webmvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> <!--使用 StandardServletMultipartResolver 必须指定 location--> <multipart-config> <location>/tmp/uploads</location> </multipart-config> </servlet>
CommonMutipartResolver
-
在 pom 中导入
commons-fileupload
依赖<dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.4</version> </dependency>
-
在 spring-webmvc.xml 中注册
CommonsMultipartResolver
<!--文件上传--> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"/>
SpringMVC 拦截器
HandlerInterceptor 是 SpringMVC 中的一个接口
拦截流程
SpringMVC 执行流程和拦截器拦截时机
Interceptor 和 WebFilter 的区别:
- WebFilter 是 Servlet 规范中的内容, 而 HandlerInterceptor 是由 spring-webmvc 提供的内容
- 进行拦截的方法不同, WebFilter 通过 doFilter() 方法实现请求的拦截和放行, 而 HandlerInterceptor 通过 preHandle() 进行请求的拦截和放行。
- 后置处理 (此时获取到的 response 对象不为空) 的方法不同, WebFilter 通过在 doFilter() 方法中
chain.doFilter(request, response);
语句执行之后进行后置处理, 而 HandlerInterceptor 拥有两个后置处理方法postHandle()
和afterCompletion()
。其中postHandle()
可以获取到 SpringMVC 中 handler 执行之后返回的ModelAndView
对象 (此时视图还未进行渲染),afterCompletion()
则为视图数据渲染完毕之后的回调。
定义登录拦截器:
package com.xtyuns.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (null != request.getSession().getAttribute("user")) return true;
response.sendRedirect(request.getContextPath() + "/index.jsp");
return false;
}
}
配置登录拦截器 (spring-webmvc.xml):
<!--拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<!--拦截: 转发页面-->
<mvc:mapping path="/page/**"/>
<!--拦截: 用户操作页面-->
<mvc:mapping path="/user/**"/>
<!--放行: 用户登录页面-->
<mvc:exclude-mapping path="/page/toLogin"/>
<bean class="com.xtyuns.interceptor.LoginInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
异常处理器
@ControllerAdvice
类
@ExceptionHandler(Exception.class)
方法()