一.拦截器入门及使用技巧
Interceptor拦截器入门
拦截器是SpringMVC里的一个高级组件,拦截器英文单词为Interceptor。他的作用和我之前学习过的J2EE中的过滤器filter有非常相似的地方,但是实现的方式不同。。拦截器的主要作用是用于对URL请求进行前置/后置过滤。 interceptor底层就是基于SpringAOP面向切面编程实现。 与SpringAOP的环绕通知非常的像。
下面来了解一下拦截器的开发流程:
1.maven依赖导入servlet-api
2. 实现HandlerInterceptor接口
3. applicationContext配置过滤地址
下面对这几个步骤进行演示:
1.用上一节使用过的restful的项目来进行演示,首先要打开pom.xml导入相关的依赖,如下:
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.13</version> </dependency> <!--jackson的核心包--> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.13.2</version> </dependency> <!--jackson数据绑定包--> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.13.2</version> </dependency> <!--jackson注解的包--> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>2.13.2</version> </dependency> <!--servlet-api的依赖--> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope> </dependency> </dependencies>
一般把servlet-api依赖的scope设置为provided,因为tomcat里面会有这个依赖,避免冲突。
2.新增加一个包,名为interceptor包,在这个包中出现的所有类都是拦截器,然后再这个包中再创建一个MyInterceptor的类。同时这个类要实现一个接口HandlerInterceptor,这是拦截器必须要实现的接口。这个接口里面包含了3个方法需要我们实现。这3个方法分别是preHandle ,postHandle 和afterCompletion,这3个方法对应了执行的3个不同时机。
package com.haiexijun.restful.interceptor; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class MyInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println(request.getRequestURL()+"---准备执行"); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println(request.getRequestURL()+"---请求处理成功"); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println(request.getRequestURL()+"---响应内容已经产生"); } }
preHandle是前置执行处理,就是在我们的一个请求产生了以后,还没有进controller之前,就会先执行preHandle,对这个请求进行预处理。方法会返回一个boolean类型,如果返回true,请求就会被送达给后面的拦截器或者是控制器,如果返回false的话,那当前的请求就会被阻止,直接产生响应返回客户端了。
postHandle 是目标资源已经被SpringMVC框架处理时执行。举个例子,如果在controller中,postHandle执行时机就是在内部的方法return了以后,但是并没有产生响应文本之前执行的。
afterCompletion是响应文本已经产生后执行。
3.在applicationContext中添加如下的配置
<mvc:interceptors> <mvc:interceptor> <!--对所有的请求进行拦截,注意是** ,两个*哦--> <mvc:mapping path="/**"/> <bean class="com.haiexijun.restful.interceptor.MyInterceptor"/> </mvc:interceptor> </mvc:interceptors>
上面代码,拦截器是配置好了,但是,会出现一个问题。就是当我们访问一个页面时(这里我新建一个页面client.html,代码和index.html一样)。当我们访问client.html这个页面时,会发现请求的所有资源,如js和网页图标,都会触发拦截器。如下图:
但我们并不希望,这些静态资源被拦截处理。需要将这些不需要的URL排除在外。配置如下:
<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.haiexijun.restful.interceptor.MyInterceptor"/> </mvc:interceptor> </mvc:interceptors>
再次运行,发现,那些静态资源就不会被拦截了。
但上面我们这种配置还是有点麻烦,毕竟静态资源的种类也是非常多的,在实际开发中我们可以把这些资源放在webapp下的新建的一个resources目录下,resources下面创建其他目录如js目录,CSS目录等来存放放静态资源,我们就只要把/resource目录排除在外就行了。
多拦截器的执行顺序:
二.案例—开发"用户流量"拦截器
本节,要开发一个用户流量拦截器,在前面虽然学习了拦截器的基本使用,但是在项目中到底什么时候使用拦截器还不太清除。在这里,就以这个经典的案例来学习它的用法。
案例的需求就是,当用户在访问我们的应用的时候,自动对用户的一些底层信息进行收集。比如说访问的时间,访问的网址,用户用的是什么浏览器什么系统或者什么手机访问的,用户的IP地址是什么。别看这些简单的数据,如果将他们综合起来,就能得到很多有用的信息。比如说,我们做的是一个电商网站,那么根据URL我们就能分析出来,什么商品是最受用户欢迎的。在根据用户访问的时间,我们就知道到底用户是夜猫子呢,还是白天上班时打开我们的网站。同时,我们这个案例也将学习到如何使用logback日志组件,来对用户数据进行日志存储。
下面就来实现这个需求:
还是在之前用的restful工程下来完成这个案例,打开interceptor这个包,在这个包下额外再创建一个类,叫AccessHistoryInterceptor,然后实现HandlerInterceptor接口。因为是所有请求在处理之前要被AccessHistoryInterceptor这个拦截器所记录,所以这属于一个前置处理,因此我们要实现preHandle这个方法,因为不涉及到请求阻断,return true。
package com.haiexijun.restful.interceptor; import org.slf4j.LoggerFactory; import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.logging.Logger; public class AccessHistoryInterceptor implements HandlerInterceptor { private Logger logger= (Logger) LoggerFactory.getLogger(AccessHistoryInterceptor.class); @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { StringBuffer log=new StringBuffer(); log.append(request.getRemoteAddr()); log.append("|"); log.append(request.getRequestURL()); log.append("|"); log.append(request.getHeader("user-agent")); logger.info(log.toString()); return true; } }
上面说到,我们一般把用户这些信息放在日志里面存储,所以我们还要再导入logback的依赖,springmvc就能使用logback默认打印最低级别的系统日志了。
<dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> </dependency>
但是只有这些系统日志是远远不够的,我们就要对logback的配置文件进行设置,同时也要在拦截器中对用户的数据进行登记。
我们打开src,在resource目录下,额外地创建一个新的文件,文件名固定为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> <root level="debug"> <!--生成按天滚动的日志文件--> <appender name="accessHistoryLog" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!--日志文件存放地址--> <rollingPolic clsss="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>d:/logs/history.%d.log</fileNamePattern> </rollingPolic> <encoder> <pattern>[%thread] %d %level %logger{10} - %msg%n</pattern> </encoder> </appender> <appender-ref ref="console"/> </root> <loger name="com.haiexijun.restful.interceptor.AccessHistoryInterceptor" level="INFO" additivity="false"> <<appender-ref ref="accessHistoryLog"/> </logger> </configuration>