首页 > 编程语言 >Java Spring Boot 拦截器的使用小结

Java Spring Boot 拦截器的使用小结

时间:2023-12-12 18:24:47浏览次数:41  
标签:web 拦截器 Java Spring request springframework org import public

很多时候,我们在开发项目中,总是希望在接口中,尽量进行业务处理,其余的事项交给其他组件来处理,比如:

  • 登录验证
  • 日志记录
  • 接口性能

在 Spring Boot 中,正如大多数框架一样,可以用到拦截件进行处理,不管叫中间件还是拦截件,总之都是为了让我们更好的专注于业务,解耦功能。

我们看看 Spring Boot 中应该怎样应用拦截件。

环境:

  • Spring Boot:3.1.16
  • JDK:17

拦截件接口声明

如果我们自己写个拦截件,通常需要实现这个接口:

package org.springframework.web.servlet;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.lang.Nullable;

public interface HandlerInterceptor {
    default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return true;
    }

    default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
    }

    default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
    }
}

正如接口声明一样[1]:

  • boolean preHandle(),方法在请求处理之前被调用。该方法在 Interceptor 类中最先执行,用来进行一些前置初始化操作或是对当前请求做预处理,也可以进行一些判断来决定请求是否要继续进行下去。该方法的返回至是 Boolean 类型,当它返回 false 时,表示请求结束,后续的 Interceptor 和 Controller 都不会再执行;当它返回为 true 时会继续调用下一个 Interceptor 的 preHandle 方法,如果已经是最后一个 Interceptor 的时候就会调用当前请求的 Controller 方法。

  • void postHandle(),方法在当前请求处理完成之后,也就是 Controller 方法调用之后执行,但是它会在 DispatcherServlet 进行视图返回渲染之前被调用,所以我们可以在这个方法中对 Controller 处理之后的 ModelAndView 对象进行操作。

  • void afterCompletion(),方法需要在当前对应的 Interceptor 类的 postHandler 方法返回值为 true 时才会执行。顾名思义,该方法将在整个请求结束之后,也就是在 DispatcherServlet 渲染了对应的视图之后执行。此方法主要用来进行资源清理。

拦截件的执行顺序

这里不考虑过滤器,根据注册拦截件的顺序和限定的 pathPattern,执行顺序如下:

image.png

接下来我们在某一需求中,实现我们的拦截件。

需求:

  • 需要统计某些接口的性能
  • 需要记录请求响应的日志
  • 需要统计接口的访问次数
  • 需要对登陆过的请求做拦截

基于上面的需求,我们逐步实现。

拦截件实现流程

说说大致流程:

1.明确拦截需求

2.编写拦截件

3.注册拦截件

4.编写业务接口

接下来是代码实现,在实现中我们暂不依赖其他三方库。

代码目录:

image.png

拦截件的实现

性能拦截件

package com.example.springbootinterceptorsdemo.interceptors;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.core.NamedThreadLocal;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

/**
 * function: record time cost of every api handler
 */
public class ApiBenchMarkInterceptor implements HandlerInterceptor {

    private NamedThreadLocal<Long> startTimeThreadLocal = new NamedThreadLocal<>("StopWatch-StartTime");

    private final long THRESHOLD = 500;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("------ApiBenchMarkInterceptor.preHandle------");
        long beginTime = System.currentTimeMillis();
        startTimeThreadLocal.set(beginTime);

        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("------ApiBenchMarkInterceptor.postHandle------");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("------ApiBenchMarkInterceptor.afterCompletion------");
        long endTime = System.currentTimeMillis();
        long beginTime = startTimeThreadLocal.get();
        long cost = endTime - beginTime;

        // record slow request
        if (cost > THRESHOLD) {
            System.out.println(String.format("%s cost %d millis", request.getRequestURI(), cost));
        }
    }
}

接口访问拦截件

package com.example.springbootinterceptorsdemo.interceptors;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * function: record api access count
 */
public class ApiAccessRecordInterceptors implements HandlerInterceptor {
    private static Map<String, Integer> pv = new ConcurrentHashMap<>();

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("------ApiAccessRecordInterceptors.preHandle------");
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();
        if (pv.get(method.getName()) == null) {
            pv.put(method.getName(), 1);
        } else {
            Integer count = pv.get(method.getName());
            pv.put(method.getName(), count+1);
        }
        return true;
    }

    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("------ApiAccessRecordInterceptors.postHandle------");
    }

    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("------ApiAccessRecordInterceptors.afterCompletion------");
        System.out.println(pv.toString());
    }
}

日志记录拦截件

package com.example.springbootinterceptorsdemo.interceptors;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

/**
 * function: record request and response
 */
public class LogRecordInterceptors implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String path = request.getRequestURI();
        String method = request.getMethod();
        String clientIp = request.getHeader("X-Real-IP");
        if (clientIp == null)
        clientIp = request.getHeader("X-Forwarded-For");
        System.out.println("------LogRecordInterceptors.preHandle------");
        System.out.println(String.format("Log -> clientIP: %s, path: %s, method: %s", clientIp, path, method));

        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("------LogRecordInterceptors.postHandle------");
    }

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

登陆验证拦截件

package com.example.springbootinterceptorsdemo.interceptors;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import java.lang.reflect.Method;

/**
 * function: check auth
 */
public class AuthCheckInterceptors implements HandlerInterceptor {
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("------AuthCheckInterceptors.postHandle------");
        String token = request.getHeader("token");
        // TODO: verify token
        return true;
    }

    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("------AuthCheckInterceptors.postHandle------");
    }

    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("------AuthCheckInterceptors.afterCompletion------");
    }
}

注册拦截件

需要明确哪些接口需要用到,什么时候用到。

package com.example.springbootinterceptorsdemo.config;

import com.example.springbootinterceptorsdemo.interceptors.ApiAccessRecordInterceptors;
import com.example.springbootinterceptorsdemo.interceptors.ApiBenchMarkInterceptor;
import com.example.springbootinterceptorsdemo.interceptors.AuthCheckInterceptors;
import com.example.springbootinterceptorsdemo.interceptors.LogRecordInterceptors;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * register interceptors class
 * attention:
 * 1.if record api performance, add in the first
 * 2.if no path patterns, stands for intercept all request
 * 3.if add exclude path patterns, app will intercept other request exclude the patterns in exclude ones
 */
@Configuration
public class InterceptorsConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new ApiBenchMarkInterceptor()).addPathPatterns("/performance"); // record performance, set first
        registry.addInterceptor(new ApiAccessRecordInterceptors()); // record method and count
        registry.addInterceptor(new LogRecordInterceptors()); // record request info
        registry.addInterceptor(new AuthCheckInterceptors()).addPathPatterns("/user/*", "/article/*");
    }
}

用户类

package com.example.springbootinterceptorsdemo.model;

public class User {

    private String name;

    private String password;

    private int age = 20;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

控制器类

index控制器类

package com.example.springbootinterceptorsdemo.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class IndexController {

    @GetMapping("/index")
    public String index() {
        System.out.println("this is from index handler");
        return "Hello, this is Spring Boot web application!";
    }
}

性能测试控制器类

package com.example.springbootinterceptorsdemo.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Random;

@RestController
public class CostTimeController {

    @GetMapping("/performance")
    public Object performance() throws InterruptedException {
        long start = System.currentTimeMillis();
        Random random = new Random();
        Thread.sleep(random.nextLong(1000));
        return "performance is ok, cost: " + (System.currentTimeMillis() - start);
    }
}

登录控制器类

package com.example.springbootinterceptorsdemo.controller;

import com.example.springbootinterceptorsdemo.model.User;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import java.util.UUID;

@RestController
public class AuthController {
    private static String name = "admin";

    private static String password = "12345678";

    @PostMapping("/login")
    public Object login(HttpServletRequest request, HttpServletResponse response, @RequestBody User user) {
        if (user.getName().equals(name) && user.getPassword().equals(password)) {
            String token = UUID.randomUUID().toString();
            response.addHeader("token", token);
        }

        return "login success!";
    }
}

用户增删改查控制器类

package com.example.springbootinterceptorsdemo.controller;

import com.example.springbootinterceptorsdemo.model.User;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Base64;

@RestController
@RequestMapping("/user")
public class UserController {

    @GetMapping("/get")
    public Object getUserInfo() {
        User user = new User();
        user.setName("admin");
        user.setAge(88);
        user.setPassword(Base64.getEncoder().encode("12345678".getBytes()).toString());

        return user;
    }
}

参考:

标签:web,拦截器,Java,Spring,request,springframework,org,import,public
From: https://www.cnblogs.com/davis12/p/17897523.html

相关文章

  • select for update在springboot里实现分布式锁
    //mapper,注意,这里的参数最好就是主键或者唯一键,否则产生的是表锁@Select("<script>select*fromfoowhereid=#{id}forupdate</script>")FooselectForUpdateById(@Param("id")Longid);//service,必须加事物,否则selectForUpdateById无法实现分布式锁的作用@Transac......
  • 《Effective Java》阅读笔记-第五章
    EffectiveJava阅读笔记第五章泛型第26条不要使用原生类型随着泛型的普及,这条没什么可说的。如果不知道具体类型,可以使用<?>来代替。第27条消除unchecked警告原生类型到泛型转换时,编译会有警告,可以使用@SuppressWarnings("unchecked")来消除警告。并且应该在尽可......
  • 秦疆的Java课程笔记:65 面向对象 创建对象内存分析
    先写两个类//创建一个Pet类==============================packageOOP.demo;publicclassPet{publicStringname;publicintage;publicvoidshout(){System.out.println("喵~~");}}//主程序Application================......
  • java计算二个经纬度间的距离(百度坐标)
    1:背景工作中遇到计算二个地点之间的距离,根据百度经纬度进行计算。2:maven依赖<dependency><groupId>org.gavaghan</groupId><artifactId>geodesy</artifactId><version>1.1.3</version></dependency>3:代码实现packagecom.pacific.transfe......
  • 零基础30天学会Java-韩顺平
    第一章概述了解了该视频课程的大纲和Java的基本知识,Java1995年推出,目前稳定维护的有Java8和Java11版本。JVM(Java虚拟机):JVM包含于JDK中,Java虚拟机机制屏蔽了底层运行平台的差别,实现了“一次编译,到处运行"JRE(Java运行环境):JRE=JVM+Java的核心类库。JDK(Java开发工具包):JDK=JRE+Jav......
  • Java-04数组
    tip:[start]程序=逻辑+数据,数组是存储数据的强而有力的手段。——闫学灿tip:[end]一维数组数组的定义数组的定义方式和变量类似。java中数组的定义[]是写在数组名前面(与c++区分),开辟长度需要new,即面向对象。publicclassMain{publicstaticvoidmain(String[]......
  • java异常
    一、概述 二、JVM对异常的处理方案 三、异常处理1、try..catch 示例: 2、Throwable  3、throws  示例:  四、编译时异常和运行时异常区别示例:  五、自定义异常 六、throws和throw ......
  • java实现文件上传和下载
    本文实例为大家分享了java实现文件上传和下载的具体代码,供大家参考,具体内容如下文件的上传upload:文件上传客户端通过表单的文件域file 把客户端的文件上传保存到服务器的硬盘上页面首先对上传的表单有以下要求:必须有文件域:inputtype=file表单提交方式:method=post表......
  • java基本类型包装类
    一、概述  二、Integer包装类 三、int和String的转换 1、方式一 2、方式二 3、方式三 4、方式四 四、自动装箱和拆箱 五、日期格式化 示例: ......
  • springboot listener、filter登录实战
    转载自:www.javaman.cn博客系统访问:http://175.24.198.63:9090/front/index登录功能1、前端页面采用的是layui-admin框架,文中的验证码内容,请参考作者之前的验证码功能<!DOCTYPEhtml><htmllang="zh"xmlns:th="http://www.thymeleaf.org"><head><title>ds博客</tit......