SpringMVC
文章目录
简介
- MVC模式
- M (model) 模型 : 处理业务逻辑,封装实体
- V (view) 视图 : 展示内容
- C (controller) 控制器 : 负责调度分发 (1.接收请求 2.调用模型 3.转发到视图)
- SpringMVC框架的作用 (封装原来Servlet中的共有行为)
- 参数封装、视图转发等
基础配置
- 导入依赖
<!--springMVC坐标-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<!--servlet坐标-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!--jsp坐标-->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
<scope>provided</scope>
</dependency>
- web.xml中配置前端控制器DospathcerServlet
<!--前端控制器-->
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--引入本地配置文件spring-mvc.xml-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<!--会匹配到所有的访问路径如/login活或/main ...,不会匹配如*.jsp带后缀名访问的URL-->
<url-pattern>/</url-pattern>
</servlet-mapping>
-
编写controller和视图界面
-
使用注解配置Controller类业务方法的映射地址
@Controller
@RequestMapping("/employee")
public class EmployeeController {
@RequestMapping("/findAll")
public String findAll(){
System.out.println("findAll");
return "/WEB-INF/view/list.jsp";
}
}
- 配置核心配置文件spring-mvc.xml
<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
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--组件注解扫描-->
<context:component-scan base-package="com.hezhe.controller"/>
<!--配置试图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/view/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!--实现静态资源映射:网页静态资源加载如js、css等-->
<mvc:default-servlet-handler/>
</beans>
SpringMVC组件
执行流程
1. 用户发送请求至前端控制器DispatcherServlet。
2. DispatcherServlet收到请求调用HandlerMapping处理器映射器。
3. 处理器映射器找到具体的处理器(可以根据xml配置、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。
4.DispatcherServlet调用HandlerAdapter处理器适配器。
5. HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)。
6. Controller执行完成返回ModelAndView。
7. HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet。
8. DispatcherServlet将ModelAndView传给ViewReslover视图解析器。
9. ViewReslover解析后返回具体View。
10. DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。
11. DispatcherServlet将渲染后的视图响应响应用户。
组件解析
1. 前端控制器:DispatcherServlet
用户请求到达前端控制器,它就相当于 MVC 模式中的 C,DispatcherServlet 是整个流程控制的中心,由它调用其它组件处理用户的请求,DispatcherServlet 的存在降低了组件之间的耦合性。
2. 处理器映射器:HandlerMapping (SpringMVC三大组件之一)
HandlerMapping 负责根据用户请求找到 Handler 即处理器,SpringMVC 提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。
3. 处理器适配器:HandlerAdapter (SpringMVC三大组件之一)
通过 HandlerAdapter 对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。
4. 处理器:Handler【**开发者编写**】
它就是我们开发中要编写的具体业务控制器。由 DispatcherServlet 把用户请求转发到Handler。由Handler 对具体的用户请求进行处理。
5. 视图解析器:ViewResolver (SpringMVC三大组件之一)
View Resolver 负责将处理结果生成 View 视图,View Resolver 首先根据逻辑视图名解析成物理视图名,即具体的页面地址,再生成 View 视图对象,最后对 View 进行渲染将处理结果通过页面展示给用户。
6. 视图:View 【**开发者编写**】
SpringMVC 框架提供了很多的 View 视图类型的支持,包括:jstlView、freemarkerView、pdfView等。最常用的视图就是 jsp。一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由程序员根据业务需求开发具体的页面。
SpringMVC注解解析
-
@Controller
将使用该注解的类放入Spring容器中。
-
@RequestMapping
-
作用:用于建立请求 URL 和处理请求方法之间的对应关系
-
位置
- 类上:第一级访问目录,便于区分功能模块。可以不写,若写需以 / 开头
- 方法上:第二级访问目录,具体方法。和一级组成完整 URL 路径
-
属性
- value:指定请求URL,作用与path属于一样
- method:用于限定请求类型
- params:用于限定请求参数条件
- params={“name”} 表示请求参数中必须携带name
- params={“num!=0”} 表示请求参数中num不能是0
SpringMVC请求
参数类型
-
基础数据类型、对象类型、数组类型、集合类型
-
要求业务方法中的参数名与请求参数名一致,即可自动匹配和类型转换。
中文乱码过滤器
<!--配置全局过滤的filter-->
<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>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
自定义类型转换器
- 编写转换器类实现Converter<T,T>接口
//将字符转换为Date数据类型的自定义转换
public class DateConverter implements Converter<String, Date> {
public Date convert(String dateStr) {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
Date date = null;
try {
date = format.parse(dateStr);
} catch(){
e.printStackTrace();
}
return date;
}
}
- 配置文件中配置转换器
<!--处理器映射器和适配器增强-->
<mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>
<!--自定义转换器配置-->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="com.hezhe.converter.DateConverter"></bean>
</set>
</property>
</bean>
相关注解
-
@RequestParam
当请求参数name名称与业务方法参数名称不一致时使用
/*
defaultValue 设置参数默认值
name 匹配页面传递参数的名称
required 设置是否必须传递参数,默认值为true;如果设置了默认值,值自动改为false
*/
@RequestMapping("/findAll")
public String findAll(@ReRequestParam(name = 'username' , defaultValue = 'zhangsan' , required = false) String name){
System.out.println("findAll");
return "/WEB-INF/view/list.jsp";
}
-
@RequestHeader
获取请求头的数据
@RequestMapping("/requestHead")
public String requestHead(@RequestHeader("cookie") String cookie) {
System.out.println(cookie);
return "success";
}
-
@CookieValue
获取cookie中的数据
@RequestMapping("/cookieValue")
public String cookieValue(@CookieValue("JSESSIONID") String jesessionId) {
System.out.println(jesessionId);
return "success";
}
-
获取Servlet相关API
SpringMVC支持使用原始ServletAPI对象作为控制方法的参数进行注入
@RequestMapping("/servletAPI")
public String servletAPI(HttpServletRequest request, HttpServletResponse response, HttpSession session) {
System.out.println(request);
System.out.println(response);
System.out.println(session);
return "success";
}
SpringMVC响应
响应方式
-
页面跳转
- 返回字符串逻辑视图
- void原始ServletAPI
- ModelAndView
-
返回数据
- 直接返回字符串数据
- 将对象或集合转为json返回
转发和重定向
- 返回字符串逻辑视图实现的页面跳转其实就是转发。
- 请求转发既可以转发到jsp,也可以转发到其他控制器方法
@RequestMapping("/findAll")
public String findAll(){
System.out.println("findAll");
return "forward:/WEB-INF/view/list.jsp";
}
- 重定向可以不写虚拟路径,SpringMVC框架会自动拼接,并将Model中的数据拼接到url地址上
@RequestMapping("/findAll")
public String findAll(String name,Model model){
System.out.println("findAll");
model.addAtrribute
return "redirect:/list.jsp";
}
ModelAndView
- 方式1 (自己创建ModelAndView对象,并设置视图名)
@RequestMapping("/returnModelAndView")
public ModelAndView returnModelAndView() {
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("username", " hezhe");
modelAndView.setViewName("success");
return modelAndView;
}
- 直接在请求参数
@RequestMapping("/returnModelAndView")
public ModelAndView returnModelAndView(ModelAndView modelAndView) {
modelAndView.addObject("username", "itheima");
modelAndView.setViewName("success");
return modelAndView;
}
@SessionAttributes
如果在多个请求之间共用数据,则可以在控制器类上标注一个 @SessionAttributes,配置需要在session中存放的数据范围,Spring MVC将存放在model中对应的数据暂存到 HttpSession 中。
该注解只能定义在类上
@Controller
@SessionAttributes("username") //向request域存入的key为username时,同步到session域中
public class UserController {
@RequestMapping("/returnModelAndView")
public String findAll(Model model) {
model.addAttribute("username", "mason");
return “success”;
}
}
静态资源访问的开启
由于DispatcherServlet配置的url-pattern的值是 /,代表对所有的静态都进行处理。不会去执行Tomcat内置的DefaultServlet处理。
方式一:
<!--在springmvc配置文件中指定放行资源-->
<mvc:resources mapping="/js/**" location="/js/"/>
<mvc:resources mapping="/css/**" location="/css/"/>
<mvc:resources mapping="/img/**" location="/img/"/>
方式二:
<!--在springmvc配置文件中开启DefaultServlet处理静态资源-->
<mvc:default-servlet-handler/>
AJAX异步交互
SpringMVC默认用MappingJackson2HttpMessageConverter对json数据进行转换,需加入jackson的包;同时要配置注解扫描
<!--json转换包坐标-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.8</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.8</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.0</version>
</dependency>
@RequestBody
该注解用于Controller的方法的形参声明,当使用ajax提交并指定contentType为json形式时,通过HttpMessageConverter接口转换为对应的POJO对象。
//AJAX请求简单样例
function findById() {
$.ajax({
type:'GET',
url:'${pageContext.request.contextPath}/employee/findById',
contentType:'application/json;charset=utf-8',
data:'{"id":1}',
success:function (resp) {
alert(JSON.stringify(resp));
}
});
}
//@RequestBody注解样例
//根据员工ID查询
@RequestMapping(value = "findById")
public String findById(@RequestBody Integer id, Model model){
Employee employee = employeeService.findById(id);
model.addAttribute("employee",employee);
return "edit";
}
@ResponseBody
该注解用于将Controller的方法返回的对象,通过HttpMessageConverter接口转换为指定格式的数据如:json,xml等,通过Response响应给客户端。
//@ResponseBody样例
//查询所有部门信息
@RequestMapping("queryDept")
@ResponseBody
public List<Dept> queryDept(){
List<Dept> deptList = deptMapper.findAll();
return deptList;
}
文件上传
文件上传三要素
- 表单项 type=‘file’
- 表单的提交方式 method=‘POST’
- 表单的enctype属性是多部份表单形式 enctype=‘multipart/form-data’
文件上传原理
-
当form表单修改为多部份表单时,request.getParameter() 将失效
-
当form表单的enctype取值为application/x-www-form-urlencoded时,
- form表单的正文内容格式是:name=value&name=value
-
当form表单的enctype取值为mutilpart/form-data时,请求正文内容就变成多部分形式:
------WebKitFormBoundary5DpNmhhN2bw1Uya2--
Content-Disposition: form-data; name="name";
mason
------WebKitFormBoundary5DpNmhhN2bw1Uya2
Content-Disposition: form-data; name="file"; filename="甘雨.jpg"
Content-Type: image/jpeg
------WebKitFormBoundary5DpNmhhN2bw1Uya2--
单文件上传案例
- 引入fileUpload和io的依赖
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.3</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
- 配置文件上传解析器
<!--文件上传解析器-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!--设定文件上传的最大值为5MB,5*1024*1024-->
<property name="maxUploadSize" value="5242880"></property>
<!--设定文件上传时写入内存的最大值,如果小于这个参数不会生成临时文件,默认为10240-->
<property name="maxInMemorySize" value="40960"></property>
</bean>
- 编写文件上传的代码
<form action="${pageContext.request.contextPath}/fileUpload" method="post" enctype="multipart/form-data">
名称:<input type="text" name="username"> <br>
文件:<input type="file" name="filePic"> <br>
<input type="submit" value="单文件上传">
</form>
@RequestMapping("/fileUpload")
public String fileUpload(String username, MultipartFile filePic) throws IOException {
System.out.println(name);
//获取文件名
String originalFilename = filePic.getOriginalFilename();
//保存文件
filePic.transferTo(new File("d:/upload/"+originalFilename));
return "success";
}
多文件上传
<form action="${pageContext.request.contextPath}/fileUpload" method="post" enctype="multipart/form-data">
名称:<input type="text" name="username"> <br>
文件1:<input type="file" name="filePic"> <br>
文件1:<input type="file" name="filePic"> <br>
<input type="submit" value="多文件上传">
</form>
@RequestMapping("/fileUpload")
public String fileUpload(String username, MultipartFile[] filePic) throws IOException {
System.out.println(name);
for (MultipartFile multipartFile : filePic) {
//获取文件名
String originalFilename = filePic.getOriginalFilename();
//保存文件
filePic.transferTo(new File("d:/upload/"+originalFilename));
}
return "success";
}
异常处理
处理异常有两种方式:
1.try-catch直接处理 2.向上抛
根据第2种衍生出了SpringMVC的异常处理机制。
系统的dao、serice、controller出现的异常都向上抛,最后由springmvc前端控制器交给异常处理器(HandlerExceptionResolver)进行异常处理。
自定义异常处理器
- 创建异常处理器类实现HandlerExceptionResolver
public class GlobalExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request,HttpServletResponse response, Object handler, Exception ex) {
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("error", ex.getMessage());
modelAndView.setViewName("error");
return modelAndView;
}
}
-
配置异常处理器(注入spring容器中)
-
编写异常界面
web的处理异常机制
<!--处理500异常-->
<error-page>
<error-code>500</error-code>
<location>/500.jsp</location>
</error-page>
<!--处理404异常-->
<error-page>
<error-code>404</error-code>
<location>/404.jsp</location>
</error-page>
拦截器
拦截器(interceptor)的作用
Spring MVC 的拦截器类似于 Servlet 开发中的过滤器 Filter,用于对处理器进行预处理和后处理。
拦截器与过滤器的区别
区别 | 过滤器 | 拦截器 |
---|---|---|
使用范围 | 是servlet规范中的一部分,任何Java Web项目都可使用 | 是SpringMVC框架自己的,只有使用了SpringMVC框架的工程才使用 |
拦截范围 | 在 url-pattern中配置了/*之后,可以对所有要访问的资源拦截 | 只会拦截访问的控制器方法,如果访问的是jsp,html,css,image或者js是不会拦截的 |
简单使用
- 创建拦截器类实现HandlerInterceptor接口
public class MyInterceptor implements HandlerInterceptor {
// 在目标方法执行之前拦截
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle");
return false;
}
// 在目标方法执行之后,视图对象返回之前执行
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle");
}
// 在流程都执行完毕后执行
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion");
}
}
- 配置拦截器
<!--配置拦截器或拦截器链-->
<mvc:interceptors>
<mvc:interceptor>
<!--对哪些资源执行拦截操作-->
<mvc:mapping path="/**"/>
<bean class="com.hezhe.interceptor.MyInterceptor1"/>
</mvc:interceptor>
<mvc:interceptor>
<!--对哪些资源执行拦截操作-->
<mvc:mapping path="/**"/>
<bean class="com.hezhe.interceptor.MyInterceptor2"/>
</mvc:interceptor>
</mvc:interceptors>
拦截器中的方法说明
方法名 | 说明 |
---|---|
preHandle | 方法将请求处理之前进行调用,该方法的返回值是布尔值Boolean类型的,当它返回false时,表示请求结束,后续的Interceptor和Controller都不会再执行;当返回值为true时就会继续调用下一个Interceptor的preHandle方法 |
postHandle | 该方法是在当前请求进行处理之后被调用,前提是preHandle方法的返回值为true时才能被调用,且它回在DispatcherServlet进行视图返回渲染之前被调用,所以我们可以在这个方法中对Controller处理之后的ModelAndView对象进行操作 |
afterCompletion | 该方法将在整个请求结束之后,也就是在DispatcherServlet渲染了对应的视图之后执行,前提时preHandle方法的返回值为true时才能被调用 |