SpringMVC学习笔记03
1.Ajax研究
1.1 简介
什么是Ajax
- AJAX=Asynchronous JavaScript and XML(异步的JavaScript和XML)
- AJAX是一种无需重新加载整个网页的情况下,能够更新部分网页的技术
- AJAX不是一种新的编程语言,而是一种用于创建更好更快以及交互性更强的WEB应用程序的技术
- 在2005年,Google通过其Google Suggest使AJAX变得流行起来,Google Suggest能够自动帮助你完成搜索单词
- Google Suggest使用AJAX创造出动态性极强的web页面,当你在Google的输入框输入关键字时,JavaScript会把这些字符发送到服务器,然后服务器会返回一个搜索建议的列表
- 就和国内百度的搜索框一样
- 传统的网页(即不用ajax技术的网页),想要更新内容或者提交一个表单,都需要重新加载整个网页
- 使用ajax技术的网页,通过在后台服务器进行少量的数据交换,就可以实现异步局部更新
- 使用ajax,用户可以创建接近本地桌面应用的直接,高可用,更丰富,更动态的web用户页面
伪造Ajax
我们可以使用前端的一个标签来伪造一个ajax的样子,iframe标签
- 新建一个module:springmvc-06,导入web支持
- 编写一个ajax-iframe.html 使用iframe测试,感受下效果
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>jcsune</title></head><body><script type="text/javascript">window.onload=function () {var myDate=new Date();document.getElementById('currentTime').innerText=myDate.getTime();};function LoadPage(){var targetUrl=document.getElementById('url').value;console.log(targetUrl);document.getElementById("iframePosition").src=targetUrl;}</script><div><p>请输入要加载的地址:<span id="currentTime"></span></p><p><input id="url" type="text" value="https://www.baidu.com/"/><input type="button" value="提交" onclick="LoadPage()"/></p></div><div><h3>加载页面位置:</h3><iframe id="iframePosition" style="width:100%;height:500px"></iframe></div></body></html>
- 使用idea开浏览器测试
- 利用Ajax可以做:
- 注册时,输入用户名自动检测用户是否存在
- 登录时,提示用户名密码错误
- 删除数据行时,将行ID发送到后台,后台在数据库中删除,数据库删除成功后,在页面DOM中将数据行也删除
- ……
1.2 JQuery.ajax
纯js原生实现Ajax这里不做介绍,有关知识可以了解下JS原生XMLHttpRequest
Ajax的核心是XMLHttpRequest对象(XHR) XHR为向服务器发送请求和解析服务器响应提供了接口,能够以异步方式从服务器获取新数据
JQuery提供多个与AJAX有关的方法
通过JQueryAJAX方法,你能够使用HTTP Get 和HTTP Post从远程服务器上请求文本、HTML、XML、或JSON,同时你能够把这些外部数据直接载入网页的被选元素中
JQuery不是生产者,而是大自然的搬运工
JQuery Ajax本质就是XMLHttpRequest,对他进行了封装,方便调用
来一个简单的测试,使用最原始的HttpServletResponse处理,最简单,最通用
- 配置web.xml和springmvc的配置文件
web.xml
<?xml version="1.0" encoding="UTF-8"?><web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"version="4.0"><servlet><servlet-name>springmvc</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>classpath:applicationContext.xml</param-value></init-param><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>springmvc</servlet-name><url-pattern>/</url-pattern></servlet-mapping><filter><filter-name>encoding</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>encoding</filter-name><url-pattern>/*</url-pattern></filter-mapping></web-app>
applicationContext.xml 引入jQuery时必须配置静态资源过滤
<?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:context="http://www.springframework.org/schema/context"xmlns:mvc="http://www.springframework.org/schema/mvc"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/mvchttps://www.springframework.org/schema/mvc/spring-mvc.xsd"><!-- 自动扫描指定的包,下面所有注解类交给IOC容器管理 --><context:component-scan base-package="com.jcsune.controller"/><!--静态资源过滤--><mvc:default-servlet-handler /><mvc:annotation-driven><mvc:message-converters register-defaults="true"><bean class="org.springframework.http.converter.StringHttpMessageConverter"><constructor-arg value="UTF-8"/></bean><bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"><property name="objectMapper"><bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean"><property name="failOnEmptyBeans" value="false"/></bean></property></bean></mvc:message-converters></mvc:annotation-driven><!-- 视图解析器 --><bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"id="internalResourceViewResolver"><!-- 前缀 --><property name="prefix" value="/WEB-INF/jsp/" /><!-- 后缀 --><property name="suffix" value=".jsp" /></bean></beans>
- 编写一个AjaxController
package com.jcsune.controller;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletResponse;import java.io.IOException;@Controllerpublic class AjaxController {@RequestMapping("/t1")public void ajax1(String name, HttpServletResponse response) throws IOException {if("admin".equals(name)){response.getWriter().print("true");}else{response.getWriter().print("false");}}}
- 导入jQuery,可以使用在线的CDN,也可以下载导入
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script><script src="${pageContext.request.contextPath}/statics/js/jquery-3.5.1.min.js"></script>
- 编写index.jsp测试
<%@ page contentType="text/html;charset=UTF-8" language="java" %><html><head><title>$Title$</title><script src="${pageContext.request.contextPath}/statics/js/jquery-3.5.1.js"></script><script>function a() {$.post({url:"${pageContext.request.contextPath}/t1",data:{"name":$("#username").val()},success:function (data,status) {alert(data);alert(status);}});}</script></head><body><%--失去焦点的时候,发起一个请求(携带信息)到后台--%>用户名:<input type="text" id="username" onblur="a()"></body></html>
- 启动Tomcat测试,打开浏览器的控制台,当鼠标离开输入框时,可以看出发出了一个ajax的请求,是后台返回给我们的结果!测试成功

1.3 springmvc实现
实体类User
package com.jcsune.pojo;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;//需要导入lombok@Data@AllArgsConstructor@NoArgsConstructorpublic class User {private String name;private int age;private String sex;}
获取一个集合对象,展示到前端页面
@RequestMapping("/t2")public List<User> ajax2(){List<User> list = new ArrayList<User>();list.add(new User("刘也一号",23,"男"));list.add(new User("刘也二号",23,"男"));list.add(new User("刘也三号",23,"男"));return list;//由于@RestController注解,将list转成json格式返回}
前端页面:test.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %><html><head><title>Title</title></head><body><input type="button" id="btn" value="获取数据"/><table width="80%" align="center"><tr><td>姓名</td><td>年龄</td><td>性别</td></tr><tbody id="content"></tbody></table><script src="${pageContext.request.contextPath}/statics/js/jquery-3.5.1.js"></script><script>$(function () {$("#btn").click(function () {$.post("${pageContext.request.contextPath}/t2",function (data) {console.log(data)var html="";for (var i = 0; i <data.length ; i++) {html+= "<tr>" +"<td>" + data[i].name + "</td>" +"<td>" + data[i].age + "</td>" +"<td>" + data[i].sex + "</td>" +"</tr>"}$("#content").html(html);});})})</script></body></html>
成功实现了数据回显,可以体会下Ajax的好处
1.4 注册提示效果
在这里测试一个小demo,思考一下我们平时注册的时候,输入框后面的实时提示怎么做到的,如何优化
Controller
@RequestMapping("/t3")public String ajax3(String name,String pwd){String msg="";if(name!=null){//admin 这些数据应该在数据库中查if("admin".equals(name)){msg="ok";}else{msg="用户名有误";}}if(pwd!=null){//admin 这些数据应该在数据库中查if("123456".equals(pwd)){msg="ok";}else{msg="密码有误";}}return msg;}
前端页面:login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %><html><head><title>ajax</title><script src="${pageContext.request.contextPath}/statics/js/jquery-3.5.1.js"></script><script>function a1(){$.post({url:"${pageContext.request.contextPath}/t3",data:{'name':$("#name").val()},success:function (data) {if (data.toString()=='ok'){$("#userInfo").css("color","green");}else {$("#userInfo").css("color","red");}$("#userInfo").html(data);}});}function a2(){$.post({url:"${pageContext.request.contextPath}/t3",data:{'pwd':$("#pwd").val()},success:function (data) {if (data.toString()=='ok'){$("#pwdInfo").css("color","green");}else {$("#pwdInfo").css("color","red");}$("#pwdInfo").html(data);}});}</script></head><body><p>用户名:<input type="text" id="name" onblur="a1()"/><span id="userInfo"></span></p><p>密码:<input type="text" id="pwd" onblur="a2()"><span id="pwdInfo"></span></p></body></html>
测试效果:动态请求响应,局部刷新
2.拦截器+文件上传下载
2.1 拦截器
概述
SpringMVC中的处理器拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理,开发者可以自己定义一些拦截器来实现特定的功能
过滤器与拦截器的区别:拦截器是AOP思想的具体应用
过滤器
- servlet规范中的一部分,任何java web工程都可以使用
- 在url-pattern中配置了/*之后,可以对所有要访问的资源进行拦截
拦截器
- 拦截器是SpringMVC框架自己的,只有使用了SpringMVC框架的工程才可以使用
- 拦截器只会拦截访问的控制器方法,如果访问的是jsp/html/css/image/js是不会进行拦截的
自定义拦截器
想要自定义拦截器,必须实现HandlerInterceptor接口
- 新建一个module,springmvc-07,添加web支持
- 配置web.xml和applicationContext.xml文件
- 编写一个拦截器
package com.jcsune.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 MyInterceptor implements HandlerInterceptor {//在请求处理的方法之前执行//如果返回true执行下一个拦截器//如果返回false就不执行下一个拦截器public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("----------处理前-----------");return true;}//在请求处理方法执行之后进行public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("----------处理后-----------");}//在dispatcherServlet处理后执行,做清理工作public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("----------清理------------");}}
- 在applicationContext.xml配置文件中配置拦截器
<!--关于拦截器的配置--><mvc:interceptors><mvc:interceptor><!--/** 包括路径及其子路径--><!--/admin/* 拦截的是/admin/add等等这种 , /admin/add/user不会被拦截--><!--/admin/** 拦截的是/admin/下的所有--><mvc:mapping path="/**"/><!--bean配置的就是拦截器--><bean class="com.jcsune.interceptor.MyInterceptor"/></mvc:interceptor></mvc:interceptors>
- 编写一个Controller,接收请求
package com.jcsune.controller;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;//测试拦截器的控制器@RestControllerpublic class InterceptorController {@RequestMapping("/t2")public String testFunction(){System.out.println("控制器中的方法执行了");return "hello";}}
- 前端 index.jsp
<a href="${pageContext.request.contextPath}/t2">拦截器测试</a>
- 启动Tomcat测试一下
2.2 登录判断验证
验证用户是否登录(认证用户)
实现思路
- 有一个登录页面,需要写一个controller访问页面
- 登录页面有一提交表单的动作,需要在Controller中处理,判断用户名和密码是否正确,如果正确,向session中写入用户信息,返回登录成功
- 拦截用户请求,判断用户是否登录,如果用户已经登录,放行,如果用户未登录,跳转到登录页面
测试:
- 编写一个登录页面 login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %><html><head><title>Title</title></head><h1>登录页面</h1><hr><body><form action="${pageContext.request.contextPath}/user/login">用户名:<input type="text" name="username"><br>密码:<input type="password" value="pwd"><br><input type="submit" value="提交"></form></body></html>
- 编写一个Controller处理请求
package com.jcsune.controller;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import javax.servlet.http.HttpSession;@Controller@RequestMapping("/user")public class UserController {//跳转到登录页面@RequestMapping("/jumplogin")public String jumpLogin() throws Exception{return "login";}//跳转到成功页面@RequestMapping("/jumpsuccess")public String jumpSuccess() throws Exception{return "success";}//登录提交@RequestMapping("/login")public String Login(HttpSession session,String username,String pwd)throws Exception{//向session纪录用户身份信息System.out.println("接收前端==="+username);session.setAttribute("user",username);return "success";}//退出登录@RequestMapping("logout")public String logout(HttpSession session)throws Exception{//session 过期session.invalidate();return "login";}}
- 编写一个登录成功的页面 success.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %><html><head><title>Title</title></head><body><h1>登录成功页面</h1><hr>${user}<a href="${pageContext.request.contextPath}/user/logout">注销</a></body></html>
- 在index页面上测试跳转,启动Tomcat测试,未登录也可以跳转到主页
<%@ page contentType="text/html;charset=UTF-8" language="java" %><html><head><title>Title</title></head><h1>登录页面</h1><hr><body><form action="${pageContext.request.contextPath}/user/login">用户名:<input type="text" name="username"><br>密码:<input type="password" value="pwd"><br><input type="submit" value="提交"></form></body></html>
- 编写用户登录拦截器
package com.jcsune.interceptor;import org.springframework.web.servlet.HandlerInterceptor;import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.HttpSession;public class LoginInterceptor implements HandlerInterceptor {public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//如果是登录页面则放行System.out.println("uri:"+request.getRequestURI());if(request.getRequestURI().contains("login")){return true;}HttpSession session=request.getSession();//如果用户已登录也放行if(session.getAttribute("user")!=null){return true;}//用户没有登录跳转到登录页面request.getRequestDispatcher("/WEB-INF/jsp/logon.jsp").forward(request,response);return false;}public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {}public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {}}
- 在springmvc的配置文件中注册拦截器
<!--关于拦截器的配置--><mvc:interceptors><mvc:interceptor><!--/** 包括路径及其子路径--><!--/admin/* 拦截的是/admin/add等等这种 , /admin/add/user不会被拦截--><!--/admin/** 拦截的是/admin/下的所有--><mvc:mapping path="/**"/><!--bean配置的就是拦截器--><!-- <bean class="com.jcsune.interceptor.MyInterceptor"/>--><bean id="loginInterceptor" class="com.jcsune.interceptor.LoginInterceptor"/></mvc:interceptor></mvc:interceptors>
- 再次重启Tomcat测试
2.3 文件上传和下载
准备工作
文件上传是项目开发中最常见的功能之一,springmvc可以很好的支持文件上传,但是springmvc上下文中默认没有装配multipartResolver,因此默认情况下其不能处理文件上传工作,如果想使用spring的文件上传功能,则需要在上下文中配置multipartResolver
前端表单要求:为了能上传文件,必须将表单的method设置为POST,并将enctype设置为multipart/form-data,只有在这样的情况下,浏览器才会把用户选择的文件以二进制数据发送给服务器
对表中的enctype属性做个详细的说明
- application/x-www=form-urlencoded:默认方式,只处理表单域中的value属性值,采用这种编码方式的表单会将表单域中的值处理成URL编码方式
- multipart/form-data:这种编码方式会以二进制流的方式来处理表单数据,这种编码方式会把文件域指定文件的内容也封装到请求参数中,不会对字符编码。
- text/plain:除了把空格转换为 “+” 号外,其他字符都不做编码处理,这种方式适用直接通过表单发送邮件。
<form action="" enctype="multipart/form-data" method="post"><input type="file" name="file"/><input type="submit"></form>
一旦设置了enctype为multipart/form-data,浏览器即会采用二进制流的方式来处理表单数据,而对于文件上传的处理则涉及在服务器端解析原始的HTTP响应,在2003年,Apache Software Foundation发布了开源的Commons FileUpload组件其很快成为Servlet/jsp程序员上传文件的最佳选择
- Servlet3.0规范已经提供方法来处理文件上传,但这种上传需要在Servlet中完成
- 而SpringMVC则提供了更简单的封装
- SpringMVC为文件的上传提供了直接的支持,这种支持是用即插即用的MultipartResolver实现的
- SpringMVC使用Apache Commons FileUpload技术实现了一个MultipartResolver实现类
- CommonMultipartResolver,因此SpringMVC的文件上传还需要依赖Apache Commons FileUpload的组件
2.3.1 文件上传
- 导入文件上传的jar包 commons-fileupload
<!--文件上传--><dependency><groupId>commons-fileupload</groupId><artifactId>commons-fileupload</artifactId><version>1.3.3</version></dependency><!--servlet-api导入高版本的--><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>4.0.1</version></dependency>
- 配置bean: multipartResolver
注意这个bean的id必须为multipartResolver,否则上传文件会报400的错误
<!--文件上传配置--><bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"><!-- 请求的编码格式,必须和jSP的pageEncoding属性一致,以便正确读取表单的内容,默认为ISO-8859-1 --><property name="defaultEncoding" value="utf-8"/><!-- 上传文件大小上限,单位为字节(10485760=10M) --><property name="maxUploadSize" value="10485760"/><property name="maxInMemorySize" value="40960"/></bean>
CommonsMultipartFile的常用方法:
- String getOriginalFilename():获取上传文件的原名
- InputStream getInputStream():获取文件流
- void transfer To(File dest):将上传文件保存到一个目录文件中
接着去实际测试一波
- 编写前端页面 index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %><html><head><title>$Title$</title></head><body><form action="/upload" enctype="multipart/form-data" method="post"><input type="file" name="file"/><input type="submit" value="upload"></form></body></html>
- Controller
package com.jcsune.controller;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.multipart.commons.CommonsMultipartFile;import javax.servlet.http.HttpServletRequest;import java.io.*;@Controllerpublic class FileController {//@RequestParam("file") 将name=file控件得到的文件封装成CommonsMultipartFile 对象//批量上传CommonsMultipartFile则为数组即可@RequestMapping("/upload")public String fileUpload(@RequestParam("file") CommonsMultipartFile file , HttpServletRequest request) throws IOException {//获取文件名 : file.getOriginalFilename();String uploadFileName = file.getOriginalFilename();//如果文件名为空,直接回到首页!if ("".equals(uploadFileName)){return "redirect:/index.jsp";}System.out.println("上传文件名 : "+uploadFileName);//上传路径保存设置String path = request.getServletContext().getRealPath("/upload");//如果路径不存在,创建一个File realPath = new File(path);if (!realPath.exists()){realPath.mkdir();}System.out.println("上传文件保存地址:"+realPath);InputStream is = file.getInputStream(); //文件输入流OutputStream os = new FileOutputStream(new File(realPath,uploadFileName)); //文件输出流//读取写出int len=0;byte[] buffer = new byte[1024];while ((len=is.read(buffer))!=-1){os.write(buffer,0,len);os.flush();}os.close();is.close();return "redirect:/index.jsp";}/** 采用file.Transto 来保存上传的文件*/@RequestMapping("/upload2")public String fileUpload2(@RequestParam("file") CommonsMultipartFile file, HttpServletRequest request) throws IOException {//上传路径保存设置String path = request.getServletContext().getRealPath("/upload");File realPath = new File(path);if (!realPath.exists()){realPath.mkdir();}//上传文件地址System.out.println("上传文件保存地址:"+realPath);//通过CommonsMultipartFile的方法直接写文件(注意这个时候)file.transferTo(new File(realPath +"/"+ file.getOriginalFilename()));return "redirect:/index.jsp";}}
- 测试上传文件,OK
2.3.2 文件下载
文件下载步骤:
- 设置response响应头
- 读取文件—InputStream
- 写出文件—OutputStream
- 执行操作
- 关闭流(先开后关)
代码实现
@RequestMapping(value="/download")public String downloads(HttpServletResponse response ,HttpServletRequest request) throws Exception{//要下载的图片地址String path = request.getServletContext().getRealPath("/upload");String fileName = "spring.jpg";//1、设置response 响应头response.reset(); //设置页面不缓存,清空bufferresponse.setCharacterEncoding("UTF-8"); //字符编码response.setContentType("multipart/form-data"); //二进制传输数据//设置响应头response.setHeader("Content-Disposition","attachment;fileName="+URLEncoder.encode(fileName, "UTF-8"));File file = new File(path,fileName);//2、 读取文件--输入流InputStream input=new FileInputStream(file);//3、 写出文件--输出流OutputStream out = response.getOutputStream();byte[] buff =new byte[1024];int index=0;//4、执行 写出操作while((index= input.read(buff))!= -1){out.write(buff, 0, index);out.flush();}out.close();input.close();return null;}
前端:
<a href="/download">点击下载</a>
测试:








