1、认识拦截器
SpringMVC的拦截器(Interceptor)不是Filer,同样可以实现请求的预处理、后处理。使用拦截器仅需要两个步骤
实现拦截器
注册拦截器
1.1实现拦截器
实现拦截器可以自定义实现HandleInterceptor接口,也可以继承HandleInterceptorAdatper类,后者是前者的实现类。
下面是拦截器实现的一个例子,目的是判断用户是否登录。如果preHandle方法return true ,则后续方法继续执行。
1 package Interceptor; 2 3 import javax.servlet.http.HttpServletRequest; 4 import javax.servlet.http.HttpServletResponse; 5 6 import org.springframework.web.servlet.ModelAndView; 7 import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; 8 9 /** 10 * 实现拦截器 11 * 12 */ 13 public class LoginInterceptor extends HandlerInterceptorAdapter{ 14 15 /** 16 * 预处理回调方法,实现处理器的预处理(如登录检查) 17 * 第三个参数为相应的处理器,即controller 18 * 返回true表示流程继续调用下一个拦截器或处理器 19 * 返回false表示流程中断,通过response产生响应 20 */ 21 @Override 22 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) 23 throws Exception { 24 System.out.println("------------------------------------preHandle"); 25 // 验证用户是否登陆 26 Object obj = request.getSession().getAttribute("username"); 27 if (obj == null || !(obj instanceof String)) { 28 response.sendRedirect(request.getContextPath() + "/index.html"); 29 return false; 30 } 31 return true; 32 } 33 34 /** 35 *当前请求进行处理之后,也就是Controller 方法调用之后执行, 36 *但是它会在DispatcherServlet 进行视图返回渲染之前被调用。 37 *此时我们可以通过modelAndView对模型数据进行处理或对视图进行处理。 38 */ 39 @Override 40 public void postHandle(HttpServletRequest request, HttpServletResponse response, 41 Object handler, ModelAndView modelAndView) throws Exception { 42 System.out.println("-------------------postHandle"); 43 } 44 45 /** 46 *方法将在整个请求结束之后,也就是在DispatcherServlet 渲染了对应的视图之后执行。 47 *这个方法的主要作用是用于进行资源清理工作的。 48 */ 49 @Override 50 public void afterCompletion(HttpServletRequest request, HttpServletResponse response, 51 Object handler, Exception ex) throws Exception { 52 System.out.println("-------------------afterCompletion"); 53 } 54 55 }
1.2注册拦截器
为了使自定义的拦截器生效,需要把拦截器注册到spring容器中。具体做法是继承WebMvcConfigurerAdapter类,重写其addInterceptors(InterceptorRegistry registry)方法,然后使用@Component 或者 @Configuration将Bean注册到Spring容器中。
可以单独建一个类继承WebMvcConfigurerAdaoter类
1 package Interceptor; 2 3 import org.springframework.context.annotation.Configuration; 4 import org.springframework.web.servlet.config.annotation.InterceptorRegistration; 5 import org.springframework.web.servlet.config.annotation.InterceptorRegistry; 6 import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; 7 8 /** 9 * 注册拦截器 10 * 11 */ 12 @Configuration 13 public class InterceptorConfiguration extends WebMvcConfigurerAdapter{ 14 15 @Override 16 public void addInterceptors(InterceptorRegistry registry) { 17 //注册拦截器 18 InterceptorRegistration addInterceptor = registry.addInterceptor(new LoginInterceptor()); 19 // 配置拦截的路径 20 addInterceptor.addPathPatterns("/**"); 21 // 配置不拦截的路径 22 addInterceptor.excludePathPatterns("/**.html"); 23 24 // 还可以在这里注册其它的拦截器 25 //registry.addInterceptor(new OtherInterceptor()).addPathPatterns("/**"); 26 } 27 }
也可以直接在SpringBoot的启动类中继承WebMvcConfigurerAdaoter类
1 package cn.wowkai.mall; 2 3 import java.nio.charset.Charset; 4 import java.util.Arrays; 5 6 import javax.annotation.PostConstruct; 7 import javax.annotation.Resource; 8 import javax.sql.DataSource; 9 10 import org.springframework.boot.SpringApplication; 11 import org.springframework.boot.autoconfigure.SpringBootApplication; 12 import org.springframework.context.annotation.Bean; 13 import org.springframework.data.redis.core.RedisTemplate; 14 import org.springframework.data.redis.serializer.StringRedisSerializer; 15 import org.springframework.http.HttpHeaders; 16 import org.springframework.scheduling.annotation.EnableScheduling; 17 import org.springframework.web.cors.CorsConfiguration; 18 import org.springframework.web.cors.UrlBasedCorsConfigurationSource; 19 import org.springframework.web.filter.CorsFilter; 20 import org.springframework.web.servlet.config.annotation.InterceptorRegistry; 21 import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 22 23 import com.fd.myshardingfordata.helper.ConnectionManager; 24 import com.fd.myshardingfordata.helper.TransManager; 25 26 import cn.wowkai.mall.web.interceptor.AuthInterceptor; 27 28 @EnableScheduling 29 @SpringBootApplication 30 public class ShopSaasMallApplication implements WebMvcConfigurer { 31 32 public static void main(String[] args) { 33 SpringApplication.run(ShopSaasMallApplication.class, args); 34 } 35 36 @Resource 37 protected RedisTemplate<String, Object> redisTemplate; 38 39 @PostConstruct 40 private void init() { 41 redisTemplate.setKeySerializer(new StringRedisSerializer(Charset.forName("UTF8"))); 42 redisTemplate.setValueSerializer(new StringRedisSerializer(Charset.forName("UTF8"))); 43 } 44 45 @Override 46 public void addInterceptors(InterceptorRegistry registry) { 47 registry.addInterceptor(new AuthInterceptor(redisTemplate)).excludePathPatterns("/mall/mp/*", 48 "/mall/notify/panganNotify", "/mall/notify/panganRechargeNotify", "/mall/notify/panganbillNotify", 49 "/mall/notify/wxpayrechargenotify", "/mall/notify/wxpaybillnotify", "/mall/notify/wxpaynotify", 50 "/mall/api/getToken", "/mall/store/getStoreListWithLongitudeLatitude", 51 "/mall/sett/getIndexTemplateListWithXcx", "/mall/product/productListForXcxSelect", 52 "/mall/product/custFindProduct", "/mall/product/custFindProductSpeceInventoryAndPrice", 53 "/mall/sett/xcx/getIndexTemplateMastList"); 54 } 55 56 @Resource 57 private DataSource dataSource; 58 59 @Bean 60 public TransManager transManager() { 61 TransManager trans = new TransManager(); 62 trans.setConnectionManager(connectionManager()); 63 return trans; 64 } 65 66 @Bean 67 public ConnectionManager connectionManager() { 68 ConnectionManager conm = new ConnectionManager(); 69 conm.setGenerateDdl(true); 70 conm.setShowSql(false); 71 conm.setInitConnect("set names utf8mb4"); 72 conm.setDataSource(dataSource); 73 conm.setReadDataSources(Arrays.asList(dataSource)); 74 75 return conm; 76 } 77 78 @Bean 79 public CorsFilter corsFilter() { 80 // 1.添加CORS配置信息 81 CorsConfiguration config = new CorsConfiguration(); 82 // 放行哪些原始域 83 config.addAllowedOrigin("*"); 84 // 是否发送Cookie信息 85 config.setAllowCredentials(true); 86 // 放行哪些原始域(请求方式) 87 config.addAllowedMethod("*"); 88 // 放行哪些原始域(头部信息) 89 config.addAllowedHeader("*"); 90 // 暴露哪些头部信息(因为跨域访问默认不能获取全部头部信息) 91 config.addExposedHeader(HttpHeaders.LOCATION); 92 config.setExposedHeaders(Arrays.asList("JSESSIONID", "SESSION", "token", HttpHeaders.LOCATION, 93 HttpHeaders.ACCEPT, HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, 94 HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, HttpHeaders.COOKIE, HttpHeaders.SET_COOKIE, 95 HttpHeaders.SET_COOKIE2)); 96 // 2.添加映射路径 97 UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource(); 98 configSource.registerCorsConfiguration("/**", config); 99 100 // 3.返回新的CorsFilter. 101 return new CorsFilter(configSource); 102 } 103 104 }
1.3拦截器的应用场景
拦截器的本质是面向切面编程(AOP),符合橫切关注点的功能都可以放在拦截器中来实现,主要的应用场景包括:
1、登录验证,判断用户是否登录。
2、权限验证,判断用户是否具有访问权限。
3、日志记录,记录请求日志,以便统计请求访问量。
4、处理cookie、本地化、国际化、主题等。
5、性能监控,监控请求处理时长等。
2、原理
2.1、工作原理
拦截器不是Filter,却实现了filter的功能,其原理在于:
所有的拦截器(Interceptor)和处理器(Handler)都注册在HandlerMapping中。
Spring MVC中所有的请求都是由DispatcherServlet
分发的。
DispatcherServlet.doDispatch()
时候,首先会得到处理该请求的Handler(即Controller中对应的方法)以及所有拦截该请求的拦截器。拦截器就是在这里被调用开始工作的。 一个拦截器,只有preHandle
方法返回true,postHandle
、afterCompletion
才有可能被执行;如果preHandle
方法返回false,则该拦截器的postHandle
、afterCompletion
必然不会被执行。
假设我们有两个拦截器,例如叫Interceptor1和Interceptor2,当一个请求过来,正常的流程和中断的流程分别如下。
2.2.1正常流程
注意两个拦截器在执行preHandle
方法和执行postHandle
、afterCompletion
方法时,顺序是颠倒的。
1 Interceptor1.preHandle 2 3 Interceptor2.preHandle 4 5 //Controller处理请求 6 7 Interceptor2.postHandle 8 9 Interceptor1.postHandle 10 11 //渲染视图 12 13 Interceptor2.afterCompletion 14 15 Interceptor1.afterCompletion
2.2.2中断流程
Interceptor2.preHandle
中报错,那么流程被中断,之前被执行过的拦截器的afterCompletion仍然会执行。在本例中,即执行了Interceptor1.afterCompletion
。1 Interceptor1.preHandle 2 3 Interceptor2.preHandle 4 5 //中间流程被中断,不再执行 6 7 Interceptor1.afterCompletion
2.3和Filter共存时的执行顺序
拦截器是在DispatcherServlet
这个servlet中执行的,因此所有的请求最先进入Filter,最后离开Filter。其顺序如下。
1 Filter 2 3 Interceptor.preHandle 4 5 Handler 6 7 Interceptor.postHandle 8 9 Interceptor.afterCompletion 10 11 Filter