springmvc拦截器

在之前学习过滤器Filter,看到拦截器就想到了Filter

Filter的作用:对请求和响应进行过滤

Filter的生命周期:实例化----->初始化------>过滤-------->销毁

原理:基于函数回调;

只能在Web容器中使用,需要在服务器中使用,是一种Servlet规范;

那么拦截器是什么呢?

拦截器:针对处理器(Controller)的拦截器,是基于spring实现的

实现原理:反射;是AOP思想的一种体现;

拦截器的应用场景:

1.权限验证

2.日志记录

3.通用行为

4.性能监控

拦截器的实现:

1.实现HandlerInterceptor,接口源码如下:

package org.springframework.web.servlet;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.lang.Nullable;
import org.springframework.web.method.HandlerMethod;

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 {
    }

}

可以看到,源码的接口中提供了三个default方法;在jdk8之后,提供了default,接口中的方法使用default修饰后,实现类不是必须实现该方法了,在使用拦截器时,创建类继承拦截器适配器;接口适配:接口A中包含很多的方法,不想全部实现,就提供一个抽象子类B,实现部分方法,继承部分方法;子类C继承子类B可以根据需要重写B中的方法;

创建一个拦截器,进行登录身份验证:如果已经登录,就放行请求,如果没有登录,就拦截请求

//创建一个拦截器,继承HandlerInterceptor适配器
public class MyInterceptor extends HandlerInterceptorAdapter {
//    登录验证是在请求之前进行验证,因此重写preHandle方法
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        Object user = request.getSession().getAttribute("user");
        if (user!=null)
            return true;//true表示该拦截器放行,false表示不放行
        return false;
    }
}

然后再mvc配置文件中注册该拦截器:

<!--注册拦截器-->
    <mvc:interceptors>
        <!--可以写多个拦截器-->
        <mvc:interceptor>
            <!--设置拦截路径-->
            <mvc:mapping path="/**"/>
            <!--如果是第一次登录请求,要放行-->
            <mvc:exclude-mapping path="/user/login"/>
            <bean class="com.zs.interceptor.MyInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>

编写登录后台:

@Controller
@RequestMapping("/user")
@SessionAttributes("user")
public class UserController {

    @RequestMapping("/login")
    public String login(String username, String password, Model model) {
//        验证登录信息
        if (username.equals("zhangsan") && password.equals("123456")) {
            model.addAttribute("user", username);
        }
        return "redirect:/view/index.jsp";
    }
}

编辑前端页面验证拦截器是否生效:

springmvc拦截器

模拟性能监控:

package com.zs.interceptor;

import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 性能监控,需要计算方法执行的时间,因此要在方法执行前,获取时间,方法执行后再获取时间,两个时间相减
 */
public class XingNengInterceptor extends HandlerInterceptorAdapter {
    //线程不同步? 
    // 本地线程局部变量:只能在当前线程中使用,Map, 实现线程同步
    ThreadLocal<Long> threadLocal = new ThreadLocal<>();
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        long start = System.currentTimeMillis();
        threadLocal.set(start);
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        long end = System.currentTimeMillis();
        System.out.println(String.format("执行时间:%d",end-threadLocal.get()));
    }
}

注册拦截器,进行测试;

因为再Web容器中运行时,拦截器是分线程的,每一个登录的用户就开启一个线程,如果直接使用变量,会导致不同线程公用同一个变量,竞争资源,线程不安全,一个线程调用了方法,给变量赋值,方法执行期间,又一个线程执行了方法,就又给变量赋值了,所以使用本地线程局部变量的方法