一、springMVC作用域对象完成数据的流转
(一)通过request
/**
* 使用request对象作用请求转发的数据流转的载体
* 注意:
* 使用request对象作为请求转发的数据流转的载体,需要在单元方法
* 上声明形参,类型为HttpServletRequest来接收DispactherServlet传递
* 的request对象。使用方式和原有我们在Servlet中的使用方式完全一致。
*
*/
@RequestMapping("testReq")
public String testReq(String uname, Integer age, HttpServletRequest request){
//处理请求
System.out.println("使用req对象作为请求转发数据流转的载体:"+uname+age);
//响应结果
//使用request的作用域存储流转数据
request.setAttribute("str","我是request对象");
//请求转发
return "forward:/req.jsp";
}
(二)通过session
/**
* 使用session作为同一个用户的不同请求的数据流转的载体
* 使用:
* 在单元方法上声明形参HttpSession来接收DispatcherServlet传递的session对象
* session的使用方式和以前相同
*/
@RequestMapping("testSession")
public String testSession(String uname, Integer age, HttpSession session){
//处理请求
System.out.println("使用session作为数据流转的载体:"+uname+age);
//响应结果
//将数据存储到session对象中
session.setAttribute("str","我是session对象");
return "redirect:/session.jsp";
}
(三)通过application
像request和session都是可以通过形参直接获得的,但是application作用域对象springmvc并不会给我们自动装配,需要通过request获取
/**
* 使用ServletContext对象作为数据流转的载体
* 注意:
* 使用ServletContext对中存储的数据为所有用户都可以使用的共享数据。
*/
@RequestMapping("testApplication")
public String testApplication(String uname, Integer age,HttpServletRequest request){
//处理请求
System.out.println("使用ServletContext作为数据流转的载体:"+uname+age);
//响应结果
//获取ServletContext对象
ServletContext context=request.getServletContext();
context.setAttribute("str","我是ServletContext对象");
return "redirect:/session.jsp";
}
(四)通过Model对象
Model对象是由DispathcerServlet创建并作为实参传递给单元方法使用的。
特点:
- 请求转发: model对象中存储的数据相当于存储到了request对象中。
- 重定向: 重定向中会将model对象的数据作为第二次请求的参数携带,并且将model对象销毁,model对象只能携带基本数据类型。
/**
* 使用Model对象作为请求转发数据流转的载体
* 特点:
* 和request对象的使用方式一样
* 使用:
* 在单元方法上声明形参Model
* 存储数据:
* model.addAttribute("键名","数据");
*/
@RequestMapping("testModel")
public String testModel(String uname, Integer age, Model model){
//处理请求
System.out.println("使用Model对象作为数据流转的载体");
//响应结果
//将数据存储到Model对象中
model.addAttribute("str","我是Model对象作为请求转发数据流转的载体");
//请求转发
return "forward:/model.jsp";
}
二、SpringMVC对Ajax请求的处理
在上述所说的单元方法中,单元方法的返回值会DispathcerServlet当作重定向或者请求转发处理, 如果我们想对AJAX请求做出响应该怎么做呢?
(一)导入jar包
这些jar包是将单元方法返回的值变成json对象,方便处理ajax请求。
(二)代码示例
//声明单元方法:处理ajax请求,响应String字符串数据
@ResponseBody
@RequestMapping("testAjax")
public String testAjax(String uname, Integer age){
//处理请求
System.out.println("单元方法,处理ajax请求:"+uname+age);
//响应结果
//直接响应
return "aa";
}
//声明单元方法:处理ajax请求,响应java对象
@ResponseBody
@RequestMapping("testAjax2")
public User testAjax2(String uname, Integer age){
//处理请求
System.out.println("单元方法,处理ajax请求:"+uname+age);
User user=new User(1,"张三","123");
//响应结果
//直接响应
return user;
}
并且在ajax回调函数中,无需使用JSON.parse()函数将响应数据转换为json对象,直接使用即可
$(function(){
$("#btn").click(()=>{
$.get("testAjax",{uname:"zhangsan",age:18},(data)=>{
alert(data.name)
})
})
})
三、springMVC自定义视图解析器
(一) springMVC的视图解析器
springMVC的视图解析器是springMVC封装好的一个类,它可以根据单元方法的返回值来判断是转发还是重定向,在老版本中是不能直接返回字符串而是应该返回InternalResourceView(请求转发)和RedirectView(重定向),在新版springMVC中,返回的值最终都会通过ModelAndView(请求转发和重定向)来处理。
(二)springMVC自定义视图解析器
当我们请求转发的资源是多重嵌套的时候,比如forward:/aa/bb/cc/dd/home.html的时候,我们请求转发就会非常的麻烦,我们可以想到可不可以用一个自己的视图解析器来帮我们提前处理好嵌套关系,这样我们只需要写home.html就可以了不需要写繁琐的嵌套路径,并且当路径发生改变的时候只需要该视图解析器里的路径就行,既方便又好维护。
实现:
springMVC给我们提供了一个自定义的视图解析器InternalResourceViewResolver,支持部分参数自定义,但是它只能够处理请求转发,无法处理重定向。
使用:
- 在springmvc.xml进行配置
- 在单元方法中按指定格式返回(不能用forward或redirect关键字)
注意:若返回值带有forward或redirect关键字则DispatcherServlet会使用ModelAndView来进行处理,不会使用自定义视图解析器
springmvc.xml
<?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:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
">
<!--配置注解扫描-->
<context:component-scan base-package="com.bjsxt.controller"></context:component-scan>
<!--配置注解解析器-->
<mvc:annotation-driven></mvc:annotation-driven>
<!--配置静态资源放行-->
<mvc:resources mapping="/js/**" location="/js/"></mvc:resources>
<mvc:resources mapping="/css/**" location="/css/"></mvc:resources>
<mvc:resources mapping="/images/**" location="/images/"></mvc:resources>
<!--配置自定义视图解析器-->
<bean id="resourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/a/b/c"></property>
<property name="suffix" value=".jsp"></property>
</bean>
</beans>
单元方法
@RequestMapping("testForward")
public String testForward(){
//处理请求
//响应结果
//请求转发
return "/page";
}
四、利用自定义视图解析器或静态资源放行来访问WEB-INF下的资源
(一)场景:
我们有些重要的页面不能通过浏览器直接访问,必须要求用户登录后完成一些信息的验证才能访问时我们该如何处理?我们需要把这些重要的页面“藏”起来,不能让用户通过url直接访问,刚好,tomcat服务器就给我们提供了这个功能,在WEN-INF下的资源,浏览器是无法直接访问的,必须通过我们内部的请求转发,才可以访问到这些资源。
但是在项目中,WEB-INF下的资源会很多,我们的资源地址很可能会发生改变,如果在项目后期,资源的路径发生改变,对程序员来说是非常痛苦的,于是我们可以借助我们刚刚学的自定义视图解析器,来解决这个问题
(二)使用restful声明公共单元方法请求转发WEB-INF下的资源
场景:
在项目中使用了自定义视图解析器后,可以在单元方法中简单的返回一个 WEB-INF下的资源的名字就可以完成资源的请求转发了,美滋滋。但是当我们WEB-INF下有很多页面的时候,我们不可能为每一个页面配置一个单元方法去返回它,那怎么办呢?
解决:
根据请求,请求转发WEB-INF下的资源的单元方法是肯定要声明的。我们可以声明一个公共的单元方法,该单元方法不参与请求的逻辑处理,只负责根据请求转发WEB-INF下的资源。
@RequestMapping("{url}")
public String getPage(@PathVariable String url)(
return url;
}
(三) 利用静态资源放行请求WEB-INF下的资源
重新配置资源放行
<mvc:resources mapping="/js/**" location="/WEN-INF/js/"></mvc:resources>
<mvc:resources mapping="/css/**" location="/WEB-INF/css/"></mvc:resources>
<mvc:resources mapping="/images/**" location="/WEB-INF/images/"></mvc:resources>
(四)小结
- jsp请求不经过DispatcherServlet,就是说jsp请求不经过单元方法也就是jsp不受restful的影响
- 静态资源放行的优先级低于单元方法的请求匹配,所以注意静态资源放行的url和单元方法的url不要冲突
五、springMVC的文件上传
在之前学习jsp的时候,我曾经做过一个文件上传的demo,可以说是及其繁琐,开发效率极低,但是在springmvc中,提供给我们了一个上传解析类CommonsMultipartResolver,需要在springmvc配置它的bean。
原理
导包
使用CommonsMultipartResolver需要导入一下的jar包
- commons-fileupload-1.3.2.jar
- commons-io.jar
代码
由于文件的网络传输一般是使用二进制的,所以我们用MultipartFile接收二进制流文件
后端代码
@RequestMapping("uploadHeadImg")
public UploadResult uploadHeadImg(MultipartFile photo, HttpServletRequest request) {
//获取上传文件后缀名
String oldName = photo.getOriginFilename();
String suffix = oldName.substring(oldName.lastIndexOf("."));
//设置新的文件名(避免文件名重复)
String newName = UUID.randomUUID() + "" + suffix;
//上传文件夹路径
ServletContext sc = request.getServletContext();
String path = sc.getRealPath("/upload");
//声明上传文件夹(若不存在则创建)
File dir = new File(path);
if(!dir.exist()){
dir.mkdirs()
}
//将文件上传至文件夹
try {
photo.transferTo(new File(dir,newName));
} catch (IOException e) {
e.printStackTrace();
return new UploadResult("error","");
}
return new UploadResult("ok","upload/"+newName); //写绝对路径更好
}
前端代码
$("#btn").click(function () {
//获取存储了选择的文件信息的对象
var reqData=$("#photo")[0].files[0];
//使用FormData对象,告诉浏览器获取资源的二进制数据作为请求数据
var fm=new FormData();//存储在FormData对象中的资源浏览器会以二进制的形式发送给服务器
fm.append("photo",reqData);//一个append方法表示一个要转换的资源
//发起Ajax请求
$.ajax({
url:"uploadHeadImg",
type:"post",
processData:false,//设置false
contentType:false,//设置false,将请求数据类型设置为multipart/form-data,以请求头的形式来告诉服务器
data:fm,
success:function (data) {//成功的回调函数
alert(data);
if(data.msg == 'ok'){
alert("上传成功");
//将上传成功的资源完成回显
$("#imgBack").attr("src",data.url).css("display","");
//将上传成功的图片的URL地址记录下来
$("#img").val(data.url)
}else{
alert("上传失败");
}
}
})
})
六、文件下载
情景:用户需要下载他自己上传的文件
思路:根据用户的信息查询文件的路径实现文件下载
注意点:
1. 设置请求头response.setHeader(“Content-Disposition”, “attachment;fileName=”+ uri.substring(uri.lastIndexOf("/")+1));
2. 请求不能是ajax
@RequestMapping("downFile")
public void userDown(HttpServletResponse response,HttpSession session,HttpServletRequest request,String fileName){
//得到用户图片存储uri
User user = (User) session.getAttribute("user");
String uri = user.getHeadImg(); // upload/f2dfb00a-560b-4487-bb79-fcdcbd0de689.png
response.setHeader("Content-Disposition", "attachment;fileName="+ uri.substring(uri.lastIndexOf("/")+1));
//得到项目绝对路径
ServletContext sc = request.getServletContext();
//得到文件绝对路径
String path = sc.getRealPath("/"); // D:\java\springMVC-02\target\springMVC-02-1.0-SNAPSHOT\
System.out.println(uri);
try {
//得到文件流
byte[] bytes = FileUtils.readFileToByteArray(new File(path,uri));
System.out.println("bytes:"+ Arrays.toString(bytes));
ServletOutputStream outputStream = response.getOutputStream();
outputStream.write(bytes);
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}