二十二. SpringMVC-拦截器

一、概要

Spring Web MVC的拦截器,类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。

二、核心接口

拦截器

preHandle方法

进入Handler方法之前执行。可以用于身份认证、身份授权。比如如果认证没有通过表示用户没有登陆,需要此方法拦截不再往下执行(return false),否则就放行(return true)。

postHandle方法

进入Handler方法之后,返回ModelAndView之前执行。可以看到该方法中有个modelAndView的形参。应用场景:从modelAndView出发:将公用的模型数据(比如菜单导航之类的)在这里传到视图,也可以在这里同一指定视图。

afterCompletion方法

执行Handler完成之后执行。应用场景:统一异常处理,统一日志处理等。

拦截器适配器

spring提供了一个HandlerInterceptorAdapter适配器(一种适配器设计模式的实现),允许我们只实现需要的回调方法

三、使用步骤

第一步 创建一个类实现 HandlerInterceptor

/**
 * 现在需要实现用户登录检查——如果发现用户没有登录,跳转到登录页面,登
 * 录成功后跳转回之前访问的页面。
 *
 * @author zhangwei
 */
@Component
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
    public static final String USER_SESSION_NAME = "username";
    /**
     * 作用
     * 预处理回调方法
     * 1> 如果方法返回true 表示继续执行下一个拦截器
     * 2> 如果方法返回false 请求处理流程中断
     * 不会继续调用其他的拦截器或处理器方法,此时需要通过response产生响应
     *
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        Object attribute = request.getSession().getAttribute(USER_SESSION_NAME);
        log.debug("VerifyInterceptor====> preHandle方法执行" );
        if (attribute != null) {
            return true;
        }
        response.sendRedirect("/account/login?next=" + request.getRequestURI());
        return false;
    }
    /**
     * 后处理回调方法,
     * 实现处理器的后处理(但在渲染视图之前),
     * 此时可以通过modelAndView对模型数据进行处理或对视图进行处理
     *
     * @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.debug("VerifyInterceptor====> preHandle2方法执行" );
    }
    /**
     * 整个请求处理完毕回调方法,即在视图渲染完毕时调用
     *
     * @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.debug("VerifyInterceptor====> afterCompletion 方法执行" );
    }
}

第二步 控制器

@Controller
@RequestMapping("/account")
public class AccountController {
    @RequestMapping("/login")
    public String login(HttpServletRequest request) {
        request.getSession().setAttribute("username", "admin");
        return   "redirect:/";
    }
}

四、注册方式

一种通过java类注册,另外一种通过xml注册

java类注册(spring-boot)

@Configuration
public class WebMvcConfigInterceptor implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 添加拦截器
        registry.addInterceptor(new VerifyInterceptor())
                    // 添加拦截的url地址
                .addPathPatterns("/**")
                    // 排除不拦截的url地址
                .excludePathPatterns("/login");
    }
}

xml注册

拦截所有的url

<mvc:interceptors>
    <bean class="com.vip.mvc.interceptor.LoginInterceptor"/>
</mvc:interceptors>

拦截匹配的URL

<mvc:interceptors >
  <mvc:interceptor>
            <interceptor下面定义的mapping和exclude-mapping都是可以有多个的>
             <!-- /user/*  -->
        <mvc:mapping path="/admin/**" />
     <mvc:exclude-mapping path="/account/login" /><!-- 不拦截 -->
        <bean class="com.vip.mvc.interceptor.LoginInterceptor"></bean>
    </mvc:interceptor>
</mvc:interceptors>

注 拦截器语法

Wildcard Description
? 匹配任何单字符
* 匹配0或者任意数量的字符
** 匹配0或者更多的目录

栗子

匹配 说明
/app/*.x 匹配(Matches)所有在app路径下的.x文件
/app/p?ttern 匹配(Matches) /app/pattern 和 /app/pXttern,但是不包括/app/pttern
/**/example 匹配(Matches) /app/example, /app/foo/example, 和 /example
/app/**/dir/file. 匹配(Matches) /app/dir/file.jsp, /app/foo/dir/file.html,/app/foo/bar/dir/file.pdf, 和 /app/dir/file.java
/*/.jsp 匹配(Matches)任何的.jsp 文件

五、栗子

需求

  • 用户请求url
  • 拦截器进行拦截校验
  • 如果请求的url是公开地址(无需登陆即可访问的url)。
  • 如果用户session不存在,则跳转到登陆页面。
  • 如果用户session存在,跳转相应的界面。

创建登录拦截器

/**
 * 现在需要实现用户登录检查——如果发现用户没有登录,跳转到登录页面,登
 * 录成功后跳转回之前访问的页面。
 *
 * @author zhangwei
 */
@Component
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
    public static final String USER_SESSION_NAME = "username";
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        Object attribute = request.getSession().getAttribute(USER_SESSION_NAME);
        log.debug("VerifyInterceptor====> preHandle方法执行" );
        if (attribute != null) {
            return true;
        }
        response.sendRedirect("/account/login?next=" + request.getRequestURI());
        return false;
    }
}

创建登录控制器

@Controller
@RequestMapping("/account")
public class AccountController {
    @RequestMapping("/login")
    public String login(HttpServletRequest request) {
        request.getSession().setAttribute("name", "admin");
        // 重定向
        return "redirect:/";
    }
}

注册拦截器

<mvc:interceptors>
  <mvc:interceptor>
    <mvc:mapping path="/"/>
    <mvc:exclude-mapping path="/account/login"/>
    <bean class="com.vip.mvc.interceptor.LoginInterceptor"/>
  </mvc:interceptor>
</mvc:interceptors>

性能检测案例

@Component
@Slf4j
public class PerformanceInterceptor extends HandlerInterceptorAdapter {
    ThreadLocal<Long> threadLocal = new ThreadLocal<>();
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //1、开始时间
        long beginTime = System.currentTimeMillis();
        threadLocal.set(beginTime);
        return true;
    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        //2、结束时间
        long endTime = System.currentTimeMillis();
        //3、消耗的时间
        long consumeTime = endTime - threadLocal.get();
        // 此处认为处理时间超过300毫秒的请求为慢请求
        if (consumeTime > 300) {
            log.error("请求url:{} 执行时间:{}" , request.getRequestURI(),consumeTime);
        }
    }
}
<mvc:interceptors>
  <mvc:interceptor>
    <mvc:mapping path="/**"/>
    <bean class="com.vip.mvc.interceptor.PerformanceInterceptor"/>
  </mvc:interceptor>
</mvc:interceptors>

六、区别

1、过滤器:

依赖于servlet容器;

  • 在实现上基于函数回调,可以对几乎所有请求进行过滤;
  • 缺点是一个过滤器实例只能在容器初始化时调用一次;
  • 使用过滤器的目的是用来做一些过滤操作,获取我们想要获取的数据,比如:在过滤器中修改字符编码;在过滤器中修改HttpServletRequest的一些参数,包括:过滤低俗文字、危险字符等。

2、拦截器:

  • 依赖于web框架,在SpringMVC中就是依赖于SpringMVC框架;
  • 在实现上基于Java的反射机制,属于面向切面编程(AOP)的一种运用;
  • 缺点是只能对controller请求进行拦截,对其他的一些比如直接访问静态资源的请求则没办法进行拦截处理;
  • 由于拦截器是基于web框架的调用,因此可以使用Spring的依赖注入(DI)进行一些业务操作,同时一个拦截器实例在一个controller生命周期之内可以多次调用。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,905评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,140评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,791评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,483评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,476评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,516评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,905评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,560评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,778评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,557评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,635评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,338评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,925评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,898评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,142评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,818评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,347评论 2 342

推荐阅读更多精彩内容