资讯专栏INFORMATION COLUMN

SpringMVC之源码分析--ViewResolver(六)

alighters / 1610人阅读

摘要:与一样,该类继承抽象类,并且通过外部的属性文件定义逻辑视图名称与真正的视图对象的关系,属性文件默认是下的,可以通过或属性来指定,该属性指的是文件的基名称,也就是说以属性值开头的属性文件。

概述

本章再学习另外两个ViewResolver,分别是XmlViewResolver和ResourceBundleViewResolver,从功能上说,这两个视图解析器都是从外部资源文件中查找视图View对象,所以放在一章学习。

本系列文章是基于Spring5.0.5RELEASE。

XmlViewResolver

该类继承AbstractCachingViewResolver抽象类,也就是该解析器支持视图缓存。XmlViewResolver通过使用额外的xml配置文件来定义视图对象,xml配置文件默认加载/WEB-INF/views.xml,可通过location属性参数重置加载文件。

配置文件中定义视图对象,并指定bean名称(id或name),该名称与Controller处理器中返回的逻辑视图名称对应,从而通过url指定的路径找到真正的视图进行渲染。

源码如下:

public class XmlViewResolver extends AbstractCachingViewResolver
    implements Ordered, InitializingBean, DisposableBean {

    /** 默认加载的视图配置文件 */
    public static final String DEFAULT_LOCATION = "/WEB-INF/views.xml";
    /** 指定视图配置文件路径 */
    @Nullable
    private Resource location;
    /** 如果开启缓存(cacheLimit>0),bean工厂缓存在该属性 */
    @Nullable
    private ConfigurableApplicationContext cachedFactory;
    /** 视图解析器排序 */
    private int order = Ordered.LOWEST_PRECEDENCE;  

    ... 省略get/set方法 ...

    /** 启动时调用 */
    @Override
    public void afterPropertiesSet() throws BeansException {
        // 开启缓存(cacheLimit>0)时,在应用启动时创建bean工厂
        if (isCache()) {
            initFactory();
        }
    }
    
    /** 返回视图名称,在父类AbstractCachingViewResolver的resolveViewName方法中调用 */
    @Override
    protected Object getCacheKey(String viewName, Locale locale) {
        return viewName;
    }
    
    /** 
     *根据视图名称加载View视图 
     */
    @Override
    protected View loadView(String viewName, Locale locale) throws BeansException {
        // 创建bean工厂
        BeanFactory factory = initFactory();
        try {
            // 根据controller返回的逻辑视图名(视图名称与bean名称对应)查找视图对象
            return factory.getBean(viewName, View.class);
        }
        catch (NoSuchBeanDefinitionException ex) {
            // Allow for ViewResolver chaining...
            return null;
        }
    }

    /** 创建bean工厂 */
    protected synchronized BeanFactory initFactory() throws BeansException {
        // 如果启用缓存,第二次直接返回
        if (this.cachedFactory != null) {
            return this.cachedFactory;
        }

        ApplicationContext applicationContext = obtainApplicationContext();

        Resource actualLocation = this.location;
        if (actualLocation == null) {
            actualLocation = applicationContext.getResource(DEFAULT_LOCATION);
        }

        // Create child ApplicationContext for views.
        GenericWebApplicationContext factory = new GenericWebApplicationContext();
        factory.setParent(applicationContext);
        factory.setServletContext(getServletContext());

        // Load XML resource with context-aware entity resolver.
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
        reader.setEnvironment(applicationContext.getEnvironment());
        reader.setEntityResolver(new ResourceEntityResolver(applicationContext));
        reader.loadBeanDefinitions(actualLocation);

        factory.refresh();
        // 启用缓存,赋值属性变量进行存储
        if (isCache()) {
            this.cachedFactory = factory;
        }
        return factory;
    }
    
}

以上是XmlViewResolver的核心代码。

ResourceBundleViewResolver

与XmlViewResolver一样,该类继承AbstractCachingViewResolver抽象类,并且通过外部的属性文件定义逻辑视图名称与真正的视图View对象的关系,属性文件默认是classpath下的views.properties,可以通过basename或basenames属性来指定,该属性指的是文件的基名称,也就是说以basename属性值开头的属性文件。

ResourceBundleViewResolver类具有缓存功能,即把 properties 文件中定义好的属性按照它自身的规则生成一个个的 bean 对象注册到该 BeanFactory 中,之后会把该 BeanFactory 对象保存起来,所以 ResourceBundleViewResolver 缓存的是 BeanFactory ,而不是直接的缓存从 BeanFactory 中取出的视图 bean。

Spring 通过 properties 文件生成 bean 的规则是把 properties 文件中定义的属性名称按最后一个点“ . ”进行分割,把点前面的内容当做是 bean 名称,点后面的内容当做是 bean 的属性。

源码如下:

public class ResourceBundleViewResolver extends AbstractCachingViewResolver
    implements Ordered, InitializingBean, DisposableBean {

    /** 配置文件的默认基名称,即以此开头的属性文件,默认从classpath路径查找加载 */
    public static final String DEFAULT_BASENAME = "views";
    /** 支持多文件加载 */
    private String[] basenames = new String[] {DEFAULT_BASENAME};
    private ClassLoader bundleClassLoader = Thread.currentThread().getContextClassLoader();
    
    @Nullable
    private String defaultParentView;
    @Nullable
    private Locale[] localesToInitialize;

    private int order = Ordered.LOWEST_PRECEDENCE;  // default: same as non-Ordered
    
    /* Locale -> BeanFactory */
    private final Map localeCache = new HashMap<>();

    /* List of ResourceBundle -> BeanFactory */
    private final Map, ConfigurableApplicationContext> bundleCache = new HashMap<>();
    
    /**********get/set**********/
    public void setBasename(String basename) {
        setBasenames(basename);
    }
    public void setBasenames(String... basenames) {
        this.basenames = basenames;
    }
    public void setBundleClassLoader(ClassLoader classLoader) {
        this.bundleClassLoader = classLoader;
    }
    protected ClassLoader getBundleClassLoader() {
        return this.bundleClassLoader;
    }
    public void setDefaultParentView(String defaultParentView) {
        this.defaultParentView = defaultParentView;
    }
    public void setLocalesToInitialize(Locale... localesToInitialize) {
        this.localesToInitialize = localesToInitialize;
    }
    public void setOrder(int order) {
        this.order = order;
    }
    @Override
    public int getOrder() {
        return this.order;
    }

    /** 启动时调用,创建初始化bean工厂 */
    @Override
    public void afterPropertiesSet() throws BeansException {
        // localesToInitialize属性通过配置进行设置
        if (this.localesToInitialize != null) {
            for (Locale locale : this.localesToInitialize) {
                initFactory(locale);
            }
        }
    }
    
    /** 查找视图View,在父类AbstractCachingViewResolver的resolverViewName方法中调用 */
    @Override
    protected View loadView(String viewName, Locale locale) throws Exception {
        // 初始化bean工厂
        BeanFactory factory = initFactory(locale);
        try {
            return factory.getBean(viewName, View.class);
        }
        catch (NoSuchBeanDefinitionException ex) {
            // Allow for ViewResolver chaining...
            return null;
        }
    }

    protected synchronized BeanFactory initFactory(Locale locale) throws BeansException {

        // 开启缓存,通过cacheLimit属性大于0控制
        if (isCache()) {
            // localeCache属性map中缓存locale和beanFactory映射
            BeanFactory cachedFactory = this.localeCache.get(locale);
            if (cachedFactory != null) {
                return cachedFactory;
            }
        }

        // 创建ResourceBundle集合,支持多属性文件
        List bundles = new LinkedList<>();
        for (String basename : this.basenames) {
            ResourceBundle bundle = getBundle(basename, locale);
            bundles.add(bundle);
        }

        // 开启缓存,
        if (isCache()) {
            BeanFactory cachedFactory = this.bundleCache.get(bundles);
            if (cachedFactory != null) {
                this.localeCache.put(locale, cachedFactory);
                return cachedFactory;
            }
        }

        // 创建视图ApplicationContext上下文
        GenericWebApplicationContext factory = new GenericWebApplicationContext();
        factory.setParent(getApplicationContext());
        factory.setServletContext(getServletContext());

        // 从资源文件中加载bean定义
        PropertiesBeanDefinitionReader reader = new PropertiesBeanDefinitionReader(factory);
        reader.setDefaultParentBean(this.defaultParentView);
        for (ResourceBundle bundle : bundles) {
            reader.registerBeanDefinitions(bundle);
        }

        factory.refresh();

        // 设置缓存
        if (isCache()) {
            this.localeCache.put(locale, factory);
            this.bundleCache.put(bundles, factory);
        }

        return factory;
    }

    protected ResourceBundle getBundle(String basename, Locale locale) throws MissingResourceException {
        return ResourceBundle.getBundle(basename, locale, getBundleClassLoader());
    }

    @Override
    public void destroy() throws BeansException {
        for (ConfigurableApplicationContext factory : this.bundleCache.values()) {
            factory.close();
        }
        this.localeCache.clear();
        this.bundleCache.clear();
    }

}

以上是ResourceBundleViewResolver的核心代码。

实战

使用XmlViewResolver

spring mvc配置文件中配置XmlViewResolver视图解析器,代码如下:



    

在classpath下创建views.xml配置文件,代码如下:


    

启动测试,可以正常进行渲染。

使用ResourceBundleViewResolver

spring mvc配置文件中配置ResourceBundlerViewResolver视图解析器,代码如下:



    
    
        
            
            properties/test
            properties/views
        
    
    
    
    
    

test.properties配置如下:

// 配置视图View
test.(class)=org.springframework.web.servlet.view.InternalResourceView
// 对应真实视图url
test.url=/WEB-INF/jsp/test.jsp

views.properties配置与test一样。

启动测试,正常解析渲染。

总结

经过六章的分析,学习了ViewResolver视图解析器,回想一下,可划分为三部分:

组合的ViewResolver,这部分是直接继承ViewResolver接口的,不具有缓存功能,包括像ViewResolverComposite、ContentNegotiatingViewResolver等

基于url的Viewresolver,这部分包括UrlBasedViewResolver、InternalResourceViewResolver等

基于外部文件的Viewresolver,包括XmlViewResolver、ResourceBundleViewResolver

关于视图解析器,就分析到这,希望对大家有帮助,谢谢!

最后创建了qq群方便大家交流,可扫描加入,同时也可加我qq:276420284,共同学习、共同进步,谢谢!

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/69689.html

相关文章

  • SpringMVC源码分析--ViewResolver(三)

    摘要:概述本节学习下的功能,简单来说,该类的作用就是把多个视图解析器进行组装,内部使用存储配置使用的视图解析器。总结本章介绍了类,根据测试,了解到属性不影响中配置使用的视图解析器顺序。 概述 本节学习下ViewResolverComposite的功能,简单来说,该类的作用就是把多个ViewResolver视图解析器进行组装,内部使用list存储配置使用的视图解析器。 本系列文章是基于Spri...

    fox_soyoung 评论0 收藏0
  • SpringMVC源码分析--ViewResolver(二)

    摘要:概述上篇学习了视图解析器作用及处理流程,为我们提供了丰富的视图解析器见下图本系列文章是基于。该视图解析器是根据处理器返回的逻辑视图名称,在应用上下文中查找该名称的视图对象视图对象就是的对象。 概述 上篇学习了Spring MVC ViewResolver视图解析器作用及处理流程,Spring为我们提供了丰富的视图解析器(见下图):showImg(https://segmentfault...

    jas0n 评论0 收藏0
  • SpringMVC源码分析--ViewResolver(一)

    摘要:概述本章开始进入另一重要的组件,即视图组件,处理视图组件使用两个主要的接口是和。接口的作用是用于处理视图进行渲染。延用之前的介绍流程,本章分两部分进行阐述启动初始化和请求处理。 概述 本章开始进入另一重要的组件,即视图组件,Spring MVC处理视图组件使用两个主要的接口是ViewResolver和View。根据名称可知,ViewResolver即视图解析器,其作用是把逻辑视图名称解...

    pf_miles 评论0 收藏0
  • SpringMVC源码分析--ViewResolver(四)

    摘要:概述本章继续学习另一个实现类解析器,该类的主要作用是根据同一请求的某些策略,选择对应的进行渲染。可以把理解为适配器,对不同类型进行适配。值得注意的是处理的为同一个。本系列文章是基于。实战需求目标实现后缀名或参数控制,显示不同的视图。 概述 本章继续学习ViewResolver另一个实现类ContentNegotiatingViewResolver解析器,该类的主要作用是根据同一请求的某...

    jcc 评论0 收藏0
  • SpringMVC源码分析--ViewResolver(五)

    摘要:此解析器与差不多,更改下配置文件中的类全路径即可。总结本章介绍了以及三个视图解析器。这部分内容有点儿多,我会尽快结束。 概述 通过上几篇的学习,我们分析了并试验了ViewResolverComposite、BeanNameViewResolver和ContentNegotiatingViewResolver,这三个类都直接实现ViewResolver接口。Spring MVC提供了很多...

    klinson 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<