Add support for Thymeleaf 3 while keeping Thymeleaf 2 as the default
Closes gh-4393
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright 2012-2016 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 javax.annotation.PostConstruct;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolver;
|
||||
import org.thymeleaf.templateresolver.TemplateResolver;
|
||||
|
||||
import org.springframework.boot.autoconfigure.template.TemplateLocation;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
/**
|
||||
* Abstract base class for the configuration of a Thymeleaf {@link TemplateResolver}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
abstract class AbstractTemplateResolverConfiguration {
|
||||
|
||||
private static final Log logger = LogFactory
|
||||
.getLog(AbstractTemplateResolverConfiguration.class);
|
||||
|
||||
private final ThymeleafProperties properties;
|
||||
|
||||
private final ApplicationContext applicationContext;
|
||||
|
||||
AbstractTemplateResolverConfiguration(ThymeleafProperties properties,
|
||||
ApplicationContext applicationContext) {
|
||||
this.properties = properties;
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void checkTemplateLocationExists() {
|
||||
boolean checkTemplateLocation = this.properties.isCheckTemplateLocation();
|
||||
if (checkTemplateLocation) {
|
||||
TemplateLocation location = new TemplateLocation(this.properties.getPrefix());
|
||||
if (!location.exists(this.applicationContext)) {
|
||||
logger.warn("Cannot find template location: " + location
|
||||
+ " (please add some templates or check "
|
||||
+ "your Thymeleaf configuration)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SpringResourceTemplateResolver defaultTemplateResolver() {
|
||||
SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
|
||||
resolver.setApplicationContext(this.applicationContext);
|
||||
resolver.setPrefix(this.properties.getPrefix());
|
||||
resolver.setSuffix(this.properties.getSuffix());
|
||||
resolver.setTemplateMode(this.properties.getMode());
|
||||
if (this.properties.getEncoding() != null) {
|
||||
resolver.setCharacterEncoding(this.properties.getEncoding().name());
|
||||
}
|
||||
resolver.setCacheable(this.properties.isCache());
|
||||
Integer order = this.properties.getTemplateResolverOrder();
|
||||
if (order != null) {
|
||||
resolver.setOrder(order);
|
||||
}
|
||||
return resolver;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2012-2016 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 java.util.LinkedHashMap;
|
||||
|
||||
import org.thymeleaf.spring4.SpringTemplateEngine;
|
||||
import org.thymeleaf.spring4.view.ThymeleafViewResolver;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.util.MimeType;
|
||||
|
||||
/**
|
||||
* Abstract base class for the configuration of a {@link ThymeleafViewResolver}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
abstract class AbstractThymeleafViewResolverConfiguration {
|
||||
|
||||
private final ThymeleafProperties properties;
|
||||
|
||||
private final SpringTemplateEngine templateEngine;
|
||||
|
||||
protected AbstractThymeleafViewResolverConfiguration(ThymeleafProperties properties,
|
||||
SpringTemplateEngine templateEngine) {
|
||||
this.properties = properties;
|
||||
this.templateEngine = templateEngine;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(name = "thymeleafViewResolver")
|
||||
@ConditionalOnProperty(name = "spring.thymeleaf.enabled", matchIfMissing = true)
|
||||
public ThymeleafViewResolver thymeleafViewResolver() {
|
||||
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
|
||||
configureTemplateEngine(resolver, this.templateEngine);
|
||||
resolver.setCharacterEncoding(this.properties.getEncoding().name());
|
||||
resolver.setContentType(appendCharset(this.properties.getContentType(),
|
||||
resolver.getCharacterEncoding()));
|
||||
resolver.setExcludedViewNames(this.properties.getExcludedViewNames());
|
||||
resolver.setViewNames(this.properties.getViewNames());
|
||||
// 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);
|
||||
resolver.setCache(this.properties.isCache());
|
||||
return resolver;
|
||||
}
|
||||
|
||||
protected abstract void configureTemplateEngine(ThymeleafViewResolver resolver,
|
||||
SpringTemplateEngine templateEngine);
|
||||
|
||||
private String appendCharset(MimeType type, String charset) {
|
||||
if (type.getCharset() != null) {
|
||||
return type.toString();
|
||||
}
|
||||
LinkedHashMap<String, String> parameters = new LinkedHashMap<String, String>();
|
||||
parameters.put("charset", charset);
|
||||
parameters.putAll(type.getParameters());
|
||||
return new MimeType(type, parameters).toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,16 +16,13 @@
|
||||
|
||||
package org.springframework.boot.autoconfigure.thymeleaf;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.servlet.Servlet;
|
||||
|
||||
import com.github.mxab.thymeleaf.extras.dataattribute.dialect.DataAttributeDialect;
|
||||
import nz.net.ultraq.thymeleaf.LayoutDialect;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.thymeleaf.dialect.IDialect;
|
||||
import org.thymeleaf.extras.conditionalcomments.dialect.ConditionalCommentsDialect;
|
||||
import org.thymeleaf.extras.java8time.dialect.Java8TimeDialect;
|
||||
@@ -34,7 +31,6 @@ import org.thymeleaf.spring4.SpringTemplateEngine;
|
||||
import org.thymeleaf.spring4.resourceresolver.SpringResourceResourceResolver;
|
||||
import org.thymeleaf.spring4.view.ThymeleafViewResolver;
|
||||
import org.thymeleaf.templateresolver.ITemplateResolver;
|
||||
import org.thymeleaf.templateresolver.TemplateResolver;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
@@ -43,18 +39,15 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnJava;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.autoconfigure.template.TemplateLocation;
|
||||
import org.springframework.boot.autoconfigure.web.ConditionalOnEnabledResourceChain;
|
||||
import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.MimeType;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.web.servlet.resource.ResourceUrlEncodingFilter;
|
||||
|
||||
/**
|
||||
@@ -69,62 +62,107 @@ import org.springframework.web.servlet.resource.ResourceUrlEncodingFilter;
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(ThymeleafProperties.class)
|
||||
@ConditionalOnClass(SpringTemplateEngine.class)
|
||||
@ConditionalOnMissingClass("org.thymeleaf.templatemode.TemplateMode")
|
||||
@AutoConfigureAfter(WebMvcAutoConfiguration.class)
|
||||
public class ThymeleafAutoConfiguration {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(ThymeleafAutoConfiguration.class);
|
||||
@Configuration
|
||||
@ConditionalOnMissingClass("org.thymeleaf.templatemode.TemplateMode")
|
||||
static class Thymeleaf2Configuration {
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnMissingBean(name = "defaultTemplateResolver")
|
||||
static class DefaultTemplateResolverConfiguration
|
||||
extends AbstractTemplateResolverConfiguration {
|
||||
|
||||
DefaultTemplateResolverConfiguration(ThymeleafProperties properties,
|
||||
ApplicationContext applicationContext) {
|
||||
super(properties, applicationContext);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SpringResourceResourceResolver thymeleafResourceResolver() {
|
||||
return new SpringResourceResourceResolver();
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass({ Servlet.class })
|
||||
@ConditionalOnWebApplication
|
||||
static class Thymeleaf2ViewResolverConfiguration
|
||||
extends AbstractThymeleafViewResolverConfiguration {
|
||||
|
||||
Thymeleaf2ViewResolverConfiguration(ThymeleafProperties properties,
|
||||
SpringTemplateEngine templateEngine) {
|
||||
super(properties, templateEngine);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configureTemplateEngine(ThymeleafViewResolver resolver,
|
||||
SpringTemplateEngine templateEngine) {
|
||||
resolver.setTemplateEngine(templateEngine);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass(ConditionalCommentsDialect.class)
|
||||
static class ThymeleafConditionalCommentsDialectConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ConditionalCommentsDialect conditionalCommentsDialect() {
|
||||
return new ConditionalCommentsDialect();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnMissingBean(name = "defaultTemplateResolver")
|
||||
public static class DefaultTemplateResolverConfiguration {
|
||||
@ConditionalOnClass(name = "org.thymeleaf.templatemode.TemplateMode")
|
||||
static class Thymeleaf3Configuration {
|
||||
|
||||
private final ThymeleafProperties properties;
|
||||
@Configuration
|
||||
@ConditionalOnMissingBean(name = "defaultTemplateResolver")
|
||||
static class DefaultTemplateResolverConfiguration
|
||||
extends AbstractTemplateResolverConfiguration {
|
||||
|
||||
private final ApplicationContext applicationContext;
|
||||
DefaultTemplateResolverConfiguration(ThymeleafProperties properties,
|
||||
ApplicationContext applicationContext) {
|
||||
super(properties, applicationContext);
|
||||
}
|
||||
|
||||
public DefaultTemplateResolverConfiguration(ThymeleafProperties properties,
|
||||
ApplicationContext applicationContext) {
|
||||
this.properties = properties;
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void checkTemplateLocationExists() {
|
||||
boolean checkTemplateLocation = this.properties.isCheckTemplateLocation();
|
||||
if (checkTemplateLocation) {
|
||||
TemplateLocation location = new TemplateLocation(
|
||||
this.properties.getPrefix());
|
||||
if (!location.exists(this.applicationContext)) {
|
||||
logger.warn("Cannot find template location: " + location
|
||||
+ " (please add some templates or check "
|
||||
+ "your Thymeleaf configuration)");
|
||||
@Configuration
|
||||
@ConditionalOnClass({ Servlet.class })
|
||||
@ConditionalOnWebApplication
|
||||
static class Thymeleaf3ViewResolverConfiguration
|
||||
extends AbstractThymeleafViewResolverConfiguration {
|
||||
|
||||
Thymeleaf3ViewResolverConfiguration(ThymeleafProperties properties,
|
||||
SpringTemplateEngine templateEngine) {
|
||||
super(properties, templateEngine);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configureTemplateEngine(ThymeleafViewResolver resolver,
|
||||
SpringTemplateEngine templateEngine) {
|
||||
Method setTemplateEngine;
|
||||
try {
|
||||
setTemplateEngine = ReflectionUtils.findMethod(resolver.getClass(),
|
||||
"setTemplateEngine",
|
||||
Class.forName("org.thymeleaf.ITemplateEngine", true,
|
||||
resolver.getClass().getClassLoader()));
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
ReflectionUtils.invokeMethod(setTemplateEngine, resolver, templateEngine);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TemplateResolver defaultTemplateResolver() {
|
||||
TemplateResolver resolver = new TemplateResolver();
|
||||
resolver.setResourceResolver(thymeleafResourceResolver());
|
||||
resolver.setPrefix(this.properties.getPrefix());
|
||||
resolver.setSuffix(this.properties.getSuffix());
|
||||
resolver.setTemplateMode(this.properties.getMode());
|
||||
if (this.properties.getEncoding() != null) {
|
||||
resolver.setCharacterEncoding(this.properties.getEncoding().name());
|
||||
}
|
||||
resolver.setCacheable(this.properties.isCache());
|
||||
Integer order = this.properties.getTemplateResolverOrder();
|
||||
if (order != null) {
|
||||
resolver.setOrder(order);
|
||||
}
|
||||
return resolver;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SpringResourceResourceResolver thymeleafResourceResolver() {
|
||||
return new SpringResourceResourceResolver();
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@@ -194,18 +232,6 @@ public class ThymeleafAutoConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass(ConditionalCommentsDialect.class)
|
||||
protected static class ThymeleafConditionalCommentsDialectConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ConditionalCommentsDialect conditionalCommentsDialect() {
|
||||
return new ConditionalCommentsDialect();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnJava(ConditionalOnJava.JavaVersion.EIGHT)
|
||||
@ConditionalOnClass(Java8TimeDialect.class)
|
||||
@@ -219,51 +245,6 @@ public class ThymeleafAutoConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass({ Servlet.class })
|
||||
@ConditionalOnWebApplication
|
||||
protected static class ThymeleafViewResolverConfiguration {
|
||||
|
||||
private final ThymeleafProperties properties;
|
||||
|
||||
private final SpringTemplateEngine templateEngine;
|
||||
|
||||
protected ThymeleafViewResolverConfiguration(ThymeleafProperties properties,
|
||||
SpringTemplateEngine templateEngine) {
|
||||
this.properties = properties;
|
||||
this.templateEngine = templateEngine;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(name = "thymeleafViewResolver")
|
||||
@ConditionalOnProperty(name = "spring.thymeleaf.enabled", matchIfMissing = true)
|
||||
public ThymeleafViewResolver thymeleafViewResolver() {
|
||||
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
|
||||
resolver.setTemplateEngine(this.templateEngine);
|
||||
resolver.setCharacterEncoding(this.properties.getEncoding().name());
|
||||
resolver.setContentType(appendCharset(this.properties.getContentType(),
|
||||
resolver.getCharacterEncoding()));
|
||||
resolver.setExcludedViewNames(this.properties.getExcludedViewNames());
|
||||
resolver.setViewNames(this.properties.getViewNames());
|
||||
// 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);
|
||||
resolver.setCache(this.properties.isCache());
|
||||
return resolver;
|
||||
}
|
||||
|
||||
private String appendCharset(MimeType type, String charset) {
|
||||
if (type.getCharset() != null) {
|
||||
return type.toString();
|
||||
}
|
||||
LinkedHashMap<String, String> parameters = new LinkedHashMap<String, String>();
|
||||
parameters.put("charset", charset);
|
||||
parameters.putAll(type.getParameters());
|
||||
return new MimeType(type, parameters).toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnWebApplication
|
||||
protected static class ThymeleafResourceHandlingConfig {
|
||||
|
||||
Reference in New Issue
Block a user