深入剖析Spring Web源码(十八) - 视图解析和视图显示 - 更多的视图解析器

4.3.2 更多的视图解析器

 

上一节中,我们介绍了基于URL的视图解析器和视图的实现。事实上,还存在这一些视图解析器,他们不仅支持某一种类型的视图实现,而是能在多种视图实现中互相转换和选择。这一节中我们将分析四种这样类型的视图解析器的实现。

 

4.3.2.1 Bean名视图解析器

 

Bean名视图解析器通过把逻辑视图名作为Web应用程序环境中的Bean名来解析视图。如下代码所示,

 

public class BeanNameViewResolver extends WebApplicationObjectSupport implements ViewResolver, Ordered {

 

    private int order = Integer.MAX_VALUE;  // default: same as non-Ordered

 

 

    public void setOrder(int order) {

        this.order = order;

    }

 

    public int getOrder() {

        return order;

    }

 

 

    public View resolveViewName(String viewName, Locale locale) throws BeansException {

        取得Web应用程序环境

        ApplicationContext context = getApplicationContext();

       

        如果Web应用程序环境中不包含一个Bean,它的名字是逻辑视图名,则返回空的视图

        if (!context.containsBean(viewName)) {

            // Allow for ViewResolver chaining.

            return null;

        }

       

        如果Web应用程序环境中包含这样的一个Bean,则返回这个Bean作为视图

        return (View) context.getBean(viewName, View.class);

    }

 

}

 

4.3.2.2 内容选择视图解析器

 

内容选择视图解析器根据HTTP请求所指定的媒体类型来选择一个合适的视图解析器来解析视图,但是它自己并不解析视图。一个HTTP请求可以通过下列的方式之一来制定媒体类型,

 

1.       根据URL路径的扩展名。

2.       根据制定的参数值。

3.       根据HTTP头的接受内容类型。

 

首先在初始化的时候它加载了所有的其他的视图解析器。如下代码注释,

 

@Override

protected void initServletContext(ServletContext servletContext) {

    if (this.viewResolvers == null) {

        // Web应用程序环境中找到所有的视图解析器的实现

        Map<String, ViewResolver> matchingBeans =

                BeanFactoryUtils.beansOfTypeIncludingAncestors(getApplicationContext(), ViewResolver.class);

        this.viewResolvers = new ArrayList<ViewResolver>(matchingBeans.size());

        for (ViewResolver viewResolver : matchingBeans.values()) {

            // 保存所有的视图解析器,并且排除自己

            if (this != viewResolver) {

                this.viewResolvers.add(viewResolver);

            }

        }

    }

   

    // 如果不能找到视图解析器,则打印警告日志

    if (this.viewResolvers.isEmpty()) {

        logger.warn("Did not find any ViewResolvers to delegate to; please configure them using the " +

                "'viewResolvers' property on the ContentNegotiatingViewResolver");

    }

   

    // 排序视图解析器,ContentNegotiatingViewResolver的优先级是最高的

    OrderComparator.sort(this.viewResolvers);

}

 

当处理一个HTTP请求的时候,它首先通过上述的三个规则解析请求所指定的媒体类型,如下代码所示,

 

protected List<MediaType> getMediaTypes(HttpServletRequest request) {

    // 如果扩展名解析优先,从URL的扩展名中解析媒体类型

    if (this.favorPathExtension) {

        String requestUri = urlPathHelper.getRequestUri(request);

        String filename = WebUtils.extractFullFilenameFromUrlPath(requestUri);

        MediaType mediaType = getMediaTypeFromFilename(filename);

        if (mediaType != null) {

            if (logger.isDebugEnabled()) {

                logger.debug("Requested media type is '" + mediaType + "' (based on filename '" + filename + "')");

            }

            List<MediaType> mediaTypes = new ArrayList<MediaType>();

            mediaTypes.add(mediaType);

            return mediaTypes;

        }

    }

   

    // 如果参数解析优先,从参数中解析媒体类型

    if (this.favorParameter) {

        if (request.getParameter(this.parameterName) != null) {

            String parameterValue = request.getParameter(this.parameterName);

            MediaType mediaType = getMediaTypeFromParameter(parameterValue);

            if (mediaType != null) {

                if (logger.isDebugEnabled()) {

                    logger.debug("Requested media type is '" + mediaType + "' (based on parameter '" +

                            this.parameterName + "'='" + parameterValue + "')");

                }

                List<MediaType> mediaTypes = new ArrayList<MediaType>();

                mediaTypes.add(mediaType);

                return mediaTypes;

            }

        }

    }

   

    // 如果不忽略HTTP头中支持的接受头信息,则从HTTP头中解析媒体类型

    if (!this.ignoreAcceptHeader) {

        String acceptHeader = request.getHeader(ACCEPT_HEADER);

        if (StringUtils.hasText(acceptHeader)) {

            List<MediaType> mediaTypes = MediaType.parseMediaTypes(acceptHeader);

            if (logger.isDebugEnabled()) {

                logger.debug("Requested media types are " + mediaTypes + " (based on Accept header)");

            }

            return mediaTypes;

        }

    }

   

    // 如果解析不成功,则使用缺省内容类型

    if (this.defaultContentType != null) {

        return Collections.singletonList(this.defaultContentType);

    }

    else {

        return Collections.emptyList();

    }

}

 

在得知一个请求所能接受的媒体类型后,它将要选择一个最佳的能处理当前媒体类型的视图解析器来解析具体的视图,如下代码注释,

 

public View resolveViewName(String viewName, Locale locale) throws Exception {

    // 取得Servlet请求属性

    RequestAttributes attrs = RequestContextHolder.getRequestAttributes();

    Assert.isInstanceOf(ServletRequestAttributes.class, attrs);

    ServletRequestAttributes servletAttrs = (ServletRequestAttributes) attrs;

 

    // Servlet请求属性中得到HTTP请求对象

    List<MediaType> requestedMediaTypes = getMediaTypes(servletAttrs.getRequest());

    // 如果支持的媒体类型多余一个,则进行排序

    if (requestedMediaTypes.size() > 1) {

        // avoid sorting attempt for empty list and singleton list

        Collections.sort(requestedMediaTypes);

    }

 

    // 如果某个视图解析器能够解析当前请求,解析的视图则成为候选视图

    SortedMap<MediaType, View> views = new TreeMap<MediaType, View>();

    List<View> candidateViews = new ArrayList<View>();

    for (ViewResolver viewResolver : this.viewResolvers) {

        View view = viewResolver.resolveViewName(viewName, locale);

        if (view != null) {

            candidateViews.add(view);

        }

    }

   

    // 缺省视图也是候选视图

    if (!CollectionUtils.isEmpty(this.defaultViews)) {

        candidateViews.addAll(this.defaultViews);

    }

 

    // 遍历所有候选视图

    for (View candidateView : candidateViews) {

        // 取得候选试图支持的内容类型

        String contentType = candidateView.getContentType();

        if (StringUtils.hasText(contentType)) {

            // 转换成为媒体类型对象

            MediaType viewMediaType = MediaType.parseMediaType(contentType);

            // 遍历HTTP请求支持的媒体类型

            for (MediaType requestedMediaType : requestedMediaTypes) {

                // 如果HTTP请求支持的媒体类型包含候选视图的媒体类型,则使用这个视图

                if (requestedMediaType.includes(viewMediaType)) {

                    if (!views.containsKey(requestedMediaType)) {

                        views.put(requestedMediaType, candidateView);

                        break;

                    }

                }

            }

        }

    }

 

    // 如果解析到一个或者多个视图,则使用第一个

    if (!views.isEmpty()) {

        MediaType mediaType = views.firstKey();

        View view = views.get(mediaType);

        if (logger.isDebugEnabled()) {

            logger.debug("Returning [" + view + "] based on requested media type '" + mediaType + "'");

        }

        return view;

    }

    else {

        return null;

    }

}

 

 

4.3.2.3 资源绑定视图解析器

 

资源绑定视图解析器从资源绑定中加载Bean定义,然后通过视图逻辑名来解析一个定义的Bean作为视图名,如下代码注释,

 

@Override

protected View loadView(String viewName, Locale locale) throws Exception {

    从资源绑定中加载Bean工厂

    BeanFactory factory = initFactory(locale);

    try {

        Bean工厂中解析Bean

        return factory.getBean(viewName, View.class);

    }

    catch (NoSuchBeanDefinitionException ex) {

        // to allow for ViewResolver chaining

        return null;

    }

}

 

protected synchronized BeanFactory initFactory(Locale locale) throws BeansException {

    // Try to find cached factory for Locale:

    // Have we already encountered that Locale before?

    if (isCache()) {

        BeanFactory cachedFactory = this.localeCache.get(locale);

        if (cachedFactory != null) {

            return cachedFactory;

        }

    }

 

    // Build list of ResourceBundle references for Locale.

    List<ResourceBundle> bundles = new LinkedList<ResourceBundle>();

    for (String basename : this.basenames) {

        ResourceBundle bundle = getBundle(basename, locale);

        bundles.add(bundle);

    }

 

    // Try to find cached factory for ResourceBundle list:

    // even if Locale was different, same bundles might have been found.

    if (isCache()) {

        BeanFactory cachedFactory = this.bundleCache.get(bundles);

        if (cachedFactory != null) {

            this.localeCache.put(locale, cachedFactory);

            return cachedFactory;

        }

    }

 

    // Create child ApplicationContext for views.

    GenericWebApplicationContext factory = new GenericWebApplicationContext();

    factory.setParent(getApplicationContext());

    factory.setServletContext(getServletContext());

 

    // Load bean definitions from resource bundle.

    PropertiesBeanDefinitionReader reader = new PropertiesBeanDefinitionReader(factory);

    reader.setDefaultParentBean(this.defaultParentView);

    for (ResourceBundle bundle : bundles) {

        reader.registerBeanDefinitions(bundle);

    }

 

    factory.refresh();

 

    // Cache factory for both Locale and ResourceBundle list.

    if (isCache()) {

        this.localeCache.put(locale, factory);

        this.bundleCache.put(bundles, factory);

    }

 

    return factory;

}

 

 

4.3.2.4 XML视图解析器

 

XML视图解析器从一个XML资源文件中加载Bean定义,然后通过视图逻辑名来解析一个定义的Bean作为视图名,如下代码注释,

 

protected synchronized BeanFactory initFactory() throws BeansException {

    检查是否已经缓存

    if (this.cachedFactory != null) {

        return this.cachedFactory;

    }

 

    从配置的位置或者缺省的位置加载XML资源

    Resource actualLocation = this.location;

    if (actualLocation == null) {

        actualLocation = getApplicationContext().getResource(DEFAULT_LOCATION);

    }

 

    // Create child ApplicationContext for views.

    GenericWebApplicationContext factory = new GenericWebApplicationContext();

    factory.setParent(getApplicationContext());

    factory.setServletContext(getServletContext());

 

    // Load XML resource with context-aware entity resolver.

    XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);

    reader.setEntityResolver(new ResourceEntityResolver(getApplicationContext()));

    reader.loadBeanDefinitions(actualLocation);

 

    factory.refresh();

 

    if (isCache()) {

        this.cachedFactory = factory;

    }

    return factory;

}

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值