拦截器(Interceptor)用于对URL请求进行前置/后置过滤。
Interceptor与Filter用途相似,但实现方式不同。
Interceptor是SpringMVC的标准组件,Interceptor在被创建之后是天然运行在IOC容器之中的。、
Filter是J2EE的标准组件,不同的Filter是由不同的容器厂商所实现的。
Interceptor底层就是基于SpringAOP面向切面编程实现。
拦截器开发流程:
Maven依赖servlet-api
实现HandlerLnterceptor接口
applicationContext.xml拦截配置
1.拦截器的基本使用
(1)引入依赖包
<dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <!--防止servlet自带api与tomcat配置的api相互冲突--> <!--provided意味着只有在开发编译才会进行引用--> <scope>provided</scope> </dependency>
(2)实现HandlerLnterceptor接口
//拦截器必须要继承HandlerInterceptor接口 public class MyInterceptor implements HandlerInterceptor { //同时必须实现以下三个方法 //preHandler:前置执行处理 //postHandle:目标资源已被SpringMVC框架处理,在return之后,但是还没有产生响应文本之前。 //afterCompletion:响应文本已经产生,比如在jackson自动实现序列化之后 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println(request.getRequestURL() + "准备执行"); //必须返回布尔值,true请求会继续向后方(拦截器或者控制器)传递,false请求被阻止,直接返回响应 return true; } public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println(request.getRequestURL() + "目标处理成功"); } public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println(request.getRequestURL() + "响应内容已产生"); } }
(3)applicationContext.xml拦截配置
<mvc:interceptors> <mvc:interceptor> <!--对那些URL进行拦截--> <mvc:mapping path="/**"/> <!--拦截之后使用哪个class进行处理--> <bean class="com.ikidana.restful.interceptor.MyInterceptor"/> </mvc:interceptor> </mvc:interceptors>
(4)结果测试
如果访问:http://localhost:8080/restful/persons
会出现如下信息:
http://localhost:8080/restful/persons准备执行
http://localhost:8080/restful/persons目标处理成功
http://localhost:8080/restful/persons响应内容已产生
2.Interceptor使用技巧
(1)排除对静态资源的拦截
只需要在applicationContext.xml中配置即可:
<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.ikidana.restful.interceptor.MyInterceptor"/> </mvc:interceptor> </mvc:interceptors>
但是上面配置存在的问题是:配置项可能很多
我们可以将所有静态文件全部放在一个目录下,这样拦截目录即可:
下方就是拦截整个目录:
<mvc:exclude-mapping path="/resources/**"/>
针对某个前缀也是如此:
<mvc:exclude-mapping path="/restful/**"/>
(2)如果一个请求被多个拦截器拦截,会先执行哪一个了?
如果我们配置多个拦截器:
<mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/**"/> <bean class="com.ikidana.restful.interceptor.MyInterceptor"/> </mvc:interceptor> <mvc:interceptor> <mvc:mapping path="/**"/> <bean class="com.ikidana.restful.interceptor.MyInterceptor1"/> </mvc:interceptor> </mvc:interceptors>
如果访问http://localhost:8080/restful/persons这个URL:
http://localhost:8080/restful/persons准备执行
http://localhost:8080/restful/persons准备执行-1
http://localhost:8080/restful/persons目标处理成功-1
http://localhost:8080/restful/persons目标处理成功
http://localhost:8080/restful/persons响应内容已产生-1
http://localhost:8080/restful/persons响应内容已产生
处理顺序并不完全按照实际的配置顺序:
有点类似于python中的中间件
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println(request.getRequestURL() + "准备执行"); response.getWriter().println("拦截器返回"); return false; }
拦截器可以直接控制返回值。
无论是拦截器或者中间件对于处理登陆注册都特别有用。
3.用户基础信息采集拦截器开发
(1)引入日志依赖
<dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> </dependency>
(2)配置日志格式,将其存储到目录
<?xml version="1.0" encoding="UTF-8"?> <configuration> <!--RollingFileAppender追加器用于生成按天滚动的日志文件--> <appender name="accessHistoryLog" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!--rollingPolicy滚动策略,TimeBasedRollingPolicy按照时间进行滚动--> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!--配置日志存储路径--> <fileNamePattern>C:java_studyproject estfullog estful.%d.log</fileNamePattern> </rollingPolicy> <!--定义日志输出的格式--> <encoder> <pattern>[%thread] %d %level %logger{10} - %msg%n</pattern> </encoder> </appender> <!--日志的最低输出级别为debug,输出地址为控制台--> <!--AccessHistoryInterceptor这个类所产生的日志都会使用下面标签所产生的规则--> <!--additivity是否向控制台输出,false在只会向指定规则下输出--> <logger name="com.ikidana.restful.interceptor.AccessHistoryInterceptor" level="info" additivity="false"> <appender-ref ref="accessHistoryLog"/> </logger> </configuration>
(3)创建拦截器,并填入信息
public class AccessHistoryInterceptor implements HandlerInterceptor { //创建一个logger日志对象 private Logger 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()); //地址 log.append("|"); log.append(request.getRequestURL()); //URL log.append("|"); log.append(request.getHeader("user-agent")); logger.info(log.toString()); return true; } }
(4)注册使用
<mvc:interceptor> <mvc:mapping path="/**"/> <bean class="com.ikidana.restful.interceptor.AccessHistoryInterceptor"/> </mvc:interceptor>
(5)效果