SpringBoot源码分析:拦截器注册原理

注册拦截器原理

配置拦截器

在介绍底层添加拦截器之前,首先简单介绍一下如何添加拦截器。我们只需要一个实现HandlerInterceptor接口的类并在MVC配置类中通过重写addInterceptors方法将该类添加到拦截器中。

  1. 实现HandlerInterceptor
public class LoginHandlerInterceptor implements HandlerInterceptor {
	//用于拦截
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        Object user = request.getSession().getAttribute("loginUser");
        if(user == null){
            request.setAttribute("msg", "No authority. Please sign in first");
            request.getRequestDispatcher("/login").forward(request, response);	//请求转发
            return false;
        }else{
            return true;
        }
    }
	//Controller方法处理完之后,DispatcherServlet进行视图渲染之前;可以对ModelAndView进行操作
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }
	//DispatcherServlet进行视图的渲染之后;多用于清理资源
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }
}
  1. 添加拦截器配置
@Configuration
public class MvcConfig implements WebMvcConfigurer {
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
    	//"/**"默认拦截所有请求,会将静态资源也拦截
        registry.addInterceptor(new LoginHandlerInterceptor()).addPathPatterns("/**")   
                .excludePathPatterns("/login", "/", "/user/login", "/asserts/**");
    }
}

那么springboot又是如何将我们自定义的拦截器添加进来的呢?

底层添加拦截器的主要的步骤:

下面将结合源码说明底层添加拦截器的原理;

说明:为方便讲解,展示的源码只包含部分相关的内容。

首先,SpringBoot会引入很多自动配置类(xxxAutoConfiguraion),WebMvcAutoConfiguration就是关于mvc配置的,会给容器添加很多mvc相关的组件;

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);
		}
    }
}

看一看继承关系:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dZQmajVd-1609229878668)(C:/Users/黄臻/AppData/Roaming/Typora/typora-user-images/image-20201229144937185.png)]

这个方法最终会跳转到WebMvcConfigurationSupport中,该类是提供Java config配置MVC功能的主要类;

WebMvcConfigurationSupport创建RequestMappingHandlerMapping组件主要做了这几件事:

  1. 首先会使用createRequestMappingHandlerMapping()方法创建RequestMappingHandlerMapping对象;

  2. 然后通过mapping.setInterceptors(getInterceptors())为该对象添加拦截器;

  3. getInterceptors()方法会返回所有拦截器;那么拦截器是从哪里扫描到的呢?

  4. getInterceptors()方法会先获得一个InterceptorRegistry对象,该对象会调用addInterceptor方法将自定义添加的以及从mvcConversionService和mvcResourceUrlProvider获得的拦截器添加进来,并最后将所有拦截器返回;

//这是提供通过Java config配置MVC的主要类
public class WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware {
    
    @Bean
	@SuppressWarnings("deprecation")
	public RequestMappingHandlerMapping requestMappingHandlerMapping(
			@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
			@Qualifier("mvcConversionService") FormattingConversionService conversionService,
			@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {

		RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
		mapping.setOrder(0);
        //将获取到的拦截器添加到RequestMappingHandlerMapping
		mapping.setInterceptors(getInterceptors(conversionService, resourceUrlProvider));

		return mapping;
	}
    
    //获取拦截器
    protected final Object[] getInterceptors(
        FormattingConversionService mvcConversionService,
        ResourceUrlProvider mvcResourceUrlProvider) {

		if (this.interceptors == null) {
			InterceptorRegistry registry = new InterceptorRegistry();
            //通过重写该方法给registry中添加自定义的interceptor
			addInterceptors(registry);
            //mvcConversionService和mvcResourceUrlProvider中的拦截器
			registry.addInterceptor(new ConversionServiceExposingInterceptor(mvcConversionService));
			registry.addInterceptor(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider));
			this.interceptors = registry.getInterceptors();
		}
		return this.interceptors.toArray();
	}
    
    //通过重写此方法,方法体中加入registry.addInterceptors来添加自定义拦截器
    protected void addInterceptors(InterceptorRegistry registry) {
	}

而我们是通过重写WebMvcConfigurer的addInterceptors方法来添加拦截器的,又是怎么添加到WebMvcConfigurationSupport中的呢?

我们再来看看WebMvcConfigurationSupport中addInterceptors的调用过程:

addInterceptors首先被它的子类DelegatingWebMvcConfiguration重写了:

public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {

	private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
	
	@Override
	protected void addInterceptors(InterceptorRegistry registry) {
   		this.configurers.addInterceptors(registry);
	}
}

该类中,又会去配置类引入组件的类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接口的类,我们添加拦截器就是重写addInterceptors()方法:

@Configuration
public class MvcConfig implements WebMvcConfigurer {
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
    	//"/**"默认拦截所有请求,会将静态资源也拦截
        registry.addInterceptor(new LoginHandlerInterceptor()).addPathPatterns("/**")   
                .excludePathPatterns("/login", "/", "/user/login", "/asserts/**");
    }
}

那么在delegate.addInterceptors(registry)时就会调用我们自己重写的方法;

因此,我们在mvc配置类中重写addInterceptors方法,会被添加进来,最终被getInterceptors方法获取到,添加到RequestMappingHandlerMapping中;

总结:

  1. SpringBoot自动配置类WebMvcAutoConfiguration通过调用超类(WebMvcConfigurationSupport)注册RequestMappingHandlerMapping;
  2. WebMvcConfigurationSupport将获得的RequestMappingHandlerMapping对象添加拦截器:mapping.setInterceptors(getInterceptors);
  3. getInterceptors首先会创建一个InterceptorRegistry对象,用于注册拦截器;
  4. 然后给registry添加拦截器:
    1. 添加自定义拦截器(实现HandlerInterceptor的类):addInterceptors(registry);

      1. 方法首先由它的子类DelegatingWebMvcConfiguration调用;
      2. 该类又会去配置类引入组件的类WebMvcConfigurerComposite中调用addInterceptors方法
      3. WebMvcConfigurerComposite会读取mvc配置类(继承WebMvcConfigurer)的信息,就会调用我们呢自己重写的addInterceptors
    2. 从mvcConversionService和mvcResourceUrlProvider获取拦截器,添加到registry中;

  5. 最后将registry中的拦截器加入到interceptors对象;
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值