likes
comments
collection
share

Java 拦截器深入了解学习

作者站长头像
站长
· 阅读数 6

Java 拦截器深入了解学习

Java 拦截器深入了解学习

命运总是不如愿。 但往往是在无数的痛苦中,在重重的矛盾和艰难中,才使人成熟起来,坚强起来;虽然这些东西在实际感受中给人带来的并不都是欢乐。 ————路遥《平凡的世界》

什么是拦截器(Interceptor)

在Spring Boot中,拦截器(Interceptor)是一种用于处理HTTP请求的机制,主要用于执行一些预处理或后处理的逻辑。与AOP不同,拦截器更专注于HTTP请求的处理,而不是面向方法调用等更细粒度的横切关注点。以下是Spring Boot拦截器的详细解释:

1. 拦截器接口:

在Spring Boot中,拦截器需要实现HandlerInterceptor接口。这个接口定义了三个主要的方法:

  • preHandle: 在请求处理之前被调用,用于进行一些预处理操作。
  • postHandle: 在请求处理之后、视图渲染之前被调用,用于进行一些后处理操作。
  • afterCompletion: 在整个请求处理完成后被调用,用于进行一些资源清理操作。

2. 配置拦截器:

在Spring Boot中配置拦截器主要通过实现WebMvcConfigurer接口,并覆盖addInterceptors方法。下面是一个简单的例子:

@Configuration
public class MyWebMvcConfigurer implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new MyInterceptor())
                .addPathPatterns("/api/**")  // 拦截的路径
                .excludePathPatterns("/public/**");  // 排除的路径
    }
}

在上述例子中,MyInterceptor 是实现了HandlerInterceptor接口的拦截器类。通过addPathPatterns指定需要拦截的路径,通过excludePathPatterns指定排除的路径。

3. 拦截器的实现:

拦截器的实现类需要实现HandlerInterceptor接口,并覆盖其中的方法,如下所示:

public class MyInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 在请求处理之前执行的逻辑
        return true; // 返回true表示继续执行后续操作,返回false表示中断请求处理
    }

    @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 {
        // 在整个请求处理完成后执行的逻辑,包括视图渲染之后
    }
}

4. 使用场景:

拦截器常用于:

  • 身份验证和权限控制
  • 日志记录
  • 统一异常处理
  • 请求参数处理等

拦截器(Interceptor)和切面(AOP)之间的区别

尽管拦截器(Interceptor)和AOP(面向切面编程)都是用于处理横切关注点的机制,但它们之间存在一些关键的区别。下面是它们之间的主要区别:

1. 关注点:

  • 拦截器: 主要关注HTTP请求的处理,通常用于预处理、后处理、日志记录等与HTTP请求相关的操作。拦截器工作在Controller层之上,能够截获请求的生命周期。

  • AOP: 关注点更广泛,可以应用于方法调用、对象的创建、属性的获取等各个层面。AOP更灵活,可以在更细粒度的操作上进行横切关注点的处理。

2. 作用范围:

  • 拦截器: 主要作用于HTTP请求处理,对Controller层的处理有直接影响。拦截器的作用范围更集中。

  • AOP: 可以作用于整个应用程序的多个模块,跨足多个层次。AOP的作用范围更广泛,不仅限于HTTP请求。

3. 使用场景:

  • 拦截器: 常用于身份验证、权限控制、日志记录等与HTTP请求生命周期相关的场景。

  • AOP: 常用于横切关注点,如日志记录、性能监控、事务管理等。AOP更适合处理那些与业务逻辑解耦的横切关注点。

4. 实现方式:

  • 拦截器: 在Spring Boot中通过实现HandlerInterceptor接口来创建拦截器。

  • AOP: 在Spring中,可以通过配置切面和通知,使用@Aspect注解等方式实现AOP。

5. 粒度:

  • 拦截器: 作用于整个请求处理阶段,较为粗粒度。

  • AOP: 可以根据需要选择切入点,可以是方法调用、对象的创建等,较为细粒度。

6. 依赖:

  • 拦截器: 主要依赖于Spring MVC框架,用于处理HTTP请求。

  • AOP: 可以独立于任何框架使用,不仅限于Spring框架。

综上所述,拦截器和AOP在关注点、作用范围、实现方式等方面存在差异,开发者可以根据具体需求选择合适的机制。通常情况下,如果只涉及HTTP请求处理相关的横切关注点,拦截器是一个较为合适的选择。如果需要更广泛的横切关注点处理,AOP可能更适合。

所以啊,可能很多时候可能AOP用的更多一些,比如若依的日志系统用的就是AOP切面进行实现,再言之,可能拦截器更加适合权限管理

一般可以这样理解,但并不是绝对的。虽然AOP通常被用于实现横切关注点的功能性需求,例如日志、事务、性能监控等,而拦截器则更常用于处理与HTTP请求生命周期相关的事务,如权限控制。

AOP的功能性:

  • 日志记录: 可以通过AOP实现在方法调用前后记录日志。
  • 事务管理: 可以使用AOP确保一组操作在事务的上下文中执行。
  • 性能监控: 可以通过AOP监控方法的执行时间等性能指标。

拦截器更适合权限控制:

  • 身份验证和授权: 拦截器可以用于对HTTP请求进行身份验证和授权,例如检查用户是否具有执行某个操作的权限。
  • 请求预处理: 可以在拦截器中进行请求的预处理,例如解析请求参数、检查请求头等。

虽然上述是一种常见的用法,但并不是绝对的规则。在实际应用中,AOP和拦截器可以灵活结合,根据具体需求进行选择。例如,权限控制可以通过AOP来实现,而拦截器也可以用于实现功能性的需求。

拦截器(Interceptor)的实现

示例一

在Spring Boot中,可以通过实现HandlerInterceptor接口来创建一个拦截器,用于判断请求是否携带了 token。以下是一个简单的例子:

import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class TokenInterceptor extends HandlerInterceptorAdapter {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 从请求头中获取 token
        String token = request.getHeader("Authorization");

        // 判断 token 是否存在
        if (token == null || token.isEmpty()) {
            // 如果不存在,返回未授权状态码,并终止请求
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            return false;
        }

        // 如果存在 token,继续处理请求
        return true;
    }
}

在这个例子中,TokenInterceptor 继承了 HandlerInterceptorAdapter 类,重写了 preHandle 方法。在 preHandle 方法中,从请求头中获取了名为 "Authorization" 的 token,然后判断是否存在。如果不存在,返回未授权状态码(SC_UNAUTHORIZED)并终止请求;如果存在,继续处理请求。

接下来,你需要在配置类中注册这个拦截器:

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 注册拦截器,并设置拦截的路径
        registry.addInterceptor(new TokenInterceptor())
                .addPathPatterns("/api/**"); // 设置需要拦截的路径
    }
}

在这个例子中,WebMvcConfig 类实现了 WebMvcConfigurer 接口,并覆盖了 addInterceptors 方法,用于注册拦截器。在 addInterceptors 方法中,通过 registry.addInterceptor(new TokenInterceptor()) 注册了 TokenInterceptor 拦截器,并使用 .addPathPatterns("/api/**") 指定了需要拦截的路径,可以根据实际需求进行修改。

示例二

我们再升级一下,需求是在请求前鉴权,如果没有携带 token 则返回 401,在请求后判断逻辑错误返回 500,并在请求完成后输出日志。

import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class TokenInterceptor extends HandlerInterceptorAdapter {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 从请求头中获取 token
        String token = request.getHeader("Authorization");

        // 判断 token 是否存在
        if (token == null || token.isEmpty()) {
            // 如果不存在,返回未授权状态码并终止请求
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            return false;
        }

        // 在这里可以进行进一步的鉴权逻辑
        // 如果鉴权失败,可以返回 401 并终止请求
        // ...

        return true; // 鉴权通过,继续处理请求
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // 请求处理完成后的逻辑
        if (ex != null) {
            // 如果有异常,返回 500 状态码
            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            // 这里可以记录日志或进行其他逻辑处理
            System.err.println("Request completed with error: " + ex.getMessage());
        } else {
            // 请求正常完成,可以记录日志或进行其他逻辑处理
            System.out.println("Request completed successfully");
        }
    }
}

在这个示例中,preHandle 方法用于在请求前进行鉴权,如果没有携带 token 则返回 401,如果鉴权失败可以在这里终止请求。afterCompletion 方法用于在请求完成后进行逻辑处理,如果有异常则返回 500 并记录错误日志,否则记录请求正常完成的日志。请根据实际需求进行适当的调整。