异步调用
发送异步请求
$(function () {
$("#add_btn").on("click",function () {
var name=$("input[name='name']").val();
var username=$("input[name='username']").val();
var password=$("input[name='password']").val();
var sex=$("input[name='sex']").val();
var age=$("input[name='age']").val();
var birthday=$("input[name='birthday']").val();
var user={
name:name,
username:username,
password:password,
sex:sex,
age:age,
birthday:birthday
};
var str=JSON.stringify(user);
$.ajax({
url:"user/ajax_add",
type:"post",
data:str,
dataType:"json",//服务器返回的数据类型是json格式字符串
contentType:"application/json",//客户端传递参数格式是json格式字符串
success:function (data){
$("#id").html(data.id);
$("#name").html(data.name);
$("#username").html(data.username);
$("#password").html(data.password);
$("#sex").html(data.sex);
$("#age").html(data.age);
$("#birthday").html(data.birthday);
$("#createTime").html(data.createTime);
alert(data);
}
})
});
});
接受异步请求参数
@RequestBody
类型: 形参注解
位置:处理器类中的方法形参前方
作用:将异步提交数据组织成标准请求参数格式,并赋值给形参
范例:
@RequestMapping("/ajax_add")
//@ResponseBody 把服务器端的java对象转换成json格式字符串发送到客户端
@ResponseBody
//@RequestBody把客户端传递的json格式字符串转换成java对象
public User add2(@RequestBody User user){
System.out.println(user);
return user;
}
- 注解添加到Pojo参数前方时,封装的异步提交数据按照Pojo的属性格式进行关系映射
- 注解添加到集合参数前方时,封装的异步提交数据按照集合的存储结构进行关系映射
@RequestMapping("/ajaxPojoToController")
//如果处理参数是POJO,且页面发送的请求数据格式与POJO中的属性对应,@RequestBody注解可以自动映射对应请求数据到POJO中
//注意:POJO中的属性如果请求数据中没有,属性值为null,POJO中没有的属性如果请求数据中有,不进行映射
public String ajaxPojoToController(@RequestBody User user){
System.out.println("controller pojo :"+user);
return "page.jsp";
}
@RequestMapping("/ajaxListToController")
//如果处理参数是List集合且封装了POJO,且页面发送的数据是JSON格式的对象数组,数据将自动映射到集合参数中
public String ajaxListToController(@RequestBody List<User> userList){
System.out.println("controller list :"+userList);
return "page.jsp";
}
异步请求接受响应数据
- 方法返回值为Pojo时,自动封装数据成json对象数据
@RequestMapping("/ajaxReturnJson")
@ResponseBody
public User ajaxReturnJson(){
System.out.println("controller return json pojo...");
User user = new User();
user.setName("Jockme");
user.setAge(40);
return user;
}
- 方法返回值为List时,自动封装数据成json对象数组数据
@RequestMapping("/ajaxReturnJsonList")
@ResponseBody
//基于jackon技术,使用@ResponseBody注解可以将返回的保存POJO对象的集合转成json数组格式数据
public List ajaxReturnJsonList(){
System.out.println("controller return json list...");
User user1 = new User();
user1.setName("Tom");
user1.setAge(3);
User user2 = new User();
user2.setName("Jerry");
user2.setAge(5);
ArrayList al = new ArrayList();
al.add(user1);
al.add(user2);
return al;
}
拦截器
拦截器概念
拦截器( Interceptor)是一种动态拦截方法调用的机制
作用:
1. 在指定的方法调用前后执行预先设定后的的代码
2. 阻止原始方法的执行
核心原理: AOP思想
拦截器链:多个拦截器按照一定的顺序,对原始被调用功能进行增强
拦截器VS过滤器
- 归属不同: Filter属于Servlet技术, Interceptor属于SpringMVC技术
- 拦截内容不同: Filter对所有访问进行增强, Interceptor仅针对SpringMVC的访问进行增强
自定义拦截器开发过程
- 实现HandlerInterceptor接口
//自定义拦截器需要实现HandleInterceptor接口
public class MyInterceptor implements HandlerInterceptor {
//处理器运行之前执行
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
System.out.println("前置运行----a1");
//返回值为false将拦截原始处理器的运行
//如果配置多拦截器,返回值为false将终止当前拦截器后面配置的拦截器的运行
return true;
}
//处理器运行之后执行
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("后置运行----b1");
}
//所有拦截器的后置执行全部结束后,执行该操作
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) throws Exception {
System.out.println("完成运行----c1");
}
//三个方法的运行顺序为 preHandle -> postHandle -> afterCompletion
//如果preHandle返回值为false,三个方法仅运行preHandle
}
- 配置拦截器
<!--配置自定义拦截器,拦截器业务处理类和拦截的请求地址/**指根路径下所有的请求,不管目录层数-->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.entor.interceptor.TestInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
<mvc:interceptors>
<!--开启具体的拦截器的使用,可以配置多个-->
<mvc:interceptor>
<!--设置拦截器的拦截路径,支持*通配-->
<!--/** 表示拦截所有映射-->
<!--/* 表示拦截所有/开头的映射-->
<!--/user/* 表示拦截所有/user/开头的映射-->
<!--/user/add* 表示拦截所有/user/开头,且具体映射名称以add开头的映射-->
<!--/user/*All 表示拦截所有/user/开头,且具体映射名称以All结尾的映射-->
<mvc:mapping path="/*"/>
<mvc:mapping path="/**"/>
<mvc:mapping path="/handleRun*"/>
<!--设置拦截排除的路径,配置/**或/*,达到快速配置的目的-->
<mvc:exclude-mapping path="/b*"/>
<!--指定具体的拦截器类-->
<bean class="MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
注意:配置顺序为先配置执行位置,后配置执行类
拦截器配置与方法参数
前置处理方法
原始方法之前运行
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler//目标对象,也就是要访问controller里的方法
) throws Exception {
System.out.println("preHandle");
return true;
}
参数
request:请求对象
response:响应对象
handler:被调用的处理器对象,本质上是一个方法对象,对反射中的Method对象进行了再包装
返回值
返回值为false,被拦截的处理器将不执行
后置处理方法
原始方法运行后运行,如果原始方法被拦截,则不执行
public void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("postHandle");
}
参数
modelAndView:如果处理器执行完成具有返回结果,可以读取到对应数据与页面信息,并进行调整
完成处理方法
拦截器最后执行的方法,无论原始方法是否执行
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) throws Exception {
System.out.println("afterCompletion");
}
参数
ex:如果处理器执行过程中出现异常对象,可以针对异常情况进行单独处理
多拦截器配置
责任链模式
责任链模式是一种行为模式
特征:
沿着一条预先设定的任务链顺序执行,每个节点具有独立的工作任务
优势:
独立性:只关注当前节点的任务,对其他任务直接放行到下一节点
隔离性:具备链式传递特征,无需知晓整体链路结构,只需等待请求到达后进行处理即可
灵活性:可以任意修改链路结构动态新增或删减整体链路责任
解耦:将动态任务与原始任务解耦
弊端:
链路过长时,处理效率低下
可能存在节点上的循环引用现象,造成死循环,导致系统崩溃
异常处理
HandlerExceptionResolver接口(异常处理器)
@Component
public class ExceptionResolver implements HandlerExceptionResolver {
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) {
System.out.println("异常处理器正在执行中");
ModelAndView modelAndView = new ModelAndView();
//定义异常现象出现后,反馈给用户查看的信息
modelAndView.addObject("msg","出错啦! ");
//定义异常现象出现后,反馈给用户查看的页面
modelAndView.setViewName("error.jsp");
return modelAndView;
}
}
根据异常的种类不同,进行分门别类的管理,返回不同的信息
public class ExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) {
System.out.println("my exception is running ...."+ex);
ModelAndView modelAndView = new ModelAndView();
if( ex instanceof NullPointerException){
modelAndView.addObject("msg","空指针异常");
}else if ( ex instanceof ArithmeticException){
modelAndView.addObject("msg","算数运算异常");
}else{
modelAndView.addObject("msg","未知的异常");
}
modelAndView.setViewName("error.jsp");
return modelAndView;
}
}
注解开发异常处理器
使用注解实现异常分类管理
名称: @ControllerAdvice
类型: 类注解
位置:异常处理器类上方
作用:设置当前类为异常处理器类
使用注解实现异常分类管理
名称: @ExceptionHandler
类型: 方法注解
位置:异常处理器类中针对指定异常进行处理的方法上方
作用:设置指定异常的处理方式
范例:
说明:处理器方法可以设定多个
范例:
/**
* @ControllerAdvice+@ExceptionHandler实现全局异常捕获,系统其他类方法中发生异常都会被捕获到
*/
//@ControllerAdvice
@RestControllerAdvice//相当于@ControllerAdvice+@ExceptionHandler
public class GlobalException {
@ExceptionHandler(Exception.class)
public String exception(){
return "exception";
}
@ExceptionHandler(RuntimeException.class)
public String runtimeException(){
return "runtimeException";
}
@ExceptionHandler(NullPointerException.class)
public String nullPointerException(){
return "nullPointerException";
}
@ExceptionHandler(ArithmeticException.class)
public String arithmeticException(){
return "arithmeticException";
}
}
文件上传下载
MultipartResolver接口
- MultipartResolver接口定义了文件上传过程中的相关操作,并对通用性操作进行了封装
- MultipartResolver接口底层实现类CommonsMultipartResovler
- CommonsMultipartResovler并未自主实现文件上传下载对应的功能,而是调用了apache的文件上传下载组件
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
文件上传
controller代码
<body>
<form method="post" enctype="multipart/form-data" action="uploadfile">
文件:<input type="file" name="file"><br>
文件:<input type="file" name="file"><br>
文件:<input type="file" name="file"><br>
<input type="submit" value="上传"/>
</form>
</body>
jsp页面
@Controller
public class UploadController {
@RequestMapping("/uploadfile")
public String uploadFile(MultipartFile[] file, HttpServletRequest request){
//获取服务器存放上传文件的绝对路径
String realPath = request.getServletContext().getRealPath("upload");
File f=new File(realPath);
if(!f.exists()){//判断文件是否存在,不存在则创建
f.mkdirs();
}
System.out.println(f);
for(MultipartFile ff:file){
//获取上传文件的名称
String filename = ff.getOriginalFilename();
//获取上传文件的大小
long size = ff.getSize();
//获取上传文件类型
String contentType = ff.getContentType();
try {
ff.transferTo(new File(f,filename));
} catch (IOException e) {
e.printStackTrace();
}
}
return "success";
}
}
文件下载
jsp页面
<head>
<title>文件下载</title>
</head>
<body>
<a href="downLoad?filename=4.jpg">4.jpg</a><br>
<a href="downLoad?filename=3.jpg">3.jpg</a><br>
<a href="downLoad?filename=1.jpg">1.jpg</a><br>
</body>
</html>
controller代码
public class DownloadController {
@RequestMapping("/downLoad")
public ResponseEntity<byte[]> downLoad(HttpServletRequest request, HttpServletResponse response , String filename ) throws IOException {
//服务器上下载文件存放绝对路径地址
String realPath = request.getServletContext().getRealPath("upload");
//下载文件对象
File file=new File(realPath,filename);
FileInputStream fis = new FileInputStream(file);
//创建文件大小的字节数组缓冲区
byte[] b = new byte[fis.available()];
//把文件内容读取到数组中
fis.read(b);
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add("Content-Disposition","attachment;filename="+ URLEncoder.encode(file.getName(),"utf-8"));
return new ResponseEntity<>(b,httpHeaders, HttpStatus.OK);
}