文章目录
1. SpringMVC的 拦截器
1.1 拦截器和过滤器
SpringMVC的处理器拦截器类似Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。
过滤器是交给tomcat来操作的,而拦截器是通过spring来实现的。
过滤器和拦截器的区别:拦截器是AOP思想的具体应用。
1.2 自定义拦截器
想要自定义拦截器,必须实现HandlerInterceptor接口。
直接一点就是实现了HandlerInterceptor接口的类就是一个拦截器。
定义一个拦截器类MyInterceptor:
package com.itholmes.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 {
//return true; 就是放行,执行下一个拦截器。
//return false; 就是不放行,不执行下一个拦截器,就拒绝访问。
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("===========处理前===========");
return false;//不放行
//return true;//放行
}
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("===========处理后===========");
}
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("===========清理===========");
}
}
有了拦截器,就得在springmvc的配置文件里面注册一下:
<?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/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
">
<!--1.注解驱动-->
<mvc:annotation-driven/>
<!--2.静态资源过滤,如果不配置我们的js文件加载不进去。-->
<mvc:default-servlet-handler/>
<!--3.扫描包-->
<context:component-scan base-package="com.itholmes.controller"/>
<!--4.视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!--拦截器配置-->
<mvc:interceptors>
<!--第一类: 放到外面的拦截器bean是全局的!-->
<bean class="com.itholmes.config.MyInterceptor"/>
<mvc:interceptor>
<!--
这里的path就是拦截路径。
?、/、/*和/**的区别配置:
"/index?"能够匹配到"/indexA","/indexB",可是不能匹配"/index",也不能匹配"/indexAA";请求
"/index*"能够匹配"/indexA","/indexAA",可是不能匹配"/index/A";index
"/index/*"能够匹配"/index/","/index/A","/index/AA","/index/ABC",可是不能匹配"/index",也不能匹配"/index/A/B";
"/index/**"能够匹配"/index/"下的多个子路径,好比"/index/A/B/C/D";
-->
<mvc:mapping path="/**"/>
<!--第二类: 放到单独interceptor的拦截类bean是拦截部分的。-->
<bean class="com.itholmes.config.MyInterceptor2"/>
</mvc:interceptor>
</mvc:interceptors>
</beans>
控制器类TestController :
package com.itholmes.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
@GetMapping("/t1")
public String test(){
System.out.println("TestController==test方法执行了");
return "OK";
}
}
之后我们随便访问一个控制器,他就会显示这种效果。
?、/、/*和/**的区别配置:
- “/index? :能够匹配到/indexA”,“/indexB”,可是不能匹配"/index",也不能匹配"/indexAA";请求
- “/index* :能够匹配/indexA”,“/indexAA”,可是不能匹配"/index/A";index
- “/index/* :能够匹配/index/”,“/index/A”,“/index/AA”,“/index/ABC”,可是不能匹配 “/index”,也不能匹配"/index/A/B";
- “/index/**:能够匹配/index/“下的多个子路径,好比”/index/A/B/C/D”;
1.3 SpringBoot 整合拦截器
使用全注解的方式:
创建一个类使用@Configuration注解使他成为一个配置类,实现 WebMvcConfigurer接口,重写addInterceptors方法
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())
//拦截的路径
.addPathPatterns("/**")
//放行的路径
.excludePathPatterns("/login");
}
}
2. SpringMVC的上传文件
2.1 前端上传文件方式
SpringMVC可以很好的支持文件上传,但是SpringMVC上下文中默认没有装配MultipartResolver,因此默认情况下它是不能处理文件上传工作。如果想让SpringMVC的文件上传功能,则需要在上下文中配置MultipartResolver。
multipart/form-data类型:这种编码方式会以二进制流的方式来处理表单数据。
2.2 后台接受方式
第一步:导入commons-fileupload和javax.servlet-api两个包。
<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
<!--Servlet-->
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
第二步:配置springmvc的核心配置文件。
- SpringMVC为文件上传提供了直接支持,这种支持是即插即用的MultipartResolver实现。
- SpringMVC使用Apache Commons FileUpload技术实现了一个MultipartResolver实现类:CommonsMultipartResolver。因此,SpringMVC文件上传还是需要依赖Apache COmmons FileUpload的组件!
在springmvc-config.xml文件中,配置CommonsMultipartResolver类的一个bean对象,注意这里的bean对象名字不能错误!因为在Spring中有类在用它!
<!--文件上传的位置-->
<!--这里的id必须是multipartResolver,因为在Spring中有类在用它!-->
<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>
第三步:配置Controller文件。
第一种方式:IO读取。
package com.itholmes.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.commons.CommonsMultipartFile;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
@RestController
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 readPath = new File(path);
if(!readPath.exists()){
readPath.mkdir();
}
System.out.println("上传文件保存地址:"+readPath);
//获取文件流
InputStream is = file.getInputStream();
OutputStream os = new FileOutputStream(new File(readPath, 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";
}
}
第二种方式:直接写到文件中。
package com.itholmes.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.commons.CommonsMultipartFile;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
@RestController
public class FileController {
@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";
}
}
第四步:就直接测试就可以了。
- 正常开发中,这些代码都是固定的格式,拿来即用!
- 我们可以在target或out文件中能查看到。
3. SpringMVC的下载文件
文件下载步骤:
对应控制类写法:
package com.itholmes.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.commons.CommonsMultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
@RestController
public class FileController {
@RequestMapping("/download")
public String downloads(HttpServletResponse response,HttpServletRequest request) throws IOException {
String path = request.getServletContext().getRealPath("/upload");
String fileName = "项目.md";
//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 input = new FileInputStream(file);
//3.写出文件--输出流
OutputStream out = response.getOutputStream();
byte[] buff = new byte[1024];
int index = 0;
while ((index=input.read(buff))!=-1){
out.write(buff,0,index);
out.flush();
}
out.close();
input.close();
return null;
}
}
4. 补充
4.1 " / "的作用在web.xml中
/ 的作用就是来兜底(缺省)的!
servlet的 / 是兜底的效果,没有找到servleturl路径的时候就走它。
因此我们的前段控制器这只的url-pattern都是设置为 / ,目的就是不配置servlet,让请求们去访问呢去找前段控制器。
解释一下init-param的作用,就相当于我们代码中调用ClassPathXmlApplicationContext类的这个方法,并且传递了值。
4.2 Spring MVC不能使用基本数据类型!
springmvc不能使用基本类型来接受参数。
原因很简单,当浏览器发送数据过来,但是没有有一个参数没有发送过来,例如:一个int类型的参数,SpringMVC会给它赋值一个null,这样就会报错,因为int类型默认是0,如果赋值null就会报错!
因此,我们使用他们的包装类就可以,因为包装类是对象,就可以赋值为null。
4.3 WEB-INF
web-inf 默认是对外资源不开放的!
4.4 控制器走视图解析器的几个情况
两种情况:
- 当返回值为String的时候,会拿着返回的字符串走视图解析器。
- 当无返回值或者返回值不为String的时候,会拿着当前的请求路径走视图解析器。
4.5 拦截器复习
拦截器属于springmvc,而过滤器是拦截servlet。过滤器和springmvc同级的。简而言之,过滤器相当于拦截器的上级领导的同事。
4.6 /*是单层路径,/**是多层路径
/*是单层路径,/**是多层路径。
意思就是/*是包揽当前目录下的所有请求或页面。而/**是包揽了多层目录下的所有请求或页面。
5. 补充2
5.1 bigint是对应java的long类型
数据库汇总的biging类型应该对象到java的long类型,而不是int类型!!!
5.2 @RequestBody注解
@RequestBody主要用来接收前端传递给后端的json字符串中的数据的(请求体中的数据的),所以只能发送POST请求。
GET方式无请求体,所以使用@RequestBody接收数据时,前端不能使用GET方式提交数据,而是用POST方式进行提交
5.3 @RequestParamer注解作用
这里的@RequestParam("empno1") Integer aa
相当于
String aa = request.getParameter("empno1")
5.4 接受参数的三种方式
下面三种方式默认form表单就是就可以:
- @RequestParam只能接受form表单格式的数据。
- 对于对象接受参数,也是默认form表单格式。
但是对于json格式只能发送post请求。
- 想要接受json格式的参数必须使用@RequestBody注解,才能拿到对象中的数据。
@ResponseBody作用:将响应的数据直接放到响应体里面,不走视图解析器。
- 对于返回值为字符串类型,就直接返回字符串。
- 对于返回值为对象类型,就直接返回一个该对象类型的json字符串。
5.5 ModelAndView进一步解释
记住点:
- ModelAndView就是把数据和页面放到一起了。
- 就算使用了@ResponseBody,ModelAndView也会走视图解析器!!
- ModelAndView是离不开JSP的!!!
6. SpringMVC统一异常处理的两种方式
6.1 自定义类实现HandlerExceptionResolver接口
这种方式是给客户端返回一个统一异常处理的页面。
解释一下resolveException的参数:
- request和response参数就是请求和响应对象。
- handler参数指明哪里出错。
- ex参数错误的类型。
package com.itholmes.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
//记住这个类要被创建到spring容器中!
@Component
public class MyException implements HandlerExceptionResolver{
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception ex) {
// TODO Auto-generated method stub
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("error"); // 这里走视图解析器拼接到error.jsp
modelAndView.addObject("msg", "页面崩溃了");
return modelAndView;
}
}
因为这种方式返回值是ModelAndView,不管有没有@RequestBody注解都会去走视图解析器的!因此,它处理异常的方式是返回一个jsp页面到前段。
如果前段使用ajax来请求的就无法处理了。因此,这种方式不常用的!
6.2 @ControllerAdvice和@ExceptionHandler注解来统一处理SpringMVC异常(常用)
通过@ControllerAdvice类定义普通类和@ExceptionHandler注解标记异常方法,来实现SpringMVC的统一异常处理
- @ExceptionHandler(异常的class) 注解很重要!
- @ResponseStatus(HttpStatus.OK)可以设置http状态码。
package com.itholmes.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import com.itholmes.pojo.Result;
@ControllerAdvice
public class MyException{
//这样这个方法就专门来处理空指针的异常
@ExceptionHandler(NullPointerException.class)
@ResponseBody//不走视图解析器,直接返回。
@ResponseStatus(HttpStatus.OK)
public Result myException(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception ex) {
//与实现接口统一异常处理不同的是:这里的object不指定哪里报错了!
Result result = new Result();
result.setCode(0);
result.setMsg("空指针异常");
result.setData(ex);
return result;
}
}
7. SpringMVC的参数理解
SpringMVC中,注解的默认值:
- 如果注解只有一个属性,那么肯定是赋值给该属性。
- 如果注解有多个属性,而且前提是这多个属性都有默认值,那么你不写注解名赋值,注解先会查找一个名字为“value”这属性,然后赋值给他;如果value名的属性不存在,就会报错undefineded 属性名value。
- 如果注解有多个属性,其中就有,没有设置默认值的属性,那么当你不写属性名进行赋值的时候,是会报错的。
8. SSM整合 细节总结
8.1 原生的Servlet 八大监听器
原生的Servlet是有八大监听器的。
每一个监听器,监听不同的功能。
8.2 Spring整合到web.xml文件中
先是Spring整合了mybatis。
后SpringMVC和Spirng(整合了mybatis) 将这两个再整合到context整个web容器中。
上图的ContextLoaderListener监听器是Spring的监听器。让监听器来给我们创建一个classpathxmlApplicationContext,从而得到ioc容器。
8.3 Spring读取外部文件,传到dataSource中
8.4 事务管理器的作用
事务管理器就是来管理datasource的事务的。
9. <mvc:view-controller>的使用
配置view-controller,直接把请求地址和视图名称关联起来,不必写handler(controller)方法了。
<!-- 配置view-controller,直接把请求地址和视图名称关联起来,不必写handler方法了。 -->
<!--
@RequestMapping("/admin/to/login/page.html")
public String testReceiveArrayTwo(ParamData paramData) {
return "admin-login";
}
相当于上面这段代码。
-->
<mvc:view-controller path="/admin/to/login/page.html" view-name="admin-login"/>
10. DispatcherServlet的url-pattern:/ 和 /* 的区别
< url-pattern > / </ url-pattern > 不会匹配到*.jsp,即:*.jsp不会进入spring的 DispatcherServlet类 。
< url-pattern > /* </ url-pattern > 会匹配*.jsp,会出现返回jsp视图时再次进入spring的DispatcherServlet 类,导致找不到对应的controller所以报404错。