• 快速入手SpringMVC 之 JSR303与拦截器


    目录

    一、JSR303

    简介

    导入所需依赖

    pom依赖

     后台

    实体类

    前端界面

    实现结果:

    JSR303服务端总结

     二、拦截器

    1、什么是拦截器

    2、拦截器与过滤器

    2.1、什么是过滤器(Filter)

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

    3、应用场景

    4.拦截器快速入门

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


    一、JSR303

    简介

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

    导入所需依赖

    pom依赖

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

     后台

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

    实体类

    Clazz

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

    1. package com.oyang.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>

    实现结果:

    直接点击提交

     

    JSR303服务端总结

    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.oyang.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 oyang
    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.oyang.ssm.intercept.OneHandlerInterceptor">bean>
    3. mvc:interceptors>

    创建HelloController

    1. package com.oyang.ssm.controller;
    2. import org.springframework.stereotype.Controller;
    3. import org.springframework.web.bind.annotation.RequestMapping;
    4. /**
    5. * @author oyang
    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

     运行结果:

    以看到我们只执行了预处理 ,没有实现业务方法。同时,界面的Hello Word也看不见了

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

    TwoHandlerInterceptor

    1. package com.oyang.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 oyang
    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.oyang.ssm.intercept.OneHandlerInterceptor"> bean>
    5. mvc:interceptor>
    6. <mvc:interceptor>
    7. <mvc:mapping path="/clz/**"/>
    8. <bean class="com.oyang.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

    我们先实现第一种情况,看是否运行了One的三种方法 

    运行第二种情况:

     6、登录权限控制

    首先我们将配置拦截链的一些配置修改一下

     我们想做到的是:以前可以直接访问我们的数据,而先在我们需要登陆才能访问

     LoginController 

    1. package com.oyang.ssm.controller;
    2. import org.springframework.stereotype.Controller;
    3. import org.springframework.web.bind.annotation.RequestMapping;
    4. import javax.servlet.http.HttpServletRequest;
    5. /**
    6. * @author oyang
    7. * @site https://blog.csdn.net
    8. * @qq 1828190940
    9. * @create  2022-08-19 16:35
    10. */
    11. @Controller
    12. public class LoginController {
    13. //登录
    14. @RequestMapping("/login")
    15. public String login(HttpServletRequest request){
    16. //登录成功 需要保存用户登陆信息
    17. String uname = request.getParameter("uname");
    18. if("oyang".equals(uname)){//数据定死了,如果用户名是oyang就代表登录成功
    19. //登录成功就保存到session里去
    20. request.getSession().setAttribute("uname",uname);
    21. }
    22. return "index";
    23. }
    24. //登出
    25. @RequestMapping("/logout")
    26. public String logout(HttpServletRequest request){
    27. //销毁
    28. request.getSession().invalidate();
    29. return "index";
    30. }
    31. }

    OneHandlerInterceptor

    判断是否登录

    1. @Override
    2. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    3. //预处理
    4. System.out.println("[OneHandlerInterceptor] . preHandle"+"---预处理");
    5. //如果login/logout 这个请求,就直接放行
    6. String url = request.getRequestURL().toString();
    7. if(url.indexOf("/login")>0 || url.indexOf("/logout")>0){//代表是login/logout 其中的任何一个 截取
    8. //放行
    9. return true;
    10. }
    11. //对于请求业务方法 只有登录过存在session数据 才能访问
    12. else{
    13. Object uname = request.getSession().getAttribute("uname");
    14. if(uname==null||"".equals(uname)){//代表没有登录
    15. response.sendRedirect("/login.jsp");
    16. }
    17. }
    18. return true;
    19. }

     回车

    因为没有登录,就直接跳转到登录界面了

     输入用户名

     就跳转到了欢迎界面了,同时,我们再查看一下数据(输入地址栏)

    ​​​​​​​

  • 相关阅读:
    Linux下安装navicat
    Mybatis关联(嵌套)查询与延迟加载
    软磁交流测试系统参考标准
    本地域名 127.0.0.1 / localhost
    创意写作类国际竞赛有哪些?
    力扣(LeetCode)2731. 移动机器人(C++)
    一招告别百度广告烦恼,同时效率提高100倍的几个常用搜索技巧!
    视频I420裸流保存为文件
    JavaWeb-JavaScript中篇总结
    禁止安装新软件怎么设置(超详细图文介绍)
  • 原文地址:https://blog.csdn.net/weixin_65211978/article/details/126430940