SpringMVC源码情操陶冶-FreeMarker之web配置

以xml的bean方式展示如下

    <!-- 视图配置 -->
    <bean id="viewResolver"
          class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
        <property name="cache" value="true" />
        <property name="prefix" value="screen/" />
        <property name="suffix" value=".html" />
        <property name="contentType" value="text/html;charset=UTF-8" />
        <!-- 设置requestContext变量的名称 -->
        <property name="requestContextAttribute" value="request" />
        <!-- 配置是否在生成模板内容之前把HTTPsession中的数据放入model中 -->
        <property name="exposeSessionAttributes" value="true" />
        <!-- 配置是否在生成模板内容之前把HTTPrequest中的数据放入model中 -->
        <property name="exposeRequestAttributes" value="true" />
        <!-- 使用spring lib时 是否暴露 RequestContext 变量 默认为true -->
        <property name="exposeSpringMacroHelpers" value="true" />
    </bean>
    <bean id="freemarkerConfig"
          class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
        <property name="templateLoaderPath" value="/WEB-INF/views/" />
        <property name="freemarkerSettings">
            <props>
                <prop key="template_update_delay">0</prop>
                <prop key="default_encoding">utf-8</prop>
                <prop key="number_format">0.##########</prop>
                <prop key="datetime_format">yyyy-MM-dd HH:mm:ss</prop>
                <prop key="classic_compatible">true</prop>
                <prop key="template_exception_handler">ignore</prop>
                <!-- 自动引入模板 -->
                <!--  <prop key="auto_import">components/spring.ftl as p</prop>-->
            </props>
        </property>
    </bean>

以上简单的两个bean配置便完成了springmvc整合FreeMarker,上述的FreeMarkerViewResolver解析直接点击前言的链接即可。本文就FreeMarkerConfigurer类进行简单的分析

FreeMarkerConfigurer#FreeMarker的web配置

其是FreeMarkerConfig接口的唯一实现类,在SpringMVC源码情操陶冶-View视图渲染中提到FreeMarkerView在初始化过程中如果springmvc上下文不存在FreeMarkerConfigbean对象不存在则会直接抛出异常,表明使用FreeMarker其必须配置。

入口函数afterPropertiesSet()

FreeMarkerConfigurer继承了父类FreeMarkerConfigurationFactory,并实现了InitialzingBean接口

    @Override
    public void afterPropertiesSet() throws IOException, TemplateException {
        if (this.configuration == null) {
            //调用父类来实现创建,相关的配置则保存至freemaker包中的Configuration中
            this.configuration = createConfiguration();
        }
    }

FreeMarkerConfigurationFactory#createConfiguration()-创建FreeMarker的web配置

先看下父类的内部属性,其在springmvc配置中也常见

    //可以直接指定某个配置文件路径,直接读取
    private Resource configLocation;
    
    //额外配置
    private Properties freemarkerSettings;

    //可以简单的指定模板加载路径,支持,分隔并支持classpath模式加载
    private String[] templateLoaderPaths;

直接看create方法的源码

    public Configuration createConfiguration() throws IOException, TemplateException {
        Configuration config = newConfiguration();
        Properties props = new Properties();

        // 可以直接通过configLocation加载FreeMarker的基本配置
        if (this.configLocation != null) {
            if (logger.isInfoEnabled()) {
                logger.info("Loading FreeMarker configuration from " + this.configLocation);
            }
            PropertiesLoaderUtils.fillProperties(props, this.configLocation);
        }

        // Merge local properties if specified.
        if (this.freemarkerSettings != null) {
            props.putAll(this.freemarkerSettings);
        }

        //只会保存已有的内部属性,比如time_format。更多的可查看Configuration#setSetting()方法
        if (!props.isEmpty()) {
            config.setSettings(props);
        }

        if (!CollectionUtils.isEmpty(this.freemarkerVariables)) {
            config.setAllSharedVariables(new SimpleHash(this.freemarkerVariables, config.getObjectWrapper()));
        }

        if (this.defaultEncoding != null) {
            config.setDefaultEncoding(this.defaultEncoding);
        }

        List<TemplateLoader> templateLoaders = new LinkedList<TemplateLoader>(this.templateLoaders);

        // Register template loaders that are supposed to kick in early.
        if (this.preTemplateLoaders != null) {
            templateLoaders.addAll(this.preTemplateLoaders);
        }

        // Register default template loaders.
        if (this.templateLoaderPaths != null) {
            for (String path : this.templateLoaderPaths) {
                //加载templateLoaderPath指定的资源,创建相应的加载器
                templateLoaders.add(getTemplateLoaderForPath(path));
            }
        }
        //将templateLoaders放入内部属性templateLoaders集合中
        postProcessTemplateLoaders(templateLoaders);

        // Register template loaders that are supposed to kick in late.
        if (this.postTemplateLoaders != null) {
            templateLoaders.addAll(this.postTemplateLoaders);
        }
        //选取一个templateLoader用于加载真实的view视图资源
        TemplateLoader loader = getAggregateTemplateLoader(templateLoaders);
        if (loader != null) {
            config.setTemplateLoader(loader);
        }
        //默认为空方法
        postProcessConfiguration(config);
        return config;
    }

由以上代码可知,具体的加载视图对应的真实资源是通过templateLoader来加载的,下面具体分析下

FreeMarkerConfigurationFactory#getTemplateLoaderForPath()-创建模板资源加载器

源码奉上

    protected TemplateLoader getTemplateLoaderForPath(String templateLoaderPath) {
        //preferFileSystemAccess属性默认为true
        if (isPreferFileSystemAccess()) {
            // Try to load via the file system, fall back to SpringTemplateLoader
            // (for hot detection of template changes, if possible).
            try {
                //通过DefaultResourceLoader的getResource()来获取Resource
                Resource path = getResourceLoader().getResource(templateLoaderPath);
                //此file为目录
                File file = path.getFile();  // will fail if not resolvable in the file system
                //默认为FileTemplateLoader加载器
                return new FileTemplateLoader(file);
            }
            catch (IOException ex) {
                //获取文件异常时使用SpringTemplateLoader
                return new SpringTemplateLoader(getResourceLoader(), templateLoaderPath);
            }
        }
        else {
            //也可以设置preferFileSystemAccess为false而直接使用SpringTemplateLoader
            return new SpringTemplateLoader(getResourceLoader(), templateLoaderPath);
        }
    }

我们接着看其如何获取到templateLoader资源加载器

DefaultResourceLoader#getResource()
    @Override
    public Resource getResource(String location) {
        //此处location代表templateLoaderPath
        Assert.notNull(location, "Location must not be null");
        //如果路径以"/"开头,通常此处多指加载WEB-INF目录下的资源
        if (location.startsWith("/")) {
            //此处的加载是通过ServletContextResourceLoader加载的,具体如何加载可查看其源码
            return getResourceByPath(location);
        }
        else if (location.startsWith(CLASSPATH_URL_PREFIX)) {
            //加载classpath:为前缀的路径资源
            return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
        }
        else {
            try {
                // Try to parse the location as a URL...
                URL url = new URL(location);
                return new UrlResource(url);
            }
            catch (MalformedURLException ex) {
                //最终都是由ServletContextResourceLoader来加载资源
                return getResourceByPath(location);
            }
        }
    }

FreeMarker对templateLoaderPath指定的路径展开以下两种解析

  1. 以"/"为开头的路径,通常为"/WEB-INF",其通过ServletContextResourceLoader来加载服务器的资源,用到的通常是ServletContext.getRealPath()方法来获取真实资源。其也是默认的FreeMarker资源加载器

  2. 以"classpath:"为开头的路径,通过常见的resourceLoader加载器加载classpath路径下的资源,即可以加载src/main/resources路径下的资源

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值