Add FreeMarker support
This commit adds auto-configuration and a starter, spring-boot-starter-freemarker, for using FreeMarker view templates in a web application. A new abstraction, TemplateAvailabilityProvider, has been introduced. This decouples ErrorMvcAutoConfiguration from the various view technologies that Spring Boot now supports, allowing it to determine when a custom error template is provided without knowing the details of each view technology. Closes #679
This commit is contained in:
parent
1143f6dbb1
commit
7c91176186
|
@ -66,6 +66,11 @@
|
|||
<artifactId>jetty-webapp</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.freemarker</groupId>
|
||||
<artifactId>freemarker</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hibernate</groupId>
|
||||
<artifactId>hibernate-entitymanager</artifactId>
|
||||
|
|
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
* Copyright 2012-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.autoconfigure.freemarker;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.servlet.Servlet;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration;
|
||||
import org.springframework.boot.bind.RelaxedPropertyResolver;
|
||||
import org.springframework.context.EnvironmentAware;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
|
||||
import org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for FreeMarker.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass(freemarker.template.Configuration.class)
|
||||
@ConditionalOnWebApplication
|
||||
@AutoConfigureAfter(WebMvcAutoConfiguration.class)
|
||||
public class FreeMarkerAutoConfiguration {
|
||||
|
||||
public static final String DEFAULT_TEMPLATE_LOADER_PATH = "classpath:/templates/";
|
||||
|
||||
public static final String DEFAULT_PREFIX = "";
|
||||
|
||||
public static final String DEFAULT_SUFFIX = ".ftl";
|
||||
|
||||
@Configuration
|
||||
public static class FreemarkerConfigurerConfiguration implements EnvironmentAware {
|
||||
|
||||
@Autowired
|
||||
private final ResourceLoader resourceLoader = new DefaultResourceLoader();
|
||||
|
||||
private RelaxedPropertyResolver environment;
|
||||
|
||||
@Override
|
||||
public void setEnvironment(Environment environment) {
|
||||
this.environment = new RelaxedPropertyResolver(environment,
|
||||
"spring.freeMarker.");
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void checkTemplateLocationExists() {
|
||||
Boolean checkTemplateLocation = this.environment.getProperty(
|
||||
"checkTemplateLocation", Boolean.class, true);
|
||||
if (checkTemplateLocation) {
|
||||
Resource resource = this.resourceLoader.getResource(this.environment
|
||||
.getProperty("templateLoaderPath", DEFAULT_TEMPLATE_LOADER_PATH));
|
||||
Assert.state(resource.exists(), "Cannot find template location: "
|
||||
+ resource + " (please add some templates "
|
||||
+ "or check your FreeMarker configuration)");
|
||||
}
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(name = "freeMarkerConfigurer")
|
||||
public FreeMarkerConfigurer freeMarkerConfigurer() {
|
||||
FreeMarkerConfigurer freeMarkerConfigurer = new FreeMarkerConfigurer();
|
||||
freeMarkerConfigurer.setTemplateLoaderPath(this.environment.getProperty(
|
||||
"templateLoaderPath", DEFAULT_TEMPLATE_LOADER_PATH));
|
||||
freeMarkerConfigurer.setDefaultEncoding(this.environment.getProperty(
|
||||
"templateEncoding", "UTF-8"));
|
||||
Map<String, Object> settingsMap = this.environment
|
||||
.getSubProperties("settings.");
|
||||
Properties settings = new Properties();
|
||||
settings.putAll(settingsMap);
|
||||
freeMarkerConfigurer.setFreemarkerSettings(settings);
|
||||
return freeMarkerConfigurer;
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass(Servlet.class)
|
||||
public static class FreemarkerViewResolverConfiguration implements EnvironmentAware {
|
||||
|
||||
private RelaxedPropertyResolver environment;
|
||||
|
||||
@Override
|
||||
public void setEnvironment(Environment environment) {
|
||||
this.environment = new RelaxedPropertyResolver(environment,
|
||||
"spring.freeMarker.");
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(name = "freeMarkerViewResolver")
|
||||
public FreeMarkerViewResolver freeMarkerViewResolver() {
|
||||
FreeMarkerViewResolver resolver = new FreeMarkerViewResolver();
|
||||
resolver.setPrefix(this.environment.getProperty("prefix", DEFAULT_PREFIX));
|
||||
resolver.setSuffix(this.environment.getProperty("suffix", DEFAULT_SUFFIX));
|
||||
resolver.setCache(this.environment.getProperty("cache", Boolean.class, true));
|
||||
resolver.setContentType(this.environment.getProperty("contentType",
|
||||
"text/html"));
|
||||
resolver.setViewNames(this.environment.getProperty("viewNames",
|
||||
String[].class));
|
||||
resolver.setExposeRequestAttributes(this.environment.getProperty(
|
||||
"exposeRequestAttributes", Boolean.class, false));
|
||||
resolver.setAllowRequestOverride(this.environment.getProperty(
|
||||
"allowRequestOverride", Boolean.class, false));
|
||||
resolver.setExposeSessionAttributes(this.environment.getProperty(
|
||||
"exposeSessionAttributes", Boolean.class, false));
|
||||
resolver.setAllowSessionOverride(this.environment.getProperty(
|
||||
"allowSessionOverride", Boolean.class, false));
|
||||
resolver.setExposeSpringMacroHelpers(this.environment.getProperty(
|
||||
"exposeSpringMacroHelpers", Boolean.class, false));
|
||||
resolver.setRequestContextAttribute(this.environment
|
||||
.getProperty("requestContextAttribute"));
|
||||
|
||||
// This resolver acts as a fallback resolver (e.g. like a
|
||||
// InternalResourceViewResolver) so it needs to have low precedence
|
||||
resolver.setOrder(Ordered.LOWEST_PRECEDENCE - 5);
|
||||
|
||||
return resolver;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright 2012-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.autoconfigure.freemarker;
|
||||
|
||||
import org.springframework.boot.web.TemplateAvailabilityProvider;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* {@link TemplateAvailabilityProvider} that provides availability information
|
||||
* for FreeMarker view templates
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*
|
||||
*/
|
||||
public class FreeMarkerTemplateAvailabilityProvider implements
|
||||
TemplateAvailabilityProvider {
|
||||
|
||||
@Override
|
||||
public boolean isTemplateAvailable(String view, Environment environment,
|
||||
ClassLoader classLoader, ResourceLoader resourceLoader) {
|
||||
if (ClassUtils.isPresent("freemarker.template.Configuration",
|
||||
classLoader)) {
|
||||
String loaderPath = environment.getProperty("spring.freemarker.templateLoaderPath",
|
||||
FreeMarkerAutoConfiguration.DEFAULT_TEMPLATE_LOADER_PATH);
|
||||
String prefix = environment.getProperty("spring.freemarker.prefix",
|
||||
FreeMarkerAutoConfiguration.DEFAULT_PREFIX);
|
||||
String suffix = environment.getProperty("spring.freemarker.suffix",
|
||||
FreeMarkerAutoConfiguration.DEFAULT_SUFFIX);
|
||||
return resourceLoader.getResource(loaderPath + prefix + view + suffix).exists();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -50,8 +50,9 @@ import org.thymeleaf.templateresolver.TemplateResolver;
|
|||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for Thymeleaf.
|
||||
*
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass(SpringTemplateEngine.class)
|
||||
|
@ -108,16 +109,6 @@ public class ThymeleafAutoConfiguration {
|
|||
protected SpringResourceResourceResolver thymeleafResourceResolver() {
|
||||
return new SpringResourceResourceResolver();
|
||||
}
|
||||
|
||||
public static boolean templateExists(Environment environment,
|
||||
ResourceLoader resourceLoader, String view) {
|
||||
String prefix = environment.getProperty("spring.thymeleaf.prefix",
|
||||
ThymeleafAutoConfiguration.DEFAULT_PREFIX);
|
||||
String suffix = environment.getProperty("spring.thymeleaf.suffix",
|
||||
ThymeleafAutoConfiguration.DEFAULT_SUFFIX);
|
||||
return resourceLoader.getResource(prefix + view + suffix).exists();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright 2012-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.autoconfigure.thymeleaf;
|
||||
|
||||
import org.springframework.boot.web.TemplateAvailabilityProvider;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* {@link TemplateAvailabilityProvider} that provides availability information for
|
||||
* Thymeleaf view templates
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*
|
||||
*/
|
||||
public class ThymeleafTemplateAvailabilityProvider implements
|
||||
TemplateAvailabilityProvider {
|
||||
|
||||
@Override
|
||||
public boolean isTemplateAvailable(String view, Environment environment,
|
||||
ClassLoader classLoader, ResourceLoader resourceLoader) {
|
||||
if (ClassUtils.isPresent("org.thymeleaf.spring4.SpringTemplateEngine",
|
||||
classLoader)) {
|
||||
String prefix = environment.getProperty("spring.thymeleaf.prefix",
|
||||
ThymeleafAutoConfiguration.DEFAULT_PREFIX);
|
||||
String suffix = environment.getProperty("spring.thymeleaf.suffix",
|
||||
ThymeleafAutoConfiguration.DEFAULT_SUFFIX);
|
||||
return resourceLoader.getResource(prefix + view + suffix).exists();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -17,6 +17,7 @@
|
|||
package org.springframework.boot.autoconfigure.web;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.Servlet;
|
||||
|
@ -33,21 +34,21 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
|
|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.autoconfigure.condition.SearchStrategy;
|
||||
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
|
||||
import org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration.DefaultTemplateResolverConfiguration;
|
||||
import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
|
||||
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
|
||||
import org.springframework.boot.context.embedded.ErrorPage;
|
||||
import org.springframework.boot.web.TemplateAvailabilityProvider;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.expression.MapAccessor;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.PropertyPlaceholderHelper;
|
||||
import org.springframework.util.PropertyPlaceholderHelper.PlaceholderResolver;
|
||||
import org.springframework.web.servlet.DispatcherServlet;
|
||||
|
@ -57,8 +58,9 @@ import org.springframework.web.servlet.view.BeanNameViewResolver;
|
|||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} to render errors via a MVC error
|
||||
* controller.
|
||||
*
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class })
|
||||
@ConditionalOnWebApplication
|
||||
|
@ -116,22 +118,20 @@ public class ErrorMvcAutoConfiguration implements EmbeddedServletContainerCustom
|
|||
@Override
|
||||
public ConditionOutcome getMatchOutcome(ConditionContext context,
|
||||
AnnotatedTypeMetadata metadata) {
|
||||
if (ClassUtils.isPresent("org.thymeleaf.spring4.SpringTemplateEngine",
|
||||
context.getClassLoader())) {
|
||||
if (DefaultTemplateResolverConfiguration.templateExists(
|
||||
context.getEnvironment(), context.getResourceLoader(), "error")) {
|
||||
return ConditionOutcome
|
||||
.noMatch("Thymeleaf template found for error view");
|
||||
List<TemplateAvailabilityProvider> availabilityProviders = SpringFactoriesLoader
|
||||
.loadFactories(TemplateAvailabilityProvider.class,
|
||||
context.getClassLoader());
|
||||
|
||||
for (TemplateAvailabilityProvider availabilityProvider : availabilityProviders) {
|
||||
if (availabilityProvider.isTemplateAvailable("error",
|
||||
context.getEnvironment(), context.getClassLoader(),
|
||||
context.getResourceLoader())) {
|
||||
return ConditionOutcome.noMatch("Template from "
|
||||
+ availabilityProvider + " found for error view");
|
||||
}
|
||||
}
|
||||
if (ClassUtils.isPresent("org.apache.jasper.compiler.JspConfig",
|
||||
context.getClassLoader())) {
|
||||
if (WebMvcAutoConfiguration.templateExists(context.getEnvironment(),
|
||||
context.getResourceLoader(), "error")) {
|
||||
return ConditionOutcome.noMatch("JSP template found for error view");
|
||||
}
|
||||
}
|
||||
return ConditionOutcome.match("no error template view detected");
|
||||
|
||||
return ConditionOutcome.match("No error template view detected");
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright 2012-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.autoconfigure.web;
|
||||
|
||||
import org.springframework.boot.web.TemplateAvailabilityProvider;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
public class JspTemplateAvailabilityProvider implements TemplateAvailabilityProvider {
|
||||
|
||||
@Override
|
||||
public boolean isTemplateAvailable(String view, Environment environment,
|
||||
ClassLoader classLoader, ResourceLoader resourceLoader) {
|
||||
if (ClassUtils.isPresent("org.apache.jasper.compiler.JspConfig", classLoader)) {
|
||||
String prefix = environment.getProperty("spring.view.prefix",
|
||||
WebMvcAutoConfiguration.DEFAULT_PREFIX);
|
||||
String suffix = environment.getProperty("spring.view.suffix",
|
||||
WebMvcAutoConfiguration.DEFAULT_SUFFIX);
|
||||
return resourceLoader.getResource(prefix + view + suffix).exists();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -43,7 +43,6 @@ import org.springframework.core.Ordered;
|
|||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.core.convert.converter.GenericConverter;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
|
@ -75,6 +74,7 @@ import org.springframework.web.servlet.view.InternalResourceViewResolver;
|
|||
*
|
||||
* @author Phillip Webb
|
||||
* @author Dave Syer
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnWebApplication
|
||||
|
@ -118,15 +118,6 @@ public class WebMvcAutoConfiguration {
|
|||
return new HiddenHttpMethodFilter();
|
||||
}
|
||||
|
||||
public static boolean templateExists(Environment environment,
|
||||
ResourceLoader resourceLoader, String view) {
|
||||
String prefix = environment.getProperty("spring.view.prefix",
|
||||
WebMvcAutoConfiguration.DEFAULT_PREFIX);
|
||||
String suffix = environment.getProperty("spring.view.suffix",
|
||||
WebMvcAutoConfiguration.DEFAULT_SUFFIX);
|
||||
return resourceLoader.getResource(prefix + view + suffix).exists();
|
||||
}
|
||||
|
||||
// Defined as a nested config to ensure WebMvcConfigurerAdapter it not read when not
|
||||
// on the classpath
|
||||
@Configuration
|
||||
|
|
|
@ -11,9 +11,9 @@ org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration,\
|
|||
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.data.JpaRepositoriesAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.data.MongoRepositoriesAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.redis.RedisAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.jms.JmsTemplateAutoConfiguration,\
|
||||
|
@ -23,6 +23,7 @@ org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\
|
|||
org.springframework.boot.autoconfigure.mongo.MongoDataAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.reactor.ReactorAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.redis.RedisAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.security.FallbackWebSecurityAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\
|
||||
|
@ -34,3 +35,10 @@ org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguratio
|
|||
org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.websocket.WebSocketAutoConfiguration
|
||||
|
||||
# Template availability providers
|
||||
org.springframework.boot.web.TemplateAvailabilityProvider=\
|
||||
org.springframework.boot.autoconfigure.freemarker.FreeMarkerTemplateAvailabilityProvider,\
|
||||
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafTemplateAvailabilityProvider,\
|
||||
org.springframework.boot.autoconfigure.web.JspTemplateAvailabilityProvider
|
||||
|
||||
|
|
|
@ -0,0 +1,205 @@
|
|||
/*
|
||||
* Copyright 2012-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.autoconfigure.freemarker;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.boot.test.EnvironmentTestUtils;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.mock.web.MockServletContext;
|
||||
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
|
||||
import org.springframework.web.servlet.View;
|
||||
import org.springframework.web.servlet.support.RequestContext;
|
||||
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
|
||||
import org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Tests for {@link FreeMarkerAutoConfiguration}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class FreeMarkerAutoConfigurationTests {
|
||||
|
||||
private AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
|
||||
|
||||
@Before
|
||||
public void registerServletContext() {
|
||||
MockServletContext servletContext = new MockServletContext();
|
||||
this.context.setServletContext(servletContext);
|
||||
}
|
||||
|
||||
@After
|
||||
public void close() {
|
||||
if (this.context != null) {
|
||||
this.context.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultConfiguration() {
|
||||
this.context.register(FreeMarkerAutoConfiguration.class);
|
||||
this.context.refresh();
|
||||
|
||||
assertNotNull(this.context.getBean(FreeMarkerViewResolver.class));
|
||||
|
||||
assertNotNull(this.context.getBean(FreeMarkerConfigurer.class));
|
||||
}
|
||||
|
||||
@Test(expected = BeanCreationException.class)
|
||||
public void nonExistentTemplateLocation() {
|
||||
EnvironmentTestUtils.addEnvironment(this.context,
|
||||
"spring.freemarker.templateLoaderPath:classpath:/does-not-exist/");
|
||||
|
||||
this.context.register(FreeMarkerAutoConfiguration.class);
|
||||
this.context.refresh();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void emptyTemplateLocation() {
|
||||
new File("target/test-classes/templates/empty-directory").mkdir();
|
||||
|
||||
EnvironmentTestUtils
|
||||
.addEnvironment(this.context,
|
||||
"spring.freemarker.templateLoaderPath:classpath:/templates/empty-directory/");
|
||||
|
||||
this.context.register(FreeMarkerAutoConfiguration.class);
|
||||
this.context.refresh();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultViewResolution() throws Exception {
|
||||
this.context.register(FreeMarkerAutoConfiguration.class);
|
||||
this.context.refresh();
|
||||
|
||||
MockHttpServletResponse response = render("home");
|
||||
String result = response.getContentAsString();
|
||||
|
||||
assertTrue("Wrong output: " + result, result.contains("home"));
|
||||
assertEquals("text/html", response.getContentType());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customContentType() throws Exception {
|
||||
EnvironmentTestUtils.addEnvironment(this.context,
|
||||
"spring.freemarker.contentType:application/json");
|
||||
|
||||
this.context.register(FreeMarkerAutoConfiguration.class);
|
||||
this.context.refresh();
|
||||
|
||||
MockHttpServletResponse response = render("home");
|
||||
String result = response.getContentAsString();
|
||||
|
||||
assertTrue("Wrong output: " + result, result.contains("home"));
|
||||
assertEquals("application/json", response.getContentType());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customPrefix() throws Exception {
|
||||
EnvironmentTestUtils.addEnvironment(this.context,
|
||||
"spring.freemarker.prefix:prefix/");
|
||||
|
||||
this.context.register(FreeMarkerAutoConfiguration.class);
|
||||
this.context.refresh();
|
||||
|
||||
MockHttpServletResponse response = render("prefixed");
|
||||
String result = response.getContentAsString();
|
||||
|
||||
assertTrue("Wrong output: " + result, result.contains("prefixed"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customSuffix() throws Exception {
|
||||
EnvironmentTestUtils.addEnvironment(this.context,
|
||||
"spring.freemarker.suffix:.freemarker");
|
||||
|
||||
this.context.register(FreeMarkerAutoConfiguration.class);
|
||||
this.context.refresh();
|
||||
|
||||
MockHttpServletResponse response = render("suffixed");
|
||||
String result = response.getContentAsString();
|
||||
|
||||
assertTrue("Wrong output: " + result, result.contains("suffixed"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customTemplateLoaderPath() throws Exception {
|
||||
EnvironmentTestUtils.addEnvironment(this.context,
|
||||
"spring.freemarker.templateLoaderPath:classpath:/custom-templates/");
|
||||
|
||||
this.context.register(FreeMarkerAutoConfiguration.class);
|
||||
this.context.refresh();
|
||||
|
||||
MockHttpServletResponse response = render("custom");
|
||||
String result = response.getContentAsString();
|
||||
|
||||
assertTrue("Wrong output: " + result, result.contains("custom"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void disableCache() {
|
||||
EnvironmentTestUtils
|
||||
.addEnvironment(this.context, "spring.freemarker.cache:false");
|
||||
|
||||
this.context.register(FreeMarkerAutoConfiguration.class);
|
||||
this.context.refresh();
|
||||
|
||||
assertEquals(0, this.context.getBean(FreeMarkerViewResolver.class)
|
||||
.getCacheLimit());
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Test
|
||||
public void customFreeMarkerSettings() {
|
||||
EnvironmentTestUtils.addEnvironment(this.context,
|
||||
"spring.freemarker.settings.boolean_format:yup,nope");
|
||||
|
||||
this.context.register(FreeMarkerAutoConfiguration.class);
|
||||
this.context.refresh();
|
||||
|
||||
assertEquals("yup,nope", this.context.getBean(FreeMarkerConfigurer.class)
|
||||
.getConfiguration().getSetting("boolean_format"));
|
||||
}
|
||||
|
||||
private MockHttpServletResponse render(String viewName) throws Exception {
|
||||
|
||||
View view = this.context.getBean(FreeMarkerViewResolver.class).resolveViewName(
|
||||
viewName, Locale.UK);
|
||||
assertNotNull(view);
|
||||
|
||||
HttpServletRequest request = new MockHttpServletRequest();
|
||||
request.setAttribute(RequestContext.WEB_APPLICATION_CONTEXT_ATTRIBUTE,
|
||||
this.context);
|
||||
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
|
||||
view.render(null, request, response);
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Copyright 2012-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.autoconfigure.freemarker;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.boot.web.TemplateAvailabilityProvider;
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.mock.env.MockEnvironment;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Tests for {@link FreeMarkerTemplateAvailabilityProvider}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class FreeMarkerTemplateAvailabilityProviderTests {
|
||||
|
||||
private final TemplateAvailabilityProvider provider = new FreeMarkerTemplateAvailabilityProvider();
|
||||
|
||||
private final ResourceLoader resourceLoader = new DefaultResourceLoader();
|
||||
|
||||
private final MockEnvironment environment = new MockEnvironment();
|
||||
|
||||
@Test
|
||||
public void availabilityOfTemplateInDefaultLocation() {
|
||||
assertTrue(this.provider.isTemplateAvailable("home", this.environment, getClass()
|
||||
.getClassLoader(), this.resourceLoader));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void availabilityOfTemplateThatDoesNotExist() {
|
||||
assertFalse(this.provider.isTemplateAvailable("whatever", this.environment,
|
||||
getClass().getClassLoader(), this.resourceLoader));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void availabilityOfTemplateWithCustomLoaderPath() {
|
||||
this.environment.setProperty("spring.freemarker.templateLoaderPath",
|
||||
"classpath:/custom-templates/");
|
||||
|
||||
assertTrue(this.provider.isTemplateAvailable("custom", this.environment,
|
||||
getClass().getClassLoader(), this.resourceLoader));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void availabilityOfTemplateWithCustomPrefix() {
|
||||
this.environment.setProperty("spring.freemarker.prefix", "prefix/");
|
||||
|
||||
assertTrue(this.provider.isTemplateAvailable("prefixed", this.environment,
|
||||
getClass().getClassLoader(), this.resourceLoader));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void availabilityOfTemplateWithCustomSuffix() {
|
||||
this.environment.setProperty("spring.freemarker.suffix", ".freemarker");
|
||||
|
||||
assertTrue(this.provider.isTemplateAvailable("suffixed", this.environment,
|
||||
getClass().getClassLoader(), this.resourceLoader));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* Copyright 2012-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.autoconfigure.thymeleaf;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.boot.web.TemplateAvailabilityProvider;
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.mock.env.MockEnvironment;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Tests for {@link ThymeleafTemplateAvailabilityProvider}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class ThymeleafTemplateAvailabilityProviderTests {
|
||||
|
||||
private final TemplateAvailabilityProvider provider = new ThymeleafTemplateAvailabilityProvider();
|
||||
|
||||
private final ResourceLoader resourceLoader = new DefaultResourceLoader();
|
||||
|
||||
private final MockEnvironment environment = new MockEnvironment();
|
||||
|
||||
@Test
|
||||
public void availabilityOfTemplateInDefaultLocation() {
|
||||
assertTrue(this.provider.isTemplateAvailable("home", this.environment, getClass()
|
||||
.getClassLoader(), this.resourceLoader));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void availabilityOfTemplateThatDoesNotExist() {
|
||||
assertFalse(this.provider.isTemplateAvailable("whatever", this.environment,
|
||||
getClass().getClassLoader(), this.resourceLoader));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void availabilityOfTemplateWithCustomPrefix() {
|
||||
this.environment.setProperty("spring.thymeleaf.prefix",
|
||||
"classpath:/custom-templates/");
|
||||
|
||||
assertTrue(this.provider.isTemplateAvailable("custom", this.environment,
|
||||
getClass().getClassLoader(), this.resourceLoader));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void availabilityOfTemplateWithCustomSuffix() {
|
||||
this.environment.setProperty("spring.thymeleaf.suffix", ".thymeleaf");
|
||||
|
||||
assertTrue(this.provider.isTemplateAvailable("suffixed", this.environment,
|
||||
getClass().getClassLoader(), this.resourceLoader));
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
custom
|
|
@ -0,0 +1 @@
|
|||
home
|
|
@ -0,0 +1 @@
|
|||
prefixed
|
|
@ -0,0 +1 @@
|
|||
suffixed
|
|
@ -51,6 +51,7 @@
|
|||
<commons-dbcp.version>1.4</commons-dbcp.version>
|
||||
<commons-pool.version>1.6</commons-pool.version>
|
||||
<crashub.version>1.3.0-beta14</crashub.version>
|
||||
<freemarker.version>2.3.20</freemarker.version>
|
||||
<gemfire.version>7.0.1</gemfire.version>
|
||||
<gradle.version>1.6</gradle.version>
|
||||
<groovy.version>2.3.0-rc-2</groovy.version>
|
||||
|
@ -351,6 +352,11 @@
|
|||
<artifactId>gemfire</artifactId>
|
||||
<version>${gemfire.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.freemarker</groupId>
|
||||
<artifactId>freemarker</artifactId>
|
||||
<version>${freemarker.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hamcrest</groupId>
|
||||
<artifactId>hamcrest-library</artifactId>
|
||||
|
|
|
@ -80,6 +80,22 @@ content into your application; rather pick only the properties that you need.
|
|||
spring.thymeleaf.content-type=text/html # ;charset=<encoding> is added
|
||||
spring.thymeleaf.cache=true # set to false for hot refresh
|
||||
|
||||
# FREEMARKER ({sc-spring-boot-autoconfigure}}/freemarker/FreeMarkerAutoConfiguration.{sc-ext}[FreeMarkerAutoConfiguration])
|
||||
spring.freemarker.allowRequestOverride=false
|
||||
spring.freemarker.allowSessionOverride=false
|
||||
spring.freemarker.cache=true
|
||||
spring.freemarker.checkTemplateLocation=true
|
||||
spring.freemarker.contentType=text/html
|
||||
spring.freemarker.exposeRequestAttributes=false
|
||||
spring.freemarker.exposeSessionAttributes=false
|
||||
spring.freemarker.exposeSpringMacroHelpers=false
|
||||
spring.freemarker.prefix=
|
||||
spring.freemarker.requestContextAttribute=
|
||||
spring.freemarker.suffix=
|
||||
spring.freemarker.templateEncoding=UTF-8
|
||||
spring.freemarker.templateLoaderPath=classpath:/templates/
|
||||
spring.freemarker.viewNames=
|
||||
|
||||
# INTERNATIONALIZATION ({sc-spring-boot-autoconfigure}/MessageSourceAutoConfiguration.{sc-ext}[MessageSourceAutoConfiguration])
|
||||
spring.messages.basename=messages
|
||||
spring.messages.encoding=UTF-8
|
||||
|
|
|
@ -38,6 +38,9 @@ The following auto-configuration classes are from the `spring-boot-autoconfigure
|
|||
|{sc-spring-boot-autoconfigure}/web/EmbeddedServletContainerAutoConfiguration.{sc-ext}[EmbeddedServletContainerAutoConfiguration]
|
||||
|{dc-spring-boot-autoconfigure}/web/EmbeddedServletContainerAutoConfiguration.{dc-ext}[javadoc]
|
||||
|
||||
|{sc-spring-boot-autoconfigure}/freemarker/FreeMarkerAutoConfiguration.{sc-ext}[FreeMarkerAutoConfiguration]
|
||||
|{dc-spring-boot-autoconfigure}/freemarker/FreeMarkerAutoConfiguration.{dc-ext}[javadoc]
|
||||
|
||||
|{sc-spring-boot-autoconfigure}/orm/jpa/HibernateJpaAutoConfiguration.{sc-ext}[HibernateJpaAutoConfiguration]
|
||||
|{dc-spring-boot-autoconfigure}/orm/jpa/HibernateJpaAutoConfiguration.{dc-ext}[javadoc]
|
||||
|
||||
|
|
|
@ -809,8 +809,16 @@ added.
|
|||
`spring.thymeleaf.suffix`, defaults ``classpath:/templates/'' and ``.html''
|
||||
respectively). It can be overridden by providing a bean of the same name.
|
||||
|
||||
Checkout {sc-spring-boot-autoconfigure}/web/WebMvcAutoConfiguration.{sc-ext}[`WebMvcAutoConfiguration`]
|
||||
and {sc-spring-boot-autoconfigure}/thymeleaf/ThymeleafAutoConfiguration.{sc-ext}[`ThymeleafAutoConfiguration`]
|
||||
* If you use FreeMarker you will also have a `FreeMarkerViewResolver` with id
|
||||
``freeMarkerViewResolver''. It looks for resources in a loader path (externalized to
|
||||
`spring.freemarker.templateLoaderPath`, default ``classpath:/templates/'') by
|
||||
surrounding the view name with a prefix and suffix (externalized to `spring.freemarker.prefix`
|
||||
and `spring.freemarker.suffix`, defaults ``'' and ``.ftl'' respectively). It can be overriden
|
||||
by providing a bean of the same name.
|
||||
|
||||
Check out {sc-spring-boot-autoconfigure}/web/WebMvcAutoConfiguration.{sc-ext}[`WebMvcAutoConfiguration`],
|
||||
{sc-spring-boot-autoconfigure}/thymeleaf/ThymeleafAutoConfiguration.{sc-ext}[`ThymeleafAutoConfiguration`], and
|
||||
{sc-spring-boot-autoconfigure}/freemarker/FreeMarkerAutoConfiguration.{sc-ext}['FreeMarkerAutoConfiguration']
|
||||
|
||||
|
||||
|
||||
|
@ -1145,11 +1153,11 @@ you encounter a server error (machine clients consuming JSON and other media typ
|
|||
see a sensible response with the right error code). To switch it off you can set
|
||||
`error.whitelabel.enabled=false`, but normally in addition or alternatively to that you
|
||||
will want to add your own error page replacing the whitelabel one. If you are using
|
||||
Thymeleaf you can do this by adding an `error.html` template. In general what you need is
|
||||
a `View` that resolves with a name of `error`, and/or a `@Controller` that handles the
|
||||
`/error` path. Unless you replaced some of the default configuration you should find a
|
||||
`BeanNameViewResolver` in your `ApplicationContext` so a `@Bean` with id `error` would be
|
||||
a simple way of doing that.
|
||||
Thymeleaf or FreeMarker you can do this by adding an `error.html` or `error.ftl` template
|
||||
respectively. In general what you need is a `View` that resolves with a name of `error`,
|
||||
and/or a `@Controller` that handles the `/error` path. Unless you replaced some of the
|
||||
default configuration you should find a `BeanNameViewResolver` in your `ApplicationContext`
|
||||
so a `@Bean` with id `error` would be a simple way of doing that.
|
||||
Look at {sc-spring-boot-autoconfigure}/web/ErrorMvcAutoConfiguration.{sc-ext}[`ErrorMvcAutoConfiguration`] for more options.
|
||||
|
||||
|
||||
|
@ -1251,7 +1259,15 @@ tools.
|
|||
=== Reload Thymeleaf templates without restarting the container
|
||||
If you are using Thymeleaf, then set `spring.thymeleaf.cache` to `false`. See
|
||||
{sc-spring-boot-autoconfigure}/thymeleaf/ThymeleafAutoConfiguration.{sc-ext}[`ThymeleafAutoConfiguration`]
|
||||
for other template customization options.
|
||||
for other Thymeleaf customization options.
|
||||
|
||||
|
||||
|
||||
[[howto-reload-freemarker-content]]
|
||||
=== Reload FreeMarker templates without restarting the container
|
||||
If you are using FreeMarker, then set `spring.freemarker.cache` to `false`. See
|
||||
{sc-spring-boot-autoconfigure}/freemarker/FreeMarkerAutoConfiguration.{sc-ext}[`FreeMarkerAutoConfiguration`]
|
||||
for other FreeMarker customization options.
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
= Spring Boot Reference Guide
|
||||
Phillip Webb; Dave Syer; Josh Long; Stéphane Nicoll; Rob Winch;
|
||||
Phillip Webb; Dave Syer; Josh Long; Stéphane Nicoll; Rob Winch; Andy Wilkinson;
|
||||
:doctype: book
|
||||
:toc:
|
||||
:toclevels: 4
|
||||
|
|
|
@ -892,14 +892,16 @@ and it will be silently ignored by most build tools if you generate a jar.
|
|||
[[boot-features-spring-mvc-template-engines]]
|
||||
==== Template engines
|
||||
As well as REST web services, you can also use Spring MVC to serve dynamic HTML content.
|
||||
Spring MVC supports a variety of templating technologies including: velocity, freemarker,
|
||||
Spring MVC supports a variety of templating technologies including: Velocity, FreeMarker,
|
||||
and JSPs. Many other templating engines also ship their own Spring MVC integrations.
|
||||
|
||||
Spring Boot includes auto-configuration support for the Thymeleaf templating engine.
|
||||
Thymeleaf is an XML/XHTML/HTML5 template engine that can work both in web and non-web
|
||||
environments. It allows you to create natural templates that can be correctly displayed
|
||||
by browsers and therefore work also as static prototypes. Thymeleaf templates will be
|
||||
picked up automatically from `src/main/resources/templates`.
|
||||
Spring Boot includes auto-configuration support for the Thymeleaf and FreeMarker
|
||||
templating engines. Thymeleaf is an XML/XHTML/HTML5 template engine that can work both in
|
||||
web and non-web environments. If allows you to create natural templates that can be
|
||||
correctly displayed by browsers and therefore work also as static prototypes. Both
|
||||
FreeMarker and Thymeleaf templates will be picked up automatically from
|
||||
`src/main/resources/templates`.
|
||||
|
||||
|
||||
TIP: JSPs should be avoided if possible, there are several
|
||||
<<boot-features-jsp-limitations, known limitations>> when using them with embedded
|
||||
|
|
|
@ -229,6 +229,9 @@ and Hibernate.
|
|||
|`spring-boot-starter-data-rest`
|
||||
|Support for exposing Spring Data repositories over REST via `spring-data-rest-webmvc`.
|
||||
|
||||
|`spring-boot-starter-freemarker`
|
||||
|Support for the FreeMarker templating engine
|
||||
|
||||
|`spring-boot-starter-integration`
|
||||
|Support for common `spring-integration` modules.
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
<module>spring-boot-sample-tomcat</module>
|
||||
<module>spring-boot-sample-tomcat-multi-connectors</module>
|
||||
<module>spring-boot-sample-traditional</module>
|
||||
<module>spring-boot-sample-web-freemarker</module>
|
||||
<module>spring-boot-sample-web-method-security</module>
|
||||
<module>spring-boot-sample-web-secure</module>
|
||||
<module>spring-boot-sample-web-static</module>
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<!-- Your own application should inherit from spring-boot-starter-parent -->
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-samples</artifactId>
|
||||
<version>1.1.0.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>spring-boot-sample-web-freemarker</artifactId>
|
||||
<name>Spring Boot Web FreeMarker Sample</name>
|
||||
<description>Spring Boot Web FreeMarker Sample</description>
|
||||
<url>http://projects.spring.io/spring-boot/</url>
|
||||
<organization>
|
||||
<name>Pivotal Software, Inc.</name>
|
||||
<url>http://www.spring.io</url>
|
||||
</organization>
|
||||
<properties>
|
||||
<main.basedir>${basedir}/../..</main.basedir>
|
||||
<m2eclipse.wtp.contextRoot>/</m2eclipse.wtp.contextRoot>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-freemarker</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<useSystemClassLoader>false</useSystemClassLoader>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright 2012-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package sample.freemarker;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
@ComponentScan
|
||||
public class SampleWebFreeMarkerApplication {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
SpringApplication.run(SampleWebFreeMarkerApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright 2012-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package sample.freemarker;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
@Controller
|
||||
public class WelcomeController {
|
||||
|
||||
@Value("${application.message:Hello World}")
|
||||
private String message = "Hello World";
|
||||
|
||||
@RequestMapping("/")
|
||||
public String welcome(Map<String, Object> model) {
|
||||
model.put("time", new Date());
|
||||
model.put("message", this.message);
|
||||
return "welcome";
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
application.message: Hello, Andy
|
|
@ -0,0 +1,9 @@
|
|||
<!DOCTYPE html>
|
||||
|
||||
<html lang="en">
|
||||
|
||||
<body>
|
||||
Something went wrong: ${status} ${error}
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,13 @@
|
|||
<!DOCTYPE html>
|
||||
|
||||
<html lang="en">
|
||||
|
||||
<body>
|
||||
Date: ${time?date}
|
||||
<br>
|
||||
Time: ${time?time}
|
||||
<br>
|
||||
Message: ${message}
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Copyright 2012-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package sample.freemarker;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.boot.test.IntegrationTest;
|
||||
import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||
import org.springframework.boot.test.TestRestTemplate;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Basic integration tests for FreeMarker application.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringApplicationConfiguration(classes = SampleWebFreeMarkerApplication.class)
|
||||
@WebAppConfiguration
|
||||
@IntegrationTest
|
||||
@DirtiesContext
|
||||
public class SampleWebFreeMarkerApplicationTests {
|
||||
|
||||
@Test
|
||||
public void testFreeMarkerTemplate() throws Exception {
|
||||
ResponseEntity<String> entity = new TestRestTemplate().getForEntity(
|
||||
"http://localhost:8080", String.class);
|
||||
assertEquals(HttpStatus.OK, entity.getStatusCode());
|
||||
assertTrue("Wrong body:\n" + entity.getBody(),
|
||||
entity.getBody().contains("Hello, Andy"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFreeMarkerErrorTemplate() throws Exception {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setAccept(Arrays.asList(MediaType.TEXT_HTML));
|
||||
HttpEntity<String> requestEntity = new HttpEntity<String>(headers);
|
||||
|
||||
ResponseEntity<String> responseEntity = new TestRestTemplate().exchange(
|
||||
"http://localhost:8080/does-not-exist", HttpMethod.GET, requestEntity,
|
||||
String.class);
|
||||
|
||||
assertEquals(HttpStatus.NOT_FOUND, responseEntity.getStatusCode());
|
||||
assertTrue("Wrong body:\n" + responseEntity.getBody(), responseEntity.getBody()
|
||||
.contains("Something went wrong: 404 Not Found"));
|
||||
}
|
||||
|
||||
}
|
|
@ -29,6 +29,7 @@
|
|||
<module>spring-boot-starter-data-mongodb</module>
|
||||
<module>spring-boot-starter-data-neo4j</module>
|
||||
<module>spring-boot-starter-data-rest</module>
|
||||
<module>spring-boot-starter-freemarker</module>
|
||||
<module>spring-boot-starter-integration</module>
|
||||
<module>spring-boot-starter-jdbc</module>
|
||||
<module>spring-boot-starter-jetty</module>
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starters</artifactId>
|
||||
<version>1.1.0.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>spring-boot-starter-freemarker</artifactId>
|
||||
<name>Spring Boot FreeMarker Starter</name>
|
||||
<description>Spring Boot FreeMarker Starter</description>
|
||||
<url>http://projects.spring.io/spring-boot/</url>
|
||||
<organization>
|
||||
<name>Pivotal Software, Inc.</name>
|
||||
<url>http://www.spring.io</url>
|
||||
</organization>
|
||||
<properties>
|
||||
<main.basedir>${basedir}/../..</main.basedir>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.freemarker</groupId>
|
||||
<artifactId>freemarker</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context-support</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
|
@ -0,0 +1 @@
|
|||
provides: freemarker,spring-context-support
|
|
@ -101,6 +101,11 @@
|
|||
<artifactId>spring-boot-starter-data-rest</artifactId>
|
||||
<version>${spring-boot.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-freemarker</artifactId>
|
||||
<version>${spring-boot.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-integration</artifactId>
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright 2012-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.web;
|
||||
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
|
||||
/**
|
||||
* A {@code TemplateAvailabilityProvider} indicates the availability of view templates for
|
||||
* a particular templating engine such as FreeMarker or Thymeleaf
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*
|
||||
*/
|
||||
public interface TemplateAvailabilityProvider {
|
||||
|
||||
/**
|
||||
* Returns {@code true} if a template is available for the given {@code view},
|
||||
* otherwise {@code false} is returned.
|
||||
*/
|
||||
boolean isTemplateAvailable(String view, Environment environment,
|
||||
ClassLoader classLoader, ResourceLoader resourceLoader);
|
||||
|
||||
}
|
Loading…
Reference in New Issue