springMVC源码简读——2.5 RequestToViewNameTranslator

    xiaoxiao2023-11-23  171

    概述 (RequestToViewNameTranslator)

    解决请求到视图名称的业务逻辑

    接口 (RequestToViewNameTranslator)

    public interface RequestToViewNameTranslator { /** * 根据请求,获得其视图名 */ @Nullable String getViewName(HttpServletRequest request) throws Exception; }

    实现类 (RequestToViewNameTranslator)

    DefaultRequestToViewNameTranslator

    默认且是唯一的 RequestToViewNameTranslator 实现类

    构造方法&属性

    // DefaultRequestToViewNameTranslator.java /** * 前缀 */ private String prefix = ""; /** * 后缀 */ private String suffix = ""; /** * 分隔符 */ private String separator = SLASH; /** * 是否移除开头 {@link #SLASH} */ private boolean stripLeadingSlash = true; /** * 是否移除末尾 {@link #SLASH} */ private boolean stripTrailingSlash = true; /** * 是否移除拓展名 */ private boolean stripExtension = true; /** * URL 路径工具类 */ private UrlPathHelper urlPathHelper = new UrlPathHelper();

    getViewName

    获得视图名称

    // DefaultRequestToViewNameTranslator.java // 根据配置的参数将传入的请求URI转换为视图名称 @Override public String getViewName(HttpServletRequest request) { // 获得请求路径 String lookupPath = this.urlPathHelper.getLookupPathForRequest(request); // 获得视图名 return (this.prefix + transformPath(lookupPath) + this.suffix); } @Nullable protected String transformPath(String lookupPath) { String path = lookupPath; // 移除开头 SLASH if (this.stripLeadingSlash && path.startsWith(SLASH)) { path = path.substring(1); } // 移除末尾 SLASH if (this.stripTrailingSlash && path.endsWith(SLASH)) { path = path.substring(0, path.length() - 1); } // 移除拓展名 if (this.stripExtension) { path = StringUtils.stripFilenameExtension(path); } // 替换分隔符 if (!SLASH.equals(this.separator)) { path = StringUtils.replace(path, SLASH, this.separator); } return path; }

    概述(ViewResolver)

    实体解析器接口,根据视图名和国际化,获得最终的视图 View 对象

    顶级接口(ViewResolver)

    public interface ViewResolver { /** * 根据视图名和国际化,获得最终的 View 对象 */ @Nullable View resolveViewName(String viewName, Locale locale) throws Exception; }

    类图 (ViewResolver)

    初始化(ViewResolver)

    springboot 配置下的初始化

    DispatcherServlet.java 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"); } } }

    这个代码的逻辑,emmmmmm……是不是和HandlerExceptionResolver很像,一个套路。

    实现类(ViewResolver)

    ContentNegotiatingViewResolver

    继承 WebApplicationObjectSupport 抽象类,基于内容类型来获取对应 View 的 ViewResolver 实现类,内容类型指的是 “Content-Type” 和拓展后缀

    构造方法

    ContentNegotiatingViewResolver.java /** * 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 属性

    ContentNegotiatingViewResolver.java @Override protected void initServletContext(ServletContext servletContext) { // 扫描所有 ViewResolver 的 Bean 们 Collection<ViewResolver> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(obtainApplicationContext(), ViewResolver.class).values(); // 情况一,如果 viewResolvers 为空,则将 matchingBeans 作为 viewResolvers 。 if (this.viewResolvers == null) { this.viewResolvers = new ArrayList<>(matchingBeans.size()); for (ViewResolver viewResolver : matchingBeans) { if (this != viewResolver) { this.viewResolvers.add(viewResolver); } } } // 情况二,如果 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); } } // 排序 viewResolvers 数组 AnnotationAwareOrderComparator.sort(this.viewResolvers); // 设置 cnmFactoryBean 的 servletContext 属性 this.cnmFactoryBean.setServletContext(servletContext); }

    afterPropertiesSet

    初始化 contentNegotiationManager 属性,afterPropertiesSet为啥可以继续初始化类,因为来源于InitializingBean这个就不细说了,可以搜下这个接口

    ContentNegotiatingViewResolver.java public void afterPropertiesSet() { if (this.contentNegotiationManager == null) { this.contentNegotiationManager = this.cnmFactoryBean.build(); } if (this.viewResolvers == null || this.viewResolvers.isEmpty()) { logger.warn("No ViewResolvers configured"); } }

    resolveViewName

    根据名称获得视图的逻辑

    ContentNegotiatingViewResolver.java @Override @Nullable public View resolveViewName(String viewName, Locale locale) throws Exception { RequestAttributes attrs = RequestContextHolder.getRequestAttributes(); Assert.state(attrs instanceof ServletRequestAttributes, "No current ServletRequestAttributes"); // 获得 MediaType 数组 List<MediaType> requestedMediaTypes = getMediaTypes(((ServletRequestAttributes) attrs).getRequest()); if (requestedMediaTypes != null) { // 获得匹配的 View 数组 List<View> candidateViews = getCandidateViews(viewName, locale, requestedMediaTypes); // 筛选最匹配的 View 对象 View bestView = getBestView(candidateViews, requestedMediaTypes, attrs); // 如果筛选成功,则返回 if (bestView != null) { return bestView; } } String mediaTypeInfo = logger.isDebugEnabled() && requestedMediaTypes != null ? " given " + requestedMediaTypes.toString() : ""; // 如果匹配不到 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; } }

    getMediaTypes

    获得 MediaType 数组

    ContentNegotiatingViewResolver.java @Nullable protected List<MediaType> getMediaTypes(HttpServletRequest request) { Assert.state(this.contentNegotiationManager != null, "No ContentNegotiationManager set"); try { // 创建 ServletWebRequest 对象 ServletWebRequest webRequest = new ServletWebRequest(request); List<MediaType> acceptableMediaTypes = this.contentNegotiationManager.resolveMediaTypes(webRequest); // // 从请求中,获得可接受的 MediaType 数组。默认实现是,从请求头 ACCEPT 中获取 List<MediaType> producibleMediaTypes = getProducibleMediaTypes(request); Set<MediaType> compatibleMediaTypes = new LinkedHashSet<>(); // 将符合的 producibleType 添加到 mediaTypesToUse 结果数组中 for (MediaType acceptable : acceptableMediaTypes) { for (MediaType producible : producibleMediaTypes) { if (acceptable.isCompatibleWith(producible)) { compatibleMediaTypes.add(getMostSpecificMediaType(acceptable, producible)); } } } // 进行排序然后输出 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); } } /** * 返回具体可以可使用的media types * 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 数组

    ContentNegotiatingViewResolver.java private List<View> getCandidateViews(String viewName, Locale locale, List<MediaType> requestedMediaTypes) throws Exception { // 创建 View 数组 List<View> candidateViews = new ArrayList<>(); // 假如存在this.viewResolvers if (this.viewResolvers != null) { Assert.state(this.contentNegotiationManager != null, "No ContentNegotiationManager set"); for (ViewResolver viewResolver : this.viewResolvers) { // 使用视图解析,使用viewName, locale获得视图,并添加至candidateViews View view = viewResolver.resolveViewName(viewName, locale); if (view != null) { candidateViews.add(view); } // 带有文拓展后缀的方式,获得 View 对象,添加到 candidateViews 中 for (MediaType requestedMediaType : requestedMediaTypes) { // 获得 MediaType 对应的拓展后缀的数组 List<String> extensions = this.contentNegotiationManager.resolveFileExtensions(requestedMediaType); for (String extension : extensions) { // 获得拓展后缀的值,然后解析出视图对象保存到 candidateViews 中 String viewNameWithExtension = viewName + '.' + extension; view = viewResolver.resolveViewName(viewNameWithExtension, locale); if (view != null) { candidateViews.add(view); } } } } } // defaultViews 到 candidateViews 中 if (!CollectionUtils.isEmpty(this.defaultViews)) { candidateViews.addAll(this.defaultViews); } return candidateViews; }

    主要还是根据视图名称,本地化参数和MediaType的值,使用视图解析获得对应view

    getBestView

    筛选最匹配的 View 对象

    ContentNegotiatingViewResolver.java @Nullable private View getBestView(List<View> candidateViews, List<MediaType> requestedMediaTypes, RequestAttributes attrs) { // 遍历 candidateView 数组,如果有重定向的 View 类型,则返回它 for (View candidateView : candidateViews) { if (candidateView instanceof SmartView) { SmartView smartView = (SmartView) candidateView; if (smartView.isRedirectView()) { return candidateView; } } } // 遍历 requestedMediaTypes 数组 for (MediaType mediaType : requestedMediaTypes) { // 遍历 candidateViews 数组 for (View candidateView : candidateViews) { // 如果 MediaType 类型匹配,则返回该 View 对象 if (StringUtils.hasText(candidateView.getContentType())) { 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; }

    主要还是两种返回可能

    重定向mediaType相匹配

    BeanNameViewResolver

    基于 Bean 的名字获得 View 对象的 ViewResolver 实现类

    构造方法

    BeanNameViewResolver.java /** * 最低优先级 */ private int order = Ordered.LOWEST_PRECEDENCE; // default: same as non-Ordered

    resolveViewName

    获得 Bean 的名字获得 View 对象

    BeanNameViewResolver.java @Override @Nullable public View resolveViewName(String viewName, Locale locale) throws BeansException { // 假如容器中不包含指定视图名的bean则返回空, 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); }

    很简单就是从容器中取得view的bean

    ViewResolverComposite

    实现 ViewResolver、Ordered、InitializingBean、ApplicationContextAware、ServletContextAware 接口,复合的 ViewResolver 实现类

    构造方法

    ViewResolverComposite.java /** * ViewResolver 数组 */ private final List<ViewResolver> viewResolvers = new ArrayList<>(); /** * 顺序,优先级最低 */ private int order = Ordered.LOWEST_PRECEDENCE;

    afterPropertiesSet

    进一步初始化

    ViewResolverComposite.java /** * Set the list of view viewResolvers to delegate to. * 进一步初始化,设置视图解析列表 */ public void setViewResolvers(List<ViewResolver> viewResolvers) { this.viewResolvers.clear(); if (!CollectionUtils.isEmpty(viewResolvers)) { this.viewResolvers.addAll(viewResolvers); } }

    resolveViewName

    视图名称处理

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

    使用传递过来的视图解析器的逻辑进行解析,具体逻辑自己不实现,emmmm代理

    AbstractCachingViewResolver

    提供通用的缓存的 ViewResolver 抽象类。对于相同的视图名,返回的是相同的 View 对象,所以通过缓存,可以进一步提供性能

    构造方法

    AbstractCachingViewResolver.java /** * 缓存视图的最大数量 */ /** 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) { } }; /** * 缓存上限。如果 cacheLimit = 0 ,表示禁用缓存 */ /** The maximum number of entries in the cache. */ private volatile int cacheLimit = DEFAULT_CACHE_LIMIT; /** * 是否缓存空 View 对象 */ /** 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. */ private final Map<Object, View> viewAccessCache = new ConcurrentHashMap<>(DEFAULT_CACHE_LIMIT); /** * View 的缓存的映射。增加了 synchronized 锁 */ /** Map from view key to View instance, synchronized for View creation. */ @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; } } };

    resolveViewName

    根据视图名称和本地化解析出视图resolveViewName

    @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 (this.viewCreationCache) { // 从 viewCreationCache 中,获得 View 对象 view = this.viewCreationCache.get(cacheKey); 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"); } } return (view != UNRESOLVED_VIEW ? view : null); } }

    createView

    创建 viewName 对应的 View 对象,最终还是使用loadView未实现

    AbstractCachingViewResolver.java @Nullable protected View createView(String viewName, Locale locale) throws Exception { return loadView(viewName, locale); }

    loadView

    加载 viewName 对应的 View 对象,emmm 没实现,交给子类实现

    AbstractCachingViewResolver.java @Nullable protected abstract View loadView(String viewName, Locale locale) throws Exception;

    UrlBasedViewResolver

    AbstractCachingViewResolver的子类也是实现loadView的地方,基于 Url 的 ViewResolver 实现类。

    构造方法

    UrlBasedViewResolver.java /** * 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 的属性 */ @Nullable private String requestContextAttribute; /** Map of static attributes, keyed by attribute name (String). */ private final Map<String, Object> staticAttributes = new HashMap<>(); /** * 是否暴露路径变量 */ @Nullable private Boolean exposePathVariables; @Nullable private Boolean exposeContextBeansAsAttributes; @Nullable private String[] exposedContextBeanNames; /** * 处理的视图名们 */ @Nullable private String[] viewNames; /** * 顺序,优先级最低 */ private int order = Ordered.LOWEST_PRECEDENCE;

    getCacheKey

    忽略 locale 参数,仅仅使用 viewName 作为缓存 KEY

    UrlBasedViewResolver.java @Override protected Object getCacheKey(String viewName, Locale locale) { return viewName; }

    canHandle

    判断传入的视图名是否可以被处理

    UrlBasedViewResolver.java protected boolean canHandle(String viewName, Locale locale) { String[] viewNames = getViewNames(); return (viewNames == null || PatternMatchUtils.simpleMatch(viewNames, viewName)); }

    applyLifecycleMethods

    应用Spring生命周期

    UrlBasedViewResolver.java 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; }

    大概逻辑是假如存在spring容器,则应用于spring的容器创建出来的view,否则直接返回视图。

    createView

    增加了对重定向和转发的情况的处理

    UrlBasedViewResolver.java 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 对象

    UrlBasedViewResolver.java protected View loadView(String viewName, Locale locale) throws Exception { // 创建 viewName 对应的 View 对象 AbstractUrlBasedView view = buildView(viewName); // 应用spring进行生命周期管理 View result = applyLifecycleMethods(viewName, view); return (view.checkResource(locale) ? result : null); } 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; }

    InternalResourceViewResolver

    继承 UrlBasedViewResolver 类,解析出 JSP 的 ViewResolver 实现类。这个现在用的应该没多少了吧。

    构造方法

    InternalResourceViewResolver.java public InternalResourceViewResolver() { // 获得 viewClass Class<?> viewClass = requiredViewClass(); if (InternalResourceView.class == viewClass && jstlPresent) { viewClass = JstlView.class; } // 设置 viewClass setViewClass(viewClass); } // 视图名会是 InternalResourceView @Override protected Class<?> requiredViewClass() { return InternalResourceView.class; }

    buildView

    绑定视图

    InternalResourceViewResolver.java @Override protected AbstractUrlBasedView buildView(String viewName) throws Exception { // 使用 InternalResourceView.class创建视图 InternalResourceView view = (InternalResourceView) super.buildView(viewName); // 设置视图属性 if (this.alwaysInclude != null) { view.setAlwaysInclude(this.alwaysInclude); } view.setPreventDispatchLoop(true); return view; }

    AbstractTemplateViewResolver

    继承 UrlBasedViewResolver 类,解析出 AbstractTemplateView 的 ViewResolver 抽象类

    构造方法

    AbstractTemplateViewResolver.java /** * 是否将所有 RequestAttributes 暴露给 View 使用 */ private boolean exposeRequestAttributes = false; /** * 当 RequestAttributes 中存在 Model 中同名的参数,是否允许使用 RequestAttributes 中的值将 Model 中的值进行覆盖 */ private boolean allowRequestOverride = false; /** * 是否将 SessionAttributes 暴露给 View 使用 */ private boolean exposeSessionAttributes = false; /** * 当 SessionAttributes 中存在 Model 中同名的参数,是否允许使用 SessionAttributes 中的值将 Model 中的值进行覆盖 */ private boolean allowSessionOverride = false; /** * 是否将 RequestContext 暴露给 view 为 spring 的宏( Macro )所使用 */ private boolean exposeSpringMacroHelpers = true;

    requiredViewClass

    AbstractTemplateViewResolver解析器对应的类

    AbstractTemplateViewResolver.java @Override protected Class<?> requiredViewClass() { return AbstractTemplateView.class; }

    buildView

    创建视图

    AbstractTemplateViewResolver.java @Override protected AbstractUrlBasedView buildView(String viewName) throws Exception { // 调用父类方法使用AbstractTemplateView.class获得视图,并设置视图属性 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

    没什么大的变化无非是requiredViewClass方法返回的FreeMarkerView.class

    FreeMarkerViewResolver.java /** * Requires {@link FreeMarkerView}. */ @Override protected Class<?> requiredViewClass() { return FreeMarkerView.class; } GroovyMarkupViewResolver

    requiredViewClass方法返回的GroovyMarkupView.class,然后缓存上考虑的本地化的因素

    GroovyMarkupViewResolver.java @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; }

    总结

    视图解析主要的解析工作逻辑可以查看AbstractCachingViewResolver相关内容,主要是做了视图解析的一些列工作。 其后续的实现类中主要查看其requiredViewClass方法,不同的实现类通过不同的requiredViewClass来返回不同的视图操作。

    最新回复(0)