目录
一、前提
设计一个图书管理系统,具有:登陆功能、显示图书列表功能、增加图书、更新图书、删除图书、批量删除图书、强制登陆。目前已经实现了前6个功能,借助myBatis,以及一些插件:分页展示、批量选择等,所涉及到的知识点前面文章已详细介绍,此处就不再详细展开了,文章后面会附上关键代码(后端响应和前端请求)。
二、实现强制登陆传统方式
除登陆页面外,在每个页面执行请求前,先根据session判断是否登陆,未登陆的返回状态码401,前端接收到401状态码后跳转到登陆页面,若已经登陆,则返回body即可,默认状态码为200。
该方式在前面博客,基于sevlet实现博客系统已实现,此处就不再编写代码了。
我们想一个问题:除登陆页面外,每次在其他页面实现请求时都要检查是否登陆,我们可否对于这些请求进行同一拦截,并进行session检验---拦截器。
三、拦截器
1、概念
是spring框架提供的功能,对于指定请求,可以在方法前后执行指定的预代码,对于请求方法前的预代码,返回结果为true是才会进入请求方法。
2、拦截器
对于强制登陆,我们希望在执行方法前就进行判断是否登陆,利用拦截器,若未登陆,结束响应。
(1)定义拦截器
实现Handlerlnterceptor接口,并重写所有方法。
①preHandle方法:目标方法执行前执行。返回true,执行目标方法;返回false,中断执行。
②postHandle方法:目标方法执行完成后执行。
③afterCompletion方法:视图渲染后执行,最后执行。
(2)注册拦截器
实现WebMvcConfigurer接口,重写addInterceptors方法。
(3)运行
3、使用拦截器实现强制登陆
(1)定义
(2)注册
(3)运行
当完成登录后就可以访问了。
4、DispatcherServlet源码解析
当tomcat服务器启动后,有一个核心类---DispatcherServlet,由他控制程序的执行顺序。
(1)该类的初始化--init方法
在其父类HttpServletBean中实现了init方法,主要用来加载 web.xml 中 DispatcherServlet 的 配置, 并调⽤⼦类的初始化。
(2)initServletBean()方法
在HttpServletBean类的init方法中,调用了其父类FrameworkServlet的initServletBean方法,主要是用来建立上下文(运行环境,WebApplicationContext容器),并加载SpringMvc配置文件中定义的bean到该容器中,最后将容器添加到ServletContext中。
(3)initStrategies()方法
在DispatcherServlet类中,调用了initDStrategies方法,该方法可以进行九大组件的初始化,如果没有配置相应的组件,就使用默认定义的组件。
(4)doDispatch()方法
DispatcherServlet类在接收到请求后,会调用doDispatch方法,进行调度,将请求转给Controller
执行步骤:①遍历所有的HandlerMapping,找到与请求对应的Handler;②遍历所有的HandlerAdapter(适配器),找到可以处理该Handler的HandlerAdapter。③执行拦截器的preHandle方法;④执行目标方法;⑤执行拦截器postHndle方法;⑥处理视图,处理完成后执行拦截器的afterCompletion方法。
四、适配器模式
上面在执行doDispatch()方法时,使用到了适配器,使得在执行不同的请求时,例如:Controller、Servlet等,这些类的调用过程类实现了适配器的接口,使得适配器对于不同请求也能做出响应。
1、适配器模式概念
用于将一个类的接口转换为客户端目标的接口。
2、适配器模式角色
(1)target:目标接口,客回端希望的接口;
(2)Adaptee:适配者,与目标接口不兼容;适配器
(3)Adapter:适配器类,通过继承或向下转型将适配者转为目标接口;
(4)client:需要使用适配器的对象。
3、适配器应用场景
可以用来补救设计上的缺陷,设计之初若解决接口不兼容的问题,就可以不使用适配器模式了。
五、统一数据格式返回
为了使代码更规范,可以使响应可以具有统一的返回格式,也提高了前端与后端人员的沟通效率。方便前端人员解析响应。
针对上述图书管理系统,进行统一数据格式返回,返回有3种状态:①未登录,401(拦截器已实现);②已登陆,但出现异常;③已登录且成功响应。
1、对后端响应结果进行统一
2、对于不同状态执行不同方法
3、统一功能实现
实现ResponseBodyAdvice接口,加上@ControllerAdvice注解,重写supports和beforeBodyWrite方法。
supports():判断是否要执行beforeBodyWrite方法,true时执行,false时不执行
beforeBodyWrite():对后端的响应结果进行从处理。
没有进行统一功能时返回的结果:
进行统一功能返回的结果:
4、统一功能实现存在的问题
(1)结果重复包装
试想一下,若我们返回的结果本来就是统一结果的类型,是否会对结果再次包装???
访问url:发现结果重复包装
做出修改:
(2)响应内容是字符串时无法进行包装
①结果是Integer类型
②结果是boolean类型
③结果是String类型
发现报错了,500是后端代码出错。查看日志:
读源码发现:当返回的数据是⾮字符串时, 使⽤的 MappingJackson2HttpMessageConverter 写⼊返回对象;当返回的数据是字符串时, StringHttpMessageConverter 会先被遍历到,这时会认为 StringHttpMessageConverter可以使⽤。由于 StringHttpMessageConverter 重写了addDefaultHe-aders方法,所以会执⾏⼦类的⽅法,然⽽⼦类 StringHttpMessageConverter 的addDefaultH-eaders⽅法定义接收参数为String, 统一结果处理后,此时UnifiedResult类型, 所以出现类型不匹配"UnifiedResult cannot be cast to java.lang.String"的异常
解决方法:将结果修改为json类型
(3)对于异常结果无法返回正确代码状态
以上出现空指针异常,服务器状态码已经为500了,失败状态,结果里的200也应该为-1,代表失败,但是返回的还是200,是因为没有对异常进行处理,使得结果调用的都是success方法,对异常处理,出现异常调用fail方法。
六、统一异常处理
@ControllerAdvice+@ResponseBody+@ExceptionHandler+异常捕获
访问url:
上面异常继承关系:NullPointerException---->RuntimeException---->Exception
注:若没有NullPointerException异常声明,则会报运行异常RuntimeException;若没有NullPointerException和RuntimeException异常声明,则会报异常Exception。
七、图书管理系统部分重要代码
1、登陆功能
(1)前端请求+处理后端响应:
(2)后端处理请求:
2、增加图书功能
(1)前端请求+处理后端响应:
(2)后端处理请求:
3、显示图书列表功能
(1)前端请求+处理后端响应:
(2)后端处理请求:
4、更新图书功能
(1)前端请求+处理后端响应:
(2)后端处理请求:
5、删除图书功能
(1)前端请求+处理后端响应:
(2)后端处理请求:
6、批量删除功能
(1)前端请求+处理后端响应:
(2)后端处理请求:
注:
若需要全部源码,可以进入:葛雅宁 (ge-yaning) - Gitee.com
点击java学习下的myBookSystem