学习笔记-拦截器

本文介绍了拦截器的概念,包括Struts2中的官方解释和简单理解。详细阐述了Spring MVC和SpringBoot中如何实现拦截器,以及在Spring Boot项目中注册和配置拦截器的步骤。此外,讨论了拦截器的应用场景,如登录验证、权限检查、日志记录等,并提供了登录验证的示例。最后,解析了拦截器的执行流程,包括preHandle、主方法执行、postHandle和afterCompletion的顺序和条件。
摘要由CSDN通过智能技术生成

学习笔记-拦截器

什么是拦截器

1.官方解释:拦截器(Interceptor)是Struts2框架的核心功能之一,[Struts 2](https://baike.baidu.com/item/Struts 2/2187934)是一个基于MVC设计模式开源框架, 主要完成请求参数的解析、将页面表单参数赋给值栈中相应属性、执行功能检验、程序异常调试等工作。 java里的拦截器是动态拦截Action调用的对象,它提供了一种机制可以使开发者在一个Action执行的前后执行一段代码,也可以在一个Action执行前阻止其执行,同时也提供了一种可以提取Action中可重用部分代码的方式。在AOP中,拦截器用于在某个方法或者字段被访问之前,进行拦截然后再之前或者之后加入某些操作。

2.简单理解:拦截器将很多service或者Controller中共有的行为提炼出来,在某些方法执行的前后执行,提炼为通用的处理方式,让被拦截的方法都能享受这一共有的功能,让代码更加简洁,同时,当共有的功能需要发生调整、变动的时候,不必修改很多的类或者方法,只要修改这个拦截器就可以了,可复用性很强。

如何实现拦截器

Spring MVC 中的Interceptor拦截请求是通过HandlerInterceptor来实现的。

返回值类型方法声明描述
booleanpreHandle(HttpServletRequest request, HttpServletResponse response, Object handler)该方法在控制器处理请求方法前执行,其返回值表示是否中断后续操作,返回 true 表示继续向下执行,返回 false 表示中断后续操作。
voidpostHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)该方法在控制器处理请求方法调用之后、解析视图之前执行,可以通过此方法对请求域中的模型和视图做进一步修改。
voidafterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)该方法在视图渲染结束后执行,可以通过此方法实现资源清理、记录日志信息等工作。

SpringBoot中使用拦截器

在 Spring Boot 项目中,使用拦截器功能通常需要以下 3 步:

  1. 定义拦截器;
  2. 注册拦截器;
  3. 指定拦截规则(如果是拦截所有,静态资源也会被拦截)。

浅浅的介绍一下HandlerIntercepter

package org.springframework.web.servlet;

import javax.servlet.http.HttpServletRequest;
import javax.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 {
    }
}

代码实现

定义拦截器

package com.tamir.interceptor.interceptor1;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;

public class MyInterceptor implements HandlerInterceptor {
	private static final Logger logger = LoggerFactory.getLogger(MyInterceptor.class);
	
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
		HandlerMethod handlerMethod = (HandlerMethod) handler;
		Method method = handlerMethod.getMethod();
		String methodName = method.getName();
		logger.info("==拦截到了:{},在该方法之前执行==",methodName);
		return true;
	}
	
	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
		logger.info("执行完方法之后进执行(Controller方法调用之后),但是此时还没进行视图渲染");
	}
	
	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
		logger.info("整个请求都处理完咯,DispatcherServlet也渲染了对应的视图咯,此时我可以做一些清理的工作了");
	}
}

注册拦截器;

@Configuration
public class MyInterceptorConfig  extends WebMvcConfigurationSupport {
	@Override
	protected void addInterceptors(InterceptorRegistry registry) {
		registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**")
				.excludePathPatterns("/index");
		super.addInterceptors(registry);
	}
}

解决可能存在的问题,当全局拦截后,静态资源也被拦截可以在 resources/static/ 目录下放置一个图片资源或者 HTML 文件,之后启动项目直接访问,即可看到无法访问的现象。也就是说,虽然 Spring Boot 2.0 废弃了 WebMvcConfigurerAdapter,但是 WebMvcConfigurationSupport 又会导致默认的静态资源被拦截,这就需要我们手动将静态资源放开。

@Configuration
public class MyInterceptorConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 实现 WebMvcConfigurer 不会导致静态资源被拦截
        registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**");
    }
}

通过实现 WebMvcConfigure 接口,使 Spring Boot 默认的静态资源不会拦截。

实现适合原因
继承 WebMvcConfigurationSupport前后端分离的项目会拦截静态资源
实现接口 WebMvcConfigure非前后端分离的项目不会拦截静态资源

继承 WebMvcConfigurationSupport 类的方式可以用在前后端分离的项目中,后台不需要访问静态资源(就不需要放开静态资源了);实现 WebMvcConfigure 接口的方式可以用在非前后端分离的项目中,因为需要读取一些图片、CSS、JS 文件等等。

应用场景

拦截器本质上是面向切面编程(AOP),符合横切关注点的功能都可以放在拦截器中来实现,主要的应用场景包括:

  • 登录验证,判断用户是否登录。
  • 权限验证,判断用户是否有权限访问资源,如校验token
  • 日志记录,记录请求操作日志(用户ip,访问时间等),以便统计请求访问量。
  • 处理cookie、本地化、国际化、主题等。
  • 性能监控,监控请求处理时长等。

使用实例

  1. 判断用户有没有登录

    一般用户的登录功能,我们可以这么实现

    第一种:在 Session 中写一个 user

    第二种:针对每个 user 生成一个 Token,相比之下,第二种要更好。

    第二种方式中,如果用户登录成功,每次请求时都会带上该用户的 Token,如果未登录,则没有该 Token,服务端可以检测这个 Token 参数的有无来判断用

    户有没有登录,从而实现拦截功能。我们改造一下 preHandle 方法,如下:

@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
		HandlerMethod handlerMethod = (HandlerMethod) handler;
		Method method = handlerMethod.getMethod();
		String methodName = method.getName();
		logger.info("==拦截到了:{},在该方法之前执行==",methodName);
		// 判断用户有没有登陆,一般登陆之后的用户都有一个对应的 token
		String token = request.getParameter("token");
		if (null == token || "".equals(token)) {
			logger.info("用户未登录,没有权限执行……请登录");
			return false;
		}
		// 返回 true 才会继续执行,返回 false 则取消当前请求
		return true;
	}

请求http://localhost:8881/login 不带token结果:

页面无输出;
控制台:
2022-06-13 10:35:34.142  INFO 7144 --- [nio-8881-exec-1] c.t.i.interceptor3.MyInterceptor         : ==拦截到了:login,在该方法之前执行==
2022-06-13 10:35:34.143  INFO 7144 --- [nio-8881-exec-1] c.t.i.interceptor3.MyInterceptor         : 用户未登录,没有权限执行……请登录

请求http://localhost:8881/login 带token结果:

页面输出:
success
控制台:
2022-06-13 10:36:27.812  INFO 7144 --- [nio-8881-exec-3] c.t.i.interceptor3.MyInterceptor         : ==拦截到了:login,在该方法之前执行==
2022-06-13 10:36:27.831  INFO 7144 --- [nio-8881-exec-3] c.t.i.interceptor3.MyInterceptor         : 执行完方法之后进执行(Controller方法调用之后),但是此时还没进行视图渲染
2022-06-13 10:36:27.831  INFO 7144 --- [nio-8881-exec-3] c.t.i.interceptor3.MyInterceptor         : 整个请求都处理完咯,DispatcherServlet也渲染了对应的视图咯,此时我可以做一些清理的工作了

拦截器执行流程

1、拦截器执行顺序是按照Spring配置文件中定义的顺序而定的。

2、会先按照顺序执行所有拦截器的preHandle方法,一直遇到return false为止,比如第二个preHandle方法是return false,则第三个以及以后所有拦截器都不会执行。若都是return true,则按顺序加载完preHandle方法。

3、然后执行主方法(自己的controller接口),若中间抛出异常,则跟return false效果一致,不会继续执行postHandle,只会倒序执行afterCompletion方法。

4、在主方法执行完业务逻辑(页面还未渲染数据)时,按倒序执行postHandle方法。若第三个拦截器的preHandle方法return false,则会执行第二个和第一个的postHandle方法和afterCompletion(postHandle都执行完才会执行这个,也就是页面渲染完数据后,执行after进行清理工作)方法。(postHandle和afterCompletion都是倒序执行)

post和after是倒序的!!!!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值