SpringMVC源码分析-DispatcherServlet-init方法分析

上一篇:SpringMVC源码分析-DispatcherServlet实例化干了些什么html

先吐槽一下。。。写了两小时的博客忽然被俺家小屁孩按了刷新,东西不见了,建议OSCHINA可以自动定时保存啊。让我先安静一下。。。。java

由于前面在阅读Tomcat源码的时候时序图中没有画调用Servlet init方法的步骤,在这里补充一下文字说明。 Tomcat调用顺序:web

StandardWrapperValve.invoke()
StandardWrapper.allocate()
StandardWrapper.loadServlet()
DispatcherServet(实际是父类的).init()

经过上面的调用过程最终到达了DispatcherServlet的init(),由Spring的DefaultListableBeanFactory去完成DispatcherServlet.properties中配置类的初始化工做spring

时序图

说明

图中最核心的方法就是DispatcherServlet.initStrategies(),见源代码json

protected void initStrategies(ApplicationContext context) {
		/**
		 * 这里的每个方法都很是的重要,他们都遵循一个逻辑:若是在容器中没有拿到对应类型的对象,则使用DispatcherServlet.properties当中配置
		 * 的值,使用Spring容器建立一个新的Bean(注意是prototype,因此在singletonObjebs中没法找到它们。。。为何是prototype喃,说实话我也不
		 * 是很肯定,可能由于Servlet可能会有多个吧。。。但想一想又以为很牵强由于DispatcherServlet只有一个对象,并且又是在init方法初始化的,
		 * init方法只会被调用一次,不管处理多少请求,它们都会是线程共享的啊。。。也许Spring并不想在singletonObjects当中存放它才设置为prototype
		 * 的吧
		 *
		 * 由于是用Spring进行实例化的,因此可使用Spring提供的扩展点干预这些默认的类的行为,好比能够将protyotype设置为singleton,能够修改它们
		 * 一些属性的默认值等等
		 */
		//文件上传相关
		initMultipartResolver(context);
		//国际化 先从容器中拿名为localResolve的LocaleResolver.class对象,若是没有则使用默认的AcceptHeaderLocaleResolver(Spring-createBean)
		initLocaleResolver(context);
		//主题样式 先从容器中拿名为themeResolver的ThemeResolver.class对象,若是没有则使用默认的FixedThemeResolver(Spring-createBean)
		initThemeResolver(context);
		/**--------------很是重要-------------
		 * 初始化请求映射规则
		 * 先从容器中拿名为HandlerMapping.class对象列表,若是没有则使用默认的如下三个配置项
		 * RequestMappingHandlerMapping:这是最重要的,是一般咱们在Controller当中配置的RequestMapping的映射处理类
		 * BeanNameUrlHandlerMapping:是咱们的Controller实现了Controller接口,而后用@Component("beanName"),访问时url:localhost:port/contextpath/beanName
		 * RouterFunctionMapping:经过HttpRequestHandler实现Controller,具体细节不清楚,历来没见过更没用过
		 */
		initHandlerMappings(context);
		/**--------------很是重要-------------
		 * Request\Response的重要处理,好比入参与出参的一样格式化
		 * 先从容器中拿名为HandlerAdapter.class对象列表,若是没有则使用默认的如下四个配置项:
		 * HttpRequestHandlerAdapter:
		 * SimpleControllerHandlerAdapter:
		 * RequestMappingHandlerAdapter:
		 * HandlerFunctionAdapter:
		 */
		initHandlerAdapters(context);
		/**
		 * 异常 先从容器中拿HandlerExceptionResolver.class对象列表,若是没有则使用默认的如下三个配置项
		 * ExceptionHandlerExceptionResolver:
		 * ResponseStatusExceptionResolver:
		 * DefaultHandlerExceptionResolver:
		 */
		initHandlerExceptionResolvers(context);
		//请求到视图的转换 从容器中拿名为viewNameTranslator的RequestToViewNameTranslator,若是没有则使用默认的DefaultRequestToViewNameTranslator
		initRequestToViewNameTranslator(context);
		//视图转换器 从容器中拿ViewResolver对象列表,若是没有则使用默认的InternalResourceViewResolver
		initViewResolvers(context);
		//重定向视图管理器 从容器中拿名为flashMapManager的FlashMapManager,若是没有则使用默认的SessionFlashMapManager
		initFlashMapManager(context);
	}

源码中已经比较详细的写了每一个方法的注释,就再也不赘述了。缓存

概要说明

这里面的每一个方法固然都很重要,可是结合平常开发来分析,initHandlerMappings,initHandlerAdapters,initViewResolvers它们三个是解决请求映射、参数解析-绑定-格式化、视图渲染等功能,因此重点拿initHandlerMappings,initHandlerAdapters方法来分析。这两个方法底层逻辑都是先从Spring中找有么有对应的Bean,若是没有则使用DispatcherServlet.properties中配置的Bean,由Spring完成他们的实例化。因为时间、篇幅等缘由就不分析里面的每个类的实例化和功能,这三种分别拿一个比较典型的类进行分析。app

initHandlerMappings

使用RequestMappingHandlerMapping做为典型进行讲解它的实例化,不包含它的处理请求的部分源码分析

此类干的重要事情就是收集Controller当中的符合规则的HandlerMethod,在处理请求的时候,使用请求路径和这些HandlerMethod进行匹配,找最优匹配进行处理ui

虽然我选择了这个类作讲解,可是从哪里入手看喃?既然这个类是被Spring实例化的,那么它确定实现什么接口口、继承了什么类或者使用了什么Spring的特殊注解,拿出它的类结构图看看this

能够看到实现了InitializingBean接口,Spring在实例化对象快结束的时候会调用实现类的afterPropertiesSet()方法。

那就看看这个方法

public void afterPropertiesSet() {
		this.config = new RequestMappingInfo.BuilderConfiguration();
		this.config.setUrlPathHelper(getUrlPathHelper());
		this.config.setPathMatcher(getPathMatcher());
		this.config.setSuffixPatternMatch(this.useSuffixPatternMatch);
		this.config.setTrailingSlashMatch(this.useTrailingSlashMatch);
		this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch);
		this.config.setContentNegotiationManager(getContentNegotiationManager());
		//这里面就是收集Controller的映射关系
		super.afterPropertiesSet();
	}

把断点打到这里一步步的往下走,最终能够找到关键代码,这个过程就省略了,我直接把调用栈贴出来,已经对比较重要方法作注释

RequestMappingHandlerMapping.afterPropertiesSet()
AbstractHandlerMethodMapping.afterPropertiesSet()
AbstractHandlerMethodMapping.initHandlerMethods()
AbstractHandlerMethodMapping.processCandidateBean()
AbstractHandlerMethodMapping.detectHandlerMethods()
RequestMappingHandlerMapping.getMappingForMethod()

AbstractHandlerMethodMapping.initHandlerMethods()

protected void initHandlerMethods() {
		//循环Spring Context当中全部的Bean,看是否知足被@Controller或者@RequestMapping修饰的条件,若是知足则进行映射关系收集
		for (String beanName : getCandidateBeanNames()) {
			if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
				processCandidateBean(beanName);
			}
		}
		/**
		 * 开启锁,返回一个只可读的请求路径与HandlerMethod的映射Map
		 */
		handlerMethodsInitialized(getHandlerMethods());
	}

AbstractHandlerMethodMapping.processCandidateBean()

protected void processCandidateBean(String beanName) {
		Class<?> beanType = null;
		try {
			beanType = obtainApplicationContext().getType(beanName);
		}
		catch (Throwable ex) {
			// An unresolvable bean type, probably from a lazy bean - let's ignore it.
			if (logger.isTraceEnabled()) {
				logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
			}
		}
		//isHandler:看BeanType是否@Controller或者@RequestMapping修饰,若是知足条件,才进行下面的映射关系收集逻辑
		if (beanType != null && isHandler(beanType)) {
			detectHandlerMethods(beanName);
		}
	}

AbstractHandlerMethodMapping.detectHandlerMethods()

protected void detectHandlerMethods(Object handler) {
		Class<?> handlerType = (handler instanceof String ?
				obtainApplicationContext().getType((String) handler) : handler.getClass());

		if (handlerType != null) {
			Class<?> userType = ClassUtils.getUserClass(handlerType);
			/**
			 * 从这里开始有两层调用都用到了lambda表达式,看源码时须要注意,在DEBUG时,会跳回到上一层的lambda表达式对应的代码段执行
			 *
			 * selectMethods里面是在从缓存(建立Bean的时候由于其余功能已经解析了一遍类的Class)中拿类的全部Method,而后
			 * 调用getMappingForMethod()方法将Controller符合条件的method与指望请求路创建关系以备请求时作匹配使用,并将匹配到结果放入methods集合
			 * 当中
			 */
			Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
					(MethodIntrospector.MetadataLookup<T>) method -> {
						try {
							return getMappingForMethod(method, userType);
						}
						catch (Throwable ex) {
							throw new IllegalStateException("Invalid mapping on handler class [" +
									userType.getName() + "]: " + method, ex);
						}
					});
			if (logger.isTraceEnabled()) {
				logger.trace(formatMappings(userType, methods));
			}
			/**
			 * 注册HandlerMethod,只有注册以后,在后面才能从registry当中拿到一个只读的Mapping
			 */
			methods.forEach((method, mapping) -> {
				Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
				registerHandlerMethod(handler, invocableMethod, mapping);
			});
		}
	}

RequestMappingHandlerMapping.getMappingForMethod()

protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
		/**
		 * 从Method上面拿到请求路径
		 */
		RequestMappingInfo info = createRequestMappingInfo(method);
		if (info != null) {
			/**
			 * 从Type上面拿到请求路径
			 */
			RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
			if (typeInfo != null) {
				/**
				 * 进行组合:好比method上面是"/user/{age}",type上面是"/my",则组合后的结果就是"/my/user/{age}"
				 */
				info = typeInfo.combine(info);
			}
			String prefix = getPathPrefix(handlerType);
			if (prefix != null) {
				info = RequestMappingInfo.paths(prefix).build().combine(info);
			}
		}
		return info;
	}

这里有必要把RequestMappingInfo单独解释一下,RequestMappingInfo对应@RequestMapping,里面的属性在init的时候也会根据@RequestMapping的参数值对应设置

public final class RequestMappingInfo implements RequestCondition<RequestMappingInfo> {
​
    @Nullable
    private final String name;
    //Patterns对应url,就是RequestMapping注解value中的配置
    private final PatternsRequestCondition patternsCondition;
    //Methods对应 http method,如GET,POST,PUT,DELETE等
    private final RequestMethodsRequestCondition methodsCondition;
    //params对应http request parameter
    private final ParamsRequestCondition paramsCondition;
    //headers 对应http request 的请求头
    private final HeadersRequestCondition headersCondition;
    //consumes对应request的提交内容类型content type,如application/json, text/html
    private final ConsumesRequestCondition consumesCondition;
    //produces指定返回的内容类型的content type,仅当request请求头中的(Accept)类型中包含该指定类型才返回
    private final ProducesRequestCondition producesCondition;
    //若是以上都还不能达到你过滤请求的目的,还能够自定义
    private final RequestConditionHolder customConditionHolder;
    
    //-------------省略不少代码------------------
    /**
     * 请求匹配到多个RequestMapping,则须要排序,选择最优的HandlerMethod进行处理
     */
    public int compareTo(RequestMappingInfo other, HttpServletRequest request) {
        int result;
        // Automatic vs explicit HTTP HEAD mapping
        if (HttpMethod.HEAD.matches(request.getMethod())) {
            result = this.methodsCondition.compareTo(other.getMethodsCondition(), request);
            if (result != 0) {
                return result;
            }
        }
        result = this.patternsCondition.compareTo(other.getPatternsCondition(), request);
        if (result != 0) {
            return result;
        }
        result = this.paramsCondition.compareTo(other.getParamsCondition(), request);
        if (result != 0) {
            return result;
        }
        result = this.headersCondition.compareTo(other.getHeadersCondition(), request);
        if (result != 0) {
            return result;
        }
        result = this.consumesCondition.compareTo(other.getConsumesCondition(), request);
        if (result != 0) {
            return result;
        }
        result = this.producesCondition.compareTo(other.getProducesCondition(), request);
        if (result != 0) {
            return result;
        }
        // Implicit (no method) vs explicit HTTP method mappings
        result = this.methodsCondition.compareTo(other.getMethodsCondition(), request);
        if (result != 0) {
            return result;
        }
        result = this.customConditionHolder.compareTo(other.customConditionHolder, request);
        if (result != 0) {
            return result;
        }
        return 0;
    }
​
}

到此Spring已经使用RequestMappingHandlerMapping为使用@Controller和@RequestMapping注解的Controller类收集好了全部的请求映射,等待处理请求。

initHandlerAdapters

使用RequestMappingHandlerAdapter做为典型进行讲解它的实例化,不包含它的处理请求的部分

分析它的套路和RequestMappingHandlerMapping同样,上来就看afterPropertiesSet()方法

public void afterPropertiesSet() {
		/**
		 * 找到Spring容器中被@ControllerAdvice和@RestControllerAdvice修饰的Bean,并添加到一个List当中
		 * @ControllerAdvice是一个Controller加强器,在项目中曾经被用来作异常统一处理
		 * 这两个注解能够结合@ExceptionHandler, @InitBinder, @ModelAttribute三个注解使用将方法做用到全局上面
		 * 详细使用参考官网:https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html
		 */
		initControllerAdviceCache();
		/**
		 * ----------------很是重要----------------------
		 * argumentResolvers:参数解析器
		 * initBinderArgumentResolvers:初始化参数绑定解析器
		 * returnValueHandlers:Controller.method invoke以后返回值解析器
		 *
		 * SpringMVC都默认提供了一大批各类各样的解析器,它们共同组成了SpringMVC的强大功能
		 *
		 */
		if (this.argumentResolvers == null) {
			/**
			 * 获取默认的参数解析器,这些解析器是写死在代码中的
			 */
			List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
			this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
		}
		if (this.initBinderArgumentResolvers == null) {
			/**
			 * 取默认的InitBinder参数解析器,这些解析器是写死在代码中的
			 */
			List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
			this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
		}
		if (this.returnValueHandlers == null) {
			/**
			 * 取默认的返回值解析器,这些解析器是写死在代码中的
			 */
			List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
			this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
		}
	}

此方法很简单,就是实例化默认的各类解析器,在此以前将@ControllerAdvice修饰的Bean找出来放在一个集合当中

关于@ControllerAdvice能够在官网文档中去出看或者百度

随便找一个Revolver看看它的功能

RequestParamMethodArgumentResolver负责识别@RequestParam的参数,将其中的name和真正请求当中的Key进行匹配

PathVariableMethodArgumentResolver负责识别rest风格中具体位置中的参数值

Spring命名确实也很规范,经过看名称几乎就能知道它是干什么的,因此开发当中命名很重要,不要嫌弃名称太长,写起来太麻烦。

相关文章
相关标签/搜索