拦截器原理
代理
通过cglib/jdk proxy创建对象的代理实例回调
生成代理对象就是对原对象功能的增强,修改字节码文件,在特定位置插入回调方法(目标对象的方法)

HandlerInterceptor介绍
Spring提供的拦截器Interceptor与Servlet中的Filter不同的是, Interceptor采用AOP的方式在Servlet的service方法执行之前进行拦截, 可以进行更精细的控制
Interceptor中有如下方法:
- preHandle: 在Controller处理之前调用, 返回false时整个请求结束
- postHandle: 在Controller调用之后执行, 但它会在DispatcherServlet进行视图的渲染之前执行, 也就是说在这个方法中你可以对ModelAndView进行操作
- afterCompletion: 在整个请求完成之后执行, 也就是DispatcherServlet已经渲染了视图之后执行; 这个方法的主要作用是用于清理资源的
- afterConcurrentHandlingStarted: 这个方法是AsyncHandlerInterceptor接口中添加的. 当Controller中有异步请求方法的时候会触发该方法, 异步请求先支持preHandle、然后执行afterConcurrentHandlingStarted, 异步线程完成之后执行会再执行preHandle、postHandle、afterCompletion
spring boot自定义拦截器实现
拦截器
public class AuthInterceptor extends HandlerInterceptorAdapter {/*** 前置检查*/@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {if (!handler.getClass().isAssignableFrom(HandlerMethod.class)) {return true;}String ip = request.getRemoteAddr();long startTime = System.currentTimeMillis();request.setAttribute("requestStartTime", startTime);HandlerMethod handlerMethod = (HandlerMethod) handler;Method method = handlerMethod.getMethod();System.out.println("用户:" + ip + ",访问目标:" + method.getDeclaringClass().getName() + "." + method.getName());return true;}/*** Controller调用之后执行*/@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {HandlerMethod handlerMethod = (HandlerMethod) handler;Method method = handlerMethod.getMethod();long startTime = (Long) request.getAttribute("requestStartTime");long endTime = System.currentTimeMillis();long executeTime = endTime - startTime;// log itif (executeTime > 1000) {System.out.println("[" + method.getDeclaringClass().getName() + "." + method.getName() + "] 执行耗时 : "+ executeTime + "ms");} else {System.out.println("[" + method.getDeclaringClass().getSimpleName() + "." + method.getName() + "] 执行耗时 : "+ executeTime + "ms");}}}
将拦截器加入springmvc中
1.继承WebMvcConfigurerAdapter(spring5该方法已过时)
@Configurationpublic class AuthHandlerAdapter extends WebMvcConfigurerAdapter {/*** 拦截器* 由于项目集成了swagger,这里直接不拦截swagger的相关请求*/@Overridepublic void addInterceptors(InterceptorRegistry registry) {//AuthInterceptor就是我们自定义的拦截器registry.addInterceptor(new AuthInterceptor()).addPathPatterns("/**").excludePathPatterns("/swagger-ui.html").excludePathPatterns("/swagger-resources/**").excludePathPatterns("/v2/api-docs");}/*** 资源处理器* swagger会和freemarker的静态资源路径冲突因此需配置swagger的资源处理器*/@Overridepublic void addResourceHandlers(ResourceHandlerRegistry registry) {registry.addResourceHandler("/swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");}}
2.继承WebMvcConfigurationSupport
@Configurationpublic class AppConfiguration extends WebMvcConfigurationSupport {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new CommonInterceptor()).addPathPatterns("/**");registry.addInterceptor(new UserInterceptor()).addPathPatterns("/manage/**").addPathPatterns("/user/logout");super.addInterceptors(registry);}@Overrideprotected void addResourceHandlers(ResourceHandlerRegistry registry) {registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");super.addResourceHandlers(registry);}}
特别注意:
重写
addInterceptors的同时,addResourceHandlers也要重写继承WebMvcConfigurationSupport会使Springboot中默认的
WebMvcAutoConfiguration不初始化,WebMvc的配置需要自己手动实现,配置文件中的
spring.mvc.xxx和spring.resources.xxx也会不起作用不建议使用
@Configuration(proxyBeanMethods = false)@ConditionalOnWebApplication(type = Type.SERVLET)@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })@ConditionalOnMissingBean(WebMvcConfigurationSupport.class) //WebMvcConfigurationSupport不存在才会实例化该类@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,ValidationAutoConfiguration.class })public class WebMvcAutoConfiguration {xxx}
3.实现WebMvcConfigurer
@Beanpublic WebMvcConfigurer webMvcConfig(){return new WebMvcConfigurer() {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new CommonInterceptor()).addPathPatterns("/**");registry.addInterceptor(new UserInterceptor()).addPathPatterns("/manage/**").addPathPatterns("/user/logout");}};}
总结
springboot默认可以访问以下路径文件(见ResourceProperties):
classpath:/static
classpath:/public
classpath:/resources
classpath:/META-INF/resources@EnableWebMvc、WebMvcConfigurationSupport、WebMvcConfigurationAdapter、WebMvcAutoConfiguration
@EnableWebMvc=WebMvcConfigurationSupport
使用了@EnableWebMvc注解等于扩展了WebMvcConfigurationSupport但是没有重写任何方法@EnableWebMvc+extends WebMvcConfigurationAdapter
在扩展的类中重写父类的方法即可,这种方式会屏蔽springboot的WebMvcAutoConfiguration中的设置@EnableWebMvc+extends WebMvcConfigurationSupport
只会使用@EnableWebMvc。 extends WebMvcConfigurationSupport,在扩展的类中重写父类的方法即可,这种方式会屏蔽springboot的WebMvcAutoConfiguration中的设置extends WebMvcConfigurationAdapter(spring5中已过时)
在扩展的类中重写父类的方法即可,这种方式依旧使用springboot的WebMvcAutoConfiguration中的设置。 在springboot2.x中,WebMvcConfigurationAdapter已经过时,通过实现接口WebMvcConfigurer可以替代原有规则实现WebMvcConfigurer(推荐使用)
自定义配置+WebMvcAutoConfiguration(默认配置)同时生效
