SpringMVC框架第二天
主要内容
1.SpringMVC的响应
2.SSM整合登录案例
3.SpringMVC中作用域对象的使用
4.SpringMVC的自定义视图解析器
5.SpringMVC的上传
6.SpringMVC的下载
一.SpringMVC的响应
1.遇到的问题
在学习了SpringMVC的配置流程以及单元方法请求数据的获取后,我们可以使用SpringMVC搭建一个项目,在单元方法中使用SpringMVC提供的方式来获取请求信息,然后根据功能需求,声明请求处理的逻辑代码,进行请求的处理.当请求处理完成后,我们需要将此次请求的处理结果响应给浏览器,以前我们是自己在Servlet中使用response对象来完成响应的,那么在SpringMVC中如何响应请求的处理结果呢?
2.解决方案
3.具体实现
3.1单元方法返回值为void
代码实现
@RequestMapping("/testReturnVoid")
public void testReturnVoid()throws Exception{
System.out.println("AccountController的testForward方法执行了...");
}
效果展示
总结
在SpringMVC中如果对于当前的控制单元,没有写对应的返回值,这个时候SpringMVC就会找和自己控制单元名称一致的页面展示,没有产生404,需要注意控制单元仍然可以进
3.2转发和重定向
3.2.1.使用ServletAPI方式
代码展示
@RequestMapping("resp")
public void demoResp(HttpServletRequest req,HttpServletResponse response)throws IOException,ServletException{
//请求转发
req.getRequestDispatcher("/index.jsp").forward(req,response);
//重定向
response.sendRedirect(req.getContextPath()+"/success.jsp");
}
效果展示
总结
单元方法的返回值类型设置void.因为使用response对象在单元方法中直接对此次请求进行了响应,不再通过DispatcherServlet了,既然已经响应了,就不需要再给DispatcherServlet返回值了.在单元方法上声明HttpServletResponse形参,来接收此次请求的response对象
3.2.2使用forward关键字完成响应
代码示例
效果展示
总结:
使用通过单元方法的返回值来告诉DispatcherServlet请求转发指定的资源
注意:如果是请求转发,forward关键字可以省略不写的
3.2.3使用redirect关键字完成响应
代码示例
效果展示
总结
使用通过单元方法的返回值来告诉DispatcherServlet重定向指定的资源,注意这个redirect关键字不可以省去
3.2.4使用View视图转发和重定向
代码实现
@RequestMapping("demo3")
public View demo3(HttpServletRequest req){
//转发
View v=newInternalResourceView("/index.jsp");
//重定向
View v = new RedirectView(req.getContextPath()+"/index.jsp");
return v;
}
效果展示
总结
RedirectView中所做的操作,最终的实现是在renderMergedOutputModel中完成实现的,简单来说RedirectView实现了链接的重定向,并且将数据保存到FlashMap中,这样在跳转后的链接中可以获取一些数据
3.2.5使用ModelAndView转发重定向
代码实现
@RequestMapping("/MyCon2")
public ModelAndView demo4(HttpServletRequest req){
ModelAndView mv = new ModelAndView();
//转发一
mv.setViewName("forward:/index.jsp");
//重定向一
mv.setViewName("redirect:/index.jsp");
//转发二
mv.setView(new InternalResourceView("/index.jsp"));
//重定向二
mv.setView(new RedirectView(req.getContextPath()+"/index.jsp"));
return mv;
}
效果展示
总结
ModelAndView中的Model代表模型,View代表视图,这个名称就很好地解释了该类的作用.业务处理器调用模型层处理完用户请求后,把结果数据存储在该类的model属性中,把要返回的视图信息存储在该类的view属性中,然后让该ModelAndView返回该SpringMVC框架
3.3ResponseBody响应json数据
问题
当浏览器发起一个ajax请求给服务器,服务器调用对应的单元方法处理ajax请求.而ajax的请求再被处理完成后,器处理结果需要直接响应.而目前我们在单元方法只给你响应ajax请求,使用的是response对象,需要我们自己将要响应的数据转换为json字符串响应,比较麻烦,而我们一直希望在单元方法中无论是否是ajax请求,都使用return语句来完成资源的响应,怎么办?
解决
既然我们希望使用单元方法的返回值来响应ajax请求的处理结果,而目前DispatcherServlet的底层会将单元方法的返回值按照请求转发或者重定向来处理,所以就需要我们告诉DispatcherServlet,单元方法的返回值不要按照请求转发或者重定向处理,而是按照直接响应处理,将单元方法的返回值直接响应给浏览器
代码示例:
a.导入jackson的jar
b.声明单元方法处理ajax请求,并在单元方法上新增注解@ResponseBody
注意:把我们要响应的数据直接return即可,返回值类型为要return的数据类型
c.在ajax的回调函数中,无需再次使用evla函数将函数响应数据转换为json对象直接使用即可
二.SSM整合登录案例
1.在数据库中创建用户信息表
2.搭建SSM开发环境
2.1使用idea创建登录功能的web项目
2.2在web-inf目录下创建lib文件夹,并导入SSM的jar包
2.3在src下创建MVC的包结构
2.4在src下创建并配置SSM的xml文件
1)applicationcontext.xml
2)db.properties
3)log4j.properties
4)springmvc.xml
5)springmvc.xml
3.实现登录功能
3.1创建登录页面
3.2创建控制器类,并声明处理登录请求的单元方法
3.3创建登录业务代码
3.4创建登录mapper层代码
三.SpringMVC中使用作用域对象完成数据的流转
1.作用域对象复习
1.1PageContext对象
作用域范围:当前jsp页面内有效
1.2request对象
作用域范围:一次请求内
作用:解决了一次请求内的资源的数据共享问题
1.3session对象
作用域范围:一次会话内有效
说明:浏览器不关闭,并且后台的session不失效,在任意请求中都可以获取到同一个
session对象
作用:解决了一个用户不同请求的数据共享问题
1.4application(ServletContext)对象
作用域范围:整个项目内有效
特点:一个项目只有一个,在服务器启动的时候即完成初始化创建无论如何获取都是同一个项目
作用:解决了不同用户的数据共享问题
2.SpringMVC中使用作用域对象流转数据
2.1使用request对象作为其请求转发数据流转的载体
注意:使用方式和原有Servlet中使用方式完全一致,只不过现在需要在单元方法中来使用
代码示例:
2.2.使用session对象作为同一个用户的不同请求的数据流转的载体
注意:使用方式和原有Servlet中使用方式完全一致,只不过现在需要在单元方法中来使用,在单元方法的形参上直接声明session即可
代码示例:
2.3使用application对象作为项目公共数据的载体
注意:application对象的获取,只能我们自己在单元方法中获取,不能使用形参的方式,让DispatcherServlet帮我们获取
代码示例:
3.SpringMVC的Model对象的使用
作用
作为数据流转的载体,SpringMVC官方提供的一个对象
使用
在单元方法上声明Model类型的形参即可
注意:Model对象是由DispatcherServlet创建并作为实参传递给单元方法使用
特点
请求转发:model对象中存储的数据,相当于存储到了request对象中我们在jsp中直接按照request对象作用域取值的方式来获取数据即可
重定向:在重定向中,会将第一次请求中model对象的数据作为第二次请求的请求数据携带,第一次请求的model对象销毁.只能携带基本类型的数据
代码示例
a.请求转发中使用Model对象作为数据流转的载体
b.重定向中使用Model对象作为数据流转的载体
四.SpringMVC的自定义视图解析器
1.SpringMVC视图解析器
问题:
目前我们在SpringMVC的响应中,虽然我们直接在单元方法中返回字符串数据来表明请求转发或者重定向的资源,但是DispatcherServlet的底层默认使用ModelAndView来完成视图资源的解析和跳转.但是ModelAndView这个视图解析器比较死板ModelAndView会将单元方法返回的字符串,根据关键拆分后来完成资源的跳转,比如:"forward:/index.jsp",那么ModelAndView就会直接请求转发index.jsp资源.但是我们在实际生产环境中往往会有很多特殊的需求,这样ModelAndView就无法满足了,比如,我们在项目下创建一个a文件夹,在a文件夹下创建b子文件夹,这样我们如果在单元方法中请求转发c文件夹中的资源,返回值路径就会很麻烦:
"forward:/a/b/c/index.jsp"
"forward:/a/b/c/page.jsp"
"forward:/a/b/c/sel.jsp"
而且后期一旦资源路径的文件夹名字发生变更,修改起来也会非常的麻烦
解决:
使用自定义视图解析器,而我们自定义的视图解析器除了可以让我们根据需求配置一些路径上的常量参数以外,还需要具备ModelAndView的逻辑.所以,我们自己需要从头创建代码,以及我们自己需要的部分常量参数.但是ModelAndView的逻辑我们是不知道的,那么能不能让SpringMVC官方提供一个支持部分数据自定义的视图解析器呢,答案是可以的.我们可以通过配置文件来配置一些我们在视图解析器中的常量数据
实现:
internalResourceViewResolver
概念:
因为InternalResourceViewResolver可以让我们通过配置文件来设置一些常量参数,所以我们将该视图解析器称为自定义视图解析器
代码示例
a.SpringMVC.xml的配置
b.测试单元方法实例代码
2.目前项目资源的舍命位置和访问中存在的问题
问题
目前我们在完成功能开发时,会将项目相关的页面资源及静态资源直接声明在web目录下,或者web目录下的子文件夹中.而web目录及其声明的子目录中的资源,在浏览器中是可以直接被访问到的.也就是说,只要我们知道某个资源的URL地址,在浏览器中是可以直接发起请求访问该资源的,极不安全
解决
加入有一天你变的很有钱,为了将钱进行保值,你就将钱都买了古董.我们将钱进行保值,你就将钱都买了古董.我们将买的古董放在家里的客厅里面,但是我们的朋友只要知道家里的地址,就可以过来把玩古董.后来因为客厅的古董实在是太多了,于是呢,我们将古董放在了厢房中一部分.而厢房我们也是对外开放的,也就说朋友来了,可以直接进入厢房来把玩.但是对于一些非常重要的古董,我们希望补鞥呢直接对外进行开放,将它们给隐藏起来.这样有朋友来了,我们可以根据这位朋友的人品.来决定是否让他来欣赏重要的古董.如果我们觉得不靠谱,就算朋友知道我们有该古董,但是我们仍然是可以拒绝的,说我没有这个东东.假如靠谱,我们可以将古董从密室中拿出来给朋友欣赏.也就是说,我们放在密室中的古董,我们自己是可以把控这些古董的访问权限的.而客厅和厢房中的我们无法把控,因为只要朋友来就可以直接访问
实现方案:
在我们的web项目中造一间密室,将重要的资源放到密室中.而密室是对外不开放的,也就是说密室中的资源必须通过tomcat服务器的内部转发才能进行访问.就算浏览器听说项目有这样密室,并且密室中也有浏览器想要的资源,浏览器发起的请求地址是正确的,但是我们可以在服务器端死不承认,我们没有这个资源,在后台给浏览器响应404.如果我们觉得靠谱的请求,我们就在服务器端请求转发资源给浏览器使用
其实我们的web项目在创建的时候就自动的在web目录下创建了密室,就是WEB-INF文件夹,也就是说WENB-INF文件夹下的资源浏览器是无法直接访问的,必须通过内部请求转发才能访问,如图所示
代码示例
3.使用自定义视图解析器优化资源跳转路径
问题
我们在将重要的项目资源放在WEB-INF文件夹中后,只能通过内部的请求转发来访问资源.如果WEB-INF下的资源较多,造成请求转发的路径书写麻烦,而且后期一旦资源的目录发生变更,修改起来hi非常的麻烦,怎么办?
解决
我们真正想在单元方法中想写的是资源的名字,而请求转发WEB-INF下的资源路径是公共的,每次都要写.而刚好我买的自定义视图解析器就是专门用来进行请求转发的,而且可以设置转发资源的公共前缀和后缀信息.所以,我们可以使用自定义视图解析器来完成WEB-INF下的资源的请求转发
实现方案:
a.SpringMVC.xml中配置自定义视图解析器
b.声明单元方法请求转发,注意:返回值直接为资源名
4.使用rstful声明公共单元方法请求转发WEB-INF下的资源
问题:
在项目使用了自定义视图解析器后,可以在单元方法中简单的返回同一个WEB-INF下的资源的名字就可以资源的请求转发了.但是我们的资源是非常多的,但是我们的单元方法的返回值只能有一个.总不能我们给WEB-INF下的每个资源都声明一个对应的单元方法来完成请求转发把,太麻烦了
解决:
根据请求,请求转发WEB-INF下的资源的单元方法是肯定要声明的.我们可以声明一个公共的单元方法,该单元方法不参与请求的逻辑处理,只负责根据请求转发WEB-INF下的资源
实现:
5.重新配置springmvc.xml文件中的资源放行
五.SpringMVC的上传
1.上传的功能需求
随着我们互联网的发展,我们的用户从直接访问网站获取信息.变为希望将自己本地的资源发送给服务器,让服务器提供给 其他人使用或者查看.还有部分的用户希望可以将本地的资源上传服务器存储起来,然后在其他的电脑中可以通过访问网站来获取上传的资源,这样用户就可以打破空间的局限性,再任何时候只要有网有电脑就可以对自己的资源进行操作,比如:云存储,云编辑
2.上传的原理图
3.上传的前台实现
3.1如何在页面中显示一个按钮
用户可以点击该按钮后选择本地要上传的文件在页面中使用input标签,type值设置为"file"即可
3.2确定上传请求的发送方式
上传成功后的响应结果在当前页面显示,使用ajax请求来完成资源的发送
3.3上传请求的请求数据及其数据格式
请求数据:
上传的文件本身普通数据:用户名,Id,密码等,建议上传功能中不携带除上传资源以外的数据
数据格式
传统的请求中,请求数据是以键值对的格式来发送给后台服务器的,但是在上传请求中,没有任何一个=键可以描述上次的数据,因为数据本身是非常大的键就相当于一个变量,我们使用一个变量存储一个10g的电影显然是不可能的.在上传请求中,将请求数据以二进制流的方式发送给服务器
3.4在ajax中如何发送二进制流数据给服务器
1)创建FormData的对象,将请求数据存储到该对象中发送
2)将processData属性的值设置为false,告诉浏览器发送对象请求数据
3)将contentType属性的值设置为false,设置请求数据的类型为二进制类型
4)正常发送ajax即可
3.5上传成功后后台服务器应该响应什么结果给浏览器
并且浏览器如何处理后台服务器处理完成后,响应一个json对象给浏览器,实例格式如下:
{state:true
msg:"服务器繁忙",
url:"上传成功的资源的请求地址"
}
3.6代码示例(注册功能,用户头像)
<%--声明js代码域--%>
<script type="text/javascript">
/***资源上传功能实现**/
$("#btnUpload").click(function(){
//获取要上传的文件资源
var file = $("#file")[0].files[0];
//创建FormData对象存储要上传的资源
var formData = new FormData();
formData.append("photo",file);
//发起ajax请求完成资源上传
$.ajax({
type:"post",//使用post类型的请求
data:formData,//请求数据
url:"regUpload",//请求地址
processData:false,
cententType:false,
success:function(data){//回调函数
//将响应数据转换为json对象
eval("ver obj="+data);
//判断
if(obj.status==true){
alert("上传成功");
}else{
alert(obj.msg);
}
}
})
})
</script>
4.上传的后台实现
4.1如何在单元方法中获取上传请求的请求数据
传统的请求中,上传的数据是键值对数据,我们可以直接使用request对象中的getParameter("键名")来获取请求数据,或者在单元方法上声明形参来接收DispatcherServlet传递的请求数据.而在上传请求中,请求数据是二进制流数据,tomcat服务器在接收到请求后,仍然将请求数据封装到request对调DispatchetServlet处理请求,并将存储了上传请求数据的request对象作为实参传递给DispatcherServlet,而DispatcherServlet会根据请求调用对应的单元方法来处理请求,而这个时候因为request中存储的是二进制请求数据我们就无法再使用req.getParameter来获取请求数据了.我们希望DispatcherServlet将request对象中的二进制数据进行解析,然后将解析后的结果传递给单元方法处理.也就是说DispatcherServlet会调用一个工具类来完成二进制数据的解析,所以需要我们在springmvc.xml文件中配置上传解析的bean对象给DispatcherServlet使用.其实说白了就是需要在springmvc.xml文件中配置SpringMVC官方提供的上传解析bean即可.我们正常的在单元方法上.声明形参直接接受解析后的结果完成请求处理即可
4.2将上传的资源存储到服务器的硬盘中
1)确定资源要写入到硬盘中的存储路径
2)确定文件存储的文件名,每次存储的文件名都是唯一的
3)使用IO流将问阿金输出到服务器硬盘中存储起来
4.3将上传的结果响应给浏览器
1)设置单元方法的返回值类型为void
2)使用response对象完成直接响应
3)响应一个json字符串给浏览器
{state:true
msg:"服务器繁忙",
url:"上传成功的资源的请求地址"
}
4.4代码实例
a.上传解析bean的配置
<!--配置长传解析bean:给DispatcherServlet使用,调用该bean对象完成request对象中的上传数据的解析-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"></bean>
b.单元方法代码示例:
//声明单元方法处理文件上传请求
/**
形参MultipartFile
该形参是用来接收DispatcherServlet解析request对象后存储了文件二进制数据的对象
形参的名字必须是上传请求中的文件的键名
@param phone
@param response
*/
@RequestMapping("regUpload")
public void regUpload(MultipartFile photo,HttpServletResponse response,HttpServlet request)throws IOException{
//1.确定问阿金存储路径
//使用ServletContext对象动态获取项目根目录下的upload文件夹的路径.作为资源存储路径
String path = request.getServletContext().getRealPath("/upload");
System.out.println(path);
//2.确定文件存储的名字
//获取问阿金的原始名a.b.c.jpg
String oldName = photo.getOriginalFilename();
//获取文件存储的后缀名
String newName = UUID.randomUUID()+""+suffix;
//3.完成存储
//创建file对象存储资源路径
File f=newFile(path);
if(!f.exists()){
f.mkdirs();//创建存储路径
}
//输出存储
photo.transferTo(new File(f,newName));
//4.响应结果
//创建UploadResult对象存储响应数据
UploadResult uploadResult = new UploadResult(true,"",newName);
//将uploadResult对象转换为json对象
String jsonStr = new Gson().toJson(uploadResult);
//直接响应
response.getWrite().write(jsonStr);
}
c.自定义异常解析器
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="utf-8"></property>
<!--设置上传文件的大小-->
<!--<property name="maxUploadSize" value="20000"></property>-->
</bean>
<!--异常解析器-->
<bean id="exceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMapping">
<props>
<!--key异常全路径,这个异常一定是Spring抛出的value:出现异常之后跳转的页面-->
<prop key="org.springframework.web.multipart.MaxUploadSizeExceededException">
redirect:/error.jsp
</prop>
</props>
</property>
</bean>
六.SpringMVC跨服务器方式的文件上传
1.分服务器上传作用
a.数据库服务器:运行我们的数据库
b.缓存和消息服务器:负责处理大并发访问的缓存和消息
c.问阿金服务器:负责存储用户上传文件的服务器
d.应用服务器:负责部署我们的应用
e.在实际开发中,我们会有很多处理不同功能的服务器.(注意:此处说的不是服务器集群)
f.总结:分服务器处理的目的是让服务器各司其职,从而提高我们项目的运行效率
2.分服务器工作示意图
3.具体实现
3.1导入jar包
3.2编写控制器实现上传图片
@Controller("fileUploadController")
public class FileUploadController2{
public static final String Fileserverurl="http://localhost:8080/day06_spring_image/uploads/";
//文件上传,保存文件到不同服务器
@RequestMapping("/fileUpload2")
public String testResponserJson(String picname,MultipartFile uploadFile)throws Exception{
//定义文件名
String fileName="";
//1.获取原始文件名
Stirng uploadFileName = uploadFile.getOriginalFilename();
//2.截取文件扩展名
Stirng extendName = uploadFileName.substring(uploadFileName.lastIndexOf(".")+1,uploadFileName.length());
//3.把文件加上随机数,防止文件重复
String uuid = UUID.randomUUID.toString();
//4.文件名
fileName = uuid+"."+extendName;
System.out.println(filename);
//5.创建sun公司提供的jersey包中的Client对象
Client client = Client.create();
//6.指定上传文件的地址,该地址是web路径
WebResource resource = client.resource(FILESERVERURL+fileName);
//7.实现上传
String result = resource.put(String.class,uploadFile.getBytes());
return "success";
}
}
七.SpringMVC的下载
1.下载的基本流程
文件的上传时将用户本地的资源发送到服务器,让服务器存储到其硬盘中的过程.而下载和上传正好是相反的过程.下载是用户发起请求,请求要下载的资源.服务器根据请求,将其硬盘中的文件资源发送给浏览器的过程
2.下载的请求数据
用户通过浏览器发起下载请求,服务器在接收到请求后,根据当前请求的用户信息,去数据库中获取当前用户要下载的资源的文件路径,然后服务器再去其硬盘中读取对应的文件,将文件响应给浏览器,基于此过程,下载请求的请求数据为简单的下载,文件的路径直接作为一个字段存储在用户信息表中用户的ID
[1]下载的后台实现
1.创建单元方法处理下载请求
2.根据请求获取要下载的资源的流对象
3.读取文件并将资源响应给浏览器
[2]下载的实例代码
//声明单元方法:处理下载请求
@RequestMapping("downFile")
public void downFile(String filename,HttpServletResponse response,HttpServletRequest request)throws IOException{
//设置下载资源的MIME类型?
//设置响应头,告诉浏览器下载的资源需要存储到客户端的硬盘中,而不是解析打开
response.setHeader("Content-Disposition","attachment;filename="+filename);
//1.获取要下载的资源的流对象
String path = request.getServletContext().getRealPath("/upload");
//获取文件的二进制数据
byte[] bytes = FileUtils.readFileToByteArray(new File(path,filename));
//2.响应浏览器
//获取输出流对象
ServletOutputStream outputStream = response.getOutputStream();
//响应资源
outputStream.write(bytes);
}