001/* 002 * Copyright 2012-2016 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.boot.autoconfigure.thymeleaf; 018 019import java.lang.reflect.Method; 020import java.util.Collection; 021 022import javax.servlet.Servlet; 023 024import com.github.mxab.thymeleaf.extras.dataattribute.dialect.DataAttributeDialect; 025import nz.net.ultraq.thymeleaf.LayoutDialect; 026import org.thymeleaf.dialect.IDialect; 027import org.thymeleaf.extras.conditionalcomments.dialect.ConditionalCommentsDialect; 028import org.thymeleaf.extras.java8time.dialect.Java8TimeDialect; 029import org.thymeleaf.extras.springsecurity4.dialect.SpringSecurityDialect; 030import org.thymeleaf.spring4.SpringTemplateEngine; 031import org.thymeleaf.spring4.resourceresolver.SpringResourceResourceResolver; 032import org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolver; 033import org.thymeleaf.spring4.view.ThymeleafViewResolver; 034import org.thymeleaf.templateresolver.ITemplateResolver; 035 036import org.springframework.beans.factory.ObjectProvider; 037import org.springframework.boot.autoconfigure.AutoConfigureAfter; 038import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 039import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 040import org.springframework.boot.autoconfigure.condition.ConditionalOnJava; 041import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 042import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass; 043import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; 044import org.springframework.boot.autoconfigure.web.ConditionalOnEnabledResourceChain; 045import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration; 046import org.springframework.boot.context.properties.EnableConfigurationProperties; 047import org.springframework.context.ApplicationContext; 048import org.springframework.context.annotation.Bean; 049import org.springframework.context.annotation.Configuration; 050import org.springframework.util.CollectionUtils; 051import org.springframework.util.ReflectionUtils; 052import org.springframework.web.servlet.resource.ResourceUrlEncodingFilter; 053 054/** 055 * {@link EnableAutoConfiguration Auto-configuration} for Thymeleaf. 056 * 057 * @author Dave Syer 058 * @author Andy Wilkinson 059 * @author Stephane Nicoll 060 * @author Brian Clozel 061 * @author Eddú Meléndez 062 */ 063@Configuration 064@EnableConfigurationProperties(ThymeleafProperties.class) 065@ConditionalOnClass(SpringTemplateEngine.class) 066@AutoConfigureAfter(WebMvcAutoConfiguration.class) 067public class ThymeleafAutoConfiguration { 068 069 @Configuration 070 @ConditionalOnMissingClass("org.thymeleaf.templatemode.TemplateMode") 071 static class Thymeleaf2Configuration { 072 073 @Configuration 074 @ConditionalOnMissingBean(name = "defaultTemplateResolver") 075 static class DefaultTemplateResolverConfiguration 076 extends AbstractTemplateResolverConfiguration { 077 078 DefaultTemplateResolverConfiguration(ThymeleafProperties properties, 079 ApplicationContext applicationContext) { 080 super(properties, applicationContext); 081 } 082 083 @Bean 084 public SpringResourceResourceResolver thymeleafResourceResolver() { 085 return new SpringResourceResourceResolver(); 086 } 087 088 } 089 090 @Configuration 091 @ConditionalOnClass({ Servlet.class }) 092 @ConditionalOnWebApplication 093 static class Thymeleaf2ViewResolverConfiguration 094 extends AbstractThymeleafViewResolverConfiguration { 095 096 Thymeleaf2ViewResolverConfiguration(ThymeleafProperties properties, 097 SpringTemplateEngine templateEngine) { 098 super(properties, templateEngine); 099 } 100 101 @Override 102 protected void configureTemplateEngine(ThymeleafViewResolver resolver, 103 SpringTemplateEngine templateEngine) { 104 resolver.setTemplateEngine(templateEngine); 105 } 106 107 } 108 109 @Configuration 110 @ConditionalOnClass(ConditionalCommentsDialect.class) 111 static class ThymeleafConditionalCommentsDialectConfiguration { 112 113 @Bean 114 @ConditionalOnMissingBean 115 public ConditionalCommentsDialect conditionalCommentsDialect() { 116 return new ConditionalCommentsDialect(); 117 } 118 119 } 120 121 } 122 123 @Configuration 124 @ConditionalOnClass(name = "org.thymeleaf.templatemode.TemplateMode") 125 static class Thymeleaf3Configuration { 126 127 @Configuration 128 @ConditionalOnMissingBean(name = "defaultTemplateResolver") 129 static class DefaultTemplateResolverConfiguration 130 extends AbstractTemplateResolverConfiguration { 131 132 DefaultTemplateResolverConfiguration(ThymeleafProperties properties, 133 ApplicationContext applicationContext) { 134 super(properties, applicationContext); 135 } 136 137 @Bean 138 @Override 139 public SpringResourceTemplateResolver defaultTemplateResolver() { 140 SpringResourceTemplateResolver resolver = super.defaultTemplateResolver(); 141 Method setCheckExistence = ReflectionUtils.findMethod(resolver.getClass(), 142 "setCheckExistence", boolean.class); 143 ReflectionUtils.invokeMethod(setCheckExistence, resolver, 144 getProperties().isCheckTemplate()); 145 return resolver; 146 } 147 148 } 149 150 @Configuration 151 @ConditionalOnClass({ Servlet.class }) 152 @ConditionalOnWebApplication 153 static class Thymeleaf3ViewResolverConfiguration 154 extends AbstractThymeleafViewResolverConfiguration { 155 156 Thymeleaf3ViewResolverConfiguration(ThymeleafProperties properties, 157 SpringTemplateEngine templateEngine) { 158 super(properties, templateEngine); 159 } 160 161 @Override 162 protected void configureTemplateEngine(ThymeleafViewResolver resolver, 163 SpringTemplateEngine templateEngine) { 164 Method setTemplateEngine; 165 try { 166 setTemplateEngine = ReflectionUtils.findMethod(resolver.getClass(), 167 "setTemplateEngine", 168 Class.forName("org.thymeleaf.ITemplateEngine", true, 169 resolver.getClass().getClassLoader())); 170 } 171 catch (ClassNotFoundException ex) { 172 throw new IllegalStateException(ex); 173 } 174 ReflectionUtils.invokeMethod(setTemplateEngine, resolver, templateEngine); 175 } 176 177 } 178 179 } 180 181 @Configuration 182 @ConditionalOnMissingBean(SpringTemplateEngine.class) 183 protected static class ThymeleafDefaultConfiguration { 184 185 private final Collection<ITemplateResolver> templateResolvers; 186 187 private final Collection<IDialect> dialects; 188 189 public ThymeleafDefaultConfiguration( 190 Collection<ITemplateResolver> templateResolvers, 191 ObjectProvider<Collection<IDialect>> dialectsProvider) { 192 this.templateResolvers = templateResolvers; 193 this.dialects = dialectsProvider.getIfAvailable(); 194 } 195 196 @Bean 197 public SpringTemplateEngine templateEngine() { 198 SpringTemplateEngine engine = new SpringTemplateEngine(); 199 for (ITemplateResolver templateResolver : this.templateResolvers) { 200 engine.addTemplateResolver(templateResolver); 201 } 202 if (!CollectionUtils.isEmpty(this.dialects)) { 203 for (IDialect dialect : this.dialects) { 204 engine.addDialect(dialect); 205 } 206 } 207 return engine; 208 } 209 210 } 211 212 @Configuration 213 @ConditionalOnClass(name = "nz.net.ultraq.thymeleaf.LayoutDialect") 214 protected static class ThymeleafWebLayoutConfiguration { 215 216 @Bean 217 @ConditionalOnMissingBean 218 public LayoutDialect layoutDialect() { 219 return new LayoutDialect(); 220 } 221 222 } 223 224 @Configuration 225 @ConditionalOnClass(DataAttributeDialect.class) 226 protected static class DataAttributeDialectConfiguration { 227 228 @Bean 229 @ConditionalOnMissingBean 230 public DataAttributeDialect dialect() { 231 return new DataAttributeDialect(); 232 } 233 234 } 235 236 @Configuration 237 @ConditionalOnClass({ SpringSecurityDialect.class }) 238 protected static class ThymeleafSecurityDialectConfiguration { 239 240 @Bean 241 @ConditionalOnMissingBean 242 public SpringSecurityDialect securityDialect() { 243 return new SpringSecurityDialect(); 244 } 245 246 } 247 248 @Configuration 249 @ConditionalOnJava(ConditionalOnJava.JavaVersion.EIGHT) 250 @ConditionalOnClass(Java8TimeDialect.class) 251 protected static class ThymeleafJava8TimeDialect { 252 253 @Bean 254 @ConditionalOnMissingBean 255 public Java8TimeDialect java8TimeDialect() { 256 return new Java8TimeDialect(); 257 } 258 259 } 260 261 @Configuration 262 @ConditionalOnWebApplication 263 protected static class ThymeleafResourceHandlingConfig { 264 265 @Bean 266 @ConditionalOnMissingBean 267 @ConditionalOnEnabledResourceChain 268 public ResourceUrlEncodingFilter resourceUrlEncodingFilter() { 269 return new ResourceUrlEncodingFilter(); 270 } 271 272 } 273 274}