SpringMVC进阶
ajax异步交互:
当我们操作json数据时
Springmvc默认用MappingJackson2HttpMessageConverter对json数据进行转换,但对应操作并没有,只是提供了平台
所以要进行json的操作,需要加入jackson的包,同时使用 <mvc:annotation-driven />,进行激活使用
< dependency>
< groupId> com.fasterxml.jackson.core</ groupId>
< artifactId> jackson-databind</ artifactId>
< version> 2.9.8</ version>
</ dependency>
< dependency>
< groupId> com.fasterxml.jackson.core</ groupId>
< artifactId> jackson-core</ artifactId>
< version> 2.9.8</ version>
</ dependency>
< dependency>
< groupId> com.fasterxml.jackson.core</ groupId>
< artifactId> jackson-annotations</ artifactId>
< version> 2.9.0</ version>
</ dependency>
这样我们就可以实现进行json操作了
@RequestBody:
该注解用于Controller的方法的形参声明,当使用ajax提交并指定contentType为json形式时
通过HttpMessageConverter接口转换为对应的POJO对象
<%--
Created by IntelliJ IDEA.
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<script src="${pageContext.request.contextPath}/js/jquery-3.4.1.min.js"></script>
<%--
注意:不要自关闭,这是html的约束,因为script在html里中是设置了不能写成自关闭的约束
否则后面的内容基本都不会显示,即不会读取了(因为全部当成了script的内容,src失效,即可以当作)
也要注意:当src属性和script标签内容一起时,内容忽略,即相当于没有写
--%>
<button id="btn">ajax提交</button>
<script>
$("#btn").click(function(){
let url = '${pageContext.request.contextPath}/user/ajax';
let data = '[{"id":1,"username":"张三"},{"id":2,"username":"李四"}]'
$.ajax({
type:'POST',//大小写可以忽略
url:url,
data:data,
contentType:'application/json;charset=utf-8',
//设置发送的数据类型,这样data必须是字符串,因为不是字符串的话,一般会认为是键值对,而不是json
//那么后台可能就会解析出错(单纯String应该不会),使得报错,当然了
//发送默认的数据类型的话,那么就不能够是字符串了,而必须是键值对
//这主要是对应的发送数据类型使得后台的不同解析
success:function (data) {
console.log(data);
}
})
})
</script>
</body>
</html>
@RequestMapping ( "ajax" )
public void ajax ( @RequestBody List < User > list) {
System . out. println ( list) ;
}
@ResponseBody (通常这个注解不能使用两次,否则一般会报错,具体看情况,可以测试一下,可能这是规定,以后或者其他spring版本并不确定,所以是通常,而不是一定):
该注解用于将Controller的方法返回的对象,通过HttpMessageConverter接口转换为指定格式的数据
如:json,xml等,通过Response响应给客户端
部分jsp的修改:
success : function ( data ) {
console. log ( data) ;
alert ( JSON . stringify ( data) ) ;
}
部分java的修改:
@ResponseBody
public List < User > ajax ( @RequestBody List < User > list) throws IOException {
System . out. println ( list) ;
return list;
}
RESTful:
什么是RESTful :
Restful是一种软件架构风格、设计风格,而不是标准,只是提供了一组设计原则和约束条件
主要用于客户端和服务器交互类的软件,基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存机制等
Restful风格的请求是使用"url+请求方式"表示一次请求目的的
HTTP 协议里面四个表示操作方式的动词如下:
GET:读取(Read)
POST:新建(Create)
PUT:更新(Update)
DELETE:删除(Delete)
注意:他们的作用实际上是一样的,除了post不是操作在url里面,我们可以看成是语义化
什么是语义化:指的是,同一个事务,用不同的单词来操作,只是该单词的意思可以让开发者知道,只是用来干什么的
也就是说,当我们操作DELETE时,虽然与get请求一样,但是我们一看就知道,这个请求是操作删除
下图基本是mvc需要处理的风格,具体看后面代码的处理(其他语言或者框架的实现可能有所不同)
代码实现:
@PathVariable
用来接收RESTful风格请求地址中占位符的值
@RestController
RESTful风格多用于前后端分离项目开发,前端通过ajax与服务器进行异步交互
我们处理器通常返回的是json数据,所以使用@RestController来替代@Controller和@ResponseBody两个注解
package com. lagou. controller ;
import org. springframework. web. bind. annotation. * ;
@RestController
@RequestMapping ( "/restful" )
public class RestfulController {
@GetMapping ( "/user/{id}/{q}" )
public String restful ( @PathVariable Integer id, @PathVariable Integer q) {
System . out. println ( q) ;
return "findById:" + id;
}
@PostMapping ( "/user" )
public String post ( ) {
return "post" ;
}
@PutMapping ( "/user" )
public String put ( ) {
return "put" ;
}
@DeleteMapping ( "/user" )
public String delete ( ) {
return "delete" ;
}
}
文件上传:
文件上传三要素
表单项 type=“file”
表单的提交方式 method=“POST”,get后面的不可以操作文件
表单的enctype属性是多部分表单形式 enctype=“multipart/form-data"
使得多种类型一起操作,如可以加上二进制(如图片的上传,只是编码一定会显示,使得他通过对应特殊流来获取数据)
因为需要字节流,而多多部分表单形式就是字节的读取
普通的,即默认的是字符读取,所以使用文件时,一般使用多部分表单形式
若你强行不用,而使用默认的,那么浏览器并不会将对应文件的数据给出
而是直接给出名称,是为了数据的完整性,这是浏览器的设置
文件上传原理 :
当form表单修改为多部分表单时,request.getParameter()将失效
主要是通过字符流获得的,在字符流中的对应操作中,可以看成一个字符流操作的对象中
多部分表单中,不可以用request.getParameter(),就算使用时也是获取不到的
主要是通过字节流获得的,在字节流中的对应操作中,可以看成一个字节流操作的对象中
当form表单的enctype取值为 application/x-www-form-urlencoded 时,这是默认的,
则form表单的正文内容格式是: name=value&name=value(这就相当于字符流的的操作)
当form表单的enctype取值为 mutilpart/form-data 时(这就相当于字节流的的操作)
请求正文内容就变成多部分形式:
单文件上传 :
步骤分析:
导入fileupload和io坐标:
< dependency>
< groupId> commons-fileupload</ groupId>
< artifactId> commons-fileupload</ artifactId>
< version> 1.3.3</ version>
</ dependency>
< dependency>
< groupId> commons-io</ groupId>
< artifactId> commons-io</ artifactId>
< version> 2.6</ version>
</ dependency>
配置文件上传解析器 :
< bean id = " multipartResolver"
class = " org.springframework.web.multipart.commons.CommonsMultipartResolver" >
< property name = " maxUploadSize" value = " 5242880" > </ property>
< property name = " maxInMemorySize" value = " 40960" > </ property>
</ bean>
jsp页面:
<%--
Created by IntelliJ IDEA.
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%--
满足文件上传三要素的表单:
表单的提交方式要是post
表单的enctype要是multipart/form-data
表单的编码要是multipart/form-data
--%>
<form action="${pageContext.request.contextPath}/fileUpload" method="post"
enctype="multipart/form-data">
名称:<input type="text" name="username"> <br>
文件:<input type="file" name="filePic"> <br>
<input type="submit" value="单文件上传">
</form>
</body>
</html>
编写文件上传代码:
package com. lagou. controller ;
import org. springframework. stereotype. Controller ;
import org. springframework. web. bind. annotation. RequestMapping ;
import org. springframework. web. multipart. MultipartFile ;
import java. io. File ;
import java. io. FileReader ;
import java. io. IOException ;
@Controller
public class FileUploadController {
@RequestMapping ( "/fileUpload" )
public String fileUpload ( String username, MultipartFile filePic) throws IOException {
System . out. println ( username) ;
String originalFilename = filePic. getOriginalFilename ( ) ;
filePic. transferTo ( new File ( "D:/upload/" + originalFilename) ) ;
return "success" ;
}
}
多文件上传 :
jsp代码:
<%--实现多文件上传--%>
<form action="${pageContext.request.contextPath}/filesUpload" method="post"
enctype="multipart/form-data">
名称:<input type="text" name="username"> <br>
文件1:<input type="file" name="filePic"> <br>
文件2:<input type="file" name="filePic"> <br>
<input type="submit" value="多文件上传">
</form>
java代码:
@RequestMapping ( "/filesUpload" )
public String filesUpload ( String username, MultipartFile [ ] filePic) throws IOException {
System . out. println ( username) ;
for ( MultipartFile multipartFile : filePic) {
String originalFilename = multipartFile. getOriginalFilename ( ) ;
multipartFile. transferTo ( new File ( "D:/upload/" + originalFilename) ) ;
}
return "success" ;
}
上面的操作springmvc帮我们封装好了对应读取文件的操作了,所以我们只要创建对应输出流来操作即可
异常处理:
异常处理的思路
在Java中,对于异常的处理一般有两种方式:
一种是当前方法捕获处理(try-catch),这种处理方式会造成业务代码和异常处理代码的耦合
另一种是自己不处理,而是抛给调用者处理(throws),调用者再抛给它的调用者,也就是一直向上抛
在这种方法的基础上,衍生出了SpringMVC的异常处理机制
系统的dao、service、controller出现都通过throws Exception向上抛出
最后由springmvc前端控制器交由异常处理器进行异常处理
如下图:
请求还是不会经过这个异常处理器,后面会有对应的操作,来实现请求的时候,指向异常页面
自定义异常处理器:
步骤分析:
创建异常处理器类实现HandlerExceptionResolver:
package com. lagou. exception ;
import org. springframework. web. servlet. HandlerExceptionResolver ;
import org. springframework. web. servlet. ModelAndView ;
import javax. servlet. http. HttpServletRequest ;
import javax. servlet. http. HttpServletResponse ;
public class GlobalExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException ( HttpServletRequest httpServletRequest, HttpServletResponse
httpServletResponse, Object handler, Exception e) {
ModelAndView modelAndView = new ModelAndView ( ) ;
modelAndView. addObject ( "error" , e. getMessage ( ) ) ;
modelAndView. setViewName ( "error" ) ;
return modelAndView;
}
}
配置异常处理器 :
< bean id = " globalExceptionResolver" class = " com.lagou.exception.GlobalExceptionResolver" > </ bean>
编写异常页面 :
<%--
Created by IntelliJ IDEA.
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
这是异常页面
${error}
</body>
</html>
测试异常跳转:
package com. lagou. controller ;
import org. springframework. stereotype. Controller ;
import org. springframework. web. bind. annotation. RequestMapping ;
@Controller
public class ExceptionController {
@RequestMapping ( "/testException" )
public String testException ( ) {
int i = 1 / 0 ;
return "success" ;
}
}
web的处理异常机制:
< error-page>
< error-code> 404</ error-code>
< location> /404.jsp</ location>
</ error-page>
< error-page>
< error-code> 500</ error-code>
< location> /500.jsp</ location>
</ error-page>
拦截器:
拦截器(interceptor)的作用
Spring MVC 的拦截器类似于 Servlet 开发中的过滤器 Filter,用于对处理器进行预处理和后处理
将拦截器按一定的顺序联结成一条链,这条链称为拦截器链(InterceptorChain)
在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用
拦截器也是AOP思想的具体实现
拦截器和过滤器区别:
关于interceptor和filter的区别,如图所示:
主要的是过滤器是监听请求信息和响应信息,而进行后续操作(请求和响应进行过滤后,才到服务器和浏览器)
而拦截器是操作方法的增强,从而实现过滤,所以只能操作控制器方法,所以可以看到,响应数据一般不会被进行拦截
使得一次请求全部执行
快速入门:
步骤分析:
创建拦截器类实现HandlerInterceptor接口:
package com. lagou. 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 MyInterceptor1 implements HandlerInterceptor {
@Override
public boolean preHandle ( HttpServletRequest request, HttpServletResponse response, Object
handler) throws Exception {
System . out. println ( "preHandle1...." ) ;
return true ;
}
@Override
public void postHandle ( HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System . out. println ( "postHandle1...." ) ;
}
@Override
public void afterCompletion ( HttpServletRequest request, HttpServletResponse response, Object
handler, Exception ex) throws Exception {
System . out. println ( "afterCompletion1...." ) ;
}
}
配置拦截器 :
< mvc: interceptors>
< mvc: interceptor>
< mvc: mapping path = " /**" />
< bean class = " com.lagou.interceptor.MyInterceptor1" > </ bean>
</ mvc: interceptor>
</ mvc: interceptors>
测试拦截器的拦截效果
编写Controller,发请求到controller,跳转页面
package com. lagou. controller ;
import org. springframework. stereotype. Controller ;
import org. springframework. web. bind. annotation. RequestMapping ;
@Controller
public class TargetController {
@RequestMapping ( "/target" )
public String targetMethod ( ) {
System . out. println ( "目标方法执行了...." ) ;
return "success" ;
}
}
修改对应jsp页面:
<%--
Created by IntelliJ IDEA.
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h3>嘿嘿嘿</h3>
${name}
<%
System.out.println(1);
%>
</body>
</html>
拦截器链 :
开发中拦截器可以单独使用,也可以同时使用多个拦截器形成一条拦截器链
开发步骤和单个拦截器是一样的,只不过注册的时候注册多个,注意这里注册的顺序就代表拦截器执行的顺序
同上,再编写一个MyHandlerInterceptor2操作,测试执行顺序:
< mvc: interceptors>
< mvc: interceptor>
< mvc: mapping path = " /**" />
< bean class = " com.lagou.interceptor.MyInterceptor1" > </ bean>
</ mvc: interceptor>
< mvc: interceptor>
< mvc: mapping path = " /**" />
< bean class = " com.lagou.interceptor.MyInterceptor2" > </ bean>
</ mvc: interceptor>
</ mvc: interceptors>
对应java代码:
package com. lagou. 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 MyInterceptor2 implements HandlerInterceptor {
@Override
public boolean preHandle ( HttpServletRequest request, HttpServletResponse response, Object
handler) throws Exception {
System . out. println ( "preHandle2...." ) ;
return true ;
}
@Override
public void postHandle ( HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System . out. println ( "postHandle2...." ) ;
}
@Override
public void afterCompletion ( HttpServletRequest request, HttpServletResponse response, Object
handler, Exception ex) throws Exception {
System . out. println ( "afterCompletion2...." ) ;
}
}
执行结果:
知识小结:
拦截器中的方法说明如下:
一般来说get请求,对请求参数格式并无关系,所以无论是json形式的字符串还是直接的字符串基本都可以操作