Freemarker 简介
这是一个相当老牌的开源的免费的模版引擎。通过 Freemarker 模版,我们可以将数据渲染成 HTML 网页、电子邮件、配置文件以及源代码等。Freemarker 不是面向最终用户的,而是一个 Java 类库,我们可以将之作为一个普通的组件嵌入到我们的产品中。
Freemarker 模版后缀为 .ftl(FreeMarker Template Language)。FTL 是一种简单的、专用的语言,它不是像 Java 那样成熟的编程语言。在模板中,你可以专注于如何展现数据, 而在模板之外可以专注于要展示什么数据。
这里先把代码显示咯,再做分析解释
就不从新建项目讲起来了,这样废话就太多了,就是记得加上SpringBoot官方给的Freemarker的依赖。
依赖
新建时,没加也问题不大,我们自己在pom.xml中手动加上就好
org.springframework.boot
spring-boot-starter-freemarker
org.springframework.boot
spring-boot-starter-web
UserController
/**
* @author ZSL
* @ClassName UserController
* @description
* @date 2019/8/6
*/
@Controller
public class UserController {
@GetMapping("/user")
public String getUser( Model model){
User user = new User();
user.setId("zid");
user.setName("zsl");
user.setAge(18);
model.addAttribute("user",user);
return "index";
}
}
在resources目录下的templates文件夹下新建.ftl的FreeMarker文件
这里你直接新建是没有.ftl文件的,你可以通过html文件改后缀也可以直接新定义新建.ftl文件的选项,.ftl和html文件差不多。
Title
${user.id}
${user.name}
${user.age}
相信上面的代码不用注释你能看得懂,都是基础代码。
那么我们来分析一下,我们知道Starter中知道了有个自动装配类xxxAutoConfiguration
的,那么我们来看看在FreeMarker中的自动装配类。
我们从头慢慢来分析
@EnableAutoConfiguration 表示启用 Spring 应用程序上下文的自动配置,该注解会自动导入一个名为 AutoConfigurationImportSelector 的类,而这个类会去读取一个名为 spring.factories 的文件, spring.factories 中则定义需要加载的自动化配置类
没错是的,我们的FreeMarkerAutoConfiguration 自动装配类就是在这个pring.factories文件中。
而这个spring.factories的路径:
org\springframework\boot\spring-boot-autoconfigure\2.1.6.RELEASE\spring-boot-autoconfigure-2.1.6.RELEASE-sources.jar!\META-INF\spring.factories
那么我们来看这个FreeMarkerAutoConfiguration
自动装配类
org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration
/**
* {@link EnableAutoConfiguration Auto-configuration} for FreeMarker.
*
* @author Andy Wilkinson
* @author Dave Syer
* @author Kazuki Shimizu
* @since 1.1.0
*/
@Configuration
@ConditionalOnClass({ freemarker.template.Configuration.class, FreeMarkerConfigurationFactory.class })
@EnableConfigurationProperties(FreeMarkerProperties.class)
@Import({ FreeMarkerServletWebConfiguration.class, FreeMarkerReactiveWebConfiguration.class,
FreeMarkerNonWebConfiguration.class })
public class FreeMarkerAutoConfiguration {
private static final Log logger = LogFactory.getLog(FreeMarkerAutoConfiguration.class);
private final ApplicationContext applicationContext;
private final FreeMarkerProperties properties;
public FreeMarkerAutoConfiguration(ApplicationContext applicationContext, FreeMarkerProperties properties) {
this.applicationContext = applicationContext;
this.properties = properties;
}
@PostConstruct
public void checkTemplateLocationExists() {
if (logger.isWarnEnabled() && this.properties.isCheckTemplateLocation()) {
List locations = getLocations();
if (locations.stream().noneMatch(this::locationExists)) {
logger.warn("Cannot find template location(s): " + locations + " (please add some templates, "
+ "check your FreeMarker configuration, or set "
+ "spring.freemarker.checkTemplateLocation=false)");
}
}
}
private List getLocations() {
List locations = new ArrayList<>();
for (String templateLoaderPath : this.properties.getTemplateLoaderPath()) {
TemplateLocation location = new TemplateLocation(templateLoaderPath);
locations.add(location);
}
return locations;
}
private boolean locationExists(TemplateLocation location) {
return location.exists(this.applicationContext);
}
}
@Configuration这是个配置类
@ConditionalOnClass({ freemarker.template.Configuration.class, FreeMarkerConfigurationFactory.class })
当 classpath 下存在 freemarker.template.Configuration 以及 FreeMarkerConfigurationFactory 时,配置才会生效,也就是说当我们引入了 Freemarker 之后,配置就会生效。你也可以理解为加了相关依赖。
@Import({ FreeMarkerServletWebConfiguration.class, FreeMarkerReactiveWebConfiguration.class,FreeMarkerNonWebConfiguration.class })
FreeMarkerServletWebConfiguration.class在web环境中,,FreeMarkerNonWebConfiguration.class不在web环境中,本地的。
但是这个FreeMarkerAutoConfiguration 自动化配置只做了模板位置检查。
因为这里不是在本地中使用FreeMarker,而是在Web环境中使用,所以配置是导入到了FreeMarkerServletWebConfiguration这个配置中
@Configuration
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnClass({ Servlet.class, FreeMarkerConfigurer.class })
@AutoConfigureAfter(WebMvcAutoConfiguration.class)
class FreeMarkerServletWebConfiguration extends AbstractFreeMarkerConfiguration {
protected FreeMarkerServletWebConfiguration(FreeMarkerProperties properties) {
super(properties);
}
@Bean
@ConditionalOnMissingBean(FreeMarkerConfig.class)
public FreeMarkerConfigurer freeMarkerConfigurer() {
FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
applyProperties(configurer);
return configurer;
}
@Bean
public freemarker.template.Configuration freeMarkerConfiguration(FreeMarkerConfig configurer) {
return configurer.getConfiguration();
}
@Bean
@ConditionalOnMissingBean(name = "freeMarkerViewResolver")
@ConditionalOnProperty(name = "spring.freemarker.enabled", matchIfMissing = true)
public FreeMarkerViewResolver freeMarkerViewResolver() {
FreeMarkerViewResolver resolver = new FreeMarkerViewResolver();
getProperties().applyToMvcViewResolver(resolver);
return resolver;
}
@Bean
@ConditionalOnEnabledResourceChain
@ConditionalOnMissingFilterBean(ResourceUrlEncodingFilter.class)
public FilterRegistrationBean resourceUrlEncodingFilter() {
FilterRegistrationBean registration = new FilterRegistrationBean<>(
new ResourceUrlEncodingFilter());
registration.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ERROR);
return registration;
}
}
2.@ConditionalOnWebApplication 表示当前配置在 web 环境下才会生效。
3.ConditionalOnClass 表示当前配置在存在 Servlet 和 FreeMarkerConfigurer 时才会生效。
4.@AutoConfigureAfter 表示当前自动化配置在 WebMvcAutoConfiguration 之后完成。
那么我们来看这个applyProperties
protected void applyProperties(FreeMarkerConfigurationFactory factory) {
factory.setTemplateLoaderPaths(this.properties.getTemplateLoaderPath());
factory.setPreferFileSystemAccess(this.properties.isPreferFileSystemAccess());
factory.setDefaultEncoding(this.properties.getCharsetName());
Properties settings = new Properties();
settings.putAll(this.properties.getSettings());
factory.setFreemarkerSettings(settings);
}
这里的applyProperties的参数是一个FreeMarkerConfigurationFactory
而我们传过来的是FreeMarkerConfigurer,查看它们的继承关系
在applyProperties中,通过在FreeMarkerConfigurationFactory中,从配置文件中获取配置信息来进行装配的。
FreeMarkerConfigurer 是 Freemarker 的一些基本配置,例如 templateLoaderPath、defaultEncoding 等
freeMarkerViewResolver方法分析
@Bean
@ConditionalOnMissingBean(name = "freeMarkerViewResolver")
@ConditionalOnProperty(name = "spring.freemarker.enabled", matchIfMissing = true)
public FreeMarkerViewResolver freeMarkerViewResolver() {
FreeMarkerViewResolver resolver = new FreeMarkerViewResolver();
getProperties().applyToMvcViewResolver(resolver);
return resolver;
}
3.FreeMarkerViewResolver 则是视图解析器的基本配置,包含了viewClass、suffix、allowRequestOverride、allowSessionOverride 等属性。
FreeMarkerServletWebConfiguration的构造方法
protected FreeMarkerServletWebConfiguration(FreeMarkerProperties properties) {
super(properties);
}
查看FreeMarkerProperties 这个类
@ConfigurationProperties(prefix = "spring.freemarker")
public class FreeMarkerProperties extends AbstractTemplateViewResolverProperties {
public static final String DEFAULT_TEMPLATE_LOADER_PATH = "classpath:/templates/";
public static final String DEFAULT_PREFIX = "";
public static final String DEFAULT_SUFFIX = ".ftl";
/**
* Well-known FreeMarker keys which are passed to FreeMarker's Configuration.
*/
private Map settings = new HashMap<>();
/**
* Comma-separated list of template paths.
*/
private String[] templateLoaderPath = new String[] { DEFAULT_TEMPLATE_LOADER_PATH };
/**
* Whether to prefer file system access for template loading. File system access
* enables hot detection of template changes.
*/
private boolean preferFileSystemAccess = true;
public FreeMarkerProperties() {
super(DEFAULT_PREFIX, DEFAULT_SUFFIX);
}
public Map getSettings() {
return this.settings;
}
public void setSettings(Map settings) {
this.settings = settings;
}
public String[] getTemplateLoaderPath() {
return this.templateLoaderPath;
}
public boolean isPreferFileSystemAccess() {
return this.preferFileSystemAccess;
}
public void setPreferFileSystemAccess(boolean preferFileSystemAccess) {
this.preferFileSystemAccess = preferFileSystemAccess;
}
public void setTemplateLoaderPath(String... templateLoaderPaths) {
this.templateLoaderPath = templateLoaderPaths;
}
}
看到这个类的很多东西是不是很眼熟了?
这里的类名都是看到类名就知道它的作用的,就不一一去说了,太多废话了。
在 SSM 的 XML 文件中配置 Freemarker ,也是配置这些东西。只不过这些配置在SpringBoot中有自动装配类 FreeMarkerServletWebConfiguration 帮我们完成了。