【SpringMVC】JSR 303与interceptor拦截器快速入门

2023-09-12 22:19:44

目录

一、JSR303

1、什么是JSR 303?

2、为什么要使用JSR 303?

3、JSR 303常用注解

3.1、常用的JSR 303注解

3.2、@Validated与@Valid区别

3.2.1、@Validated

3.2.2、@Valid

3.2.3、区别

4、使用案例

4.1、导入依赖

4.2、配置校验规则

4.3、编写校验方法

4.4、前端代码

4.5、测试

二、interceptor拦截器

1、什么是拦截器?

2、为什么要使用拦截器?

3、拦截器与过滤器

 3.1、什么是过滤器(Filter)

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

3.2.1、 过滤器(filter)

3.2.2、 拦截器(interceptor)

3.2.3、汇总

4、拦截器应用场景

5、使用案例

5.1、创建拦截器

5.2、配置拦截器

5.3、运行测试

5.4、拦截器工作原理

5.5、拦截器链

5.6、登录拦截实例

5.6.1、创建拦截器

5.6.2、配置拦截器

5.6.3、编写控制层

5.6.4、前端页面

5.6.5、测试

登入

登出


一、JSR303

1、什么是JSR 303?

JSR 303是Java规范请求(Java Specification Request)的缩写,意思是Java 规范提案。是指向JCP(Java Community Process)提出新增一个标准化技术规范的正式请求。任何人都可以提交JSR,以向Java平台增添新的API和服务。JSR已成为Java界的一个重要标准。 JSR-303 是JAVA EE 6 中的一项子规范,叫做Bean ValidationHibernate Validator Bean Validation 的参考实现 . Hibernate Validator 提供了 JSR 303 规范中所有内置 constraint(约束) 的实现,除此之外还有一些附加的 constraint。

它定义了一套用于Java Bean校验的标准。JSR 303使用注解的方式,在Spring MVC中被广泛应用于进行数据校验和验证。

验证数据是一项常见任务,它发生在从表示层到持久层的所有应用程序层中。通常在每一层都实现相同的验证逻辑,这既耗时又容易出错。为了避免重复这些验证,开发人员经常将验证逻辑直接捆绑到域模型中,将域类与验证代码混在一起,而验证代码实际上是关于类本身的元数据

2、为什么要使用JSR 303?

        在前端我们进行了数据校验,可是为什么我们还要做一篇,因为因为一点小失误我们的前端校验没有写好,但是有些人还是会绕过前端发送的请求(通过类似Postman这样的测试工具进行非常数据请求),传递一些错误的参数,这就会让我们的后端代码有大大的危险,所以我们一般都是前端一套校验,后端在一套校验,这样安全性就能够大大得到提升了。

所以我总和了JSR 303以下的好处:

  1. 提高代码的可维护性:通过在实体类中添加注解,可以清晰地标识出需要进行校验的字段和规则,使代码更易于理解和维护。
  2. 增强数据的完整性:根据定义的规则,可以自动校验输入数据的合法性,避免错误数据进入系统,保证数据的完整性和准确性。
  3. 减少重复代码:通过注解,可以在不同场景下重复使用相同的校验规则,减少编写重复代码的工作量。

3、JSR 303常用注解

3.1、常用的JSR 303注解

注解 说明 @Null 用于验证对象为null @NotNull 用于对象不能为null,无法查检长度为0的字符串 @NotBlank 只用于String类型上,不能为nulltrim()之后的size>0 @NotEmpty 用于集合类、String类不能为null,且size>0。但是带有空格的字符串校验不出来 @Size 用于对象(Array,Collection,Map,String)长度是否在给定的范围之内 @Length 用于String对象的大小必须在指定的范围内 @Pattern 用于String对象是否符合正则表达式的规则 @Email 用于String对象是否符合邮箱格式 @Min 用于NumberString对象是否大等于指定的值 @Max 用于NumberString对象是否小等于指定的值 @AssertTrue 用于Boolean对象是否为true @AssertFalse 用于Boolean对象是否为false

3.2、@Validated与@Valid区别

@Validated @Valid 是用于数据校验的注解,但它们有一些区别和应用场景的差异。

3.2.1、@Validated
  • Spring 框架提供的注解
  • 支持分组校验
  • 可以用在类型、方法和方法参数上。但是不能用在成员属性(字段)上
  • 由于无法加在成员属性(字段)上,所以无法单独完成级联校验需要配合@Valid
  • Spring 提供的校验器,默认使用的是 Hibernate Validator(实现了 JSR-303 规范),但它支持更丰富的校验场景,如分组校验。
3.2.2、@Valid
  • JDK提供的 JSR-303(Bean Validation)规范的注解
  • 不支持分组校验
  • 可以用在方法、构造函数、方法参数和成员属性(字段)上
  • 可以加在成员属性(字段)上,能够独自完成级联校验
  • 使用 @Valid 注解时,会触发 JSR-303 或者其它支持的校验框架来对被注解的对象进行校验。
3.2.3、区别
  1. 适用范围@Valid 可以应用于方法参数、返回值、字段和方法上,而 @Validated 只能应用于类、接口和方法上。
  2. 校验框架@Validated 默认使用 Hibernate Validator(实现了 JSR-303)、Spring Validator 或自定义的校验器,而 @Valid 使用的是 JSR-303 或其它支持的校验框架。
  3. 分组校验@Validated 支持分组校验,允许在不同的场景下使用不同的校验规则,而 @Valid 不直接支持分组校验。

@Validated 是 Spring 框架提供的扩展注解,并不属于 Java 标准规范。在使用时,可以根据具体的需求选择合适的注解进行数据校验。

4、使用案例

4.1、导入依赖

<!-- JSR303 -->
<hibernate.validator.version>6.0.7.Final</hibernate.validator.version>

<!-- JSR303 -->
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>${hibernate.validator.version}</version>
</dependency>

4.2、配置校验规则

在实体类里面添加配置校验

package com.tgq.model;

import lombok.ToString;
import org.hibernate.validator.constraints.NotBlank;

import javax.validation.constraints.NotNull;

@ToString
public class MyStudent {

    @NotNull(message = "学生编号不能为空")
    private String sid;

    @NotBlank(message = "学生名不能为空")
    private String sname;

    @NotBlank(message = "学生年龄不能为空")
    private String sage;

    @NotBlank(message = "学生性别不能为空")
    private String ssex;

    public MyStudent(String sid, String sname, String sage, String ssex) {
        this.sid = sid;
        this.sname = sname;
        this.sage = sage;
        this.ssex = ssex;
    }

    public MyStudent() {
        super();
    }

    public String getSid() {
        return sid;
    }

    public void setSid(String sid) {
        this.sid = sid;
    }

    public String getSname() {
        return sname;
    }

    public void setSname(String sname) {
        this.sname = sname;
    }

    public String getSage() {
        return sage;
    }

    public void setSage(String sage) {
        this.sage = sage;
    }

    public String getSsex() {
        return ssex;
    }

    public void setSsex(String ssex) {
        this.ssex = ssex;
    }
}

4.3、编写校验方法

   使用@Validated注解对我们的MyStudent进行服务端校验。

    //    给数据添加服务端校验
    @RequestMapping("/valiAdd")
    public String valiAdd(@Validated MyStudent myStudent, BindingResult result, HttpServletRequest req) {
//        如果服务端验证不通过,有错误
        if (result.hasErrors()) {
//            服务端验证了实体类的多个属性,多个属性都没有验证通过
            List<FieldError> fieldErrors = result.getFieldErrors();
            Map<String, Object> map = new HashMap<>();
            for (FieldError fieldError : fieldErrors) {
//                将多个属性的验证失败信息输送到控制台
                System.out.println(fieldError.getField() + ":" + fieldError.getDefaultMessage());
                map.put(fieldError.getField(), fieldError.getDefaultMessage());
            }
            req.setAttribute("errorMap", map);
        } else {
            this.myStudentBiz.insertSelective(myStudent);
            return "redirect:stu/list";
        }
        return "stu/edit";
    }

4.4、前端代码

使用form表单进行提交

<%--
  Created by IntelliJ IDEA.
  User: tgq
  Date: 12/9/2023
  Time: 下午8:05
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>edit</title>
</head>
<body>

<form action="${pageContext.request.contextPath }/stu/valiAdd" method="post">
    用户id:<input type="text" name="sid"><span style="color: red">${errorMap.sid}</span><br>
    用户名:<input type="text" name="sname"><span style="color: red">${errorMap.sname}</span><br>
    用户年龄:<input type="text" name="sage"><span
        style="color: red">${errorMap.sage}</span><br>
    用户性别:<input type="text" name="ssex"><span style="color: red">${errorMap.ssex}</span><br>
    <input type="submit" value="提交">
</form>
</body>
</html>

4.5、测试

点击提交,如果为空就会提示

二、interceptor拦截器

1、什么是拦截器?

        拦截器是在请求进入后端处理程序之前或之后执行特定逻辑的组件。它们能够拦截处理程序执行的流程,允许在请求处理过程中插入额外的功能。

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

2、为什么要使用拦截器?

  1. 横向功能扩展:通过拦截器,可以在不修改原有业务逻辑的情况下添加额外的功能,例如日志记录、权限验证、性能统计等。
  2. 代码复用:多个请求中可能需要相同的处理逻辑,通过拦截器可以将这部分逻辑抽取出来,减少代码的重复编写。
  3. 解耦合:通过拦截器,可以将关注点分离,在拦截器中处理通用的逻辑,使得业务处理程序更专注于业务本身。

3、拦截器与过滤器

 3.1、什么是过滤器(Filter)

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

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

3.2.1、 过滤器(filter)
  1.   filter属于Servlet技术,只要是web工程都可以使用
  2.   filter主要由于对所有请求过滤
  3.   filter的执行时机早于Interceptor
3.2.2、 拦截器(interceptor)
  1.   interceptor属于SpringMVC技术,必须要有SpringMVC环境才可以使用
  2.   interceptor通常由于对处理器Controller进行拦截
  3.   interceptor只能拦截dispatcherServlet处理的请求
3.2.3、汇总
  • 拦截器是在应用程序处理程序内部执行的,而过滤器则是在应用程序之前或之后执行的。
  • 过滤器是基于Servlet规范的,拦截器是基于应用框架的。
  • 过滤器可以在请求到达Servlet容器之前对请求进行处理,而拦截器只能在请求到达应用程序之后进行处理。

4、拦截器应用场景

  1. 权限验证:拦截器可以检查用户是否具有操作的权限,如果没有权限,可以拦截请求并返回相应的错误信息。如果没有直接返回到登录页面。
  2. 日志记录:拦截器可以记录请求的详细信息,例如请求时间、信息、请求参数等,便于后续的日志分析和故障排查。以便进行信息监控、信息统计、计算PV(Page View)等。
  3. 性能统计:拦截器可以统计请求的执行时间,便于分析系统性能并进行优化。(如果有反向代理,如apache可以自动记录);
  4. 通用行为:读取cookie得到用户信息并将用户对象放入请求,从而方便后续流程使用,还有如提取Locale、Theme信息等,只要是多个Controller中的处理方法都需要的,我们就可以使用拦截器实现。

拦截器链是由多个拦截器组成的链式结构,每个拦截器都可以在请求处理程序执行之前或之后进行特定的操作。拦截器链中的拦截器按照预定义的顺序执行,每个拦截器都有机会处理请求和响应。拦截器链可以保证各个拦截器的有序执行,以达到预期的处理逻辑。

5、使用案例

5.1、创建拦截器

创建一个拦截器的包,在包下创建拦截器

package com.tgq.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 OneInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("【OneInterceptor】:preHandle...");

        return true;//返回true / false
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("【OneInterceptor】:postHandle...");

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("【OneInterceptor】:afterCompletion...");
    }
}

5.2、配置拦截器

在我们的自己配置spring-mvc.xml里面配置我们的拦截器

<mvc:interceptors>
        <bean class="com.tgq.interceptor.OneInterceptor"></bean>
    </mvc:interceptors>

5.3、运行测试

启动项目,打开浏览器访问请求地址,测试拦截器的拦截效果。

他们的执行顺序为:preHandle --> postHandle --> afterCompletion

http://localhost:8080/sc/list

5.4、拦截器工作原理

  • preHandle:用于对拦截到的请求进行预处理,方法接收布尔(true,false)类型的返回值,返回true:放行,false:不放行。

执行时机:在处理器方法执行前执行

方法参数
参数说明
request      请求对象    
response    响应对象    
handler      拦截到的处理器方法  
ModelAndView处理器方法返回的模型和视图对象,可以在方法中修改模型和视图
  • afterCompletion:用于在整个流程完成之后进行最后的处理,如果请求流程中有异常,可以在方法中获取对象

  执行时机:视图渲染完成后(整个流程结束之后)

方法参数
参数说明
request  请求参数
response响应对象  
handler  拦截到的处理器方法
ex异常对象

5.5、拦截器链

如果多个拦截器能够对相同的请求进行拦截,则多个拦截器会形成一个拦截器链,主要理解拦截器链中各个拦截器的执行顺序。拦截器链中多个拦截器的执行顺序,根拦截器的配置顺序有关,先配置的先执行。

package com.tgq.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 TwoInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("【TwoInterceptor】:preHandle...");

        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("【TwoInterceptor】:postHandle...");

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("【TwoInterceptor】:afterCompletion...");
    }
}

配置spring-mvc.xml

<mvc:interceptors>
        <!--2) 多拦截器(拦截器链)-->
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean class="com.tgq.interceptor.OneInterceptor"/>
        </mvc:interceptor>
        <mvc:interceptor>
            <mvc:mapping path="/sc/**"/>
            <bean class="com.tgq.interceptor.TwoInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>

走一个拦截器:editicon-default.png?t=N7T8http://localhost:8080/stu/save

走两个拦截器:列表icon-default.png?t=N7T8http://localhost:8080/sc/list

5.6、登录拦截实例

5.6.1、创建拦截器
package com.tgq.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 LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("【implements】:preHandle...");
        StringBuffer url = request.getRequestURL();
        if (url.indexOf("/login") > 0 || url.indexOf("/logout") > 0) {
            //        如果是 登录、退出 中的一种
            return true;
        }
//            代表不是登录,也不是退出
//            除了登录、退出,其他操作都需要判断是否 session 登录成功过
        String sname = (String) request.getSession().getAttribute("sname");
        if (sname == null || "".equals(sname)) {
            response.sendRedirect("/page/stu/login");
            return false;
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }
}
5.6.2、配置拦截器
    <!--登录拦截器实例-->
    <mvc:interceptors>
        <bean class="com.tgq.interceptor.LoginInterceptor"></bean>
    </mvc:interceptors>

5.6.3、编写控制层
package com.tgq.web;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

@Controller
public class LoginController {
    @RequestMapping("/login")
    public String login(HttpServletRequest req) {
        String sname = req.getParameter("sname");
        HttpSession session = req.getSession();
        if ("tgq".equals(sname)) {
            session.setAttribute("sname", sname);
        }
        return "redirect:/sc/list";
    }

    @RequestMapping("/logout")
    public String logout(HttpServletRequest req) {
        req.getSession().invalidate();
        return "redirect:/sc/list";
    }
}
5.6.4、前端页面
<%--
  Created by IntelliJ IDEA.
  User: tgq
  Date: 12/9/2023
  Time: 下午10:03
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>用户登录</title>
</head>
<body>

<form action="/login" method="post">
    账号:<input name="sname">
    <input type="submit">
</form>
</body>
</html>
5.6.5、测试
登入

http://localhost:8080/logouticon-default.png?t=N7T8http://localhost:8080/login

登出

http://localhost:8080/logouticon-default.png?t=N7T8http://localhost:8080/logout

更多推荐

黑马JVM总结(十一)

(1)垃圾回收概述前面我们学了堆,里面有一个垃圾回收的机制(2)判断垃圾_引用计数指只要有一个对象被其他变量所引用,我们就让这个对象的计数加1,有个一变量不在引用,让它的计数减一,当这个对象的计数变为0的时候,说明没有变量引用它了,那么他就可以作为一个垃圾进行一个回收,但是引用计数存在一个弊端:存在循环引用问题:a对象

Pytorch实现MNIST字符识别

1.下载mnist.pkl.gz网址:http://www.iro.umontreal.ca/~lisa/deep/data/mnist/mnist.pkl.gz数据集文件夹路径是data2/mnist/mnist.pkl.gz2.读取数据frompathlibimportPathimportmatplotlib.py

设计模式:状态模式

目录组件代码示例源码中使用优缺点总结状态模式(StatePattern)是一种行为型设计模式,用于解决对象在不同状态下的行为变化问题。状态模式允许对象在内部状态发生改变时改变其行为,使得对象的行为可以根据状态的改变而灵活变化。在状态模式中,对象的行为会根据其内部状态的改变而变化,但对外部来说,对象的接口保持一致。状态模

设计模式之十:状态模式

状态模式通过改变对象内部的状态来帮助对象控制自己的行为。这是一张状态图,其中每个圆圈都是一个状态。最简单,第一反应的实现就是使用一个变量来控制状态值,并在方法内书写条件代码来处理不同情况。packageheadfirst.designpatterns.state.gumball;publicclassGumballMa

9、Spring之推断构造方法源码解析

推断构造方法流程图:Spring推断构造方法底层执行流程|ProcessOn免费在线作图,在线流程图,在线思维导图AutowiredAnnotationBeanPostProcessor中推断构造方法不同情况思维脑图:Spring中的一个bean,需要实例化得到一个对象,而实例化就需要用到构造方法。一般情况下,一个类只

【shell学习】企业运维工作中常用的shell脚本

本站以分享各种运维经验和运维所需要的技能为主《python零基础入门》:python零基础入门学习《python运维脚本》:python运维脚本实践《shell》:shell学习《terraform》持续更新中:terraform_Aws学习零基础入门到最佳实战《k8》暂未更新《docker学习》暂未更新《ceph学习

pytorch学习2

分类问题手写数字数据集其中,每个数字图片大小是28x28,矩阵中每个元素的大小为[0,1]区间的灰度值,将二维矩阵拉平(flat)为一维784,数据量不变,这样能忽略上下位置相关性,甚至左右位置相关性也可忽略,再插入一个维度变为[1,784]线性模型能解决吗一个简单的线性模型为:y=w*x+b但对于手写数字来说,用一个

城中村现代化治理,筑牢基层安全底座

7月21日,李强主持召开常务会议指出,在超大特大城市积极稳步实施城中村改造是改善民生、扩大内需、推动城市高质量发展的一项重要举措。基层(村社)治理是作为社会治理的一个基本单元,是城市治理的一个最后一公里,社会治理的重点在基层,难点也在基层。如何深化数字技术融合创新,推进共建共治共享基层治理全面提速,赋能区域数字经济高质

Linux安装包 | Git使用 | NFC搭建

dpgt使用当谈到基于Debian的操作系统中的软件包管理工具时,dpkg是一个重要的工具。它是Debian系统中用于安装、升级、配置和卸载软件包的命令行工具。以下是对dpkg的详细介绍:软件包管理:dpkg可以管理系统中的软件包。它可以安装单个或多个软件包,并处理软件包之间的依赖关系。通过dpkg-i命令,可以安装软

固定资产管理系统都有哪些功能呢

固定资产管理系统作为企业资产管理的重要工具,具有提高效率、降低成本、保证资产合理使用的多种功能。以下是一些典型的功能:资产登记和信息管理:系统可以自动记录公司的固定信息,包括资产名称、型号、购买日期、原始价值、折旧方法、折旧年限等。同时,系统还支持自动更新和查看资产信息。资产申请和偿还:员工可以通过平台申请或偿还资产,

浅谈KNX总线智能照明控制系统在北京南站房中的应用

安科瑞华楠摘要:本文简要介绍了i-busEIB/KNX智能建筑控制系统的基本原理及在北京南站房中的成功应用。阐述了这一系统强大的系统功能、灵活的控制方式节能效果。关键词:i-bus智能建筑控制;控制系统;节能1、工程概况北京新南站位于南护城河以南,马家堡西路以东,南二环右安门外东庄公园内,距离老南站0.5km。站内总建

热文推荐