SpringMVC
基本概念:是spring的后续产品,处理表现层,其实就是封装的Servlet。内部组件化程度高,即插即用。
springMVC的使用:
创建maven项目,之后在web.xml文件中配置
<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:SpringMVC.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> <init-param>表明配置文件位置 <load-on-startup>1</load-on-startup>将初始化提前到服务器启动时加载,节省时间
原来是<url-pattern>接受到请求地址到<servlet-class>指定的类中,现在是接受全部请求地址交给DispatcherServlet进行处理。
为什么用/不用/*,因为/不能处理.jsp文件,/*可以处理所有请求
还需要写mvc的配置文件springmvc.xml,在里面写需要扫描哪些类和其他配置,和Bean.xml一样
<context:component-scan base-package="com.zly"></context:component-scan> <!-- 配置Thymeleaf视图解析器 --> <bean id="viewResolver" class="org.thymeleaf.spring5.view.ThymeleafViewResolver"> <property name="order" value="1"/> <property name="characterEncoding" value="UTF-8"/> <property name="templateEngine"> <bean class="org.thymeleaf.spring5.SpringTemplateEngine"> <property name="templateResolver"> <bean class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver"> <!-- 视图前缀 --> <property name="prefix" value="/WEB-INF/templates/"/> <!-- 视图后缀 --> <property name="suffix" value=".html"/> <property name="templateMode" value="HTML5"/> <property name="characterEncoding" value="UTF-8" /> </bean> </property> </bean> </property> </bean> 视图前缀和视图后缀
类名写@RequestMapping(“url”),方法里面写return的文件名,文件名要去掉视图前缀和视图后缀
@RequestMapping写在类之前,是设置映射请求请求路径的初始信息
@RequestMapping写在方法之前,是设置映射请求请求路径的具体信息
@RequestMapping(“/hello”,“/abb”)括号中是数组类型,路径可以写多个
@RequestMapping(value = "/",method = RequestMethod.GET)method设置请求方式匹配请求,可设置多种。若浏览器的请求方式和设置的请求方式不匹配报错405。表单提交用post请求,其他用get,不设置默认为post。有派生注解@PostMapping等
springMVC支持ant风格的路径:类似于占位符
-
?任意的单个字符,不能为?
-
*任意个数的任意字符
-
** 任意层数的任意目录,使用时只能写在双斜线中/**/xxx
@RequestMapping中的占位符:当你的url中传内容时,接受的时候当成路径没法接受数据,就要使用占位符
@RequestMapping("/hello/{name}/{id}")中使用{}来表示接受的数据是哪个,然后在括号里写注解@PathVariable,写参数
@RequestMapping("/hello/{name}/{id}") public String protal(@PathVariable String name,@PathVariable String id){ System.out.println(name+id); return "success"; }
前端页面中使用的标签 <html lang="en" xmlns:th="http://www.thymeleaf.org"> <a th:href="@{/hello}">测试SpringMVC</a> 使用thymelead的标签
SpringMVC获取请求参数的方式:
1.老方法,很麻烦
@RequestMapping("/Hello/name") public String protal(HttpServletRequest request){ String username = request.getParameter("username"); String password = request.getParameter("password"); System.out.println(username+password); return "success"; }
2.将前端的名称直接写入后端的参数框内
@RequestMapping("/Hello/name") public String protal( @RequestParam(value = "username",required = false,defaultValue ="hello" ,) String name, String password){ System.out.println(name+password); return "success"; } 如果前端名称和后端不一致,就用@RequestParam @RequestParam有三个参数 1.value:将前端的name和后端相匹配 2.required:如果为true,前端传递到参数后端没接收就报错,false,前端传递的参数后端没接收不报错 3.defaultValue:设置默认值,不管前端传递的什么值,后端都默认为设置的defaultValue 其他注解:三种使用方式一样,参数一样 原生方法获得请求头信息和cookie很麻烦,所以有以下注解 @RequestParam:针对参数的 @RequestHeader:针对请求头的 @CookieValue:针对cookie的
3.最常用的方式,当前端传输的数据很多,就需要使用实体类pojo,要保证实体pojo类中的属性名和前端请求的name参数名一致
先创建一个实体类pojo写属性
@RequestMapping("/Hello/pojo") public String protal(User user){ System.out.println(user); return "success"; }
解决获取参数乱码问题:
在设置编码之前,不能获取任何请求参数
tomcat8.5之后解决了一部分乱码问题
解决方法:
在web.xml文件中写过滤器配参数CharacterEncodingFilter
<filter> <filter-name>CharacterEncodingFilter</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> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>CharacterEncodingFilter</filter-name> <url-pattern>/</url-pattern> </filter-mapping>
什么是域:
能存储并开放数据的区域
三种域对象和使用范围:
-
request:一次请求/请求转发
-
session:一次会话
-
application:任意一次请求和会话
三大域对象共同的方法:
setAttribute(name,value); 设置修改数据 getAttribute(name);获得数据的方法 removeAttribute(name);移除数据的方法
request域:
在一次请求内有效,在请求和转发时数据可以共享,除此之外没法共享数据
session域:
单次会话内有效,可以有多个请求
会话是浏览器启动和关闭的整个过程
Application域:
在一个web项目中,请求和对话中可以使用,创建项目开始,到结束项目销毁
和服务器是否关闭有关
向请求域共享数据:
1.ModelAndView
返回值对象必须是ModelAndView,使用时Model是在请求域共享数据,View是设置逻辑视图,设置跳转的位置
@RequestMapping("/Hello/pojo") public ModelAndView protal(){ ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("name","zly"); modelAndView.setViewName("success"); return modelAndView; } 调用<p th:text="${name}"></p>里面是$不是@
2.model,更简单
不用设置返回对象类型,直接调用方法,只需要在参数列表中写model就行
@RequestMapping("/Hello/pojo") public String protal(Model model){ model.addAttribute("name","zly"); return "success"; }
3.modelmap
和model一样
4.map
四种方法都能实现功能,底层都是一样的,使用model就行,简单快捷
向会话域Session域和应用域Application共享数据:
使用ServletAPI更简单,springMVC中的复杂
@RequestMapping("/Hello/pojo") public String protal(HttpSession session){ session.setAttribute("name","zly"); return "success"; } @RequestMapping("/Hello/pojo") public String protal2(HttpSession session){ ServletContext servletCo ntext = session.getServletContext(); servletContext.setAttribute("name","zly"); return "success"; } 第一种是向Session域,第二种是向Application域。两种的参数列表都是HttpSession session
前端调用共享域的时候:
request直接调用TestRequestScope
session要调用session.testSessionScope
Application要调用application.testApplicationScope
SpingMVC的视图:
springMVC的视图是View接口,作用是将model中的数据展示给用户
有很多视图,如果用的是Thymeleaf,在配置文件中配置了Thymeleaf的视图解析器,所得到的是ThymeleafView。用的最多的是ThymeleafVIew和RedirectView
如果用jsp,那么视图技术就不是Thymeleaf,用的最多的就是InternalResourceView和RedirectView
当业务逻辑成功用重定向,业务逻辑失败用转发。登录成功重定向,登录失败用转发。
<mvc:default-servlet-handler></mvc:default-servlet-handler>
这个Spring MVC xml文件的属性,主要是处理web项目的静态文件问题。 每次请求过来,先经过 DefaultServletHttpRequestHandler 判断是否是静态文件,如果是静态文件,则进行处理,不是则放行交由 DispatcherServlet 控制器处理。
重定向与转发的区别:
1.重定向访问服务器两次,转发只访问服务器一次。
2.转发页面的URL不会改变,而重定向地址会改变
3.转发只能转发到自己的web应用内,重定向可以重定义到任意资源路径。
ThymeleafView:
当方法中return的内容没有任何前缀时,默认创建的就是ThymeleafView。效果是转发
转发视图:InternalResourceView用的不多
使用时在return中写一个 Forword:/路径
转发和Thymeleaf一样,一般用ThymeleafView。为什么不用转发视图,因为转发视图只是转发,不会被渲染。Thymeleaf也是转发,但是会被Thymeleaf渲染
重定向视图:RedirectView
使用时在return的内容中写redirect:/地址
视图控制器:View-controller
当一个请求直接跳转页面时候可以使用,例如所有请求跳转到index时
需要在配置为文件中配置:
<mvc:view-controller path="/" view-name="index"></mvc:view-controller>
当开启这个配置之后,@RequestMapping标签就不能使用,所以需要另一个标签开启
<mvc:annotation-driven></mvc:annotation-driven>
RESTful:风格而已,后续再说
SpringMVC处理ajax请求:
axios:
为什么不用Jquery.ajax,因为前端现在用vue,要使用$.ajax还要引入jquery的代码库,复杂,所以推荐axios
axios({ url:'http://123.207.32.32:8000/home/multidata', params:{ type:'pop', page:1 } }).then(res=>{ console.log(res); }).catch(err=>{ console.log(err); }) 基本语法 axios(config):通用/最本质的发任意类型请求的方式 axios(url[, config]):可以只指定 url 发 get 请求 axios.request(config):等同于 axios(config) axios.get(url[, config]):发 get 请求 axios.delete(url[, config]):发 delete 请求 axios.post(url[, data, config]):发 post 请求 axios.put(url[, data, config]):发 put 请求 axios.defaults.xxx:请求的默认全局配置 axios.interceptors.request.use():添加请求拦截器 axios.interceptors.response.use():添加响应拦截器 axios.create([config]):创建一个新的 axios(它没有下面的功能) axios.Cancel():用于创建取消请求的错误对象 axios.CancelToken():用于创建取消请求的 token 对象 axios.isCancel():是否是一个取消请求的错误 axios.all(promises):用于批量执行多个异步请求 axios.spread():用来指定接收所有成功数据的回调函数的方法
@RequestBody:
在参数列表中直接写只能接受前端传来的name=value的数据,要接收请求体需要使用@RequestBody。
具体使用在参数列表中写@RequestBody String requestBody,就能接收
在接收请求体之后,需要将内容转为java对象,原方法是拆除括号逗号之类得到数据然后赋值很麻烦,现在直接用标签自动赋值
要导入jacken的依赖
要在配置文件中写mvc:annotation-driven开启注解
用法:例如有实体类User,里面的属性写好。接收时直接在参数列表里面写@RequestBody User user,就直接将前端的数据直接赋给实体类User了,不需要自己拆解数据。 如果接收类型为map,直接@RequestBody Map<String,Object> map
@RequestMapping("/Hello/pojo") public String protal(@RequestBody User user){ System.out.println(user); return "success"; }
@ResponseBody:
是加在类上的注解,是将方法的返回值当作响应体返回给浏览器数据,一般返回给浏览器的是java对象
一般需要将java对象返回给浏览器,原方法很麻烦,现在直接用标签自动返回
要导入jacken的依赖
要在配置文件中写mvc:annotation-driven开启注解
@RequestMapping("/Hello/pojj") @ResponseBody public User protal2(){ User user = new User("zly","18","nan"); return user; } 直接将user对象返回给前端,很吊 @RequestMapping("/Hello/pojj") @ResponseBody public Map<String,Object> protal2(){ User user1 = new User("zly","18","nan"); User user2 = new User("zly","18","nan"); User user3 = new User("zly","18","nan"); Map<String, Object> stringObjectMap = new HashMap<>(); stringObjectMap.put("1",user1); stringObjectMap.put("1",user2); stringObjectMap.put("1",user3); return stringObjectMap; } 直接将map返回给前端,以此类推
@RestController
复合注解,写在类上,相当于写了@Controller注解和所有方法写了@ResponseBody注解
文件上传和下载
文件下载:
通过ResponseEntity实现
@RequestMapping("/testDown") public ResponseEntity<byte[]> testResponseEntity(HttpSession session) throws IOException { //获取ServletContext对象 ServletContext servletContext = session.getServletContext(); //获取服务器中文件的真实路径 String realPath = servletContext.getRealPath("/static/img/panda.jpg"); //创建输入流 InputStream is = new FileInputStream(realPath); //创建字节数组 byte[] bytes = new byte[is.available()]; //将流读到字节数组中 is.read(bytes); //创建HttpHeaders对象设置响应头信息 MultiValueMap<String, String> headers = new HttpHeaders(); //设置要下载方式以及下载文件的名字 headers.add("Content-Disposition", "attachment;filename=panda.jpg"); //设置响应状态码 HttpStatus statusCode = HttpStatus.OK; //创建ResponseEntity对象 ResponseEntity<byte[]> responseEntity = new ResponseEntity<>(bytes, headers, statusCode); //关闭输入流 is.close(); return responseEntity; } 使用时只需更改文件所在路径即可String realPath = servletContext.getRealPath("/static/img/panda.jpg"); 图片放在webapp中的img文件夹下
文件上传:
首先导入依赖
<dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.4</version> </dependency>
然后再mvc.xml文件中配置,id一定要是multipartResolver
<bean id="MultipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> </bean> CommonsMultipartResolver
然后写前端和后端
表单配置时一定要有enctype="multipart/form-data
<form action="shangchuan" enctype="multipart/form-data" method="post"> <input type="file" name="wenjian"> <input type="submit" value="提交"> </form>
后端参数列表中的MultipartFile wenjian一定要对应前端的name
@RequestMapping(value = "/shangchuan") public String test(MultipartFile wenjian,HttpSession session) throws IOException { // 拿到文件名Filename String Filename = wenjian.getOriginalFilename(); // 获得文件的真实路径photoPath ServletContext servletContext = session.getServletContext(); String photoPath = servletContext.getRealPath("wenjian"); // 创建photoPath的file对象 File file = new File("photoPath"); // 判断文件所对应的目录是否存在 if (!file.exists()){ file.mkdir(); } // 最终路径为真实路径+文件名 String finalpath=photoPath+File.pathSeparator+Filename; // 上传文件 wenjian.transferTo(new File(finalpath)); return "success"; } 中间创建photoPath的file对象那一块相当于加了一个判断文件目录在不在
解决文件重名覆盖的问题:
加一段代码。截取.之后的,随机生成前面的进行拼接,就不会重复
String houzhui = Filename.substring(Filename.lastIndexOf(".")); String qianmian = UUID.randomUUID().toString(); Filename=houzhui+qianmian;
uuid:唯一标识码,一串码碰撞概率很低。
总代码:
@RequestMapping(value = "/shangchuan") public String test(MultipartFile wenjian,HttpSession session) throws IOException { // 拿到文件名Filename String Filename = wenjian.getOriginalFilename(); // 解决文件名重复 String houzhui = Filename.substring(Filename.lastIndexOf(".")); String qianmian = UUID.randomUUID().toString(); Filename=houzhui+qianmian; // 获得文件的真实路径photoPath ServletContext servletContext = session.getServletContext(); String photoPath = servletContext.getRealPath("wenjian"); // 创建photoPath的file对象 File file = new File("photoPath"); // 判断文件所对应的目录是否存在 if (!file.exists()){ file.mkdir(); } // 最终路径为真实路径+文件名 String finalpath=photoPath+File.pathSeparator+Filename; // 上传文件 wenjian.transferTo(new File(finalpath)); return "success"; }
拦截器:未学,可能用可能不用
异常处理器:
基于xml配置
本身默认一种异常处理方式,如果需要自定义写xml配置
基于注解式
类上加@ControllerAdvice,将此类标记为异常处理组件
方法上写@ExceptionHandler(NullPointerException.class),括号中为异常的名称.class
@ControllerAdvice public class Excepition { @ExceptionHandler(NullPointerException.class) public String test(Throwable throwable, Model model){ model.addAttribute("th",throwable); return "success"; } }
参数列表中的Throwable throwable就是获取异常信息,然后Model model共享到请求域中
监听器:ContextLoaderListener
为什么要使用监听器,因为在使用springmvc时要注入spring,所以spring要第一个创建,而监听器是服务器启动时第一个创建。
spring提供了监听器ContextLoaderListener,实现了ServletContextListener接口,可以监听ServlerContext的状态
spring的容器是父容器,springmvc是子容器,子容器可以访问父容器的bean,父不能访问子容器的bean