SpringMVC的处理器拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。开发者可以自己定义一些拦截器来实现特定的功能。
过滤器与拦截器的区别:拦截器是AOP思想的具体应用。
过滤器
拦截器
那如何实现拦截器呢?
想要自定义拦截器,必须实现 HandlerInterceptor 接口。
1、新建一个Moudule , springmvc-07-Interceptor , 添加web支持
2、配置web.xml 和 applicationContext.xml 文件
3、编写一个拦截器
package com.edgar.config; 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就不执行下一个拦截器 @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("============处理前================"); return true; } // 在请求处理方法执行之后执行 @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("============处理后================"); } // 在dispatcherServlet处理后执行,做清理工作. @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("============清理================"); } }
4、在springmvc的配置文件中配置拦截器
<!--关于拦截器的配置--> <mvc:interceptors> <mvc:interceptor> <!--/** 包括路径及其子路径--> <!--/admin/* 拦截的是/admin/add等等这种 , /admin/add/user不会被拦截--> <!--/admin/** 拦截的是/admin/下的所有--> <mvc:mapping path="/**"/> <!--bean配置的就是拦截器--> <bean class="com.edgar.config.MyInterceptor"/> </mvc:interceptor> </mvc:interceptors>
5、编写一个Controller,接收请求
package com.edgar.controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class TestController { @RequestMapping("/t1") public String test(){ System.out.println("TestController=>test()执行了"); return "OK"; } }
6、启动tomcat 测试一下!
http://localhost:8080/t1
实现思路
1、有一个登陆页面,需要写一个controller访问页面。
2、登陆页面有一提交表单的动作。需要在controller中处理。判断用户名密码是否正确。如果正确,向session中写入用户信息。返回登陆成功。
3、拦截用户请求,判断用户是否登陆。如果用户已经登陆。放行, 如果用户未登陆,跳转到登陆页面
测试:
1、编写一个登陆页面 login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <%--在WEB-INF下的所有页面或者资源,只能通过controller,或者servlet进行访问--%> <h1>登陆页面</h1> <form action="${pageContext.request.contextPath}/user/login" method="post"> 用户名:<input type="text" name="username"> 密码:<input type="text" name="password"> <input type="submit" value="提交"> </form> </body> </html>
2、编写一个Controller处理请求
package com.edgar.controller; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import javax.servlet.http.HttpSession; @Controller @RequestMapping("/user") public class LoginController { @RequestMapping("/main") public String main(){ return "main"; } @RequestMapping("/goLogin") public String goLogin(){ return "login"; } @RequestMapping("/login") public String login(HttpSession session, String username, String password, Model model){ // 把用户的信息存在session中 session.setAttribute("userLoginInfo",username); model.addAttribute("username",username); return "main"; } @RequestMapping("/logout") public String logout(HttpSession session){ session.removeAttribute("userLoginInfo"); return "login"; } }
3、编写一个登陆成功的页面 main.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <h1>首页</h1> <span>${username}</span> <p> <a href="${pageContext.request.contextPath}/user/logout">注销</a> </p> </body> </html>
4、在 index.jsp页面上测试跳转!启动Tomcat 测试,未登录也可以进入主页!
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>$Title$</title> </head> <body> <h1><a href="${pageContext.request.contextPath}/user/goLogin">登陆页面</a></h1> <h1><a href="${pageContext.request.contextPath}/user/main">首页</a></h1> </body> </html>
5、编写用户登录拦截器
package com.edgar.config; import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class LoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 放行:如果是前往登录页面 if (request.getRequestURI().contains("goLogin")) { return true; } // 放行:如果是登录提交请求 if (request.getRequestURI().contains("login")) { return true; } // 如果用户已登陆也放行 if (request.getSession().getAttribute("userLoginInfo") != null) { return true; } // 用户没有登陆跳转到登陆页面 request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request,response); return false; } }
6、在Springmvc的配置文件中注册拦截器
<!--关于拦截器的配置--> <mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/user/**"/> <bean class="com.edgar.config.LoginInterceptor"/> </mvc:interceptor> </mvc:interceptors>
7、再次重启Tomcat测试!
OK,测试登录拦截功能无误.
文件上传是项目开发中最常见的功能之一 ,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程序员上传文件的最佳选择。
1、导入文件上传的jar包,commons-fileupload , Maven会自动帮我们导入它的依赖包 commons-io包;
<!--文件上传--> <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>
2、配置bean:multipartResolver
【注意!!!这个bena的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 的 常用方法:
我们去实际测试一下
3、编写前端页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>$Title$</title> </head> <body> <form action="${pageContext.request.contextPath}/upload" method="post" enctype="multipart/form-data"> <input type="file" name="file"> <input type="submit" value="upload"> </form> </body> </html>
4、FileController
package com.edgar.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.*; @Controller public class FileController { @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("上传文件保存地址:"+path); // 文件输入流 InputStream is = file.getInputStream(); // 文件输出流 OutputStream os = new FileOutputStream(new File(path, 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.hashCode(); return "redirect:/index.jsp"; } }
5、测试上传文件,OK!
采用file.transferTo来保存上传的文件
1、编写Controller
@RequestMapping("/upload2") public String fileUpload2(@RequestParam("file") CommonsMultipartFile file,HttpServletRequest request) throws IOException { String username = request.getParameter("username"); System.out.println(username); // 上传路径保存设置 String path = request.getServletContext().getRealPath("/upload"); File realPath = new File(path); if (!realPath.exists()) { realPath.mkdir(); } // 上传文件地址 System.out.println("上传文件保存地址:"+path); // 通过CommonsMultipartFile的方法直接写文件 file.transferTo(new File(path,file.getOriginalFilename())); return "redirect:/index.jsp"; }
2、前端表单提交地址修改
3、访问提交测试,OK!
1、设置 response 响应头
2、读取文件 -- InputStream
3、写出文件 -- OutputStream
4、执行操作
5、关闭流 (先开后关)
代码实现:
@RequestMapping("/download") public String downloads(HttpServletRequest request,HttpServletResponse response) throws Exception { // 要下载的图片地址 String path = request.getServletContext().getRealPath("/upload"); String fileName = "1.jpg"; // 1.设置response响应头 response.reset(); // 设置页面不缓存,清空buffer response.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 is = new FileInputStream(file); // 3.写出文件--输出流 OutputStream os = response.getOutputStream(); 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"; }
前端
<a href="${pageContext.request.contextPath}/download">点击下载</a>
测试,文件下载OK,大家可以和我们之前学习的JavaWeb原生的方式对比一下,就可以知道这个便捷多了!