java框架 2 springboot 过滤器 拦截器 异常处理 事务管理 AOP

news2025/1/12 8:37:55

Filter 过滤器

在这里插入图片描述
对所有请求都可以过滤。
在这里插入图片描述
实现Filter接口,重写几个方法,加上@WebFilter注解,表示拦截哪些路由,如上是所有请求都会拦截。
然后还需要在入口处加上@SvlterComponentScan注解,因为Filter是javaweb三大组件之一,并不是springboot的内容。
在这里插入图片描述
chain.doFilter是放行该请求的意思,如果没有将会卡在当前过滤器上。

过滤器链

一个服务可以配置多个过滤器,多个过滤器形成过滤器链。有点像koa的洋葱模型。

过滤器1执行 -> 过滤器2执行 -> 执行主要逻辑 -> 过滤器2放行后的逻辑执行 -> 过滤器1放行后的逻辑执行

那么怎么区分那个过滤器先执行呢?通过过滤器首字母排序来决定。

Interceptor拦截器

在这里插入图片描述

拦截器是Spring框架提供的,跟filter不一样。
使用:
在这里插入图片描述
实现HandlerInterceptor接口,重写方法,其中preHanldel是在controller执行前执行,返回值作为放行的条件。
postHandle是在controller执行后执行。
实现拦截器之后,还需要配置才能生效。
在这里插入图片描述
实现WebMvcConfigurer接口,然后使用@Configuration,这样springboot启动的时候会自动扫描该注解,生效该配置。
如上就是将拦截器注册,并指定其拦截的接口。

拦截器-拦截路径

在这里插入图片描述
可以通过addPathPatterns指定哪些路径需要拦截,通过excludePathPatterns指定哪些路径不需要拦截。

执行时机

上面说过,拦截器是spring框架提供的,而过滤器是tomcat框架提供的,如图。
在这里插入图片描述
如果都存在的话,会先执行过滤器,再执行拦截器的逻辑。
过滤器会拦截所有的请求资源,而拦截器只会拦截Spring环境中的资源。

案例 实现jwt登陆验证

安装对应依赖

	<dependency>
			<groupId>io.jsonwebtoken</groupId>
			<artifactId>jjwt</artifactId>
			<version>0.9.1</version>
		</dependency>

实现jwtUtils类

package com.example.demo.util;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

import java.util.Date;
import java.util.Map;

public class JwtUtils {
    private static String singKey = "test1234"; // 密钥
    private static long expire = 432000L; //过期时间

    public static String generatorToken(Map<String, Object> data) {
        String jwt = Jwts.builder()
                .addClaims(data)
                .signWith(SignatureAlgorithm.HS256, JwtUtils.singKey)
                .setExpiration(new Date(System.currentTimeMillis() + JwtUtils.expire))
                .compact();
        return jwt;
    }


    public static Claims parseToken(String token) {
        Claims calims = Jwts.parser()
                .setSigningKey(singKey)
                .parseClaimsJws(token)
                .getBody();
        return calims;
    }

}

使用jwt生成token

@Slf4j
@RestController
public class LoginController {

    @PostMapping("/login")
    public Result login(@RequestBody LoginUser body){
        Map<String, Object> data = new HashMap<>();
        log.info("{},{}", body.getName(),body.getPassword());
        data.put("name", body.getName());
        data.put("password", body.getPassword());
        String token = JwtUtils.generatorToken(data);
        return Result.success(token);
    };
}

使用对应的拦截器进行校验

@Component("loginInterceptor")
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler) throws Exception {
        System.out.println("preHandle....." + req.getRequestURL());
        String url = req.getRequestURL().toString();
        if(url.contains("login")){
            // 登陆接口不需要校验
            return true;
        }
        String token = req.getHeader("token");
        try {
            if (token != null) {
                Claims data = JwtUtils.parseToken(token);
                req.setAttribute("user", data);
                return true;
            } else {
                throw new Exception("token不存在或者过期");
            }
        } catch (Exception e) {
            Result error = Result.error("token is not exists or expire");
            res.addHeader("Content-Type", "application/json");
            res.getWriter().write(JSONObject.toJSONString(error));
            return false;
        }
    }
}
@Configuration
public class SpringMvcConfigure implements WebMvcConfigurer {

    @Resource(name="loginInterceptor")
    LoginInterceptor loginInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry){
        registry.addInterceptor(loginInterceptor).addPathPatterns("/**"); //该拦截器拦截所有请求
    }
}

登陆接口不需要验证,让该拦截器拦截对应请求,拿到token,进行解析,解析到用户数据就塞入req中,后续controller就可以拿到该用户信息。

异常处理

  • 程序开发过程中不可避免会碰到异常,有时候返回的信息并不是后端统一的信息。

在这里插入图片描述
像nest可以配置全局异常过滤器,会俘获所有的异常然后统一信息返回。

方案

java也可以定义全局异常处理器。
在这里插入图片描述

通过注解@RestControllerAdvice指定这是一个controller异常处理器,@RestControllerAdvicd中也包含@ResponseBody,表示所有的方法返回的值会被转为json传给前端(实际上所有的请求传输都是字符串,只不过设置了contentType为json,浏览器会自动识别contentType处理。)。通过@ExceptionHandler(Exception.class)指定拦截什么类型的异常,Exception.class就是指拦截所有的异常。

import com.example.demo.pojo.Result;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(Exception.class)
    public Result ex(Exception ex){
        ex.printStackTrace();
        return Result.error("服务器报错: " + ex.getMessage());
    }
}

事务管理

在这里插入图片描述

在这里插入图片描述
springboot提供了@Transactional注解来开启事务。

回滚异常


@Transactional注解,可以传入值,可以控制出现什么异常的情况下,回滚事务。默认只有运行时异常才会处理。

事务传播行为

事务a方法调用事务b方法的时候
在这里插入图片描述
比如在a中调用b的方法,当a失败后,事务回滚,会导致b方法执行的逻辑也会回滚,事务传播行为默认是有则加入,也就是b方法会加入当前a方法的事务中。

将其改为REQUIRES_NEW,在a中调用b的时候,会先挂起a的事务,然后起一个b的事务,当a失败后,a执行的逻辑会回滚,但是b方法执行的逻辑如果没报错,会保留。

在这里插入图片描述

案例 删除一个部门,并将该部门下所有员工删除,不管删除失败成功都需要记录日志

如下,启用Propagation.REQUIRES_NEW,在调用创建日志方法的时候并不会受原本事务的影响

  @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
    public Result deleteDepts(Integer id) {
        try {
            this.dept.deleteDept(id);
            this.emp.deleteEmpByJobId(id); //删除员工表下该部门的员工
            return Result.success(0);
        } catch (Exception e) {
            return Result.error(e.getMessage());
        } finally {
            // 记录日志 也是一个事务方法,用REQUIRES_NEW则不会被上述逻辑影响,会正常记录日志
            logger.create(new Date(), "删除")
        }
    }

AOP

在这里插入图片描述
实现
在这里插入图片描述
编写AOP类,交给IOC容器管理。
通过joinPoint可以拿到原始方法。

AOP核心概念

在这里插入图片描述

  • 连接点,joinPoint,可以被AOP控制的方法,比如上图的list,delete,save等方法都属于连接点。
  • 通知,Advice,重复逻辑,比如上图的recordTime方法,共性逻辑(所有连接点都会执行)
  • 切入点:pointCut,顾名思义就是在哪里切入这个类,匹配连接点的条件。
  • 切面:描述通知与切入点的对应关系,如上图的@Around(切入点表达式)+recordTIme(通知)方法就是切面。
  • 目标对象:Target,AO类所应用的对象。
AOP的执行流程

AOP类是如何作用于目标对象的,
在这里插入图片描述
上述说过,AOP是通过动态代理实现的,如上,DeptServiceImpl类,是AOP的目标对象,他会根据通知,生成一个代理类,DeptServiceProxy,然后重新list方法,执行AOP类的逻辑,最后如上,通过@Autowired注入的deptService就不是DeptServiceImpl,而是DeptSerivceProxy这个代理对象,所以执行list方法的时候,就会执行代理对象list,从而执行通知的逻辑

通俗的说,AOP类会在不影响目标对象代码的基础上,基于目标对象,新增一些其他的逻辑,通过代理的形式生成一个新的类,交给IOC容器.

案例 记录每个controller的耗费时间

可以用拦截器做到,也可以用过滤器做到,也可以用AOP做到。

@Component
@Aspect
@Slf4j
public class TimeAspect {
    @Around("execution(* com.example.demo.controller.*.*(..))")
    public Object recordTime(ProceedingJoinPoint joinPoint) throws  Throwable{
        long begin = System.currentTimeMillis();
        Object object = joinPoint.proceed(); //调用原方法
        long end = System.currentTimeMillis();
        log.info(joinPoint.getSignature() + "执行耗时: {}ms", end- begin);
        return object; //返回
    }
}

在这里插入图片描述

AOP高阶
通知类型

上述我们使用的@Around就是通知类型。他的功能最强大,可以编写目标方法执行前的逻辑,也可以编写目标方法执行后的逻辑(可以拿到目标方法)

在这里插入图片描述
@Pointcut注解可以抽离公共的切入点表达式,服用切入点表达式
在这里插入图片描述

通知的执行顺序

当有多个通知都匹配到同一个切入点时,目标方法执行,多个通知方法都会执行。
跟过滤器一样,其实是根据AOP的类名字母排序有关,且@before和@after的执行顺序就跟洋葱模型一样,也是234 -> 432这样执行。
在这里插入图片描述

除此之外,还可以通过@Order注解来标记执行顺序。

切入点表达式

在这里插入图片描述
在这里插入图片描述

通配符号 * 和 …

在这里插入图片描述

 @Around("execution(* com.*.demo.controller.*.update*(*))")

* com表示 匹配任何返回值
com.*.demo表示二级包是任意的
controller.*表示controller下的类或者接口是任意的
update*表示以update开头的类或者接口
(*)表示匹配一个参数

上述表示 任何的返回值,com下任意的二级包里含有的demo三级包下的controller目录下的任意的以类或者接口下以update开头,且只能有一个参数的方法

如果换成…

 @Around("execution(* com..controller.*.update*(..))")

com…可以匹配任意层级的包
update*(…)表示匹配update开头的方法,且可以有任意参数。

上述表示 任何的返回值,com下任意的二级包里含有controller的目录,下的任意的以类或者接口下以update开头,且只能有一个参数的方法

多个切入点表达式可以用 || & 等组合。

@annotation

在这里插入图片描述
匹配标识有特定注解的方法
定义一个注解用来标识

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Mylog {
    
}

将execution切换成@annotation

public class TimeAspect {
    //@Around("execution(* com.example.demo.controller.*.*(..))")
    @Around("@annotation(com.example.demo.aop.Mylog)")
    public Object recordTime(ProceedingJoinPoint joinPoint) throws  Throwable{
        long begin = System.currentTimeMillis();
        Object object = joinPoint.proceed(); //调用原方法
        long end = System.currentTimeMillis();
        log.info(joinPoint.getSignature() + "执行耗时: {}ms", end- begin);
        return object; //返回
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1524197.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

深度学习pytorch——拼接与拆分(持续更新)

cat拼接 使用条件&#xff1a;合并的dim的size可以不同&#xff0c;但是其它的dim的size必须相同。 语法&#xff1a;cat([tensor1,tensor2],dim n) # 将tensor1和tensor2的第n个维度合并 代码演示&#xff1a; # 拼接与拆分 a torch.rand(4,32,8) b torch.rand(…

AI大浪潮,怎能少了国产HBM内存?

据有关报道显示&#xff0c;武汉新芯半导体制造有限公司&#xff08;XMC&#xff09;正在启动一项专注于开发和生产高带宽内存&#xff08;HBM&#xff09;的项目。 HBM作为一种关键的DRAM类型&#xff0c;对于人工智能&#xff08;AI&#xff09;和高性能计算&#xff08;HPC&…

基于springboot+vue的疗养院管理系统

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…

Google云计算原理与应用(四)

目录 七、海量数据的交互式分析工具Dremel&#xff08;一&#xff09;产生背景&#xff08;二&#xff09;数据模型&#xff08;三&#xff09;嵌套式的列存储&#xff08;四&#xff09;查询语言与执行&#xff08;五&#xff09;性能分析&#xff08;六&#xff09;小结 八、…

USB - USB Gadget on Linux

February, 2012. Embedded Linux Conference 2012. Agenda Introduction to USB USB Gadget API Existing Gadgets Design your own Gadget Demo Conclusio About the Author Software engineer at Adeneo Embedded Linux, Android Main activities: – BSP adaptation – Driv…

Anaconda安装proplot库

看了一下Anaconda中的环境&#xff0c;现在我有4个&#xff0c;其中gee是一个虚拟环境 因此一般在prompt中装库时要先进入其中一个虚拟环境 conda activate geepip install proplot --no-deps下完了之后&#xff0c;发现版本不对应 conda install matplotlib3.4.3

zookeeper快速入门三:zookeeper的基本操作

在zookeeper的bin目录下&#xff0c;输入./zkServer.sh start和./zkCli.sh启动服务端和客户端&#xff0c;然后我们就可以进行zookeeper的基本操作了。如果是windows&#xff0c;请参考前面章节zookeeper快速入门一&#xff1a;zookeeper安装与启动 目录 一、节点的增删改查 …

中间件漏洞(redis)

目录 1.Redis服务器被挖矿案例 2.redis常见用途 3.redis环境配置 4.redis的持久化机制 5.redis动态修改配置 6.webshell提权案例 7.定时任务bash反弹连接提权案例 8.SSH Key提权案例 9.redis安全加固分析 1.Redis服务器被挖矿案例 我没有体验过&#xff0c;那就看看别…

VS code配置免密连接Linux服务器

1. 服务器端 1.1 安装OpensSSH sudo apt install openssh-server 1.2 开启ssh服务 使用下面的命令查看是否开启了ssh&#xff1a; service ssh status 或者 sudo systemctl status ssh 只要看到绿色高亮的active(running)就是开启了ssh 如果没有开启&#xff0c;则使用…

MySQL实战:监控

监控指标 性能类指标 名称说明QPS数据库每秒处理的请求数量TPS数据库每秒处理的事务数量并发数数据库实例当前并行处理的会话数量连接数连接到数据库会话的数量缓存命中率Innodb的缓存命中率 功能类指标 名称说明可用性数据库是否正常对外提供服务阻塞当前是否有阻塞的会话…

30.HarmonyOS App(JAVA)鸿蒙系统app多线程任务分发器

HarmonyOS App(JAVA)多线程任务分发器 打印时间&#xff0c;记录到编辑框textfield信息显示 同步分发&#xff0c;异步分发&#xff0c;异步延迟分发&#xff0c;分组任务分发&#xff0c;屏蔽任务分发&#xff0c;多次任务分发 参考代码注释 场景介绍 如果应用的业务逻辑比…

如何用saga实现分布式事务?

SAGA事务介绍 SAGA事务模式的历史十分悠久&#xff0c;比分布式事务的概念提出还要更早。SAGA的意思是“长篇故事、长篇记叙、一长串事件”&#xff0c;它起源于1987年普林斯顿大学的赫克托 加西亚 莫利纳&#xff08;Hector Garcia Molina&#xff09;和肯尼斯 麦克米伦&a…

python之万花尺

1、使用模块 import sys, random, argparse import numpy as np import math import turtle import random from PIL import Image from datetime import datetime from math import gcd 依次使用pip下载即可 2、代码 import sys, random, argparse import numpy as np imp…

整数和浮点数在内存中存储及题目

一、整数在内存中存储 整数的2进制表⽰⽅法有三种&#xff0c;即原码、反码和补码。三种表⽰⽅法均有符号位和数值位两部分&#xff0c;符号位都是⽤0表⽰“正”&#xff0c;⽤1表⽰“负”&#xff0c;⽽数值位最⾼位的⼀位是被当做符号位&#xff0c;剩余的都是数值位 正整数…

智慧公厕建设的主要目标是什么?

随着城市化进程的不断推进&#xff0c;公共厕所作为城市基础设施的重要组成部分&#xff0c;也变得越来越重要。为了提升公共厕所的管理水平、提供更好的服务质量&#xff0c;智慧公厕应运而生。智慧公厕的建设旨在通过信息化手段实现公共厕所的全面感知监测&#xff0c;实现公…

单片机学到什么程度才可以去工作?

单片机学到什么程度才可以去工作? 如果没有名校或学位的加持&#xff0c;你还得再努力一把&#xff0c;才能从激烈的竞争中胜出。以下这些技能可以给你加分&#xff0c;你看情况学&#xff0c;不同行业对这些组件会有取舍: . Cortex-M内核:理解MCU内核各部件的工作机制&#…

【黑马程序员】Python综合案例

文章目录 数据分析案例目的需求数据准备实践数据记录类 文件解析解析csv格式解析json文件 进行数据计算读取文件数据按日期累加数据 图表展示图标绘制最终效果展示 数据分析案例 目的 文件操作实践json库使用三方库pyecharts使用面向对象实践数据容器使用 需求 给定一个csv…

3.1_10 段页式管理方式

3.1_10 段页式管理方式 &#xff08;一&#xff09;分页、分段的优缺点分析 基于分页、分段的优缺点&#xff0c;人们想出了将分页、分段结合&#xff0c;就产生了段页式管理。段页式管理具备了分页、分段各自的优点。 &#xff08;二&#xff09;分段分页段页式管理 将进程按逻…

JavaScript 中实现请求并发控制

文章目录 浏览器并发请求限制数&#xff08;图&#xff09;实现代码三方插件 假设有 30 个待办任务要执行&#xff0c;而我们希望限制同时执行的任务个数&#xff0c;即最多只有 3 个任务能同时执行。当正在执行任务列表 中的任何 1 个任务完成后&#xff0c;程序会自动从 待办…

Flink程序员开发利器本地化WebUI生成

前言 在flink程序开发或者调试过程中&#xff0c;每次部署到集群上都需要不断打包部署&#xff0c;其实是比较麻烦的事情&#xff0c;其实flink一直就提供了一种比较好的方式使得开发同学不用部署就可以观察到flink执行情况。 上代码 第一步&#xff1a;开发之前需要引入在本…