spring拦截器Interceptor:HandlerInterceptor和MethodInterceptor

在Spring Boot中,拦截器可以分为两种类型:

  • 一是WebMVC,负责拦截请求,类似于过滤器,对用户的请求在Controller接收前进行处理,在Controller处理完成后加工结果等。使用时需实现HandlerInterceptor接口。
  • 一是AOP,拦截指定类型的方法,通过动态代理模式实现,可以在方法的调用前和调用后添加功能处理。使用时需要实现MethodInterceptor接口。

拦截器(Interceptor)和过滤器(Filter)对比

相同点:

  • 都可以对请求进行提前处理和响应内容加工。
  • 都支持多个拦截器/过滤器的链路传递。

不同点:

  • 拦截器由Spring提供,过滤器由Servlet提供。

HandlerInterceptor

HandlerInterceptor接口属于顶级接口,里面一共有三个default方法(这是jdk8的新特性,用来在接口中编写带有方法体的方法。实现接口可以不重写default方法,默认调用的仍是接口中的default方法体)

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 {
    }
}
boolean preHandle:会在controller处理前调用该方法,方法返回true则进入对应的controller。方法返回false则不会进入controller。可以用来编码,安全控制,权限校验等。

void postHandle:在controller处理完成返回ModelAndView后执行。此时还没有进行视图渲染,还可以修改ModelAndView。

void afterCompletion:整个请求已经处理完成了,可能返回了正常的请求结果,也可能返回一个异常。

下面我们来做一个简单的demo。 

先定义一个controller:

@RestController
public class UserRest {

    @Autowired
    private UserService userService;

    @GetMapping("users/{id}")
    public String getUser(@PathVariable("id") String id) {
        System.out.println("controller[url=users/" + id + "]");
        userService.say();
        String word = userService.say("你好,朋友");
        System.out.println(word);
        return "testUser";
    }

    @GetMapping("good/{id}")
    public String getGood(@PathVariable("id") String id) {
        System.out.println("controller[url=users/" + id + "]");

        return "testUser";
    }

    @GetMapping("users/login")
    public String test(User user, Model model) {
        System.out.println("controller[url=users/login]");
        model.addAttribute("id", user.getId());
        model.addAttribute("name", user.getName());
        int i = 1 / 0;
        return "index";
    }
}


@Service
public class UserServiceImpl implements UserService {
    @Override
    public void say() {
        System.out.println("service实现层");
    }

    @Override
    public String say(String str) {
        return str;
    }
}

里面提供了三个请求链接:/users/{id}和/users/login,/good/{id},这里为了简单方便,我们均将请求方式设置为get。

定义一个拦截器UserInteceptor(这里先测试第一个方法preHandle):

@Component
public class UserInteceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle;");
        Map<String, String[]> map = request.getParameterMap();
        map.forEach((k, v) -> {
            System.out.println("[Key=" + k + ";Value=" + StringUtils.join(v) + "];");
        });
        return true;
    }

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

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

然后将拦截器添加进容器中,设定它的拦截路径为/users/**,/goos/**:

@Configuration
public class InteceptorConfig implements WebMvcConfigurer {

    @Autowired
    private UserInteceptor userInteceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(userInteceptor).addPathPatterns("/users/*","/good/*");
    }
}

就此一个简单的项目已经构成,我们启动程序,然后访问/users/login?id=1&username=yxf&password=123&mail=5@qq.com

可以看到访问结果:

preHandle; //----------------------------调用了preHandle方法
[Key=id;Value=1]; //---------------------打印request中的id
[Key=name;Value=yxf]; //-----------------打印request中的name
[Key=password;Value=123]; //-------------打印request中的password
[Key=mail;Value=5@qq.com]; //------------打印request中的mail
controller[url=users/login] //-----------在preHandle之后,这里进入UserController的test方法。
postHandle //----------------------------controller处理完成后调用PostHandle方法。//---------------在这中间其实还有dispatchServlet调用视图解析器对View的解析等------------------
afterCompletion //-----------------------postHandle处理完成后调用afterCompletion。

 顺序是按照preHandle→Controller→postHandle→视图渲染器→afterCompletion的顺序执行。

MethodInterceptor

MethodInterceptor继承关系:

 

在MethodInterceptor接口中,只提供了一个方法

Object invoke(MethodInvocation invocation) throws Throwable;

首先分析方法的传入参数MethodInvocation

MethodInvocation对象继承关系如下:

相关方法说明

Method getMethod(); //获取Java反射类Method

Object[] getArguments(); // 获取方法的传入参数

Object proceed() throws Throwable; // 继续执行方法

Object getThis(); // 获取方法所在的对象

AccessibleObject getStaticPart(); // 获取的也是Java反射类Method

 测试Demo

为了方便一部分功能展示,我们这里定义一个注解用来后续操作

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

    String value() default "test";
    
}

定义我们的方法过滤器,从参数对象获取到被拦截的方法的相关信息,这里为了简单直接将信息打印在控制台(实际情况因具体业务而异)。

package org.example.config;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.example.annotation.DemoAnnotation;

import java.lang.reflect.Method;

public class DemoInterceptor implements MethodInterceptor {

    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        System.out.println("--------------------------------------------------");

        // 参数
        Object[] object = methodInvocation.getArguments();
        for (Object obj : object) {
            System.out.println(obj.getClass().getName());
            if (obj instanceof String) {
                System.out.println("param = " + ((String) obj).toString());
            }
        }

        // 对象
        Object obj = methodInvocation.getThis();
        System.out.println(obj.getClass().getName());

        // 方法
        Method method = methodInvocation.getMethod();
        System.out.println(method.getName());


        // 继续执行,obj是方法执行结果,有可能是void方法,此时obj == null
        obj = methodInvocation.proceed();
        if (obj != null){
            System.out.println(obj.getClass().getName());
        }

        // 获取注解
        DemoAnnotation d = method.getAnnotation(DemoAnnotation.class);
        if (d == null) {
            System.out.println("当前类没有DemoAnnotation注解");
        } else {
            System.out.println(d.value());
        }

        System.out.println("--------------------------------------------------");
        return obj;
    }
}

配置过滤器。(方法过滤器实际可以算是实现AOP的一种方式,我们需要定义一个切面,配置它的切点和增强):

package org.example.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConditionalOnProperty(name = "aop.exception.pointcut", matchIfMissing = true)
@Slf4j
public class AopConfig {


    @Value("${aop.exception.pointcut}")
    private String exceptionExecution;

    /**
     * 异常的切面
     *
     * @return
     */
    @Bean
    public DefaultPointcutAdvisor defaultPointcutAdvisor2() {
        // 声明切点
        //JdkRegexpMethodPointcut pointcut = new JdkRegexpMethodPointcut();
        //pointcut.setPatterns("com.example.*");
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression("execution(* org.example.service..*(*))"); // 拦截org.example.service包和子包下带一个参数的任何方法

        // 声明增强
        DemoInterceptor interceptor = new DemoInterceptor();
        // 配置切面
        DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();
        advisor.setPointcut(pointcut);
        advisor.setAdvice(interceptor);
        return advisor;
    }
}

访问localhost:8080/users/123,结果如下:

--------------------------------------------------
org.example.rest.UserServiceImpl
say
service实现层
当前类没有DemoAnnotation注解
--------------------------------------------------
--------------------------------------------------
java.lang.String
param = 你好,朋友
org.example.rest.UserServiceImpl
say
java.lang.String
当前类没有DemoAnnotation注解
--------------------------------------------------
你好,朋友   ----接口返回参数

原文:spring拦截器Interceptor

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring 拦截器是一种在 Spring MVC 请求处理过程中进行拦截的组件,它可以在请求到达控制器前或者返回响应前拦截请求并进行一些处理。拦截器可以被用来实现一些通用的功能,比如日志记录、权限控制、性能监控等等。 在 Spring MVC 中,拦截器是通过实现 HandlerInterceptor 接口来实现的。HandlerInterceptor 接口定义了三个方法,分别是 preHandle()、postHandle() 和 afterCompletion()。这些方法在请求处理过程中的不同阶段被调用,具体如下: 1. preHandle() 方法在请求到达控制器前被调用,可以用来进行一些前置处理,比如权限校验等。如果该方法返回 false,则请求处理流程会被打断。 2. postHandle() 方法在控制器处理完请求后、视图渲染前被调用,可以用来对请求处理结果进行一些后置处理,比如添加一些公共的模型数据等。 3. afterCompletion() 方法在整个请求处理完毕后被调用,可以用来进行一些清理工作,比如释放资源等。 在 Spring MVC 中,我们可以通过配置 Bean 实现拦截器的注册和配置。具体步骤如下: 1. 创建一个类,实现 HandlerInterceptor 接口,实现 preHandle()、postHandle() 和 afterCompletion() 方法。 2. 在 Spring 配置文件中配置该类为 Bean,比如: ``` <bean id="myInterceptor" class="com.example.MyInterceptor"/> ``` 3. 在 Spring MVC 配置文件中配置拦截器,比如: ``` <mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/**"/> <bean class="com.example.MyInterceptor"/> </mvc:interceptor> </mvc:interceptors> ``` 上面的配置表示将 MyInterceptor 拦截器应用于所有的请求路径。我们也可以通过 path 属性指定拦截器应用于具体的请求路径。 总的来说,Spring 拦截器是一种非常强大的组件,可以帮助我们实现很多通用的功能。掌握了拦截器的原理和使用方法,我们就可以更好地利用它来提高我们的开发效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值