- 拦截器(Interceptor)用于对URL请求进行前置/后置过滤
- Interceptor与Filter用途相似,但实现方式不同
- Interceptor底层就是基于Spring AOP面向切面编程
拦截器开发流程
- Maven依赖Servlet-api
<!-- 引入Spring MVC拦截器依赖 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope> <!-- 防止项目中但servlet api 在打包时会被排除在外 -->
</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-响应内容已产生
直接看图就明白了 如下图:
还以上述多个拦截器为例,在第一个拦截器中设置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/目录下生成日志文件,如何对该日志文件进行分析等等操作.
Spring MVC 处理流程