拦截器注册的实现原理

11 篇文章 0 订阅
5 篇文章 0 订阅

一.引言

拦截器依赖于web框架,在SpringMVC中就是依赖SpringMVC框架,在实现上,基于java的反射机制,属于面向切面编程的一种应用,就是在Service方法或者一个方法前调用,或者一个方法后调用,甚至业务异常的时候做业务处理逻辑。比如动态代理就是拦截器的简单实现。由于拦截器是基于web框架的调用,因此可以使用Spring的依赖注入进行一些业务操作,同时一个拦截器实例在一个Controller声明周期之内可以多次调用。但缺点是只能对Controller进行拦截,也可以拦击静态资源,必须要添加配置才可以避免静态资源被拦截,拦截器不能拦截的只有jsp。

二.过滤器和拦截器的调用顺序和执行流程

a. 执行顺序

  • 过滤前
  • 拦截前
  • Action处理
  • 拦截后
  • 过滤后

过滤器和拦截器层级关系图

b.执行流程

过滤器和拦截器总体执行流程图

二. 实现

  • 定义拦截器

    public class PmsInterceptor extends HandlerInterceptorAdapter {
    
    }
    
    @Component
    @Slf4j
    public class AppContextInterceptor extends PmsInterceptor{
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            AppContext appContext = new AppContext();
            String token = request.getHeader("token");
            appContext.setToken(token);
            appContext.setIp(RequestUtil.getRealIp());
            if(StringUtils.isNotEmpty(token)){
                UserInfo user = getUserFromToken(token);
                if(user != null){
                    appContext.setUserId(user.getUserId());
                    appContext.setUserName(user.getUserName());
                }
            }
    
            request.setAttribute(Constants.APP_CONTEXT, appContext);
            return super.preHandle(request, response, handler);
        }
    }
    
  • 注册拦截器

    @Configuration
    @Slf4j
    public class MvcConfig implements WebMvcConfigurer {
        @Resource
        List<PmsInterceptor> pmsInterceptorList;
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            if(CollectionUtils.isEmpty(pmsInterceptorList)){
                return;
            }
    
            for(PmsInterceptor pmsInterceptor : pmsInterceptorList){
                log.info("添加拦截器:{}", pmsInterceptor);
                registry.addInterceptor(pmsInterceptor);
            }
            WebMvcConfigurer.super.addInterceptors(registry);
        }
    }
    

三.源码解析

  • 首先springboot会自动引入WebAutoConfiguration,在这个类中会通过requestMappingHandlerMapping方法来注册RequestMappingHandlerMapping的对象

    public class WebMvcAutoConfiguration {
        
        public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {
            
    		@Bean
    		@Primary
    		@Override
    		public RequestMappingHandlerMapping requestMappingHandlerMapping(
    				@Qualifier("mvcContentNegotiationManager") 
    				ContentNegotiationManager contentNegotiationManager,
    				@Qualifier("mvcConversionService") 
    				FormattingConversionService conversionService,
    				@Qualifier("mvcResourceUrlProvider") 
    				ResourceUrlProvider resourceUrlProvider) {
    			// Must be @Primary for MvcUriComponentsBuilder to work
    			return super.requestMappingHandlerMapping(contentNegotiationManager, conversionService,
    					resourceUrlProvider);
    		}
        }
    }
    
  • 实际调用WebMvcAutoConfiguration的超类WebMvcConfigurationSupport的requestMappingHandlerMapping方法

    @Bean
        public RequestMappingHandlerMapping requestMappingHandlerMapping(@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager, @Qualifier("mvcConversionService") FormattingConversionService conversionService, @Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {
            RequestMappingHandlerMapping mapping = this.createRequestMappingHandlerMapping();
            mapping.setOrder(0);
            mapping.setInterceptors(this.getInterceptors(conversionService, resourceUrlProvider));
            mapping.setContentNegotiationManager(contentNegotiationManager);
            mapping.setCorsConfigurations(this.getCorsConfigurations());
            PathMatchConfigurer pathConfig = this.getPathMatchConfigurer();
            Boolean useTrailingSlashMatch;
            if (pathConfig.getPatternParser() != null) {
                mapping.setPatternParser(pathConfig.getPatternParser());
            } else {
                mapping.setUrlPathHelper(pathConfig.getUrlPathHelperOrDefault());
                mapping.setPathMatcher(pathConfig.getPathMatcherOrDefault());
                useTrailingSlashMatch = pathConfig.isUseSuffixPatternMatch();
                if (useTrailingSlashMatch != null) {
                    mapping.setUseSuffixPatternMatch(useTrailingSlashMatch);
                }
    
                Boolean useRegisteredSuffixPatternMatch = pathConfig.isUseRegisteredSuffixPatternMatch();
                if (useRegisteredSuffixPatternMatch != null) {
                    mapping.setUseRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch);
                }
            }
    
            useTrailingSlashMatch = pathConfig.isUseTrailingSlashMatch();
            if (useTrailingSlashMatch != null) {
                mapping.setUseTrailingSlashMatch(useTrailingSlashMatch);
            }
    
            if (pathConfig.getPathPrefixes() != null) {
                mapping.setPathPrefixes(pathConfig.getPathPrefixes());
            }
    
            return mapping;
        }
    
    protected final Object[] getInterceptors(FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {
            if (this.interceptors == null) {
                InterceptorRegistry registry = new InterceptorRegistry();
                this.addInterceptors(registry);
                registry.addInterceptor(new ConversionServiceExposingInterceptor(mvcConversionService));
                registry.addInterceptor(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider));
                this.interceptors = registry.getInterceptors();
            }
    
            return this.interceptors.toArray();
        }
    
    • 通过createRequestMappingHandlerMapping方法创建一个RequestMappingHandlerMapping对象
    • 然后通过getInterceptors获取所有拦截器
    • getInterceptors先注册一个interceptorRegistry对象,该对象会调用addInterceptor方法将自定义添加的以及从mvcConversionService和mvcResourceUrlProvider获得的拦截器添加进来,并最后将所有拦截器返回
    • 为RequestMappingHandlerMapping对象设置一个拦截器
  • 我们通过重写WebMvcConfigurer的addInterceptors方法来添加拦截器的getInterceptors方法怎么获取拦截器的,又是怎么添加到WebMvcConfigurationSupport中的?首先WebMvcConfigurationSupport被子类DelegatingWebMvcConfiguration重写了

    public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
    
    	private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
    	
    	@Override
    	protected void addInterceptors(InterceptorRegistry registry) {
       		this.configurers.addInterceptors(registry);
    	}
    }
    
  • DelegatingWebMvcConfiguration调用addInterceptors时调用了WebMvcConfigurerComposite的addInterceptors方法

    class WebMvcConfigurerComposite implements WebMvcConfigurer {
    
       private final List<WebMvcConfigurer> delegates = new ArrayList<>();
    
       @Override
       public void addInterceptors(InterceptorRegistry registry) {
          for (WebMvcConfigurer delegate : this.delegates) {
             delegate.addInterceptors(registry);
          }
       }
    
  • 我们自定义的mvc配置类,正好实现WebMvcConfigurer接口的类,我们添加拦截器就是重写addInterceptor方法

    @Configuration
    @Slf4j
    public class MvcConfig implements WebMvcConfigurer {
        @Resource
        List<PmsInterceptor> pmsInterceptorList;
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            if(CollectionUtils.isEmpty(pmsInterceptorList)){
                return;
            }
    
            for(PmsInterceptor pmsInterceptor : pmsInterceptorList){
                log.info("添加拦截器:{}", pmsInterceptor);
                registry.addInterceptor(pmsInterceptor);
            }
        }
    }
    

注意:WebMvcConfigurationSupport类的getInterceptors和注册添加的一系列操作都是通过registry对象连接的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值