概述 实体解析器接口,根据视图名和国际化,获得最终的视图 View 对象。代码如下:
1 2 3 4 5 6 7 8 9 10 11 // ViewResolver.java public interface ViewResolver { /** * 根据视图名和国际化,获得最终的 View 对象 */ @Nullable View resolveViewName(String viewName, Locale locale) throws Exception; }
类图
初始化 我们以默认配置的 Spring Boot 场景下为例,来一起看看 DispatcherServlet 的 #initViewResolvers(ApplicationContext context) 方法,初始化 viewResolvers 变量。代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 // DispatcherServlet.java /** List of ViewResolvers used by this servlet. */ @Nullable private List<ViewResolver> viewResolvers; /** Detect all ViewResolvers or just expect "viewResolver" bean?. */ private boolean detectAllViewResolvers = true; private void initViewResolvers(ApplicationContext context) { // 置空 viewResolvers 处理 this.viewResolvers = null; // 情况一,自动扫描 ViewResolver 类型的 Bean 们 if (this.detectAllViewResolvers) { // Find all ViewResolvers in the ApplicationContext, including ancestor contexts. Map<String, ViewResolver> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, ViewResolver.class, true, false); if (!matchingBeans.isEmpty()) { this.viewResolvers = new ArrayList<>(matchingBeans.values()); // We keep ViewResolvers in sorted order. AnnotationAwareOrderComparator.sort(this.viewResolvers); } // 情况二,获得名字为 VIEW_RESOLVER_BEAN_NAME 的 Bean 们 } else { try { ViewResolver vr = context.getBean(VIEW_RESOLVER_BEAN_NAME, ViewResolver.class); this.viewResolvers = Collections.singletonList(vr); } catch (NoSuchBeanDefinitionException ex) { // Ignore, we'll add a default ViewResolver later. } } // Ensure we have at least one ViewResolver, by registering // a default ViewResolver if no other resolvers are found. // 情况三,如果未获得到,则获得默认配置的 ViewResolver 类 if (this.viewResolvers == null) { this.viewResolvers = getDefaultStrategies(context, ViewResolver.class); if (logger.isTraceEnabled()) { logger.trace("No ViewResolvers declared for servlet '" + getServletName() + "': using default strategies from DispatcherServlet.properties"); } } }
从实现上来说,ContentNegotiatingViewResolver 是最最最重要的 ViewResolver 实现类
4. ContentNegotiatingViewResolver 实现 ViewResolver、Ordered、InitializingBean 接口,继承 WebApplicationObjectSupport 抽象类,基于内容类型来获取对应 View 的 ViewResolver 实现类。
内容类型指的是 “Content-Type” 和拓展后缀。
构造方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 @Nullable private ContentNegotiationManager contentNegotiationManager; /** * ContentNegotiationManager 的工厂,用于创建 {@link #contentNegotiationManager} 对象 */ private final ContentNegotiationManagerFactoryBean cnmFactoryBean = new ContentNegotiationManagerFactoryBean(); /** * 在找不到 View 对象时,返回 {@link #NOT_ACCEPTABLE_VIEW} */ private boolean useNotAcceptableStatusCode = false; /** * 默认 View 数组 */ @Nullable private List<View> defaultViews; /** * ViewResolver 数组 */ @Nullable private List<ViewResolver> viewResolvers; /** * 顺序,优先级最高 */ private int order = Ordered.HIGHEST_PRECEDENCE;
initServletContext 初始化 viewResolvers 属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 @Override protected void initServletContext(ServletContext servletContext) { // <1> 扫描所有 ViewResolver 的 Bean 们 Collection<ViewResolver> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(obtainApplicationContext(), ViewResolver.class).values(); // <1.1> 情况一,如果 viewResolvers 为空,则将 matchingBeans 作为 viewResolvers 。 if (this.viewResolvers == null) { this.viewResolvers = new ArrayList<>(matchingBeans.size()); for (ViewResolver viewResolver : matchingBeans) { if (this != viewResolver) { // 排除自己 this.viewResolvers.add(viewResolver); } } // <1.2> 情况二,如果 viewResolvers 非空,则和 matchingBeans 进行比对,判断哪些未进行初始化,那么需要进行初始化 } else { for (int i = 0; i < this.viewResolvers.size(); i++) { ViewResolver vr = this.viewResolvers.get(i); // 已存在在 matchingBeans 中,说明已经初始化,则直接 continue if (matchingBeans.contains(vr)) { continue; } // 不存在在 matchingBeans 中,说明还未初始化,则进行初始化 String name = vr.getClass().getName() + i; obtainApplicationContext().getAutowireCapableBeanFactory().initializeBean(vr, name); } } // <1.3> 排序 viewResolvers 数组 AnnotationAwareOrderComparator.sort(this.viewResolvers); // <2> 设置 cnmFactoryBean 的 servletContext 属性 this.cnmFactoryBean.setServletContext(servletContext); }
afterPropertiesSet 1 2 3 4 5 6 7 8 9 10 @Override public void afterPropertiesSet() { // 如果 contentNegotiationManager 为空,则进行创建 if (this.contentNegotiationManager == null) { this.contentNegotiationManager = this.cnmFactoryBean.build(); } if (this.viewResolvers == null || this.viewResolvers.isEmpty()) { logger.warn("No ViewResolvers configured"); } }
resolveViewName 实现 #resolveViewName(String viewName, Locale locale) 方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 @Override @Nullable public View resolveViewName(String viewName, Locale locale) throws Exception { RequestAttributes attrs = RequestContextHolder.getRequestAttributes(); Assert.state(attrs instanceof ServletRequestAttributes, "No current ServletRequestAttributes"); // <1> 获得 MediaType 数组 List<MediaType> requestedMediaTypes = getMediaTypes(((ServletRequestAttributes) attrs).getRequest()); if (requestedMediaTypes != null) { // <2.1> 获得匹配的 View 数组 List<View> candidateViews = getCandidateViews(viewName, locale, requestedMediaTypes); // <2.2> 筛选最匹配的 View 对象 View bestView = getBestView(candidateViews, requestedMediaTypes, attrs); // 如果筛选成功,则返回 if (bestView != null) { return bestView; } } String mediaTypeInfo = logger.isDebugEnabled() && requestedMediaTypes != null ? " given " + requestedMediaTypes.toString() : ""; // <3> 如果匹配不到 View 对象,则根据 useNotAcceptableStatusCode ,返回 NOT_ACCEPTABLE_VIEW 或 null 。 if (this.useNotAcceptableStatusCode) { if (logger.isDebugEnabled()) { logger.debug("Using 406 NOT_ACCEPTABLE" + mediaTypeInfo); } return NOT_ACCEPTABLE_VIEW; } else { logger.debug("View remains unresolved" + mediaTypeInfo); return null; } }
调用 #getCandidateViews(HttpServletRequest request) 方法,获得 MediaType 数组
调用 #getCandidateViews(String viewName, Locale locale, List requestedMediaTypes) 方法,获得匹配的 View 数组
调用 #getBestView(List candidateViews, List requestedMediaTypes, RequestAttributes attrs) 方法,筛选最匹配的 View 对象。
如果匹配不到 View 对象,则根据 useNotAcceptableStatusCode ,返回 NOT_ACCEPTABLE_VIEW 或 null 。其中,NOT_ACCEPTABLE_VIEW 变量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 private static final View NOT_ACCEPTABLE_VIEW = new View() { @Override @Nullable public String getContentType() { return null; } @Override public void render(@Nullable Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) { response.setStatus(HttpServletResponse.SC_NOT_ACCEPTABLE); } };
这个视图的渲染,只会设置响应状态码为 SC_NOT_ACCEPTABLE 。
获得 MediaType 数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 @Nullable protected List<MediaType> getMediaTypes(HttpServletRequest request) { Assert.state(this.contentNegotiationManager != null, "No ContentNegotiationManager set"); try { // 创建 ServletWebRequest 对象 ServletWebRequest webRequest = new ServletWebRequest(request); // 从请求中,获得可接受的 MediaType 数组。默认实现是,从请求头 ACCEPT 中获取 List<MediaType> acceptableMediaTypes = this.contentNegotiationManager.resolveMediaTypes(webRequest); // 获得可产生的 MediaType 数组 List<MediaType> producibleMediaTypes = getProducibleMediaTypes(request); // 通过 acceptableTypes 来比对,将符合的 producibleType 添加到 mediaTypesToUse 结果数组中 Set<MediaType> compatibleMediaTypes = new LinkedHashSet<>(); for (MediaType acceptable : acceptableMediaTypes) { for (MediaType producible : producibleMediaTypes) { if (acceptable.isCompatibleWith(producible)) { compatibleMediaTypes.add(getMostSpecificMediaType(acceptable, producible)); } } } // 按照 MediaType 的 specificity、quality 排序 List<MediaType> selectedMediaTypes = new ArrayList<>(compatibleMediaTypes); MediaType.sortBySpecificityAndQuality(selectedMediaTypes); return selectedMediaTypes; } catch (HttpMediaTypeNotAcceptableException ex) { if (logger.isDebugEnabled()) { logger.debug(ex.getMessage()); } return null; } } @SuppressWarnings("unchecked") private List<MediaType> getProducibleMediaTypes(HttpServletRequest request) { Set<MediaType> mediaTypes = (Set<MediaType>) request.getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE); if (!CollectionUtils.isEmpty(mediaTypes)) { return new ArrayList<>(mediaTypes); } else { return Collections.singletonList(MediaType.ALL); } } /** * Return the more specific of the acceptable and the producible media types * with the q-value of the former. */ private MediaType getMostSpecificMediaType(MediaType acceptType, MediaType produceType) { produceType = produceType.copyQualityValue(acceptType); return (MediaType.SPECIFICITY_COMPARATOR.compare(acceptType, produceType) < 0 ? acceptType : produceType); }
getCandidateViews 获得匹配的 View 数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 private List<View> getCandidateViews(String viewName, Locale locale, List<MediaType> requestedMediaTypes) throws Exception { // 创建 View 数组 List<View> candidateViews = new ArrayList<>(); // <1> 来源一,通过 viewResolvers 解析出 View 数组结果,添加到 candidateViews 中 if (this.viewResolvers != null) { Assert.state(this.contentNegotiationManager != null, "No ContentNegotiationManager set"); // <1.1> 遍历 viewResolvers 数组 for (ViewResolver viewResolver : this.viewResolvers) { // <1.2> 情况��,获得 View 对象,添加到 candidateViews 中 View view = viewResolver.resolveViewName(viewName, locale); if (view != null) { candidateViews.add(view); } // <1.3> 情况二,带有文拓展后缀的方式,获得 View 对象,添加到 candidateViews 中 // <1.3.1> 遍历 MediaType 数组 for (MediaType requestedMediaType : requestedMediaTypes) { // <1.3.2> 获得 MediaType 对应的拓展后缀的数组 List<String> extensions = this.contentNegotiationManager.resolveFileExtensions(requestedMediaType); // <1.3.3> 遍历拓展后缀的数组 for (String extension : extensions) { // <1.3.4> 带有文拓展后缀的方式,获得 View 对象,添加到 candidateViews 中 String viewNameWithExtension = viewName + '.' + extension; view = viewResolver.resolveViewName(viewNameWithExtension, locale); if (view != null) { candidateViews.add(view); } } } } } // <2> 来源二,添加 defaultViews 到 candidateViews 中 if (!CollectionUtils.isEmpty(this.defaultViews)) { candidateViews.addAll(this.defaultViews); } return candidateViews; }
getBestView 筛选最匹配的 View 对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 @Nullable private View getBestView(List<View> candidateViews, List<MediaType> requestedMediaTypes, RequestAttributes attrs) { // <1> 遍历 candidateView 数组,如果有重定向的 View 类型,则返回它 for (View candidateView : candidateViews) { if (candidateView instanceof SmartView) { SmartView smartView = (SmartView) candidateView; // RedirectView 是 SmartView 的子类 if (smartView.isRedirectView()) { return candidateView; } } } // <2> 遍历 requestedMediaTypes 数组 for (MediaType mediaType : requestedMediaTypes) { // <2> 遍历 candidateViews 数组 for (View candidateView : candidateViews) { if (StringUtils.hasText(candidateView.getContentType())) { // <2.1> 如果 MediaType 类型匹配,则返回该 View 对象 MediaType candidateContentType = MediaType.parseMediaType(candidateView.getContentType()); if (mediaType.isCompatibleWith(candidateContentType)) { if (logger.isDebugEnabled()) { logger.debug("Selected '" + mediaType + "' given " + requestedMediaTypes); } attrs.setAttribute(View.SELECTED_CONTENT_TYPE, mediaType, RequestAttributes.SCOPE_REQUEST); // 设置匹配的 MediaType 到请求属性中 return candidateView; } } } } return null; }
BeanNameViewResolver 实现 ViewResolver、Ordered 接口,继承 WebApplicationObjectSupport 抽象类,基于 Bean 的名字获得 View 对象的 ViewResolver 实现类。
构造方法 1 2 3 4 /** * 顺序,优先级最低 */ private int order = Ordered.LOWEST_PRECEDENCE;
resolveViewName 获得 Bean 的名字获得 View 对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @Override @Nullable public View resolveViewName(String viewName, Locale locale) throws BeansException { // 如果 Bean 对应的 Bean 对象不存在,则返回 null ApplicationContext context = obtainApplicationContext(); if (!context.containsBean(viewName)) { // Allow for ViewResolver chaining... return null; } // 如果 Bean 对应的 Bean 类型不是 View ,则返回 null if (!context.isTypeMatch(viewName, View.class)) { if (logger.isDebugEnabled()) { logger.debug("Found bean named '" + viewName + "' but it does not implement View"); } // Since we're looking into the general ApplicationContext here, // let's accept this as a non-match and allow for chaining as well... return null; } // 获得 Bean 名字对应的 View 对象 return context.getBean(viewName, View.class); }
ViewResolverComposite 实现 ViewResolver、Ordered、InitializingBean、ApplicationContextAware、ServletContextAware 接口,复合的 ViewResolver 实现类。
构造方法 1 2 3 4 5 6 7 8 9 /** * ViewResolver 数组 */ private final List<ViewResolver> viewResolvers = new ArrayList<>(); /** * 顺序,优先级最低 */ private int order = Ordered.LOWEST_PRECEDENCE;
afterPropertiesSet 1 2 3 4 5 6 7 8 9 10 // ViewResolverComposite.java @Override public void afterPropertiesSet() throws Exception { for (ViewResolver viewResolver : this.viewResolvers) { if (viewResolver instanceof InitializingBean) { ((InitializingBean) viewResolver).afterPropertiesSet(); } } }
resolveViewName 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 // ViewResolverComposite.java @Override @Nullable public View resolveViewName(String viewName, Locale locale) throws Exception { // 遍历 viewResolvers 数组,逐个进行解析,但凡成功,则返回该 View 对象 for (ViewResolver viewResolver : this.viewResolvers) { // 执行解析 View view = viewResolver.resolveViewName(viewName, locale); // 解析成功,则返回该 View 对象 if (view != null) { return view; } } return null; }
AbstractCachingViewResolver 实现 ViewResolver 接口,继承 WebApplicationObjectSupport 抽象类,提供通用的缓存的 ViewResolver 抽象类。对于相同的视图名,返回的是相同的 View 对象,所以通过缓存,可以进一步提供性能。
构造方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 /** * Default maximum number of entries for the view cache: 1024. */ public static final int DEFAULT_CACHE_LIMIT = 1024; /** * Dummy marker object for unresolved views in the cache Maps. */ private static final View UNRESOLVED_VIEW = new View() { @Override @Nullable public String getContentType() { return null; } @Override public void render(@Nullable Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) { } }; /** * The maximum number of entries in the cache. */ private volatile int cacheLimit = DEFAULT_CACHE_LIMIT; // 缓存上限。如果 cacheLimit = 0 ,表示禁用缓存 /** * Whether we should refrain from resolving views again if unresolved once. */ private boolean cacheUnresolved = true; // 是否缓存空 View 对象 /** * Fast access cache for Views, returning already cached instances without a global lock. * * View 的缓存的映射 * * KEY:{@link #getCacheKey(String, Locale)} */ private final Map<Object, View> viewAccessCache = new ConcurrentHashMap<>(DEFAULT_CACHE_LIMIT); /** * Map from view key to View instance, synchronized for View creation. * * View 的缓存的映射。相比 {@link #viewAccessCache} 来说,增加了 synchronized 锁 */ @SuppressWarnings("serial") private final Map<Object, View> viewCreationCache = new LinkedHashMap<Object, View>(DEFAULT_CACHE_LIMIT, 0.75f, true) { @Override protected boolean removeEldestEntry(Map.Entry<Object, View> eldest) { // 如果超过上限,则从 viewAccessCache 中也移除 if (size() > getCacheLimit()) { viewAccessCache.remove(eldest.getKey()); return true; } else { return false; } } };
1 2 3 4 5 6 7 8 9 10 /** * Return the cache key for the given view name and the given locale. * <p>Default is a String consisting of view name and locale suffix. * Can be overridden in subclasses. * <p>Needs to respect the locale in general, as a different locale can * lead to a different view resource. */ protected Object getCacheKey(String viewName, Locale locale) { return viewName + '_' + locale; }
loadView 抽象方法,加载 viewName 对应的 View 对象
1 2 @Nullable protected abstract View loadView(String viewName, Locale locale) throws Exception;
createView 创建 viewName 对应的 View 对象
1 2 3 4 @Nullable protected View createView(String viewName, Locale locale) throws Exception { return loadView(viewName, locale); }
resolveViewName 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 @Override @Nullable public View resolveViewName(String viewName, Locale locale) throws Exception { // 如果禁用缓存,则创建 viewName 对应的 View 对象 if (!isCache()) { return createView(viewName, locale); } else { // 获得缓存 KEY Object cacheKey = getCacheKey(viewName, locale); // 从 viewAccessCache 缓存中,获得 View 对象 View view = this.viewAccessCache.get(cacheKey); // 如果获得不到缓存,则从 viewCreationCache 中,获得 View 对象 if (view == null) { // synchronized 锁 synchronized (this.viewCreationCache) { // 从 viewCreationCache 中,获得 View 对象 view = this.viewCreationCache.get(cacheKey); // 如果不存在,则创建 viewName 对应的 View 对象 if (view == null) { // Ask the subclass to create the View object. // 创建 viewName 对应的 View 对象 view = createView(viewName, locale); // 如果创建失败,但是 cacheUnresolved 为 true ,则设置为 UNRESOLVED_VIEW if (view == null && this.cacheUnresolved) { view = UNRESOLVED_VIEW; } // 如果 view 非空,则添加到 viewAccessCache 缓存中 if (view != null) { this.viewAccessCache.put(cacheKey, view); this.viewCreationCache.put(cacheKey, view); } } } } else { if (logger.isTraceEnabled()) { logger.trace(formatKey(cacheKey) + "served from cache"); } } // 返回 view return (view != UNRESOLVED_VIEW ? view : null); } }
子类 有三个子类:
UrlBasedViewResolver
XmlViewResolver
ResourceBundleViewResolver
其中,UrlBasedViewResolver 是相比更关键的子类
UrlBasedViewResolver 实现 Ordered 接口,继承 AbstractCachingViewResolver 抽象类,基于 Url 的 ViewResolver 实现类。
构造方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 // UrlBasedViewResolver.java /** * Prefix for special view names that specify a redirect URL (usually * to a controller after a form has been submitted and processed). * Such view names will not be resolved in the configured default * way but rather be treated as special shortcut. */ public static final String REDIRECT_URL_PREFIX = "redirect:"; /** * Prefix for special view names that specify a forward URL (usually * to a controller after a form has been submitted and processed). * Such view names will not be resolved in the configured default * way but rather be treated as special shortcut. */ public static final String FORWARD_URL_PREFIX = "forward:"; /** * View 的类型 * * 不同的实现类,会对应一个 View 的类型 */ @Nullable private Class<?> viewClass; /** * 前缀 */ private String prefix = ""; /** * 后缀 */ private String suffix = ""; /** * ContentType 类型 */ @Nullable private String contentType; /** * */ private boolean redirectContextRelative = true; private boolean redirectHttp10Compatible = true; @Nullable private String[] redirectHosts; /** * RequestAttributes 暴露给 View 使用时的属性 */ @Nullable private String requestContextAttribute; @Nullable private Boolean exposeContextBeansAsAttributes; /** Map of static attributes, keyed by attribute name (String). */ private final Map<String, Object> staticAttributes = new HashMap<>(); /** * 是否暴露路径变量给 View 使用 */ @Nullable private Boolean exposePathVariables; @Nullable private String[] exposedContextBeanNames; /** * 是否只处理指定的视图名们 */ @Nullable private String[] viewNames; /** * 顺序,优先级最低 */ private int order = Ordered.LOWEST_PRECEDENCE;
initApplicationContext 1 2 3 4 5 6 7 8 9 @Override protected void initApplicationContext() { // 调用父类该方法,进行初始化 super.initApplicationContext(); // 校验 viewClass 非空 if (getViewClass() == null) { throw new IllegalArgumentException("Property 'viewClass' is required"); } }
getCacheKey 忽略 locale 参数,仅使用 viewName 作为缓存 KEY 。
1 2 3 4 @Override protected Object getCacheKey(String viewName, Locale locale) { return viewName; }
canHandle 判断传入的视图名是否可以被处理
1 2 3 4 protected boolean canHandle(String viewName, Locale locale) { String[] viewNames = getViewNames(); return (viewNames == null || PatternMatchUtils.simpleMatch(viewNames, viewName)); }
一般情况下,viewNames 为空,所以会满足 viewNames == null 代码块。也就说,所有视图名都可以被处理。
applyLifecycleMethods 1 2 3 4 5 6 7 8 9 10 11 12 protected View applyLifecycleMethods(String viewName, AbstractUrlBasedView view) { // 情况一,如果 viewName 有对应的 View Bean 对象,则使用它 ApplicationContext context = getApplicationContext(); if (context != null) { Object initialized = context.getAutowireCapableBeanFactory().initializeBean(view, viewName); if (initialized instanceof View) { return (View) initialized; } } // 情况二,直接返回 view return view; }
createView 加了对 REDIRECT、FORWARD 的情况的处理。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 @Override protected View createView(String viewName, Locale locale) throws Exception { // If this resolver is not supposed to handle the given view, // return null to pass on to the next resolver in the chain. // 判断当前视图是否可以处理 if (!canHandle(viewName, locale)) { return null; } // Check for special "redirect:" prefix. // 如果是 REDIRECT 开头,创建 RedirectView 视图 if (viewName.startsWith(REDIRECT_URL_PREFIX)) { // 创建 RedirectView 对象 String redirectUrl = viewName.substring(REDIRECT_URL_PREFIX.length()); RedirectView view = new RedirectView(redirectUrl, isRedirectContextRelative(), isRedirectHttp10Compatible()); // 设置 RedirectView 对象的 hosts 属性 String[] hosts = getRedirectHosts(); if (hosts != null) { view.setHosts(hosts); } // 应用 return applyLifecycleMethods(REDIRECT_URL_PREFIX, view); } // Check for special "forward:" prefix. // 如果是 FORWARD 开头,创建 InternalResourceView 视图 if (viewName.startsWith(FORWARD_URL_PREFIX)) { // 创建 InternalResourceView 对象 String forwardUrl = viewName.substring(FORWARD_URL_PREFIX.length()); InternalResourceView view = new InternalResourceView(forwardUrl); // 应用 return applyLifecycleMethods(FORWARD_URL_PREFIX, view); } // Else fall back to superclass implementation: calling loadView. // 创建视图名对应的 View 对象 return super.createView(viewName, locale); }
loadView 加载 viewName 对应的 View 对象
1 2 3 4 5 6 7 8 9 @Override protected View loadView(String viewName, Locale locale) throws Exception { // <x> 创建 viewName 对应的 View 对象 AbstractUrlBasedView view = buildView(viewName); // 应用 View result = applyLifecycleMethods(viewName, view); // 返回 return (view.checkResource(locale) ? result : null); }
buildView(String viewName) 方法,创建 viewName 对应的 View 对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 protected AbstractUrlBasedView buildView(String viewName) throws Exception { Class<?> viewClass = getViewClass(); Assert.state(viewClass != null, "No view class"); // 创建 AbstractUrlBasedView 对象 AbstractUrlBasedView view = (AbstractUrlBasedView) BeanUtils.instantiateClass(viewClass); // 设置各种属性 view.setUrl(getPrefix() + viewName + getSuffix()); String contentType = getContentType(); if (contentType != null) { view.setContentType(contentType); } view.setRequestContextAttribute(getRequestContextAttribute()); view.setAttributesMap(getAttributesMap()); Boolean exposePathVariables = getExposePathVariables(); if (exposePathVariables != null) { view.setExposePathVariables(exposePathVariables); } Boolean exposeContextBeansAsAttributes = getExposeContextBeansAsAttributes(); if (exposeContextBeansAsAttributes != null) { view.setExposeContextBeansAsAttributes(exposeContextBeansAsAttributes); } String[] exposedContextBeanNames = getExposedContextBeanNames(); if (exposedContextBeanNames != null) { view.setExposedContextBeanNames(exposedContextBeanNames); } return view; }
requiredViewClass 定义了产生的视图
1 2 3 protected Class<?> requiredViewClass() { return AbstractUrlBasedView.class; }
子类
AbstractTemplateViewResolver
InternalResourceViewResolver
TilesViewResolver
ScriptTemplateViewResolver
XsltViewResolver
其中,InternalResourceViewResolver 和 AbstractTemplateViewResolver 是相比更关键的子类
InternalResourceViewResolver 继承 UrlBasedViewResolver 类,解析出 JSP 的 ViewResolver 实现类。
构造方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 /** * 判断 javax.servlet.jsp.jstl.core.Config 是否存在 */ private static final boolean jstlPresent = ClassUtils.isPresent( "javax.servlet.jsp.jstl.core.Config", InternalResourceViewResolver.class.getClassLoader()); @Nullable private Boolean alwaysInclude; public InternalResourceViewResolver() { // 获得 viewClass Class<?> viewClass = requiredViewClass(); if (InternalResourceView.class == viewClass && jstlPresent) { viewClass = JstlView.class; } // 设置 viewClass setViewClass(viewClass); } public InternalResourceViewResolver(String prefix, String suffix) { this(); setPrefix(prefix); setSuffix(suffix); } @Override protected Class<?> requiredViewClass() { return InternalResourceView.class; }
从构造方法中,可以看出,视图名会是 InternalResourceView 或 JstlView 类。 实际上,JstlView 是 InternalResourceView 的子类。
buildView 1 2 3 4 5 6 7 8 9 10 11 12 @Override protected AbstractUrlBasedView buildView(String viewName) throws Exception { // 调用父方法 InternalResourceView view = (InternalResourceView) super.buildView(viewName); // 设置 View 对象的相关属性 if (this.alwaysInclude != null) { view.setAlwaysInclude(this.alwaysInclude); } view.setPreventDispatchLoop(true); return view; }
增加设置两个属性
AbstractTemplateViewResolver 继承 UrlBasedViewResolver 类,解析出 AbstractTemplateView 的 ViewResolver 抽象类。
构造方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 // AbstractTemplateViewResolver.java /** * 是否将所有 RequestAttributes 暴露给 View 使用 */ private boolean exposeRequestAttributes = false; /** * 当 RequestAttributes 中存在 Model 中同名的参数,是否允许使用 RequestAttributes 中的值将 Model 中的值进行覆盖 * * {@link #exposeRequestAttributes} */ private boolean allowRequestOverride = false; /** * 是否将 SessionAttributes 暴露给 View 使用 */ private boolean exposeSessionAttributes = false; /** * 当 SessionAttributes 中存在 Model 中同名的参数,是否允许使用 SessionAttributes 中的值将 Model 中的值进行覆盖 * * {@link #exposeSessionAttributes} */ private boolean allowSessionOverride = false; /** * 是否将 RequestContext 暴露给 view 为 spring 的宏( Macro )所使用 */ private boolean exposeSpringMacroHelpers = true;
requiredViewClass 1 2 3 4 @Override protected Class<?> requiredViewClass() { return AbstractTemplateView.class; }
buildView 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 // AbstractTemplateViewResolver.java @Override protected AbstractUrlBasedView buildView(String viewName) throws Exception { // 调用父类方法,创建 AbstractTemplateView 对象 AbstractTemplateView view = (AbstractTemplateView) super.buildView(viewName); // 设置相关属性 view.setExposeRequestAttributes(this.exposeRequestAttributes); view.setAllowRequestOverride(this.allowRequestOverride); view.setExposeSessionAttributes(this.exposeSessionAttributes); view.setAllowSessionOverride(this.allowSessionOverride); view.setExposeSpringMacroHelpers(this.exposeSpringMacroHelpers); return view; }
增加设置五个属性
子类 有二个子类:
FreeMarkerViewResolver 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 // FreeMarkerViewResolver.java public class FreeMarkerViewResolver extends AbstractTemplateViewResolver { public FreeMarkerViewResolver() { setViewClass(requiredViewClass()); } public FreeMarkerViewResolver(String prefix, String suffix) { this(); setPrefix(prefix); setSuffix(suffix); } /** * Requires {@link FreeMarkerView}. */ @Override protected Class<?> requiredViewClass() { return FreeMarkerView.class; } }
GroovyMarkupViewResolver 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 // GroovyMarkupViewResolver.java public class GroovyMarkupViewResolver extends AbstractTemplateViewResolver { public GroovyMarkupViewResolver() { setViewClass(requiredViewClass()); } public GroovyMarkupViewResolver(String prefix, String suffix) { this(); setPrefix(prefix); setSuffix(suffix); } @Override protected Class<?> requiredViewClass() { return GroovyMarkupView.class; } /** * This resolver supports i18n, so cache keys should contain the locale. */ @Override protected Object getCacheKey(String viewName, Locale locale) { return viewName + '_' + locale; } }