Spring boot and Thymeleaf - Hot swap templates and resources once again
Asked Answered
V

5

14

I tried all tips and tricks that I found here and in docs, but still no luck. I have Spring webapp with Thymeleaf. Resources and templates are not reloaded when I call update in IDEA (it says nothing to reload). I can then press ctrl+f5 in a browser like crazy, changes are just not there.

Everything is configured in one Java class like this:

@EnableWebMvc
public class MvcConfig extends WebMvcConfigurerAdapter implements ApplicationContextAware {

My folder structure now looks like this, but I also tried to put the resources without "static" folder or to webapp/resources.

ResourceHandlerRegistry:

@Override
public void addResourceHandlers(final ResourceHandlerRegistry registry) {
    super.addResourceHandlers(registry);
    registry.addResourceHandler("/img/**").addResourceLocations("classpath:/static/img/");
    registry.addResourceHandler("/css/**").addResourceLocations("classpath:/static/css/");
    registry.addResourceHandler("/js/**").addResourceLocations("classpath:/static/js/");
}

I specified cache=false in both application.properties:

spring.thymeleaf.cache=false

and in mentioned MvcConfig class:

@Bean
public SpringResourceTemplateResolver templateResolver() {
    SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
    templateResolver.setApplicationContext(this.applicationContext);
    templateResolver.setPrefix("/WEB-INF/templates/");
    templateResolver.setSuffix(".html");
    templateResolver.setTemplateMode(TemplateMode.HTML);
    templateResolver.setCacheable(false);
    return templateResolver;
}

According to some answers on SO i added dependency for devtools:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <version>1.4.1.RELEASE</version>
    <optional>true</optional>
</dependency>

Still not working. Some said to add maven boot plugin with addResources=true, so I did:

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <version>1.4.1.RELEASE</version>
    <configuration>
        <addResources>true</addResources>
    </configuration>
</plugin>

My Idea is set properly I guess, because when I call update, my Java classes are reloaded immediately. Only resources and html files are not, I must restart server for it. Actualy *.html files are not so big a deal, but to restart server after every small css and js change is slowing me down a lot, and as I lost almost 15 hours figuring out what is wrong, it started to be really frustrating.

Any help will be greatly appreciated.

Vaughnvaught answered 15/10, 2016 at 8:57 Comment(2)
What I just found out is that changes are actualy reflected in "target" folder, but not in the running app itself.Vaughnvaught
this one did the trick for me #21400086Alp
D
36

I have spent some time on it and finally here I'll explain how I got it working. Googling around you may find several info:

My inital approach was to disable caching and add Spring dev tools:

Spring boot application.properties

spring.thymeleaf.cache=false
spring.thymeleaf.mode=LEGACYHTML5
spring.thymeleaf.prefix=/templates/

pom.xml

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <optional>true</optional>
    </dependency>

Using the snippet above however is not enough since the hot swap is done only when making the project (CTRL + F9 in Intellij Idea). This is due to the fact that the default template resolver is classpath based and that's the reason a recompilation is necessary.


A working solution is to override the defaultTemplateResolver by using a file system based resolver:

application.properties

spring.thymeleaf.cache=false
spring.thymeleaf.mode=LEGACYHTML5
spring.thymeleaf.templates_root=src/main/resources/templates/

Application class

@SpringBootApplication
public class MyApplication {

    @Autowired
    private ThymeleafProperties properties;

    @Value("${spring.thymeleaf.templates_root:}")
    private String templatesRoot;

    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }

    @Bean
    public ITemplateResolver defaultTemplateResolver() {
        FileTemplateResolver resolver = new FileTemplateResolver();
        resolver.setSuffix(properties.getSuffix());
        resolver.setPrefix(templatesRoot);
        resolver.setTemplateMode(properties.getMode());
        resolver.setCacheable(properties.isCache());
        return resolver;
    }
}

I find this solution optimal since it allow you to externalize the configuration and use different profiles (dev, prod, etc..) while having the benefit of reloading the changes by just pressing F5 :)

Dele answered 30/1, 2017 at 10:6 Comment(4)
I get a warning Cannot resolve configuration property 'spring.thymeleaf.templates_root'. Does this property really exist?Iloilo
it exist if you write it, see the example application.properties in the answerDele
use one line config can do last work(override defaultTemplateResolver): spring.thymeleaf.prefix=file:///${user.dir}/src/main/resources/templates/Darceydarci
As @Darceydarci says -- simpler still to just set spring.thymeleaf.prefix. I've posted this as an answer below: https://mcmap.net/q/225998/-spring-boot-and-thymeleaf-hot-swap-templates-and-resources-once-againBlum
B
4

The simplest solution is to configure Spring's Thymeleaf template source to load from the filesystem, rather than the classpath.

This is a super-simple -- esentially a one-liner in config -- but seems little-known on the Internet. Credit to @JianrongChen's for posting it in his comment.

application.properties

# Thymeleaf templates from filesystem, not cached
#
spring.thymeleaf.cache=false
spring.thymeleaf.prefix=file:src/main/resources/templates/

# static content from filesystem first, too
#
spring.web.resources.static-locations[0]=file:src/main/resources/static/
spring.web.resources.static-locations[1]=classpath:/static

References:

Blum answered 10/7, 2022 at 11:40 Comment(0)
V
3

Here are my settings with IntelliJ IDEA (2018.3), it's reload HTML after the changes are saved:

  1. In application.properties:

    spring.resources.static-locations = classpath:/resources/static
    spring.resources.cache.period = 0
    
  2. In pom.xml, set <addResources>true</addResources>

    <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <configuration>
            <addResources>true</addResources>
        </configuration>
    </plugin>
    
  3. Menu Run => Edit Configurations (IntelliJ IDEA)

On frame deactivation: Update resources

Vacuva answered 20/12, 2018 at 15:12 Comment(0)
V
0

Okay, so I found answer to my specific case. Problem was not at all in my app or it's config (well.. probably). Instead of using Tomcat 8.5.5 I switched back to Tomcat 7. Everything now works properly. Does somebody know why?

Vaughnvaught answered 15/10, 2016 at 12:18 Comment(0)
A
0

@Luke my solution was quite simple:

To have HTML / CSS / JS reloading automatically in Spring Thymeleaf can be simple and bug free, have only tested in IntelliJ.

Add this to maven, use ${spring.version} var or replace by your version:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <version>${spring.version}</version>
    <optional>true</optional>
    <scope>runtime</scope>
</dependency>

Add to the header of the html:

<script src="http://localhost:35729/livereload.js"></script>

When using IntelliJ:

using Ctrl+Shift+A write Registry

In IntelliJ Settings

Ascariasis answered 20/9, 2020 at 4:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.