zoukankan      html  css  js  c++  java
  • SpringMVC源码情操陶冶-FreeMarker之web配置

    前言:本文不讲解FreeMarkerView视图的相关配置,其配置基本由FreeMarkerViewResolver实现,具体可参考>>>SpringMVC源码情操陶冶-ViewResolver视图解析

    springmvc中整合freemarker

    以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

    其是FreeMarkerConfig接口的唯一实现类,在SpringMVC源码情操陶冶-View视图渲染中提到,具体的视图渲染由ViewResolver来指定特定的View对象进行解析。

    而此文则是FreeMarkerView视图来进行最终的视图渲染。
    通过观察此类的源码,发现其在初始化过程中判断出如果springmvc上下文不存在FreeMarkerConfigbean对象不存在则会直接抛出异常,表明FreeMarkerConfigurer此Bean对象必须配置。

    简单的可理解为此配置是额外的FreeMarkerView视图在渲染时所需的额外配置

    入口函数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路径下的资源

    小结

    实际应用结合理论分析,帮助大家更好的理解FreeMarker加载资源的逻辑,另外其他的视图InternalView/VelocityView等视图读者可自行分析加深印象

  • 相关阅读:
    前端高效开发必备的 js 库梳理
    前端进阶: css必知的几个底层知识和技巧
    Vue项目上线做的一些基本优化
    如何制作一个组件?论组件化思想
    15分钟带你了解前端工程师必知的javascript设计模式(附详细思维导图和源码)
    Promise的源码实现(完美符合Promise/A+规范)
    前端工程师不可不知的Nginx知识
    java EE应用概述
    javaweb学习——session和Cookie实现购物车功能
    javaweb学习——会话技术(二)
  • 原文地址:https://www.cnblogs.com/question-sky/p/7235634.html
Copyright © 2011-2022 走看看