SpringMVC之JSR303使用及拦截器使用(带你探索SpringMVC的新领域)

目录

前言

一、探索JSR303的世界

1. JSR303简介

1.1 什么是JSR303

 1.2 JSR303的重要性及使用原因

重要性:

原因:

1.3 JSR303的常用注解

扩展

2. JSR303快速入门(基本使用) 

2.1 导入依赖

 2.2 配置效验规则

 实体类属性设置

对应控制器的方法

 对应JSP页面编写

2.3 测试结果

二、研究拦截器奥秘(拦截器的使用) 

2.1 什么是拦截器

2.2 拦截器与过滤器的区别

2.3.应用场景

2.4 拦截器之快速入门

2.4.1 创建一个拦截器

2.4.2 配置拦截器(在spring-mvc.xml中配置)

 2.4.3 将拦截器类中的preHandle的返回值改为false的测试结果

2.5 拦截器运行经脉(工作原理)

原理图

 工作原理详细描述

2.6 拦截器链的秘密(讲解拦截器链)

2.6.1 再创建一个拦截器

2.6.2 配置两个拦截器(在spring-mvc.xml中配置)

2.6.3 测试结果

情况一:测试clz下的请求

情况二:测试不是clz下的请求 

2.6.4 注意

三、拦截器究极奥秘(案例模拟使用)

1. 创建案例所需拦截器

2.配置案例所需拦截器

3. 创建案例所需控制器

4. 创建模拟案例jsp页面

测试结果

模拟测试登陆

退出登陆

​编辑 


前言

         今天来给大家继续分享有关SpringMVC的知识点,今天带大家探索SpringMVC的新领域,让我们一起来学习了解吧。

一、探索JSR303的世界

1. JSR303简介

1.1 什么是JSR303

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

        它定义了一套用于验证Java Bean对象的标准注解和API。通过使用JSR 303的注解,可以对Java Bean的属性进行各种验证,例如检查字段是否为空、是否满足特定的格式、是否在指定范围内等。这样可以在数据绑定和有效性验证时,提供方便且统一的验证机制。

        注:

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

 1.2 JSR303的重要性及使用原因

重要性
  • 数据有效性验证:JSR 303允许开发人员对Java Bean属性的值进行验证,以确保数据的合法性。通过使用预定义的验证注解,可以轻松地对属性进行验证,例如检查是否为空、是否满足指定的格式、是否在合理的范围内等。

  • 统一验证规范:使用JSR 303可以定义一套统一的验证规范,比如校验器的注解列表,这样整个团队就可以遵循相同的验证规则,提高代码的一致性和可读性。

  • 减少重复代码:使用JSR 303可以减少手动编写和维护的验证逻辑代码,因为验证逻辑已经由注解定义并集成到验证框架中。这样可以减少重复代码的编写,提高开发效率。

  • 功能强大的验证:JSR 303提供了多种预定义的验证注解,如@NotNull、@Size、@Email等,可以满足大部分常见的验证需求。此外,它还支持自定义验证注解,可以根据具体业务需求创建特定的验证规则。

  • 兼容性和可移植性:JSR 303是Java EE的一部分,并且被广泛支持和应用于各种Java框架和平台,如Spring、Hibernate等。因此,使用JSR 303可以获得良好的兼容性和可移植性,可以在不同的项目和环境中重复使用验证规则。

原因
  1. 假如说前端代码校验没写好又或者是对于会一点编程的人来说,直接绕过前端发请求(通过类似Postman这样的测试工具进行非常数据请求),把一些错误的参数传过来,你后端代码不就危险了嘛。所以我们一般都是前端一套校验,后端在一套校验,这样安全性就能够大大得到提升了。

  2. JSR 303提供了一种简单、一致和可扩展的方式来验证Java Bean对象的数据有效性,提高开发效率和代码质量。通过使用JSR 303,可以减少手动编写验证逻辑的工作量,提高代码的可读性和可维护性。

1.3 JSR303的常用注解

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

@Validated与@Valid区别 :

 @Validated:

  • Spring提供的

  • 支持分组校验

  • 可以用在类型、方法和方法参数上。但是不能用在成员属性(字段)上

  • 由于无法加在成员属性(字段)上,所以无法单独完成级联校验,需要配合@Valid

@Valid:

  • JDK提供的(标准JSR-303规范)

  • 不支持分组校验

  • 可以用在方法、构造函数、方法参数和成员属性(字段)上

  • 可以加在成员属性(字段)上,能够独自完成级联校验

2. JSR303快速入门(基本使用) 

2.1 导入依赖

 2.2 配置效验规则

 实体类属性设置
package com.yx.model;

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

import javax.validation.constraints.NotNull;

@ToString
public class Clazz {
    @NotNull(message = "班级编号不能为空")
//    @Size(max = 100,min = 10,message = "大小必须在10至100之间"),不注解测会影响代码运行
    protected Integer cid;

    @NotBlank(message = "班级名不能为空")
    protected String cname;

    @NotBlank(message = "班级教员老师不能为空")
    protected String cteacher;


    private String pic;

    public Clazz(Integer cid, String cname, String cteacher, String pic) {
        this.cid = cid;
        this.cname = cname;
        this.cteacher = cteacher;
        this.pic = pic;
    }

    public Clazz() {
        super();
    }

    public Integer getCid() {
        return cid;
    }

    public void setCid(Integer cid) {
        this.cid = cid;
    }

    public String getCname() {
        return cname;
    }

    public void setCname(String cname) {
        this.cname = cname;
    }

    public String getCteacher() {
        return cteacher;
    }

    public void setCteacher(String cteacher) {
        this.cteacher = cteacher;
    }

    public String getPic() {
        return pic;
    }

    public void setPic(String pic) {
        this.pic = pic;
    }
}
对应控制器的方法
//    给数据添加服务端校验
    @RequestMapping("/valiAdd")
    public String valiAdd(@Validated Clazz clazz, 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.clazzBiz.insertSelective(clazz);
            return "redirect:list";
        }
        return "clz/edit";
    }

 对应JSP页面编写
<%@ page language="java" contentType="text/html; charset=UTF-8"
         pageEncoding="UTF-8"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>电影编辑界面</title>
</head>
<body>
<form action="${pageContext.request.contextPath }/${empty c ? 'clz/valiAdd' : 'clz/edit'}" method="post">
    班级编号:<input type="text" name="cid" value="${c.cid }"><span style="color: red;">${errorMap.cid}</span><br>
    班级名称:<input type="text" name="cname" value="${c.cname }"><span style="color: red;">${errorMap.cname}</span><br>
    教员:<input type="text" name="cteacher" value="${c.cteacher }"><span style="color: red;">${errorMap.cteachcer}</span><br>
    图片:<input type="text" name="pic" value="${c.pic }"><br>
    <input type="submit">
</form>
</body>
</html>

2.3 测试结果

二、研究拦截器奥秘(拦截器的使用) 

2.1 什么是拦截器

        拦截器(Interceptor)是一种常见的设计模式,在软件开发中用于拦截和处理请求、响应或方法调用的组件。拦截器可以允许开发人员在特定的时间点对请求进行预处理、后处理或进行额外的操作。

        在Web开发中,拦截器通常用于拦截HTTP请求和响应,以在处理请求之前或之后执行一些操作。例如,可以使用拦截器进行身份验证、日志记录、性能监控、异常处理等。拦截器在框架和平台中广泛使用,如Java的Servlet过滤器、Spring框架的拦截器、Android框架中的拦截器等。它们提供了一种可插拔、可重用和可扩展的方式来处理各种请求和操作。

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

2.2 拦截器与过滤器的区别

  • 作用范围不同:拦截器通常是在应用程序内部的某一层中使用,在MVC框架中用于拦截请求和处理;过滤器则是在Servlet容器级别上使用,用于拦截HTTP请求和响应。
  • 处理位置不同:拦截器位于特定处理程序的周围;过滤器位于请求链的前后
  • 功能不同:拦截器更多地用于对请求进行预处理和后处理,以及在请求处理过程中进行额外的操作;过滤器则更多地用于在请求和响应之间修改或转换数据
  • 实现接口不同:拦截器通常需要实现特定的接口,如Spring框架中的HandlerInterceptor接口;过滤器则需要实现javax.servlet.Filter接口
  • 导入依赖不同:拦截器通常与MVC框架或特定的请求处理框架(如Spring MVC)结合使用,因此可能依赖于这些框架的上下文和功能;过滤器则是在Servlet容器级别上执行的,与特定的框架无关。

2.3.应用场景

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

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

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

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

2.4 拦截器之快速入门

2.4.1 创建一个拦截器

public class OneInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("【OneInterceptor】:preHandle...");

        return true;
    }

    @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...");
    }
}

2.4.2 配置拦截器(在spring-mvc.xml中配置)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
      http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
      http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <!--1) 扫描com.zking.zf及子子孙孙包下的控制器(扫描范围过大,耗时)-->
    <context:component-scan base-package="com.yx"/>

    <!--2) 此标签默认注册DefaultAnnotationHandlerMapping和AnnotationMethodHandlerAdapter -->
    <mvc:annotation-driven />

    <!--3) 创建ViewResolver视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- viewClass需要在pom中引入两个包:standard.jar and jstl.jar -->
        <property name="viewClass"
                  value="org.springframework.web.servlet.view.JstlView"></property>
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

    <!--4) 单独处理图片、样式、js等资源 -->
     <mvc:resources location="/static/" mapping="/static/**"/>

<!--    处理文件的上传下载的问题-->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!-- 必须和用户JSP 的pageEncoding属性一致,以便正确解析表单的内容 -->
        <property name="defaultEncoding" value="UTF-8"></property>
        <!-- 文件最大大小(字节) 1024*1024*50=50M-->
        <property name="maxUploadSize" value="52428800"></property>
        <!--resolveLazily属性启用是为了推迟文件解析,以便捕获文件大小异常-->
        <property name="resolveLazily" value="true"/>
    </bean>

<!--    配置拦截器-->
    <mvc:interceptors>
        <bean class="com.yx.interceptor.OneInterceptor"></bean>
    </mvc:interceptors>

     <aop:aspectj-autoproxy/>
</beans>

 配置结果

 如上图所示,如果不进行进一步配置则拦截器会拦截所有的请求,接下来会进测试

 当我们进行发送请求时,如下图

则拦截器会进行拦截(因为前面未进行进一不配置则会拦截所有),控制台输出如下

它会先经过来拦截器进行预处理,然后做出输出;在经过控制器调用指定的方法;然后在经过拦截器进行控制台输出;接着回显到页面上,拦截器做出最后处理。

 2.4.3 将拦截器类中的preHandle的返回值改为false的测试结果

 发送请求时则会对该请求进行拦截,阻止方法请求的方法,导致无法访问到数据库,从而数据无法显示到页面上去,也不会进行执行后续代码操作。效果如下

页面效果

控制台输出 

2.5 拦截器运行经脉(工作原理)

原理图

 工作原理详细描述
  1. 定义拦截器:首先,需要定义一个或多个拦截器,并实现对应的拦截器接口或类。这些拦截器将包含要执行的操作的逻辑。

  2. 设置拦截器链:在需要使用拦截器的地方,例如MVC框架的控制器层,会存在一个拦截器链(Interceptor Chain)用于存储所有拦截器的实例。拦截器链按照一定的顺序组织,决定了拦截器的执行顺序。

  3. 执行拦截器逻辑:当请求到达拦截器链所在的位置时,每个拦截器将按照顺序依次执行。拦截器在请求处理的不同阶段会被调用,如请求前、请求后、视图渲染前、视图渲染后等。

  4. 拦截器的前置处理:每个拦截器在执行之前可以进行一些预处理操作,例如身份验证、参数验证、日志记录等。这些操作可以在请求到达目标处理程序之前进行,以确保请求的有效性和安全性。

  5. 处理程序执行:在拦截器链的最后一个拦截器执行后,请求将传递给目标处理程序,例如MVC框架中的控制器方法。处理程序执行特定的业务逻辑,并返回一个结果。

  6. 拦截器的后置处理:在处理程序执行完毕后,拦截器链会按照相反的顺序执行拦截器的后置处理操作。这些操作可以处理处理程序的返回结果、记录日志、清理资源等。

注:

        拦截器能够在请求的不同阶段执行预定义的操作,并对请求进行干预和处理。拦截器的工作原理以及具体的拦截器接口和方法可能有所差异,具体取决于所使用的框架和应用程序架构。

2.6 拦截器链的秘密(讲解拦截器链)

2.6.1 再创建一个拦截器
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...");
    }
}
2.6.2 配置两个拦截器(在spring-mvc.xml中配置)
 <mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        <bean class="com.yx.interceptor.OneInterceptor"/>
    </mvc:interceptor>
    <mvc:interceptor>
        <mvc:mapping path="/clz/**"/>
        <bean class="com.yx.interceptor.TwoInterceptor"/>
    </mvc:interceptor>
</mvc:interceptors>

将上述代码添加到spring-mvc.xml文件中,将原有的注释掉。

2.6.3 测试结果
情况一:测试clz下的请求

页面

因为preHandle的返回值未改为false,所以能访问到界面

控制台

情况二:测试不是clz下的请求 

2.6.4 注意

 当我们去访问图片或者静态资源映射的时候是不经过拦截器的,

三、拦截器究极奥秘(案例模拟使用)

1. 创建案例所需拦截器

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 uname = (String) request.getSession().getAttribute("uname");
        if (uname == null || "".equals(uname)){
            response.sendRedirect("/page/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 {

    }
}

2.配置案例所需拦截器

<mvc:interceptors>
        <bean class="com.yx.interceptor.LoginInterceptor"></bean>
    </mvc:interceptors>

3. 创建案例所需控制器

package com.yx.web;

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

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
/**
 * @author 君易--鑨
 * @site www.yangxin.com
 * @company 木易
 * @create  2023-09-09 15:25
 */
@Controller
public class LoginController {
    @RequestMapping("/login")
    public String login(HttpServletRequest req){
        String uname = req.getParameter("uname");
        HttpSession session = req.getSession();
        if ("zs".equals(uname)){
            session.setAttribute("uname",uname);
        }
        return "redirect:/clz/list";
    }

    @RequestMapping("/logout")
    public String logout(HttpServletRequest req){
        req.getSession().invalidate();
        return "redirect:/clz/list";
    }
}

4. 创建模拟案例jsp页面

<%--
  Created by IntelliJ IDEA.
  User: 86158
  Date: 2023/9/13
  Time: 0:15
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h1>登陆界面</h1>
<form action="/login" method="post">
    用户名:<input name="uname">
    <input type="submit">

</form>
</body>
</html>

测试结果

模拟测试登陆

退出登陆

今天对新知识的探索就到这了,希望老铁能够三连夹关注支持一波 

猜你喜欢

转载自blog.csdn.net/weixin_74352229/article/details/132838440