Spring Framework integration support for caching (Caffeine, JCache), mail (JavaMail), scheduling (Quartz), and template engines (FreeMarker)
—
Spring Context Support provides comprehensive FreeMarker template engine integration for server-side rendering. It offers factory beans for configuration, utilities for template processing, and Spring Resource-based template loading for both web and non-web contexts.
Factory for creating and configuring FreeMarker Configuration instances with Spring integration.
/**
* Factory for creating FreeMarker Configuration instances with Spring integration
*/
public class FreeMarkerConfigurationFactory {
/**
* Set the location of the FreeMarker config file
* @param resource the config file resource
*/
public void setConfigLocation(Resource resource);
/**
* Set FreeMarker settings as Properties
* @param settings the FreeMarker settings
*/
public void setFreemarkerSettings(Properties settings);
/**
* Set FreeMarker variables (objects available in all templates)
* @param variables map of variable names to objects
*/
public void setFreemarkerVariables(Map<String, Object> variables);
/**
* Set the default encoding for templates
* @param defaultEncoding the default encoding (e.g., "UTF-8")
*/
public void setDefaultEncoding(String defaultEncoding);
/**
* Set the default charset for templates
* @param defaultCharset the default charset
*/
public void setDefaultCharset(Charset defaultCharset);
/**
* Set template loaders that will be used before default loaders
* @param preTemplateLoaders template loaders to register first
*/
public void setPreTemplateLoaders(TemplateLoader... preTemplateLoaders);
/**
* Set template loaders that will be used after default loaders
* @param postTemplateLoaders template loaders to register last
*/
public void setPostTemplateLoaders(TemplateLoader... postTemplateLoaders);
/**
* Set the template loader path (single path)
* @param templateLoaderPath the template loader path
*/
public void setTemplateLoaderPath(String templateLoaderPath);
/**
* Set multiple template loader paths
* @param templateLoaderPaths array of template loader paths
*/
public void setTemplateLoaderPaths(String... templateLoaderPaths);
/**
* Set the ResourceLoader for template loading
* @param resourceLoader the ResourceLoader to use
*/
public void setResourceLoader(ResourceLoader resourceLoader);
/**
* Set whether to prefer file system access for template loading
* @param preferFileSystemAccess whether to prefer file system access
*/
public void setPreferFileSystemAccess(boolean preferFileSystemAccess);
/**
* Create the FreeMarker Configuration instance
* @return the configured Configuration
* @throws IOException if configuration fails
* @throws TemplateException if template configuration fails
*/
public Configuration createConfiguration() throws IOException, TemplateException;
/**
* Get the ResourceLoader (available after ApplicationContext injection)
* @return the ResourceLoader
*/
protected ResourceLoader getResourceLoader();
}Usage Examples:
@Configuration
public class FreeMarkerConfig {
@Bean
public FreeMarkerConfigurationFactory freeMarkerConfigurationFactory() {
FreeMarkerConfigurationFactory factory = new FreeMarkerConfigurationFactory();
factory.setTemplateLoaderPaths(
"classpath:/templates/",
"file:/opt/app/templates/"
);
factory.setDefaultEncoding("UTF-8");
Properties settings = new Properties();
settings.setProperty("number_format", "0.##");
settings.setProperty("date_format", "yyyy-MM-dd");
settings.setProperty("time_format", "HH:mm:ss");
settings.setProperty("datetime_format", "yyyy-MM-dd HH:mm:ss");
factory.setFreemarkerSettings(settings);
// Global variables available in all templates
Map<String, Object> variables = new HashMap<>();
variables.put("app", applicationProperties);
variables.put("utils", templateUtils);
factory.setFreemarkerVariables(variables);
return factory;
}
@Bean
public Configuration freeMarkerConfiguration() throws Exception {
return freeMarkerConfigurationFactory().createConfiguration();
}
}FactoryBean wrapper around FreeMarkerConfigurationFactory for easier Spring integration.
/**
* FactoryBean wrapper around FreeMarkerConfigurationFactory
*/
public class FreeMarkerConfigurationFactoryBean extends FreeMarkerConfigurationFactory
implements FactoryBean<Configuration>, InitializingBean, ResourceLoaderAware {
/**
* Get the FreeMarker Configuration instance
* @return the Configuration instance
* @throws Exception if creation fails
*/
@Override
public Configuration getObject() throws Exception;
/**
* Get the object type
* @return Configuration.class
*/
@Override
public Class<Configuration> getObjectType();
/**
* Check if this factory returns singletons
* @return true (singleton)
*/
@Override
public boolean isSingleton();
/**
* Initialize the factory bean
* @throws IOException if initialization fails
* @throws TemplateException if template configuration fails
*/
@Override
public void afterPropertiesSet() throws IOException, TemplateException;
/**
* Set the ResourceLoader for template loading
* @param resourceLoader the ResourceLoader
*/
@Override
public void setResourceLoader(ResourceLoader resourceLoader);
}Usage Examples:
@Configuration
public class FreeMarkerFactoryBeanConfig {
@Bean
public FreeMarkerConfigurationFactoryBean freeMarkerConfigurationFactoryBean() {
FreeMarkerConfigurationFactoryBean factoryBean = new FreeMarkerConfigurationFactoryBean();
factoryBean.setTemplateLoaderPath("classpath:/templates/");
factoryBean.setDefaultEncoding("UTF-8");
Properties settings = new Properties();
settings.setProperty("template_update_delay", "0"); // Development setting
settings.setProperty("default_encoding", "UTF-8");
settings.setProperty("output_encoding", "UTF-8");
settings.setProperty("locale", "en_US");
factoryBean.setFreemarkerSettings(settings);
return factoryBean;
}
}Utility methods for processing FreeMarker templates in various contexts.
/**
* Utility methods for processing FreeMarker templates
*/
public abstract class FreeMarkerTemplateUtils {
/**
* Process a FreeMarker template into a String
* @param template the FreeMarker Template
* @param model the template model (data)
* @return the processed template as String
* @throws IOException if template processing fails
* @throws TemplateException if template has errors
*/
public static String processTemplateIntoString(Template template, Object model)
throws IOException, TemplateException;
}Usage Examples:
@Service
public class ReportService {
@Autowired
private Configuration freeMarkerConfiguration;
public String generateReport(String templateName, Map<String, Object> data) {
try {
Template template = freeMarkerConfiguration.getTemplate(templateName + ".ftl");
return FreeMarkerTemplateUtils.processTemplateIntoString(template, data);
} catch (IOException | TemplateException e) {
throw new RuntimeException("Failed to generate report", e);
}
}
public String generateLocalizedReport(String templateName, Map<String, Object> data, Locale locale) {
try {
Template template = freeMarkerConfiguration.getTemplate(templateName + ".ftl", locale);
return FreeMarkerTemplateUtils.processTemplateIntoString(template, data);
} catch (IOException | TemplateException e) {
throw new RuntimeException("Failed to generate localized report", e);
}
}
}
@RestController
public class TemplateController {
@Autowired
private ReportService reportService;
@GetMapping("/reports/{type}")
public ResponseEntity<String> generateReport(@PathVariable String type) {
Map<String, Object> data = new HashMap<>();
data.put("reportDate", new Date());
data.put("title", "Monthly Report");
data.put("items", getReportItems());
String html = reportService.generateReport(type, data);
return ResponseEntity.ok()
.contentType(MediaType.TEXT_HTML)
.body(html);
}
}FreeMarker TemplateLoader implementation that loads templates from Spring Resource locations.
/**
* FreeMarker TemplateLoader that loads templates from Spring Resource locations
*/
public class SpringTemplateLoader implements TemplateLoader {
/**
* Create a SpringTemplateLoader with a ResourceLoader and template path
* @param resourceLoader the ResourceLoader to use
* @param templateLoaderPath the base path for templates
*/
public SpringTemplateLoader(ResourceLoader resourceLoader, String templateLoaderPath);
/**
* Find a template source
* @param name the template name
* @return the template source object
* @throws IOException if template loading fails
*/
@Override
public Object findTemplateSource(String name) throws IOException;
/**
* Get the last modified time of a template
* @param templateSource the template source
* @return the last modified time
*/
@Override
public long getLastModified(Object templateSource);
/**
* Get a Reader for the template content
* @param templateSource the template source
* @param encoding the character encoding
* @return a Reader for the template
* @throws IOException if reading fails
*/
@Override
public Reader getReader(Object templateSource, String encoding) throws IOException;
/**
* Close a template source
* @param templateSource the template source to close
* @throws IOException if closing fails
*/
@Override
public void closeTemplateSource(Object templateSource) throws IOException;
}Complete configuration examples for different use cases.
@Configuration
public class AdvancedFreeMarkerConfig {
/**
* Configuration for web applications with multiple template locations
*/
@Bean
@Profile("web")
public FreeMarkerConfigurationFactoryBean webFreeMarkerConfig() {
FreeMarkerConfigurationFactoryBean factoryBean = new FreeMarkerConfigurationFactoryBean();
// Multiple template locations
factoryBean.setTemplateLoaderPaths(
"classpath:/templates/", // Built-in templates
"classpath:/templates/mail/", // Email templates
"file:/opt/app/custom-templates/" // Custom templates
);
factoryBean.setDefaultEncoding("UTF-8");
factoryBean.setPreferFileSystemAccess(false); // For WAR deployment
Properties settings = new Properties();
// Development settings
settings.setProperty("template_update_delay", "0");
settings.setProperty("template_exception_handler", "html_debug");
// Production settings would use:
// settings.setProperty("template_update_delay", "60000");
// settings.setProperty("template_exception_handler", "ignore");
settings.setProperty("default_encoding", "UTF-8");
settings.setProperty("output_encoding", "UTF-8");
settings.setProperty("auto_flush", "true");
settings.setProperty("url_escaping_charset", "UTF-8");
factoryBean.setFreemarkerSettings(settings);
// Global variables
Map<String, Object> variables = new HashMap<>();
variables.put("contextPath", "${request.contextPath}");
variables.put("springMacroRequestContext", new RequestContextHashModel());
factoryBean.setFreemarkerVariables(variables);
return factoryBean;
}
/**
* Configuration for standalone/batch applications
*/
@Bean
@Profile("batch")
public FreeMarkerConfigurationFactoryBean batchFreeMarkerConfig() {
FreeMarkerConfigurationFactoryBean factoryBean = new FreeMarkerConfigurationFactoryBean();
factoryBean.setTemplateLoaderPaths(
"classpath:/batch-templates/",
"file:/data/templates/"
);
factoryBean.setDefaultEncoding("UTF-8");
factoryBean.setPreferFileSystemAccess(true); // Better performance for file system
Properties settings = new Properties();
settings.setProperty("template_update_delay", "300000"); // 5 minutes
settings.setProperty("template_exception_handler", "rethrow");
settings.setProperty("arithmetic_engine", "bigdecimal");
settings.setProperty("number_format", "0.####");
settings.setProperty("boolean_format", "yes,no");
factoryBean.setFreemarkerSettings(settings);
return factoryBean;
}
/**
* Service for template-based document generation
*/
@Service
public static class DocumentGenerationService {
@Autowired
private Configuration freeMarkerConfiguration;
public void generateDocument(String templateName, Map<String, Object> data, OutputStream output)
throws IOException, TemplateException {
Template template = freeMarkerConfiguration.getTemplate(templateName);
try (Writer writer = new OutputStreamWriter(output, StandardCharsets.UTF_8)) {
template.process(data, writer);
writer.flush();
}
}
public void generatePdfReport(String templateName, Map<String, Object> data, String outputPath)
throws IOException, TemplateException {
// Generate HTML from template
String html = generateHtmlFromTemplate(templateName, data);
// Convert to PDF (using a library like Flying Saucer or similar)
convertHtmlToPdf(html, outputPath);
}
private String generateHtmlFromTemplate(String templateName, Map<String, Object> data)
throws IOException, TemplateException {
Template template = freeMarkerConfiguration.getTemplate(templateName);
return FreeMarkerTemplateUtils.processTemplateIntoString(template, data);
}
}
}Integration with Spring's web context and request handling.
/**
* Utility methods for integrating FreeMarker with Spring web contexts
*/
public abstract class RequestContextUtils {
/**
* Get the current request context for use in templates
* @param request the HTTP request
* @param response the HTTP response
* @param servletContext the servlet context
* @return RequestContext for template use
*/
public static RequestContext getRequestContext(HttpServletRequest request,
HttpServletResponse response, ServletContext servletContext);
}
/**
* Hash model for exposing Spring RequestContext in FreeMarker templates
*/
public class RequestContextHashModel implements TemplateHashModel {
/**
* Create a RequestContextHashModel for the current request
*/
public RequestContextHashModel();
/**
* Create a RequestContextHashModel for a specific request
* @param request the HTTP request
* @param response the HTTP response
*/
public RequestContextHashModel(HttpServletRequest request, HttpServletResponse response);
@Override
public TemplateModel get(String key) throws TemplateModelException;
@Override
public boolean isEmpty() throws TemplateModelException;
}For Spring Boot applications, FreeMarker auto-configuration is available:
spring:
freemarker:
template-loader-path: classpath:/templates/
suffix: .ftl
cache: true
charset: UTF-8
check-template-location: true
content-type: text/html
expose-request-attributes: false
expose-session-attributes: false
expose-spring-macro-helpers: true
prefer-file-system-access: true
settings:
template_update_delay: 0
default_encoding: UTF-8
output_encoding: UTF-8
locale: en_US
date_format: yyyy-MM-dd
time_format: HH:mm:ss
datetime_format: yyyy-MM-dd HH:mm:ssThe Spring Context Support FreeMarker integration provides the foundation for this auto-configuration and can be used directly in non-Boot applications or for advanced customization scenarios.
Install with Tessl CLI
npx tessl i tessl/maven-org-springframework--spring-context-support