• 超详细SpringMVC 之 JSR303与拦截器


    一,JSR303

    简介

    JSR303是做服务端校验 参数验证

    实现服务器校验步骤

    1.做服务端参数校验 JSR303 的jar包依赖

    1. <groupId>org.hibernategroupId>
    2. <artifactId>hibernate-validatorartifactId>
    3. <version>6.0.7.Finalversion>

    后台

    @NotNull :作用于基本数据类型
    @NotEmpty    作用于集合
    @NotBlank    作用于字符串

    实体类

    Clazz

    @NotNull(message = "cid不能为空")  message 提示语句

    1. package com.ljj.ssm.model;
    2. import javax.validation.constraints.NotBlank;
    3. import javax.validation.constraints.NotNull;
    4. /**
    5. * @NotNull :作用于基本数据类型
    6. * @NotEmpty 作用于集合
    7. * @NotBlank 作用于字符串
    8. */
    9. public class Clazz {
    10. @NotNull(message = "cid不能为空")
    11. protected Integer cid;
    12. @NotBlank(message = "班级名称不能为空")
    13. protected String cname;
    14. @NotBlank(message = "教员老师不能为空")
    15. protected String cteacher;
    16. protected String pic;
    17. public Clazz(Integer cid, String cname, String cteacher, String pic) {
    18. this.cid = cid;
    19. this.cname = cname;
    20. this.cteacher = cteacher;
    21. this.pic = pic;
    22. }
    23. public Clazz() {
    24. super();
    25. }
    26. public Integer getCid() {
    27. return cid;
    28. }
    29. public void setCid(Integer cid) {
    30. this.cid = cid;
    31. }
    32. public String getCname() {
    33. return cname;
    34. }
    35. public void setCname(String cname) {
    36. this.cname = cname;
    37. }
    38. public String getCteacher() {
    39. return cteacher;
    40. }
    41. public void setCteacher(String cteacher) {
    42. this.cteacher = cteacher;
    43. }
    44. public String getPic() {
    45. return pic;
    46. }
    47. public void setPic(String pic) {
    48. this.pic = pic;
    49. }
    50. }

    ClazzController  

    1. /**
    2. * @Valid 是与实体类中的服务端校验 注解配合使用的
    3. * BindingResult 存放了所有违背校验的错误信息
    4. * @param clazz
    5. * @param bindingResult
    6. * @return
    7. */
    8. @RequestMapping("/valiAdd ")
    9. public String valiAdd(@Valid Clazz clazz, BindingResult bindingResult){
    10. if(bindingResult.hasErrors()){
    11. //违背了规则
    12. //拿到所有错误
    13. List fieldErrors = bindingResult.getFieldErrors();
    14. for (FieldError fieldError : fieldErrors) {
    15. // cid : cid不能为空
    16. System.out.println(fieldError.getField() + ":" + fieldError.getDefaultMessage());
    17. }
    18. }else{//反之没有违背
    19. this.clazzBiz.insertSelective(clazz);
    20. }
    21. return "redirect:/clz/list";
    22. }

    界面代码:

    1. <%@ page language="java" contentType="text/html; charset=UTF-8"
    2. pageEncoding="UTF-8"%>
    3. html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    4. <html>
    5. <head>
    6. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    7. <title>title>
    8. head>
    9. <body>
    10. <form action="${pageContext.request.contextPath }/clz/${empty b ? 'valiAdd' : 'edit'}" method="post">
    11. cid:<input type="text" name="cid" value="${b.cid }"><br>
    12. cname:<input type="text" name="cname" value="${b.cname }"><br>
    13. cteacher:<input type="text" name="cteacher" value="${b.cteacher }"><br>
    14. <input type="submit">
    15. form>
    16. body>
    17. html>

    现在我们尝试直接提交:在debug后台会有错误警告

     我们需要将后台的错误提示展示到文本框的后面,我们就需要完善ClazzController类 中的valiAdd方法

    1. /**
    2. * @Valid 是与实体类中的服务端校验 注解配合使用的
    3. * BindingResult 存放了所有违背校验的错误信息
    4. * @param clazz
    5. * @param bindingResult
    6. * @return
    7. */
    8. @RequestMapping("/valiAdd")
    9. public String valiAdd(@Valid Clazz clazz, BindingResult bindingResult,HttpServletRequest request){
    10. if(bindingResult.hasErrors()){//违背了规则
    11. Map msg=new HashMap();
    12. //拿到所有错误
    13. List fieldErrors = bindingResult.getFieldErrors();
    14. for (FieldError fieldError : fieldErrors) {
    15. // cid : cid不能为空
    16. System.out.println(fieldError.getField() + ":" + fieldError.getDefaultMessage());
    17. //相当于 msg.put(cid,cid不能为空);
    18. msg.put(fieldError.getField(),fieldError.getDefaultMessage());
    19. }
    20. request.setAttribute("msg",msg);
    21. //如果出现了错误,应该将提示语显示在表单元素后方
    22. return "clzEdit";
    23. }else{//反之没有违背
    24. this.clazzBiz.insertSelective(clazz);
    25. }
    26. return "redirect:/clz/list";
    27. }

    改进前端代码:

    1. <%@ page language="java" contentType="text/html; charset=UTF-8"
    2. pageEncoding="UTF-8"%>
    3. html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    4. <html>
    5. <head>
    6. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    7. <title>title>
    8. head>
    9. <body>
    10. <form action="${pageContext.request.contextPath }/clz/${empty b ? 'valiAdd' : 'edit'}" method="post">
    11. cid:<input type="text" name="cid" value="${b.cid }"><span style="color: red">${msg.cid}span><br>
    12. cname:<input type="text" name="cname" value="${b.cname }"><span style="color: red">${msg.cname}span><br>
    13. cteacher:<input type="text" name="cteacher" value="${b.cteacher }"><span style="color: red">${msg.cteacher}span><br>
    14. <input type="submit">
    15. form>
    16. body>
    17. html>

    此时我们再次尝试直接提交(错误提示展示到了页面中)

     总结

    1.pom依赖导入
    2.在待校验的数据库列段对应的实体属性打上校验标签
    3.在controller层,方法上添加@valid注解配合前面的校验标签,添加bindingResult,此对象包含了所有校验未通过的错误信息
    4.可以将所有的错误信息以map集合的方式保存,并且传递到前台页面展示

    二,拦截器

    1.什么是拦截器

    SpringMVC的处理器拦截器,类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理(AOP中的环绕通知)。

    依赖于web框架,在实现上基于Java的反射机制,属于面向切面编程(AOP)的一种运用。由于拦截器是基于
      web框架的调用,因此可以使用Spring的依赖注入(DI)进行一些业务操作,同时一个拦截器实例在一个 
      controller生命周期之内可以多次调用。

    2、拦截器与过滤器

    2.1、什么是过滤器(Filter)

    依赖于servlet容器。在实现上基于函数回调,可以对几乎所有请求进行过滤,但是缺点是一个过滤器实例只能在容器初始化时调用一次。使用过滤器的目的是用来做一些过滤操作,比如:在过滤器中修改字符编码;
    在过滤器中修改HttpServletRequest的一些参数,包括:过滤低俗文字、危险字符等

    2.2、拦截器与过滤器的区别

    过滤器(filter):

            1) filter属于Servlet技术,只要是web工程都可以使用

            2) filter主要对所有请求过滤

            3) filter的执行时机早于Interceptor

    拦截器(interceptor):

            1) interceptor属于SpringMVC技术,必须要有SpringMVC环境才可以使用

            2) interceptor通常对处理器Controller进行拦截

            3) interceptor只能拦截dispatcherServlet处理的请求

     3、应用场景

    1)日志记录:记录请求信息的日志,以便进行信息监控、信息统计、计算PV(Page View)等。

    2)权限检查:如登录检测,进入处理器检测是否登录,如果没有直接返回到登录页面;

    3)性能监控:有时候系统在某段时间莫名其妙的慢,可以通过拦截器在进入处理器之前记录开始时间,在处理完后记录结束时间,从而得到该请求的处理时间(如果有反向代理,如apache可以自动记录);

     4)通用行为:读取cookie得到用户信息并将用户对象放入请求,从而方便后续流程使用,还有如提取Locale、Theme信息等,只要是多个Controller中的处理方法都需要的,我们就可以使用拦截器实现

     4.拦截器快速入门

     1、创建HelloController
      2、创建自定义拦截器并实现HandlerInterceptor接口

    OneHandlerInterceptor:

    1. package com.ljj.ssm.intercept;
    2. import org.springframework.web.servlet.HandlerInterceptor;
    3. import org.springframework.web.servlet.ModelAndView;
    4. import javax.servlet.http.HttpServletRequest;
    5. import javax.servlet.http.HttpServletResponse;
    6. /**
    7. * @author ljj
    8. * @site https://blog.csdn.net
    9. * @qq 1828190940
    10. * @create 2022-08-19 19:04
    11. */
    12. public class OneHandlerInterceptor implements HandlerInterceptor {
    13. @Override
    14. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    15. //预处理
    16. System.out.println("[OneHandlerInterceptor] . preHandle"+"---预处理");
    17. return true;
    18. }
    19. @Override
    20. public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    21. //后处理
    22. System.out.println("[OneHandlerInterceptor] . postHandle"+"---后处理");
    23. }
    24. @Override
    25. public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    26. //完成后执行
    27. System.out.println("[OneHandlerInterceptor] . afterCompletion"+"---完成后执行");
    28. }
    29. }

    注意,此时preHandle 预处理方法的返回值是true 

    复制该类全路径进入Spring-servlet.xml进行配置

    1. <mvc:interceptors>
    2. <bean class="com.ljj.ssm.intercept.OneHandlerInterceptor">bean>
    3. mvc:interceptors>

    创建HelloController

    1. package com.ljj.ssm.controller;
    2. import org.springframework.stereotype.Controller;
    3. import org.springframework.web.bind.annotation.RequestMapping;
    4. /**
    5. * @author ljj
    6. * @site https://blog.csdn.net
    7. * @qq 1828190940
    8. * @create 2022-08-19 19:19
    9. */
    10. @Controller
    11. public class HelloController {
    12. @RequestMapping("/hello")
    13. public String hello(){
    14. System.out.println("进入业务方法...");
    15. return "index";
    16. }
    17. }

     测试结论:

    看向控制台,我们可以看到方法的执行顺序 ,先执行预处理->后处理->完成后执行方法

    我们可以看到预处理和后处理相当于前置通知和后置通知,所以就有一个结论我们的拦截器

    就相当于AOP处理

     我们将preHandle 预处理方法的返回值改成false

    运行结果:

     控制器打印结果:

     5、拦截器链(多拦截器)

    TwoHandlerInterceptor

    1. package com.ljj.ssm.intercept;
    2. import org.springframework.web.servlet.HandlerInterceptor;
    3. import org.springframework.web.servlet.ModelAndView;
    4. import javax.servlet.http.HttpServletRequest;
    5. import javax.servlet.http.HttpServletResponse;
    6. /**
    7. * @author ljj
    8. * @site https://blog.csdn.net
    9. * @qq 1828190940
    10. * @create 2022-08-19 19:04
    11. */
    12. public class TwoHandlerInterceptor implements HandlerInterceptor {
    13. @Override
    14. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    15. //预处理
    16. System.out.println("[TwoHandlerInterceptor] . preHandle"+"---预处理");
    17. return true;
    18. }
    19. @Override
    20. public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    21. //后处理
    22. System.out.println("[TwoHandlerInterceptor] . postHandle"+"---后处理");
    23. }
    24. @Override
    25. public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    26. //完成后执行
    27. System.out.println("[TwoHandlerInterceptor] . afterCompletion"+"---完成后执行");
    28. }
    29. }

    复制该类全路径进入Spring-servlet.xml进行配置

    1. <mvc:interceptors>
    2. <mvc:interceptor>
    3. <mvc:mapping path="/**/"/>
    4. <bean class="com.ljj.ssm.intercept.OneHandlerInterceptor"> bean>
    5. mvc:interceptor>
    6. <mvc:interceptor>
    7. <mvc:mapping path="/clz/**"/>
    8. <bean class="com.ljj.ssm.intercept.TwoHandlerInterceptor">bean>
    9. mvc:interceptor>
    10. mvc:interceptors>

    运行预测结果:
    当我们输入http://localhost:8080/hello 会执行OneHandle中的三个方法

    当我们输入http://localhost:8080/Clz/list 会执行One和Two两个接口:

    首先One--->prehandle

    Two------>prehandle

    在执行业务方法

    One---->posthandle

    Two---->posthandle

    One----->aftercomplation

    Two------>aftercomplation

    运行项目后,输入http://localhost:8080/clz/list

    查看控制台:

     

  • 相关阅读:
    HK1 BOX刷入 Armbian系统作为服务器
    feign远程调用时如何在请求头加入数据
    大气污染扩散模型Calpuff建模、数据后处理及应用
    Kubernetes禁止调度
    Spring--getBean()与@Autowired的对比
    索尼RSV文件怎么恢复为MP4视频
    收藏品集团怎样应用自动化程序实现降本增效
    六大招式,修炼极狐GitLab CI/CD “快” 字诀
    leetcode每日一题30
    基于Mendix移动原生的离线应用
  • 原文地址:https://blog.csdn.net/weixin_64313980/article/details/126434991