前言:本文不讲解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等视图读者可自行分析加深印象

SpringMVC源码情操陶冶-FreeMarker之web配置的更多相关文章

  1. SpringMVC源码情操陶冶-AnnotationDrivenBeanDefinitionParser注解解析器

    mvc:annotation-driven节点的解析器,是springmvc的核心解析器 官方注释 Open Declaration org.springframework.web.servlet.c ...

  2. SpringMVC源码情操陶冶-DispatcherServlet父类简析

    阅读源码有助于陶冶情操,本文对springmvc作个简单的向导 springmvc-web.xml配置 <servlet> <servlet-name>dispatch< ...

  3. SpringMVC源码情操陶冶-DispatcherServlet类简析(一)

    阅读源码有利于陶冶情操,此文承接前文SpringMVC源码情操陶冶-DispatcherServlet父类简析 注意:springmvc初始化其他内容,其对应的配置文件已被加载至beanFactory ...

  4. SpringMVC源码情操陶冶-DispatcherServlet简析(二)

    承接前文SpringMVC源码情操陶冶-DispatcherServlet类简析(一),主要讲述初始化的操作,本文将简单介绍springmvc如何处理请求 DispatcherServlet#doDi ...

  5. SpringMVC源码情操陶冶-HandlerAdapter适配器简析

    springmvc中对业务的具体处理是通过HandlerAdapter适配器操作的 HandlerAdapter接口方法 列表如下 /** * Given a handler instance, re ...

  6. SpringMVC源码情操陶冶-AbstractUrlHandlerMapping

    承接前文SpringMVC源码情操陶冶-AbstractHandlerMapping,前文主要讲解了如何获取handler处理对象,本文将针对beanName注册为handler对象作下解析 Abst ...

  7. SpringMVC源码情操陶冶-RequestMappingHandlerAdapter适配器

    承接前文SpringMVC源码情操陶冶-HandlerAdapter适配器简析.RequestMappingHandlerAdapter适配器组件是专门处理RequestMappingHandlerM ...

  8. SpringMVC源码情操陶冶-DispatcherServlet

    本文对springmvc核心类DispatcherServlet作下简单的向导,方便博主与读者查阅 DispatcherServlet-继承关系 分析DispatcherServlet的继承关系以及主 ...

  9. SpringMVC源码情操陶冶-AbstractHandlerMethodMapping

    承接前文SpringMVC源码情操陶冶-AbstractHandlerMapping,本文将介绍如何注册HandlerMethod对象作为handler 类结构瞧一瞧 public abstract ...

随机推荐

  1. Myeclipse8.6注册机代码,不用到处找注册机了

    import java.io.*; public class MyEclipseGen { private static final String LL = "Decompiling thi ...

  2. git与github的使用

    git和github是两个完全不同的概念,就好比如雷锋与雷峰塔的关系. git是一个版本管理工具,用来更好的管理你的程序,比如你原来提交过的内容,以后虽然修改了,但是通过git这个工具, 可以把你原来 ...

  3. 移动办公OA系统

    好久没有更新文章了,总觉得心里空空的,最近由于工作的原因,没有来的及及时更新,总感觉应该把学习到的东西做个记录,供大家学习,也供自己复习,温故而知新.今天趁着周末休息时间,把自己最近在公司的做的项目做 ...

  4. Zookeeper:分布式程序的基石

    一.目录 1.zookeeper是什么? 2.安装.配置.启动.监控 3.javaApi基础用法 4.应用场景 5.CAP理论/paxos算法 二.zookeeper简介 官方版:zookeeper是 ...

  5. windbg工具安装配置及dump抓取

    安装与配置windbg 安装与配置windbg的symbol(符号) 第一步 下载WinDBG, 第二步 双击下载的文件安装windbg.安装时注意记住安装到那里了. 第三步 windbg访问符号需要 ...

  6. 搭建开源java博客并通过域名访问

    这个博客系统是王爵在GitHub上开源的,通过简单几步就可以部署成功. 前面几步可以参照如下几个链接: 1.https://www.qcloud.com/community/article/29008 ...

  7. 【Android Developers Training】 26. 在SQL数据库中保存数据

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  8. 抽象工厂模式(Java与Kotlin版)

    前文推送 设计模式 简单工厂模式(Java与Kotlin版) 工厂方法模式(Java与Kotlin版) Kotlin基础知识 Kotlin入门第一课:从对比Java开始 Kotlin入门第二课:集合操 ...

  9. Java 9 揭秘(8. JDK 9重大改变)

    Tips 做一个终身学习的人. 在本章,主要介绍以下内容: 新的JDK版本控制方案是什么 如何使用Runtime.Version类解析JDK版本字符串 JDK JRE 9的新目录布局是什么 JDK 9 ...

  10. ArchSummit全球架构师峰会2017年深圳站 漫谈

    自去年6月跳槽到某CDN厂,从偏向移动端开发又回到了专注后端,关于做一个移动应用独立开发者的计划暂时搁置,但是如马云所讲: "梦想还是要有的,万一实现了呢".去年下半年辛苦加班加点 ...