Spring MVC
MVC设计模式简介
- MVC 是 Model、View 和 Controller 的缩写,分别代表 Web 应用程序中的 3 种职责
- 模型:用于存储数据以及处理用户请求的业务逻辑
- 视图:向控制器提交数据,显示模型中的数据
- 控制器:根据视图提出的请求判断将请求和数据交给哪个模型处理,将处理后的有关结果交给哪个视图更新显示
- 基于 Servlet 的 MVC 模式的具体实现如下
- 模型:一个或多个 JavaBean 对象,用于存储数据(实体模型,由 JavaBean 类创建)和处理业务逻辑(业务模型,由一般的 Java 类创建)
- 视图:一个或多个 JSP 页面,向控制器提交数据和为模型提供数据显示,JSP 页面主要使用 HTML 标记和 JavaBean 标记来显示数据
- 控制器:一个或多个 Servlet 对象,根据视图提交的请求进行控制,即将请求转发给处理业务逻辑的 JavaBean,并将处理结果存放到实体模型 JavaBean 中,输出给视图显示
- 基于 Servlet 的 MVC 模式的流程如下图
Spring MVC框架
-
Spring MVC 框架主要由 DispatcherServlet、处理器映射、控制器、视图解析器、视图组成,其工作原理如下图所示
-
由上图可总结出 Spring MVC 的工作流程如下:
- 客户端请求提交到 DispatcherServlet
- 由 DispatcherServlet 控制器寻找一个或多个 HandlerMapping,找到处理请求的 Controller
- DispatcherServlet 将请求提交到 Controller
- Controller 调用业务逻辑处理后返回 ModelAndView
- DispatcherServlet 寻找一个或多个 ViewResolver 视图解析器,找到 ModelAndView 指定的视图
- 视图负责将结果显示到客户端
-
Spring MVC包含四个接口:DispatcherServlet、HandlerMapping、Controller 和 ViewResolver
- Spring MVC 所有的请求都经过 DispatcherServlet 来统一分发,在 DispatcherServlet 将请求分发给 Controller 之前需要借助 Spring MVC 提供的 HandlerMapping 定位到具体的 Controller
- HandlerMapping 接口负责完成客户请求到 Controller 映射
- Controller 接口将处理用户请求,这和 Java Servlet 扮演的角色是一致的。一旦 Controller 处理完用户请求,将返回 ModelAndView 对象给 DispatcherServlet 前端控制器,ModelAndView 中包含了模型(Model)和视图(View)
- 从宏观角度考虑,DispatcherServlet 是整个 Web 应用的控制器;从微观考虑,Controller 是单个 Http 请求处理过程中的控制器,而 ModelAndView 是 Http 请求过程中返回的模型(Model)和视图(View)
- ViewResolver 接口(视图解析器)在 Web 应用中负责查找 View 对象,从而将相应结果渲染给客户
Spring集成web环境
ApplicationContext应用上下文获取方式
- 应用上下文对象是通过new ClasspathXmlapplicationContext(spring配置文件)方式获取的,但是每次从容器中获得Bean都要编写这句代码,这样的弊端是配置文件被加载多次,应用上下文对象创建多次
- 在web项目中,可以使用ServletContextListener监听web应用的启动,在web应用启动时就加载spring的配置文件,创建应用上下文对象ApplicationContext,再将其存储到最大的域servletContext域中(每个 Web 应用创建一个唯一的 ServletContext 对象代表当前的 Web 应用,该对象封装了当前 Web 应用的所有信息),这样就可以在任意位置从域中获得应用上下文ApplicaitonContext对象
Spring提供获取应用上下文的工具
-
Spring提供了一个监听器ContextLoaderListener,该监听器内部加载Spring配置文件,创建应用上下文对象,并存储到ServletContext域中,提供一个客户端工具WebApplicationContextUtils供使用者获取应用上下文对象
-
使用方法
- 在web.xml中配置ContextLoaderListener监听器(需要导入spring-web坐标)
- 使用WebApplicationContextUtils获得应用上下文对象ApplicationContext
Spring MVC开发步骤
-
开发需求:客户端发起请求,服务端接收请求,执行逻辑并进行视图跳转
-
开发步骤
- 导入Spring MVC相关坐标(spring-webmvc, spring-web)
- 在 web.xml 中配置Spring MVC核心控制器DispatcherServlet
- 创建Controller类和视图页面
- 使用注解配置Controller类中业务方法的映射地址
- 创建并配置Spring MVC核心文件spring-mvc.xml,主要用来配置 Controller 映射信息,然后DispatcherServlet使用 servlet 的 init-param 元素加载该配置文件
- 客户端发起请求进行测试
-
Spring MVC流程图
Spring MVC注解
- 在 Spring MVC 中最重要的两个注解类型是 Controller 和 RequestMapping
@Controller
-
使用基于注解的控制器具有以下两个优点
- 在基于注解的控制器类中可以编写多个处理方法,进而可以处理多个请求(动作),这就允许将相关的操作编写在同一个控制器类中,从而减少控制器类的数量,方便以后的维护
- 基于注解的控制器不需要在配置文件中部署映射,仅需要使用 RequestMapping 注释类型注解一个方法进行请求处理
-
在 Spring MVC 中使用扫描机制找到应用中所有基于注解的控制器类,所以,为了让控制器类被 Spring MVC 框架扫描到,需要在spring-mvc.xml配置文件中声明 spring-context,并使用 <context:component-scan/> 元素指定控制器类的基本包(请确保所有控制器类都在基本包及其子包下)
@RequestMapping
-
在基于注解的控制器类中可以为每个请求编写对应的处理方法。RequestMapping注解将请求与处理方法一一对应
-
RequestMapping有三个常用属性,如下所示
属性 作用 value 用于指定请求的URL,和path属性作用一样 method 用于指定请求的方式。如,method=RequestMethod.GET,表示浏览器只能用GET方法访问此方法 params 用于指定限制请求参数的条件。支持简单的表达式,要求请求参数的key和value必须和配置一模一样。如params={“accountName”},表示请求参数必须有accountName,params={“money!100”}表示请求参数中money不能为100 -
RequestMapping分为方法级别注解和类级别注解
-
方法级别注解如下示例
package controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; /** * “@Controller”表示 IndexController 的实例是一个控制器 * * @Controller相当于@Controller(@Controller) 或@Controller(value="@Controller") */ @Controller public class IndexController { @RequestMapping(value = "/index/login") public String login() { /** * login代表逻辑视图名称,需要根据Spring MVC配置 * 文件中internalResourceViewResolver的前缀和后缀找到对应的物理视图 */ return "login"; } @RequestMapping(value = "/index/register") public String register() { return "register"; } }
上述示例中有两个 RequestMapping 注解语句,它们都作用在处理方法上。注解的 value 属性将请求 URI 映射到方法,value 属性是 RequestMapping 注解的默认属性,如果只有一个 value 属性,则可以省略该属性
-
用户可以使用如下 URL 访问 login 方法(请求处理方法),在访问 login 方法之前需要事先在 /WEB-INF/jsp/ 目录下创建 login.jsp
http://localhost:8080/dmeo/index/login
-
类级别注解示例
package controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller @RequestMapping("/index") public class IndexController { @RequestMapping("/login") public String login() { return "login"; } @RequestMapping("/register") public String register() { return "register"; } }
在类级别注解的情况下,控制器类中的所有方法都将映射为类级别的请求。为了方便维护程序,建议开发者采用类级别注解,将相关处理放在同一个控制器类中
-
方法上标注的目录与类上标注的一级目录一起组成访问虚拟路径
处理器(Handler)
- 在控制类中每个请求处理方法可以有多个不同类型的参数,以及一个多种类型的返回结果
- 请求处理方法中常出现的参数类型
- 如果需要在请求处理方法中使用 Servlet API 类型,如HttpSession、HttpServletRequest和HttpServletResponse等,那么可以将这些类型作为请求处理方法的参数类型
- 除了 Servlet API 参数类型以外,还有输入输出流、表单实体类、注解类型、与 Spring 框架相关的类型等
- 特别重要的类型是 org.springframework.ui.Model 类型,该类型是一个包含 Map 的 Spring 框架类型。在每次调用请求处理方法时 Spring MVC 都将创建 org.springframework.ui.Model 对象
- 请求处理方法常见返回类型
- 常见的返回类型就是代表逻辑视图名称的 String 类型,除了 String 类型以外,还有 ModelAndView、Model、View 以及其他任意的 Java 类型
Spring MVC视图解析器(ViewResolver)
- 用户可以在spring-mvc.xml配置文件中定义 Spring MVC 的一个视图解析器(ViewResolver),示例代码如下
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" > <!--前缀--> <property name="prefix" value="/WEB-INF/jsp/"/> <!--后缀--> <property name="suffix" value=".jsp"/> </bean>
- InternalResourceViewResolver 是 URLBasedViewResolver 的子类,所以 URLBasedViewResolver 支持的特性它都支持。在实际应用中 InternalResourceViewResolver 也是使用的最广泛的一个视图解析器
- 视图解析器可以使Controller请求处理方法返回的视图路径仅需提供视图文件名称,视图解析器将会自动添加前缀和后缀。如上示例,若请求处理方法返回"login"字符串,则 InternalResourceViewResolver 就会把 login 解析为一个 InternalResourceView 对象,先把返回的模型属性都存放到对应的 HttpServletRequest 属性中,然后利用 RequestDispatcher 在服务器端把请求 forword 到 /WEB-INF/login.jsp
Spring MVC转发与重定向
-
重定向是将用户从当前处理请求定向到另一个视图(例如 JSP)或处理请求,以前的请求(request)中存放的信息全部失效,并进入一个新的 request 作用域
-
转发是将用户对当前处理的请求转发给另一个视图或处理请求,以前的 request 中存放的信息不会失效
-
转发是服务器行为,重定向是客户端行为
-
转发过程
- 客户浏览器发送 http 请求,Web 服务器接受此请求,调用内部的一个方法在容器内部完成请求处理和转发动作,将目标资源发送给客户;在这里转发的路径必须是同一个 Web 容器下的 URL,其不能转向到其他的 Web 路径上,中间传递的是自己的容器内的 request
- 在客户浏览器的地址栏中显示的仍然是其第一次访问的路径,也就是说客户是感觉不到服务器做了转发的。转发行为是浏览器只做了一次访问请求
-
重定向过程
- 客户浏览器发送 http 请求,Web 服务器接受后发送 302 状态码响应及对应新的 location 给客户浏览器,客户浏览器发现是 302 响应,则自动再发送一个新的 http 请求,请求 URL 是新的 location 地址,服务器根据此请求寻找资源并发送给客户
- 在这里 location 可以重定向到任意 URL,既然是浏览器重新发出了请求,那么就没有什么 request 传递的概念了。在客户浏览器的地址栏中显示的是其重定向的路径,客户可以观察到地址的变化。重定向行为是浏览器做了至少两次的访问请求
-
在 Spring MVC 框架中,不管是重定向或转发,都需要符合视图解析器的配置,如果直接转发到一个不需要 DispatcherServlet 的资源,例如:
return "forward:/html/index.html";
则需要使用mvc:resources配置
<mvc:resources location="/html/" mapping="/html/**" />
Spring MVC数据响应方式
- 数据响应方式主要分为如下两类
- 页面跳转
- 直接返回字符串
- 通过ModelAndView对象返回
- 回写数据
- 直接返回字符串
- 返回对象或集合
- 页面跳转
页面跳转
- 返回字符串形式
- 直接返回字符串,此种方式会将返回的字符串与视图解析器的前后缀拼接后跳转
- 注意:存放在 /WEB-INF/ 下面的内容是不能直接通过 request 请求的方式请求到的,为了安全性考虑,通常把 jsp 文件放在 WEB-INF 目录下,而 InternalResourceView 在服务器端跳转的方式可以很好的解决这个问题
- 在 Spring MVC 框架中,控制器类中处理方法的 return 语句默认就是转发实现,只不过实现的是转发到视图
- 若要使用重定向,则需要显式指明,如重定向到index.jsp视图:
return "redirect:test"
- 返回ModelAndView
- Model:模型,作用:封装数据
- View:视图,作用:展示数据
- 示例
也可以通过通过参数传入:@RequestMapping("/test") public ModelAndView test(){ ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("username", "xxx"); modelAndView.setViewName("test"); return modelAndView; }
@RequestMapping("/test") public ModelAndView test(ModelAndView mv){ mv.addObject("username", "xxx"); mv.setViewName("test"); return mv; } @RequestMapping("/test") public String test(Model model){ model.addObject("username", "xxx"); return "test"; } // spring mvc框架不常用形式,它已经帮用户封装好Servlet相关接口 @RequestMapping("/test") public String test(HttpServletRequest req){ req.setAttribute("username", "zzz"); return "test"; }
回写数据
-
直接返回字符串
- web基础阶段,客户端访问服务端,如果想直接回写字符串作为响应体返回的话,只需要使用response.getWriter().print(“hello world”)即可
- 在Spring MVC框架中,可以通过注入HttpServletResponse对象,使用response.getWriter().print(“hello world”)回写数据,此时不需要视图跳转,业务方法返回值为void
- 可以将需要回写的字符串直接返回,但此时需要通过@ResponseBody注解告知Spring MVC框架,方法返回的字符串不是跳转而是直接在http响应体中返回。示例如下
@RequestMapping("/test") @ResponseBody public String test(){ return "test test test"; }
- 使用json转换工具将对象转化成json格式字符串再回写
<!--pom.xml导入json转换依赖包--> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> </dependency>
@RequestMapping("/test") @ResponseBody public String test() throws JsonProcessingException { User user = new User(); user.setUsername("xxx"); user.setPasswd("123"); ObjectMapper objectMapper = new ObjectMapper(); String s = objectMapper.writeValueAsString(user); return s; }
-
返回对象或集合
-
这种方法需要通过spring mvc提供的映射器将返回的对象和集合映射成json格式字符串并作为消息体回写
-
spring-mvc.xml配置映射器
<!--配置处理器映射器--> <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"> <property name="messageConverters"> <list> <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"></bean> </list> </property> </bean>
-
代码可以直接返回对象或集合
@RequestMapping("/test") @ResponseBody public User test(){ User user = new User(); user.setUsername("xxx"); user.setPasswd("123"); return user; }
-
上述方法配置比较麻烦,配置代码多,spring mvc提供了注解驱动可以代替上述配置
-
在spring mvc的各个组件中,处理器映射器、处理器适配器和视图解析器称为spring mvc三大组件
- 使用<mvc:annotation-driven>自动加载RequestMappingHandlerMapping(处理映射器)和RequestMappingHadlerAdapter(处理适配器),替代sprng-mvc.xml中的配置
- 使用<mvc:annotation-driven>底层会集成jackson进行对象或集合的jason格式字符串的转换
-
spring-mvc.xml中注解驱动
<!--引入mvc命名空间--> xmlns:mvc="http://www.springframework.org/schema/mvc" http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd <!--注解驱动--> <mvc:annotation-driven/>
-
Spring MVC获得请求数据
- 客户端请求参数的格式是:name=value&name=value
- Spring MVC可以接收以下类型的参数
- 基本类型参数
- POJO参数
- 数组类型参数
- 集合类型参数
获得基本类型请求参数
- 获得基本类型请求参数,通过
http://localhost:8080/test?username=xxx&age=18
访问@RequestMapping("/test") @ResponseBody public void test(String username, int age){ System.out.println("username = " + username + ", age = " + age); }
获得POJO类型参数
- 获得POJO类型参数:Controller中的业务方法的POJO参数的属性名与请求参数的name一致,参数值会自动映射匹配。通过与上例同样的url访问
// User为一个包含username和age属性的pojo类 @RequestMapping("/test") @ResponseBody public void test(User user){ System.out.println(user); }
获取数组类型参数
- 获取数组类型参数:Controller中的业务方法数组名称与请求参数一致,参数值会自动映射匹配。通过
http://localhost:8080/test?strs=111&strs=222&strs=333
访问@RequestMapping("/test") @ResponseBody public void test(String [] strs){ for (String str : strs) { System.out.println(str); } }
获得集合类型参数
- 获得集合类型参数
-
要将集合参数包装到一个POJO中才可以。通过post表单提交数据
<html> <body> <form action="${pageContext.request.contextPath}/test10"> first username: <input type="text" name="userList[0].username"><br/> first password: <input type="text" name="userList[0].passwd"><br/> second username: <input type="text" name="userList[1].username"><br/> second password: <input type="text" name="userList[1].passwd"><br/> <input type="submit" value="submit"> </form> </body> </html>
package com.ccq.pojo; import java.util.List; public class VO { private List<User> userList; public List<User> getUserList() { return userList; } public void setUserList(List<User> userList) { this.userList = userList; } @Override public String toString() { return "VO{" + "userList=" + userList + '}'; } }
@RequestMapping("/test") @ResponseBody public void test(VO vo){ System.out.println(vo); }
-
当使用ajax提交时,可以指定contentType为json形式,在方法参数位置使用@RequestBody可以直接接收集合数据而无需使用POJO封装
@RequestMapping("/test") @ResponseBody public void test(@RequestBody List<User>userList){ System.out.println(userList); }
-
参数绑定注解@RequestParam
-
当请求的参数名称与Controller的业务方法参数名称不一致时,就需要通过@RequestParam注解显式绑定
@RequestMapping("/test") @ResponseBody // 将请求参数名称name绑定到username public void test(@RequestParam(value = "name") String username){ System.out.println(username); }
-
注解@RequestParam有如下参数可以使用
- value:请求参数名称
- required:指定此请求参数是必须包括,默认为true,提交时如果没有此参数将报错
- defaultValue:当没有指定请求参数时,使用指定的默认值赋值
获得Restful风格的参数
-
Restful是一种软件架构风格、设计风格,而不是标准,只是提供了一组设计原则和约束条件。主要用于客户端和服务器交互类的软件,基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存机制等
-
Restful风格的请求是使用“url+请求方式”表示一次请求目的的,HTTP协议里面四个表示操作方式的动词如下:
- GET:用于获取资源
- POST:用于新建资源
- PUT:用于更新资源
- DELETE:用于删除资源
-
例如
- /user/1 GET:得到id=1的user
- /user/1 DELETE:删除id=1的user
- /user/1 PUT:更新id=1的user
- /user POST:新增user
-
上述url地址/user/1中的1就是要获得的请求参数,在Spring MVC中可以使用占位符进行参数绑定。地址/user/1可以写成/user/{id},占位符{id}对应的就是1的值。在业务方法中可以使用@PathVariable注解进行占位符的匹配获取工作
@RequestMapping("/test/{username}") @ResponseBody public void test(@PathVariable(value = "username", required = true) String username){ System.out.println(username); }
这个例子可以通过以下url访问。不同的操作方式的处理方法可以通过@RequestMapping的method属性区分
http://localhost:8080/test/tom
获得请求头
-
@RequestHeader
- 使用@RequestHeader可以获得请求头信息,相当于JavaWeb阶段学习的
request.getHeader(String name)
- @RequestHeader注解的属性如下
-
value:请求头名称
-
required:是否必须携带此请求头
@RequestMapping("/test") @ResponseBody public void test(@RequestHeader(value = "User-Agent", required = false) String headerValue){ System.out.println(headerValue); }
-
- 使用@RequestHeader可以获得请求头信息,相当于JavaWeb阶段学习的
-
CookieValue
- 使用@CookieValue可以获得指定的Cookie值
- @CookieValue注解属性如下
-
value:指定cookie的名称
-
required:是否必须携带此cookie
@RequestMapping("/test") @ResponseBody public void test(@CookieValue(value = "JSESSIONID", required = false) String sessionId){ System.out.println(sessionId); }
-
类型转换器
内置类型转换器
- Spring MVC 框架的
Converter<S,T>
是一个可以将一种数据类型转换成另一种数据类型的接口,这里 S 表示源类型,T 表示目标类型。开发者在实际应用中使用框架内置的类型转换器基本上就够了,但有时需要编写具有特定功能的类型转换器 - 在 Spring MVC 框架中,对于常用的数据类型,开发者无须创建自己的类型转换器,因为 Spring MVC 框架有许多内置的类型转换器用于完成常用的类型转换。Spring MVC 框架提供的内置类型转换包括以下几种类型
- 标量转换器
- 集合、数组相关转换器
- 标量转换器
- 类型转换是在视图与控制器相互传递数据时发生的。Spring MVC 框架对于基本类型(例如 int、long、float、double、boolean 以及 char 等)已经做好了基本类型转换
- 在使用内置类型转换器时,请求参数输入值与接收参数类型要兼容,否则会报 400 错误
自定义类型转换器
- 不是所有的数据类型都提供了转换器,没有提供的就需要自定义转换器,例如:日期类型的数据就需要自定义转换器
- 自定义类型转换器的开发步骤
- 定义转换器类实现Converter接口
- 在spring-mvc.xml配置文件中声明转换器
- 在<annotation-driven>中引用转换器
解决请求数据乱码问题
-
当post请求时,中文数据可能出现乱码,类似于Servlet中的处理方式,可以设置一个过滤器来进行编码过滤。Spring MVC已经封装好解决乱码的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>
Spring MVC开放访问静态资源的方式
-
Spring MVC想要访问静态资源,如css、js、html、images等,需要在spring-mvc.xml文件中配置静态文件路径和映射
<!-- 允许css目录下的所有文件可见 --> <mvc:resources location="/static-resources/css/" mapping="/css/**" /> <!-- 允许js目录下的所有文件可见 --> <mvc:resources location="/static-resources/js/" mapping="/js/**" /> <!-- 允许fonts目录下的所有文件可见--> <mvc:resources location="/static-resources/fonts/" mapping="/fonts/**" /> <!-- 允许static-resources目录下的所有文件可见--> <mvc:resources location="/static-resources/" mapping="/**" /> <!-- 允许images目录下的所有文件可见 --> <mvc:resources location="/static-resources/images/" mapping="/images/**" />
-
或简单配置使用原始Servlet而不是DispatcherServlet处理静态资源
<!--交给原始Servlet处理其他资源--> <mvc:default-servlet-handler/>
文件上传
-
文件上传三要素
- 表单项type=“file”
- 表单的提交方式是post
- 表单的enctype属性是多部分表单形式,及
enctype="multipart/form-data"
-
文件上传原理
- 当form表单修改为多部分表单
multipart/form-data
时,request.getParameter()
将失效 enctype="application/x-www-form-urlencoded"
时,form表单的正文内容格式为key1=value1&key2=value2
- 当form表单的enctype取值为
multipart/form-data
时,请求正文内容就变成多部分形式
- 当form表单修改为多部分表单
-
服务端处理单文件上传步骤
-
导入fileupload和io坐标
<dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.4</version> </dependency> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.6</version> </dependency> </dependencies>
-
在spring-mvc.xml配置文件上传解析器
<!--配置文件上传解析器--> <bean name="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <!--上传文件总大小--> <property name="maxUploadSize" value="5000000"/> <!--上传单个文件大小--> <property name="maxUploadSizePerFile" value="5000000"/> <!--上传文件的编码类型--> <property name="defaultEncoding" value="UTF-8"/> </bean>
-
编写文件上传代码
@RequestMapping("/test11") @ResponseBody public void test11(String filename, MultipartFile uploadedFile, HttpServletRequest req) throws IOException { // 原始文件名 String originalFilename = uploadedFile.getOriginalFilename(); // 保存到服务器本地 String path = req.getServletContext().getRealPath("/uploadedFiles"); uploadedFile.transferTo(new File(path+ File.separator + originalFilename)); }
-
-
多文件上传与单文件上传类似,只需将MultipartFile参数改成数组类型
MultipartFile [] uploadedFile
Spring MVC拦截器(Interceptor)
-
拦截器作用:类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理
-
将拦截器按一定顺序链接成一条链,这条链成为拦截器链(Interceptor Chain)。在访问被拦截的方法或字段时,拦截器链中的拦截器就会按链接的顺序被调用。拦截器也是AOP思想的具体体现
-
拦截器和过滤器的区别
区别 过滤器 拦截器 使用范围 Servlet规范的一部分,任何JavaWeb工程都可以使用 使用Spring MVC框架的工程才能使用 拦截范围 在url-pattern配置了/*之后,可以对所有要访问的资源拦截 只会拦截要访问的控制器方法,如果访问的是jsp,html,css,image或者js是不会进行拦截的 -
拦截方法说明
方法名 说明 preHandler() 方法将在请求处理之前进行调用,该方法的返回值是布尔类型。当它返回false时,表示请求结束,后续的Interceptor和Controller都不会再执行;当返回true时就会继续调用下一个Interceptor的preHandle方法 postHandler() 该方法时在当前请求进行处理之后被调用,前提是preHandler方法的返回值为true时才能被调用,且它会在DispacherServlet进行视图返回渲染之前被调用,所有可以用这个方法对Controller处理之后的ModelAndView对象进行操作 afterCompletion() 该方法将在整个请求结束之后,也就是DispatcherServlet渲染了对应的视图之后执行,前提是preHandler方法的返回值为true才能被调用。可以通过此方法实现一些资源清理、记录日志信息等工作 -
单个拦截器的执行流程
- 在配置文件中如果只定义了一个拦截器,程序将首先执行拦截器类中的 preHandle 方法,如果该方法返回 true,程序将继续执行控制器中处理请求的方法,否则中断执行。如果 preHandle 方法返回 true,并且控制器中处理请求的方法执行后、返回视图前将执行 postHandle 方法,返回视图后才执行 afterCompletion 方法
-
多个拦截器的执行流程
- 在 Web 应用中通常需要有多个拦截器同时工作,这时它们的 preHandle 方法将按照配置文件中拦截器的配置顺序执行,而它们的 postHandle 方法和 afterCompletion 方法则按照配置顺序的反序执行
-
自定义拦截器步骤
- 创建拦截器类实现HandlerInterceptor接口
package com.ccq.interceptor; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class InterceptorDemo implements HandlerInterceptor { @Override // 在目标方法前执行 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("preHandle done..."); // 返回值如果为false,则请求会被拦截于此,true则继续传递 String enter = request.getParameter("enter"); if(enter.equals("yes")) return true; else{ response.setContentType("text/html;charset=utf-8"); response.getWriter().write("no access!"); return false; } } @Override // 在目标方法执行之后,ModelAndView返回之前执行 public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { modelAndView.addObject("username", "xxx"); System.out.println("postHandle done..."); } @Override // 在目标方法执行完毕且视图已经返回后执行 public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("afterCompletion done..."); } }
- 配置拦截器
<!--配置拦截器--> <mvc:interceptors> <mvc:interceptor> <!--指定对哪些资源的访问会被此拦截器拦截--> <mvc:mapping path="/**"/> <bean class="com.ccq.interceptor.InterceptorDemo"/> </mvc:interceptor> </mvc:interceptors>
- 测试拦截器效果
@RequestMapping("/test") public ModelAndView test(ModelAndView mv){ mv.setViewName("test"); System.out.println("test done"); return mv; }
- 创建拦截器类实现HandlerInterceptor接口
Spring MVC异常处理机制
-
在 Spring MVC 应用的开发中,不管是对底层数据库操作,还是业务层或控制层操作,都会不可避免地遇到各种可预知的、不可预知的异常需要处理
-
如果每个过程都单独处理异常,那么系统的代码耦合度高,工作量大且不好统一,以后维护的工作量也很大
-
如果能将所有类型的异常处理从各层中解耦出来,这样既保证了相关处理过程的功能单一,又实现了异常信息的统一处理和维护
-
异常处理思路
- 系统中异常包含两类:预期异常和运行时异常RuntimeException,前者通过捕获异常从而获取异常信息,后者主要通过规范代码开发、测试等手段减少运行时异常的发生
- 系统的Dao、Service、Controller出现的异常都通过throws Exception向上抛出,最后由Spring MVC前端控制器交给异常处理器进行统一异常处理
-
Spring MVC 统一异常处理有以下 3 种方式:
- 使用 Spring MVC 提供的简单异常处理器SimpleMappingExceptionResolver
- 实现 Spring 的异常处理接口 HandlerExceptionResolver 自定义自己的异常处理器
- 使用 @ExceptionHandler 注解实现异常处理
JSON
-
JSON(JavaScript Object Notation,JS对象标记)是一种轻量级的数据交换格式,目前使用特别广泛
-
采用完全独立于编程语言的文本格式来存储和表示数据
-
简洁和清晰的层次结构是的JSON成为理想的数据交换语言
-
已于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率
-
在JavaScript语言中,一切都是对象。因此,任何JavaScript支持的类型都可以通过JSON来表示,例如字符串、数字、对象、数组等
-
JSON语法格式
- 对象表示为键值对,数据由逗号分隔
- 花括号保存对象
- 方括号保存数组
-
JSON键值对是用来保存JavaScript对象的一种方式,和JavaScript对象的写法大同小异,键/值对组合中的键名卸载前面并用双引号包裹,使用冒号分隔,然后紧接值
{"name": "xxx"} {"age": "18"} {"sex": "男"}
-
JSON是JavaScript对象的字符串表示法,它使用文本表示一个JS对象的信息,本质是一个字符串
var obj = {a: 'hello', b: 'world'}; // 这是一个JS对象,注意键名也是可以使用引号包裹的
var json = '{"a": "hello", "b": "world"}' // 这是一个json字符串,本质是一个字符串
- JSON与JavaScript对象互转
- 要实现从JSON字符串转转换为JavaScript对象,使用JSON.parse()方法:
var obj = JSON.parse('{"a":"hello", "b":"world"}'); // 结果为 {a: 'hello', b: 'world'}
- 要实现从JavaScript对象转换成JSON字符串,使用JSON。使用JSON.stringify()方法:
- 要实现从JSON字符串转转换为JavaScript对象,使用JSON.parse()方法:
var json = JSON.stringify({a: 'hello', b: 'world'});
// 结果为'{"a": "hello", "b": "world"}'