11.处理json和HttpMessageConverter
在开发中,我们往往需要服务器返回的数据格式是按照json格式来返回的。
- 处理json-@ResponseBody
package com.zzti.springmvc.json;
public class Dog {
private String name;
private String address;
public Dog() {
}
public Dog(String name, String address) {
this.name = name;
this.address = address;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", address='" + address + '\'' +
'}';
}
}
@Controller
public class JsonController {
@RequestMapping("/getJson")
@ResponseBody
public Dog getJson(){
Dog dog = new Dog("大黄狗","上海浦东");
return dog;
}
}
2. 处理json-@RequestBody
客户端发送json字符串数据,使用@RequestBody将客户端提交的json数据,封装成JavaBean对象,再把这个Javabean以json对象形式返回
@PostMapping("/save")
@ResponseBody
public User save(@RequestBody User user){
System.out.println("user= " + user);
return user;
}
package com.zzti.springmvc.json;
public class Dog {
private String name;
private String address;
public Dog() {
}
public Dog(String name, String address) {
this.name = name;
this.address = address;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", address='" + address + '\'' +
'}';
}
}
注意细节:
- 目标方法正常返回需要的数据,可以是一个对象,也可以是一个集合
- @ResponseBody可以直接写在controller上,这样对所有方法生效
处理JSON-底层实现(HttpMessageConverter)
- 使用HttpMessageConverter将请求信息转化并绑定到处理方法的入参中,或将响应的结果转为对应类型的响应信息,Spring提供了两种途径
a.使用@ResponseBody / @RequestBody对目标方法进行标注
b.使用HttpEntity / ResponseEntity 作为目标方法的入参或返回值 - 当控制器处理方法使用到上述两种方法时,Spring首先根据请求头或响应头的Accept属性选择匹配的HttpMessageConverter,进而根据参数泛型或泛型类型的过滤得到匹配的HttpMessageConverter,若找不到可用的HttpMessageConverter将报错
12. 文件上传和下载
通过返回ResponseEntity的类型,可以实现文件下载的功能
@RequestMapping("/downFile")
public ResponseEntity<byte[]> downFile(HttpSession session) throws Exception {
//先获取到你要下载的文件
InputStream stream = session.getServletContext().getResourceAsStream("WEB-INF/imgs/g1.png");
byte[] bytes = new byte[stream.available()];
stream.read(bytes);
//构建返回的ResponseEntity<byte[]>
HttpStatus status = HttpStatus.OK;//返回成功
//根据http协议告诉浏览器,返回的就是一个文件,浏览器就淡出小窗口
HttpHeaders headers = new HttpHeaders();
/**
* context-type 指示响应内容的格式
* context-disposition 指示如何处理响应内容 (inline[直接在页面显示];attachment[以附件形式下载])
*/
headers.add("Content-Disposition","attachment;filename=g1.png");
ResponseEntity<byte[]> responseEntity = new ResponseEntity<>(bytes, headers, status);
return responseEntity;
}
Spring MVC中默认没有配置MultipartResolver,因此不能处理文件的上传工作,要是实现上传功能,就需要配置MultipartResolver
<!--配置springmvc文件上传解析器-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
</bean>
<!--使用Spring提供的过滤器,处理中文乱码-->
<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>
@RequestMapping("/fileUpload")
public String fileUpload(@RequestParam(value = "file") MultipartFile file,
HttpServletRequest request) throws Exception{
String originalFilename = file.getOriginalFilename();
System.out.println("上传文件名= " + originalFilename);
String filePath = request.getServletContext().getRealPath("WEV-INF/imgs/" + originalFilename);
File saveToFile = new File(filePath);
file.transferTo(saveToFile);
return "success";
}
13. 自定义拦截器
Spring MVC可以使用拦截器对请求进行拦截处理,用户可以自定义拦截器来实现特定的功能,自定义的拦截器必须实现HandlerInterceptor接口。自定义拦截器有三个方法: preHandler() 在业务处理器处理请求之前被调用,在该方法中对用户请求request进行处理;postHandler() 在目标方法处理完请求后执行;afterCompletion() 在完全处理请求后被调用,可以在该方法中进行一些资源清理的操作。
执行流程:
- 如果preHandler(),返回false,则不再执行目标方法,可以在此指定返回页面
- postHandler在目标方法被执行后执行,可以在方法中访问到目标方法返回的ModelAndView对象
- 若preHandler返回true,则afterCompletion(),在渲染视图之后被执行
- 若preHandler返回false,则afterCompletion(),不会被执行
- 在配置拦截器时,可以指定拦截器对哪些请求生效,哪些不生效。
在applicationContext-mvc.xml文件中添加如下配置
<!--配置自定义拦截器-->
<mvc:interceptors>
<!--
第一种方式: 在interceptors配置一个引用,
指向你需要使用的拦截器(对所有的请求都拦截)
-->
<ref bean="myInterceptor01"/>
<mvc:interceptor>
<mvc:mapping path="/hi"/>
<ref bean="myInterceptor01"/>
</mvc:interceptor>
</mvc:interceptors>
package com.zzti.springmvc.interceptor;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 完成一个自定义拦截器,如何配置拦截器和拦截器的运行流程
*/
@Component
public class MyInterceptor01 implements HandlerInterceptor {
/**
* 1. preHandle()在目标方法执行之前被调用
* 2. 返回false,就不会再执行目标方法,可以在此响应请求返回给页面
* 3. 不管返回true,还是false,都会执行当前拦截器之前的拦截器的afterCompletion()
* 不会执行当前拦截器的afterCompletion()
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("======MyInterceptor01 preHandle()===========");
return true;
}
/**
* postHandler在目标方法被执行后执行,可以在方法中访问到目标方法返回的ModelAndView对象
* @param request
* @param response
* @param handler
* @param modelAndView
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("======MyInterceptor01 postHandle()===========");
}
/**
* 1. 若preHandler返回true,则afterCompletion(),在渲染视图之后被执行
* 2. 若preHandler返回false,则afterCompletion(),不会被执行
* 3. 若当前拦截器的下一个拦截器的preHandle()返回false,则在执行下一个拦截器preHandle()后马上被执行
* 可以访问到目标方法中出现的异常
* @param request
* @param response
* @param handler
* @param ex
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("======MyInterceptor01 afterCompletion()===========");
}
}
package com.zzti.springmvc.interceptor;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/interceptor")
public class FurnController {
@RequestMapping("/hi")
public String hi(){
System.out.println("FurnController hi()");
return "success";
}
@RequestMapping("/hello")
public String hello(){
System.out.println("FurnController hello()");
return "success";
}
}
=============测试结果===================
======MyInterceptor01 preHandle()===========
FurnController hi()
======MyInterceptor01 postHandle()===========
======MyInterceptor01 afterCompletion()===========
注意事项与细节:
默认配置是所有的目标方法都拦截,也可以指定拦截目标方法
如下只拦截hi
<mvc:interceptor>
<mvc:mapping path="/hi"/>
<ref bean="myInterceptor01"/>
</mvc:interceptor>
mvc:mapping 支持通配符,同时指定不对哪些目标方法进行拦截(不拦截/hello())
<mvc:interceptor>
<mvc:mapping path="/h*"/>
<mvc:exclude-mapping path="/hello"/>
<ref bean="myInterceptor01"/>
</mvc:interceptor>
14. Spring MVC执行流程
- 前端控制器(DispacherServlet); 是整个流程的控制中心,接收请求,响应结果,相当于转发器
- 处理器映射器(HandlerMapping):根据URL去查找处理器 (Handler),Spring MVC提供了不同的映射器实现不同的映射方式,比如:
配置文件方式、实现接口方式、注解方式;
- 处理器适配器(HandlerAdapter): 会把处理器包装成适配器,这样就可以支持多种类型的处理器
- 处理器(Handler/Controller): 程序员自己处理业务逻辑的
- 视图解析器(ViewResovler): 进行视图解析,多返回的是字符串,进行渲染处理,可以解析成对应的页面,
- 视图(View): view是一个接口,实现类支持不同的view类型(html/jsp/freemarker…),这个也需要程序员自己开发
具体流程步骤如下:
- 用户发送http请求到DispacherServlet,前端控制器收到请求后不自己处理,而是委托给其他的解析器进行处理,作为转发器,进行全局的流程控制
- DispacherServlet会先调用HandlerMapping处理器映射器,HandlerMapping会把请求映射成HandlerExecutionChain处理器执行链对象(包含HandlerInterceptor拦截器(多个)、一个Handler对象)这样很容易添加新的映射策略
- DispacherServlet调用HandlerAdapter处理器适配器,HandlerAdapter将处理器包装成适配器,从而支持多种类型的处理器(适配器设计模式的应用)
- HandlerAdapter调用Handler处理器,处理器中进行一些业务逻辑的实现,HandlerAdapter将会根据适配的结果调用真正的处理器的功能方法,完成功能处理,并返回一个ModeAndView对象(包含模型数据、逻辑视图名)给前端控制器
- DispacherServlet调用ViewResovler视图解析器,ViewResovler把逻辑视图名解析为具体的View
- View会根据传进来的Model(是一个Map数据结构)模型数据进行渲染,支持多种视图技术
- 返回控制权给DispacherServlet,由DispacherServlet返回响应给用户,这样一个流程就结束。