• 拦截器(Interceptor)用于对URL请求进行前置/后置过滤
  • Interceptor与Filter用途相似,但实现方式不同
  • Interceptor底层就是基于Spring AOP面向切面编程

拦截器开发流程

  • Maven依赖Servlet-api
  1. <!-- 引入Spring MVC拦截器依赖 -->
  2. <dependency>
  3. <groupId>javax.servlet</groupId>
  4. <artifactId>javax.servlet-api</artifactId>
  5. <version>3.1.0</version>
  6. <scope>provided</scope> <!-- 防止项目中但servlet api 在打包时会被排除在外 -->
  7. </dependency>
  • 实现HandlerInterceptor接口
package com.prim.interceptor;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

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

/**
 * @author prim
 * 实现拦截器类
 */
public class MyInterceptor implements HandlerInterceptor {

    /**
     * preHandle - 前置执行处理
     *
     * @param request
     * @param response
     * @param handler
     * @return true 继续执行; false 立即结束 返回响应
     * @throws Exception
     */
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println(request.getRequestURL() + "准备执行");
        return true;
    }

    /**
     * postHandle - 目标资源已被Spring MVC框架处理
     * 控制器的目标方法执行了return之后,马上执行postHandle
     *
     * @param request
     * @param response
     * @param handler
     * @param modelAndView
     * @throws Exception
     */
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println(request.getRequestURL() + "-目标处理成功");
    }

    /**
     * afterCompletion - 响应文本已经产生
     *
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     */
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println(request.getRequestURL() + "-响应内容已产生");
    }
}
  • applicationContext配置过滤地址
<!-- 配置拦截器 可配置多个 -->
    <mvc:interceptors>
        <mvc:interceptor>
            <!-- 拦截所有请求 -->
            <mvc:mapping path="/**"/>
            <!-- 配置拦截器实现类 -->
            <bean class="com.prim.interceptor.MyInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>

拦截器使用细则

会打印出很多额外的信息,<mvc:mapping path="/**"/> 拦截所有请求,不管是访问静态资源 html js css等资源都会被拦截

http://localhost:8081/准备执行
http://localhost:8081/-目标处理成功
http://localhost:8081/-响应内容已产生
http://localhost:8081/index.html准备执行
http://localhost:8081/index.html-目标处理成功
http://localhost:8081/index.html-响应内容已产生

通过配置<mvc:exclude-mapping path="/**.ico"/> 将某些资源排除在外,不进行拦截

    <mvc:interceptors>
        <mvc:interceptor>
            <!-- 拦截所有请求 -->
            <mvc:mapping path="/**"/>
            <!-- 排序静态资源 -->
            <mvc:exclude-mapping path="/**.ico"/>
            <mvc:exclude-mapping path="/**.jpg"/>
            <mvc:exclude-mapping path="/**.gif"/>
            <mvc:exclude-mapping path="/**.js"/>
            <mvc:exclude-mapping path="/**.css"/>
            <!-- 配置拦截器实现类 -->
            <bean class="com.prim.interceptor.MyInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>

但是静态资源太多了,按照上述规则是不符合规范的 可以将所有的静态资源放到某个目录下,进行额外的排除资源

<mvc:exclude-mapping path="/resources/**"/>

只对需要的URI进行拦截,可以进行多个配置,可提高代码的规范程度.

 <mvc:mapping path="/restful/**"/>
    <mvc:mapping path="/webapi/**"/>

多个拦截器执行的规则

两个拦截器,拦截的url都是相同的哪个先执行呢 ?

    <mvc:interceptors>
        <mvc:interceptor>
            <!-- 拦截所有请求 -->
            <mvc:mapping path="/restful/**"/>
            <mvc:mapping path="/webapi/**"/>
            <!-- 排序静态资源 -->
            <mvc:exclude-mapping path="/**.ico"/>
            <mvc:exclude-mapping path="/**.jpg"/>
            <mvc:exclude-mapping path="/**.gif"/>
            <mvc:exclude-mapping path="/**.js"/>
            <mvc:exclude-mapping path="/**.css"/>
            <mvc:exclude-mapping path="/resources/**"/>
            <!-- 配置拦截器实现类 -->
            <bean class="com.prim.interceptor.MyInterceptor"/>
        </mvc:interceptor>
        <mvc:interceptor>
            <!-- 拦截所有请求 -->
            <mvc:mapping path="/restful/**"/>
            <mvc:mapping path="/webapi/**"/>
            <!-- 排序静态资源 -->
            <mvc:exclude-mapping path="/**.ico"/>
            <mvc:exclude-mapping path="/**.jpg"/>
            <mvc:exclude-mapping path="/**.gif"/>
            <mvc:exclude-mapping path="/**.js"/>
            <mvc:exclude-mapping path="/**.css"/>
            <mvc:exclude-mapping path="/resources/**"/>
            <!-- 配置拦截器实现类 -->
            <bean class="com.prim.interceptor.MyInterceptor1"/>
        </mvc:interceptor>
    </mvc:interceptors>

在前置执行过程中是按照配置顺序来的.而后面两个顺序就反了.是因为 后置处理返回的

http://localhost:8081/restful2/persons准备执行
http://localhost:8081/restful2/persons准备执行-1
http://localhost:8081/restful2/persons-目标处理成功-1
http://localhost:8081/restful2/persons-目标处理成功
http://localhost:8081/restful2/persons-响应内容已产生-1
http://localhost:8081/restful2/persons-响应内容已产生

直接看图就明白了 如下图:
image.png

还以上述多个拦截器为例,在第一个拦截器中设置preHandle返回false 就不会在执行直接返回,那么直接第二个拦截器也就不会执行了

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println(request.getRequestURL() + "准备执行");
        response.getWriter().println("[]");
        return false;
    }

返回的结果:

 http://localhost:8081/restful2/persons准备执行

实战:”用户流量”拦截器

配置logback日志,将请求的记录全部放到日志文件中,首先引入日志依赖
注意:日志依赖引入后,一定要将jar包全部添加到/WEB-INF/lib目录下,否则会出现错误


<!-- 引入logback -->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.3</version>
        </dependency>

在/resources目录下创建logback.xml (注意名字必须是logback不能更改),配置日志的输出信息和存储日志文件的路径

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>[%thread %d %level %logger{10} - %msg%n]</pattern>
        </encoder>
    </appender>
    <!-- RollingFileAppender 输出器 生成按天的日志文件 -->
    <appender name="accessHistoryLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 滚动策略 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 配置生成文件的路径 和 生成日志的名称-->
            <fileNamePattern>xxxx/logs/history.%d.log
            </fileNamePattern>
        </rollingPolicy>
        <!-- 配置日志文件输出的格式 -->
        <encoder>
            <pattern>[%thread %d %level %logger{10} - %msg%n]</pattern>
        </encoder>
    </appender>
    <root level="debug">
        <appender-ref ref="console"/>
    </root>
    <!--
    additivity="false" 日志指向文件中输出
    启用accessHistoryLog
     -->
    <logger name="com.prim.interceptor.AccessHistoryInterceptor" level="INFO" additivity="false">
        <appender-ref ref="accessHistoryLog"/>
    </logger>
</configuration>

然后在连接器中使用Logger (org.slf4j.Logger) 将你想要的信息写到日志中去.

/**
 * 在所有请求之前进行记录 使用前置处理
 *
 * @author prim
 */
public class AccessHistoryInterceptor implements HandlerInterceptor {
    private Logger logger = LoggerFactory.getLogger(AccessHistoryInterceptor.class);

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        StringBuilder log = new StringBuilder();
        log.append(request.getRemoteAddr());//IP地址
        log.append("|");
        log.append(request.getRequestURL());//用户访问的URL地址
        log.append("|");
        log.append(request.getHeader("user-agent"));
        logger.info(log.toString());
        return true;
    }
}

执行请求最终会在/logs/目录下生成日志文件,如何对该日志文件进行分析等等操作.

image.png
image.png

Spring MVC 处理流程

image.png