spring可以配置多个视图解析器,解析顺序根据viewResolver的order属性来控制


    @Bean
    public ThymeleafViewResolver  thymeleafViewResolver(TemplateEngine templateEngine) {
        ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
        viewResolver.setTemplateEngine((ISpringTemplateEngine) templateEngine);
        viewResolver.setCharacterEncoding(String.valueOf(StandardCharsets.UTF_8));
        viewResolver.setViewNames(new String[]{"pages/*"});
        viewResolver.setCache(false);
        viewResolver.setOrder(3);
        return viewResolver;
    }

    /**
     * 内部资源视图解析器
     * @return
     */
    @Bean
    public ViewResolver jspViewResolver(){
        InternalResourceViewResolver resolver=new InternalResourceViewResolver();
        //设置视图的前置路径
        resolver.setPrefix("/WEB-INF/views/");
        //设置视图名称
        resolver.setViewNames("jsp/*");
        //设置视图后缀 resolver.setSuffix(".jsp");
        //视图缓存
        resolver.setCache(false);
        //视图解析顺序,越小越靠前
        resolver.setOrder(2);
        return resolver;
    }



org.springframework.web.servlet.DispatcherServlet



    /**
	 * Resolve the given view name into a View object (to be rendered).
	 * <p>The default implementations asks all ViewResolvers of this dispatcher.
	 * Can be overridden for custom resolution strategies, potentially based on
	 * specific model attributes or request parameters.
	 * @param viewName the name of the view to resolve
	 * @param model the model to be passed to the view
	 * @param locale the current locale
	 * @param request current HTTP servlet request
	 * @return the View object, or {@code null} if none found
	 * @throws Exception if the view cannot be resolved
	 * (typically in case of problems creating an actual View object)
	 * @see ViewResolver#resolveViewName
	 */
	@Nullable
	protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,
			Locale locale, HttpServletRequest request) throws Exception {

		if (this.viewResolvers != null) {
			for (ViewResolver viewResolver : this.viewResolvers) {
				View view = viewResolver.resolveViewName(viewName, locale);
				if (view != null) {
					return view;
				}
			}
		}
		return null;
	}

遍历视图解析器,进入org\springframework\web\servlet\view\AbstractCachingViewResolver.java,如果有缓存,就取出缓存,没有就创建

@Override
	@Nullable
	public View resolveViewName(String viewName, Locale locale) throws Exception {
		if (!isCache()) {
			return createView(viewName, locale);
		}
		else {
			Object cacheKey = getCacheKey(viewName, locale);
			View view = this.viewAccessCache.get(cacheKey);
			if (view == null) {
				synchronized (this.viewCreationCache) {
					view = this.viewCreationCache.get(cacheKey);
					if (view == null) {
						// Ask the subclass to create the View object.
						view = createView(viewName, locale);
						if (view == null && this.cacheUnresolved) {
							view = UNRESOLVED_VIEW;
						}
						if (view != null && this.cacheFilter.filter(view, viewName, locale)) {
							this.viewAccessCache.put(cacheKey, view);
							this.viewCreationCache.put(cacheKey, view);
						}
					}
				}
			}
			else {
				if (logger.isTraceEnabled()) {
					logger.trace(formatKey(cacheKey) + "served from cache");
				}
			}
			return (view != UNRESOLVED_VIEW ? view : null);
		}
	}
以 org\thymeleaf\spring5\view\ThymeleafViewResolver.java视图解析类为例,

protected boolean canHandle(final String viewName, @SuppressWarnings("unused") final Locale locale) {

    //要处理的视图名称

final String[] viewNamesToBeProcessed = getViewNames();

    //例外

final String[] viewNamesNotToBeProcessed = getExcludedViewNames();

    //判断当前视图解析器能不能处理

return ((viewNamesToBeProcessed == null || PatternMatchUtils.simpleMatch(viewNamesToBeProcessed, viewName)) && (viewNamesNotToBeProcessed == null || !PatternMatchUtils.simpleMatch(viewNamesNotToBeProcessed, viewName))); } @Override protected View createView(final String viewName, final Locale locale) throws Exception { // First possible call to check "viewNames": before processing redirects and forwards if (!this.alwaysProcessRedirectAndForward && !canHandle(viewName, locale)) { vrlogger.trace("[THYMELEAF] View \"{}\" cannot be handled by ThymeleafViewResolver. Passing on to the next resolver in the chain.", viewName); return null; } // Process redirects (HTTP redirects) if (viewName.startsWith(REDIRECT_URL_PREFIX)) { vrlogger.trace("[THYMELEAF] View \"{}\" is a redirect, and will not be handled directly by ThymeleafViewResolver.", viewName); final String redirectUrl = viewName.substring(REDIRECT_URL_PREFIX.length(), viewName.length()); final RedirectView view = new RedirectView(redirectUrl, isRedirectContextRelative(), isRedirectHttp10Compatible()); return (View) getApplicationContext().getAutowireCapableBeanFactory().initializeBean(view, viewName); } // Process forwards (to JSP resources) if (viewName.startsWith(FORWARD_URL_PREFIX)) { // The "forward:" prefix will actually create a Servlet/JSP view, and that's precisely its aim per the Spring // documentation. See http://docs.spring.io/spring-framework/docs/4.2.4.RELEASE/spring-framework-reference/html/mvc.html#mvc-redirecting-forward-prefix vrlogger.trace("[THYMELEAF] View \"{}\" is a forward, and will not be handled directly by ThymeleafViewResolver.", viewName); final String forwardUrl = viewName.substring(FORWARD_URL_PREFIX.length(), viewName.length()); return new InternalResourceView(forwardUrl); } // Second possible call to check "viewNames": after processing redirects and forwards if (this.alwaysProcessRedirectAndForward && !canHandle(viewName, locale)) { vrlogger.trace("[THYMELEAF] View \"{}\" cannot be handled by ThymeleafViewResolver. Passing on to the next resolver in the chain.", viewName); return null; } vrlogger.trace("[THYMELEAF] View {} will be handled by ThymeleafViewResolver and a " + "{} instance will be created for it", viewName, getViewClass().getSimpleName()); return loadView(viewName, locale); }