JAVA三种拦截方式详解(原生过滤器Filter、springMVC拦截器、aop切面)

 更新时间:2024年05月21日 09:40:30   作者:谦风(Java)  
在Java开发中方法拦截是一种常见的技术,可以用于在方法执行前后添加额外的逻辑或修改方法的行为,这篇文章主要给大家介绍了关于JAVA三种拦截方式的相关资料,文中介绍的方式分别是原生过滤器Filter、springMVC拦截器、aop切面,需要的朋友可以参考下

最近面试有遇到拦截方式的场景,结合网上xdm的代码整理了下,分为以下三种:
java原生过滤器Filter、springMVC拦截器、aop切面

一、java原生过滤器Filter

package com.zhangximing.springbootinterceptor.interceptor;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Enumeration;

/**
 * 自定义Filter
 * 对请求的header 过滤token
 *
 * 过滤器Filter可以拿到原始的HTTP请求和响应的信息,
 *     但是拿不到你真正处理请求方法的信息,也就是方法的信息
 *
 * @Component 注解让拦截器注入Bean,从而让拦截器生效
 * @WebFilter 配置拦截规则
 *
 * 拦截顺序:filter—>Interceptor-->ControllerAdvice-->@Aspect -->Controller
 *
 */
@Slf4j
@Component
@WebFilter(urlPatterns = {"/**"},filterName = "authFilter")
public class MyFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        log.info("TokenFilter init {}",filterConfig.getFilterName());
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        String param = request.getParameter("param");

        response.setContentType("text/html;charset=UTF-8");

        //获取请求头token
        String token = "";
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        Enumeration<String> headerNames = httpServletRequest.getHeaderNames();
        while(headerNames.hasMoreElements()) {//判断是否还有下一个元素
            String nextElement = headerNames.nextElement();//获取headerNames集合中的请求头
            if ("token".equals(nextElement)){
                token = httpServletRequest.getHeader(nextElement);
                log.info("请求头key[" + nextElement + "]:" + token);
            }
        }

        log.info("doFilter-我拦截到了请求:"+ param);
        if (null != param && "pass".equals(param)){
            //验证token
            if ("7758258xx".equals(token)){
                chain.doFilter(request,response);//到下一个链
            }else{
                response.getWriter().write("doFilter-请求头token不通过");
            }
        }else{
            log.info("doFilter-参数param不符合条件");
            response.getWriter().write("doFilter-参数param不通过");
        }
    }

    @Override
    public void destroy() {
        log.info("destroy");
    }
}

简单测试直接用的postman,参数是一个param和一个请求头token:

这里补充一下:

若非springboot的情况下,不使用@WebFilter则需要自己设置配置文件

你需要在web.xml文件中配置过滤器,指定要拦截的URL以及要使用的过滤器

<filter>
    <filter-name>MyFilter</filter-name>
    <filter-class>com.zhangximing.springbootinterceptor.interceptor.MyFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>MyFilter</filter-name>
    <url-pattern>/example</url-pattern>
</filter-mapping>

二、springMVC拦截器

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;

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

/**
 * 自定义拦截器
 * 自定义拦截器后,需要配置进Spring
 *
 * 拦截器Interceptor可以拿到原始的HTTP请求和响应的信息,
 *    也可以拿到你真正处理请求方法的信息,但是拿不到传进参数的那个值。
 *
 *拦截顺序:filter—>Interceptor-->ControllerAdvice-->@Aspect -->Controller
 */
@Slf4j
@Component
public class MyInterceptor implements HandlerInterceptor {

    /**
     * 在访问Controller某个方法之前这个方法会被调用。
     * @param request
     * @param response
     * @param handler
     * @return false则表示不执行postHandle方法,true 表示执行postHandle方法
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.info("Interceptor preHandle {}","");
        String token = request.getHeader("token");
        log.info("Interceptor preHandle token :{}",token);
        log.info("Interceptor preHandle uri {}",request.getRequestURL().toString());

        response.setContentType("text/html;charset=UTF-8");

        //spring boot 2.0对静态资源也进行了拦截,当拦截器拦截到请求之后,
        // 但controller里并没有对应的请求时,该请求会被当成是对静态资源的请求。
        // 此时的handler就是 ResourceHttpRequestHandler,就会抛出上述错误。
        if (handler instanceof HandlerMethod){
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Method method = handlerMethod.getMethod();
            log.info("Token Interceptor preHandle getMethod {}",method.getName());
        }else if(handler instanceof ResourceHttpRequestHandler){//静态资源
            ResourceHttpRequestHandler resourceHttpRequestHandler = (ResourceHttpRequestHandler) handler;
            log.info("Token Interceptor preHandle getMethod {}",resourceHttpRequestHandler.getMediaTypes());
        }

        if (!"7758258xx".equals(token)){
            response.getWriter().write("doInterceptor-请求头token不通过");
            return false;
        }

        //false则表示不执行postHandle方法,不执行下一步chain链,直接返回response
        return true;
    }

    /**
     * 请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后)
     * preHandle方法处理之后这个方法会被调用,如果控制器Controller出现了异常,则不会执行此方法
     * @param request
     * @param response
     * @param handler
     * @param modelAndView
     * @throws Exception
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        log.info("Interceptor postHandle");
    }

    /**
     * 不管有没有异常,这个afterCompletion都会被调用
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.info("Interceptor afterCompletion");
    }

这里注意下,需要将拦截器配置进spring

import com.zhangximing.springbootinterceptor.interceptor.MyInterceptor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 *  MyInterceptor 自定义拦截器后,需要配置进Spring
 * 也可以mapping,跨域设置
 */
@Slf4j
@Configuration
public class MyInterceptorConfig implements WebMvcConfigurer {

    @Autowired
    MyInterceptor myInterceptor;

    /**
     * 添加拦截器
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        log.info("addInterceptors tokenInterceptor");
        registry.addInterceptor(myInterceptor)
                .addPathPatterns("/**")//指定该类拦截的url
        .excludePathPatterns( "/static/**");//过滤静态资源
    }

    /**
     * 如果实现了Filter跨域拦截,这个跨域无效
     * 拦截器实现 跨域支持
     * @param registry
     */
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        log.info("addInterceptors addCorsMappings");
        registry.addMapping("/**")
                .allowedOriginPatterns("*")  //本人测试时springboot2.7版本用的是这个
                .allowCredentials(true)
                .allowedMethods("GET", "POST", "DELETE", "PUT","OPTIONS","HEAD")
                .allowedHeaders("*")
                .maxAge(3600);
    }
}

测试同理:

三、aop切面

引入maven:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

若出现无法解析aspectjweaver则需要手动加入其他版本maven解决问题

<dependency>
   <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.4</version>
</dependency>
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;

/**
 * @Author: zhangximing
 * @Email: 530659058@qq.com
 * @Date: 2023/8/18 10:15
 * @Description: 切面
 */
@Slf4j
@Component  //表示它是一个Spring的组件
@Aspect  //表示它是一个切面
public class MyAspect {

    private static final Logger logger = LoggerFactory.getLogger(MyAspect.class);

    ThreadLocal<Long> startTime = new ThreadLocal<>();

    /**
     * 第一个*代表返回类型不限
     * 第二个*代表所有类
     * 第三个*代表所有方法
     * (..) 代表参数不限
     * com.zhangximing.springbootinterceptor.controller 测试的controller层
     */
    @Pointcut("execution(public * com.zhangximing.springbootinterceptor.controller.*.*(..))")
    public void pointCut(){};

    @Before(value = "pointCut()")
    public void before(JoinPoint joinPoint){
        System.out.println("方法执行前执行......before");
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        logger.info("<=====================================================");
        logger.info("请求来源: =》" + request.getRemoteAddr());
        logger.info("请求URL:" + request.getRequestURL().toString());
        logger.info("请求方式:" + request.getMethod());
        logger.info("响应方法:" + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
        logger.info("请求参数:" + Arrays.toString(joinPoint.getArgs()));
        logger.info("连接点的方法签名对象:"+joinPoint.getSignature());
        logger.info("连接点所在的目标对象:"+joinPoint.getTarget());
        logger.info("代理对象:"+joinPoint. getThis());
        logger.info("------------------------------------------------------");
        startTime.set(System.currentTimeMillis());
    }

    // 定义需要匹配的切点表达式,同时需要匹配参数
    /**
     * @description 要拦截修改参数的值只有使用这个方法,Around相当于before+after
     * @param pjp
     * @param arg 类型可以根据pointCut指定切点类下的方法确定,也可以使用统一的Object,也可以不写参数
     * @return
     * @throws Throwable
     */
    @Around("pointCut() && args(arg)")
    public Object around(ProceedingJoinPoint pjp, Object arg) throws Throwable{
        logger.info("入参:{}",arg);
        logger.info("方法环绕start...around");

        JSONObject param = JSONObject.parseObject(JSONObject.toJSONString(arg));
        if ("zxm".equals(param.getString("name"))){
            JSONObject result = new JSONObject();
            result.put("success",false);
            result.put("msg","error");
            return result;
        }
        param.put("exist",true);
        param.put("name","cml");

        //修改值
        Object[] objects = new Object[]{param};
        Object objectNew = pjp.proceed(objects);

        logger.info("方法环绕end...around");

        return objectNew;
    }

    @After("within(com.zhangximing.springbootinterceptor.controller.*)")
    public void after(){
        System.out.println("方法之后执行...after.");
    }

    /**
     *
     * @param AjaxResult  rst 该参数类型需要与测试的Controller层的返回值类型一致,否则不生效,也就是找不到
     *            该测试中的AjaxResult是测试项目中封装好的出参
     */
    @AfterReturning(pointcut="pointCut()",returning = "rst")
    public void afterRunning(JSONObject rst){
        if(startTime.get() == null){
            startTime.set(System.currentTimeMillis());
        }
        System.out.println("方法执行完执行...afterRunning");
        logger.info("耗时(毫秒):" +  (System.currentTimeMillis() - startTime.get()));
        logger.info("返回数据:{}", rst);
        logger.info("==========================================>");
    }

    @AfterThrowing("within(com.zhangximing.springbootinterceptor.controller.*)")
    public void afterThrowing(){
        System.out.println("异常出现之后...afterThrowing");
    }
}

实现效果用的是如下controller:

@Slf4j
@RestController
@RequestMapping("/test")
public class TestController {

    @RequestMapping("/test1")
    public String test1(@RequestParam(required = false, value = "param") String param){
        log.info("test1:"+param);
        return "test1:"+param;
    }

    @RequestMapping("/test2")
    public JSONObject test2(@RequestBody JSONObject params){
        log.info("test2:"+params.toJSONString());

        params.put("success",true);
        return params;
    }
}

参数判断拦截以及参数修改等方式都可以通过aop切面来实现,这是比较基本的aop拦截实现

最后关于aop失效补充下,切面只能对被spring代理的对象起作用,目前是针对的请求入口进行拦截,我试了下踩坑,比如说如果要对service或dao进行拦截,可以使用注解注入的方式生效。

总结 

到此这篇关于JAVA三种拦截方式(原生过滤器Filter、springMVC拦截器、aop切面)的文章就介绍到这了,更多相关JAVA拦截方式内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 详细总结Java基础类和包装类

    详细总结Java基础类和包装类

    近几天一直在复习Java基础知识,今天就带大家总结一下Java基础类和包装类,下文中有非常详细的代码示例,对正在学习Java基础的小伙伴们很有帮助,需要的朋友可以参考下
    2021-05-05
  • JAVA中通过自定义注解进行数据验证的方法

    JAVA中通过自定义注解进行数据验证的方法

    java 自定义注解验证可自己添加所需要的注解,下面这篇文章主要给大家介绍了关于JAVA中通过自定义注解进行数据验证的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考借鉴,下面随着小编来一起学习学习吧
    2018-08-08
  • java判断Long类型的方法和实例代码

    java判断Long类型的方法和实例代码

    在本篇文章里小编给大家整理的是关于java判断Long类型的方法和实例代码,对此有需要的朋友们跟着学习参考下。
    2020-02-02
  • javaweb 实现文件下载的方法及实例代码

    javaweb 实现文件下载的方法及实例代码

    这篇文章主要介绍了javaweb 实现文件下载的方法的相关资料,这里提供了实现代码,需要的朋友可以参考下
    2016-11-11
  • SpringBoot整合Sharding-JDBC实现MySQL8读写分离

    SpringBoot整合Sharding-JDBC实现MySQL8读写分离

    本文是一个基于SpringBoot整合Sharding-JDBC实现读写分离的极简教程,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的可以了解一下
    2021-07-07
  • JAVA面试题之Forward与Redirect的区别详解

    JAVA面试题之Forward与Redirect的区别详解

    这篇文章主要给大家介绍了在JAVA面试中可能遇到会遇到的一道题,就是java中Forward与Redirect两者之前的区别,文中介绍的非常详细,对大家具有一定参考学习价值,需要的朋友们下面来一起看看吧。
    2017-05-05
  • Java错误问题:找不到或无法加载主类的解决

    Java错误问题:找不到或无法加载主类的解决

    这篇文章主要介绍了Java错误问题:找不到或无法加载主类的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-03-03
  • SpringBoot配置主从数据库实现读写分离

    SpringBoot配置主从数据库实现读写分离

    现在的 Web 应用大都是读多写少,本文主要介绍了SpringBoot配置主从数据库实现读写分离,具有一定的参考价值,感兴趣的可以了解一下
    2023-11-11
  • Spark SQL 编程初级实践详解

    Spark SQL 编程初级实践详解

    这篇文章主要为大家介绍了Spark SQL 编程初级实践详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-04-04
  • Spring的RequestBodyAdvice拦截浅析

    Spring的RequestBodyAdvice拦截浅析

    本文主要介绍了Spring的RequestBodyAdvice拦截浅析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-02-02

最新评论