57

I'm evaluating Spring MVC & Boot and AngularJs for building web applications. I've run into the problem that when I make modifications to my static content (html, js, css), I have to restart the application every time. I hope there is a some way of solving that because restarting the whole application for static content changes is not efficient. Every other web app framework I've tried allows updating static content files on the fly(even just Spring MVC and plain old WAR application).

I've setup my project from "Building a RESTful Web Service with Spring Boot Actuator" guide (http://spring.io/guides/gs/actuator-service/). Basically it uses Spring Boot and MVC controllers to create a REST service. In addition, I've used "Consuming a RESTful Web Service with AngularJS" guide (http://spring.io/guides/gs/consuming-rest-angularjs/) to build a frontend with AngularJS. It creates a web page that displays the response from the REST service. The only change I've made is that the requests are made to my application instead of "http://rest-service.guides.spring.io/greeting". My static content is stored in "src/main/resources/public" folder. This setup works correctly except it doesn't reload static content.

palto
  • 3,523
  • 5
  • 32
  • 38
  • 2
    I also use IntelliJ to do application development and run the application (Run as Application) if it helps any. I've been thinking that maybe the problem is running the application with an embedded Tomcat is the problem. Should I run it in a real Tomcat when in development(to get refresh working properly) and deploy the final application with embedded Tomcat? I haven't found any recommendations in the Spring guides yet that says what is the best way to do this kind of development which bums me out. – palto Jul 15 '14 at 16:06
  • That's probably because it isn't the best way to do it. – Dave Syer Jul 15 '14 at 21:32

17 Answers17

52

A recap of the original problem

I've run into the problem that when I make modifications to my static content (html, js, css), I have to restart the application every time

I had the same problem and finally solved it by adding

<configuration>
    <addResources>true</addResources>
</configuration>

to spring-boot-maven-plugin in the pom.xml I got confused by this spring-boot-devtools thing, but it had no effect whatever I did.

My static content is stored in "src/main/resources/public" folder.

Your path is just fine. src/main/resources/static is also fine.

buræquete
  • 14,226
  • 4
  • 44
  • 89
eigil
  • 1,172
  • 2
  • 12
  • 23
  • 3
    I found this works with `mvn spring-boot:run` but now resources don't load at all in Eclipse. – pnewhook Mar 28 '17 at 20:18
  • 1
    The underlying assumption of the above solution is that you have disabled thymeleaf cache. Otherwise it wont make sense. @fudy maybe you should try setting false to spring.thymeleaf.cache property and try the above. – Abubacker Siddik Aug 20 '17 at 05:30
  • This is the correct answer if you're not using an IDE. I really wish it were easier to search "spring boot reload static content without IDE" and see this. It would have saved me hours of reading information that only applies to IDEs (such as enabling devtools) – itdoesntwork Jun 01 '18 at 19:12
  • 2
    Would you mind to share where exactly you are inserting the configuration tag inside `pom.xml`? – acarlstein Apr 18 '19 at 18:30
  • I put the spring-boot-maven-plugin into – eigil Apr 23 '19 at 07:40
  • this solved my problem after a whole day googling...thanks a lot! – Mark Mar 18 '21 at 08:36
29

Ah ... I came across this issue too.

Instead of putting your static content in the classpath src/main/resources/public folder, put them in src/main/webapp, the same as you would any other Java web app. The embedded Tomcat will automatically reload them whenever they change.

As mentioned in the comments, the default configuration will not include the resources that are in src\main\webapp. To get around this issue, you can just add the following to your pom.xml <build> node:

<plugin>
    <artifactId>maven-resources-plugin</artifactId>
    <version>2.6</version>
    <executions>
        <execution>
            <id>copy-resources</id>
            <phase>validate</phase>
            <goals>
                <goal>copy-resources</goal>
            </goals>
            <configuration>
                <outputDirectory>${basedir}/target/classes/static</outputDirectory>
                <resources>
                    <resource>
                        <directory>src/main/webapp</directory>
                        <filtering>true</filtering>
                    </resource>
                </resources>
            </configuration>
        </execution>
    </executions>
</plugin>

By using the resources plugin, you are able to do your local development by running the executable JAR:

java -jar target/.jar

While that is running you can use Chrome Dev Tools or whatever IDE you like for modifying the files, without restarts. However, whenever you run your build, then the package generated will include all of the files under src\main\webapp in src\main\resources\static.

Steve
  • 9,270
  • 5
  • 47
  • 61
  • 2
    Answer: because "src/main/webapp" is not normally packaged into a jar archive by standard build configurations (in Maven or Gradle). – Dave Syer Jul 15 '14 at 21:29
  • Spotted the issue. All but one of my apps are pure REST services, so I hadn't noticed that the packaged JAR didn't include the files under `webapp`. Maven config added to the answer to ensure that the `webapp` content gets built into the package. – Steve Jul 16 '14 at 14:39
  • Fine. But 1) you can just put it in "src/main/resources/static" in the first place and it still should work for static resources, and 2) it won't work for actual servlet context resources, 3) the mapping from "src/main/webapp" to "/static" will be confusing for most people. – Dave Syer Jul 16 '14 at 15:47
  • I'm happy with that. At least the static resources get reloaded whenever I make changes, which makes my development life much easier when I'm not running inside an IDE. And I tend not to run anything from the IDE due to the fact that I have multiple applications running simultaneously. So it's easier for me to start each of them using `java -jar`. – Steve Jul 16 '14 at 16:19
  • 1
    Thanks. If you have a WebMvcConfigurerAdapter, you'll also have to map both the classpath and file resources. `registry.addResourceHandler("/scripts/**").addResourceLocations("classpath:/public/scripts/", "/scripts/");` – softwareloop May 11 '17 at 10:02
15

The docs say "all modern IDEs allow reloading of static resources and usually also hot-swapping of Java class changes" (https://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/html/howto.html#howto-hotswapping). It's true. Eclipse does it more or less by default, and I'm not an IntelliJ user, but from what I understand you can configure it to build automatically as well.

Sergey
  • 7,985
  • 4
  • 48
  • 80
Dave Syer
  • 56,583
  • 10
  • 155
  • 143
  • 2
    Great, thanks! So it was in the official documentation after all. There is a great Stackoverflow answer on how to do hot swapping of resources on IntelliJ here: http://stackoverflow.com/a/18492881/325324. – palto Jul 16 '14 at 10:52
8

A colleague and I came across this issue as well. We found the answer in the IntelliJ documentation...

On the main menu, choose Run | Reload Changed Classes

hyness
  • 4,785
  • 1
  • 22
  • 24
  • 1
    Thanks, this worked also for static assests even if idea said no files have been reloaded. Quick shortcut: Alt+U then A – Rodislav Moldovan Jan 02 '16 at 16:27
  • 1
    I don't see this option in version Community 2016.1 – Daniil Shevelev Mar 26 '16 at 17:02
  • Nor is this option present in Ultimate 2016.1, I can't seem to find a way to make IntelliJ automatically refresh my static resources (JavaScript files) for a Sprint Boot application. I have to restart the entire app. – Craig Otis May 18 '16 at 00:33
  • 2
    Under run configurations in IntelliJ there is an option for both "On 'Update' action" and "On frame deactivation". Set both of these to "Update classes and resources". These options exist if you are deploying to Tomcat – newtonrd Jun 22 '16 at 20:05
  • I did this but it reloads all classes after restarting :( – Luís Soares May 06 '18 at 22:11
4

My solution (written in Kotlin but is quite obvious):

@Controller
class WebController : WebMvcConfigurerAdapter() {

    override fun addResourceHandlers(registry: ResourceHandlerRegistry) {
        System.getProperty("resources.local.path")?.let {
            registry.addResourceHandler("/**").addResourceLocations(it)
        }
    }
...
}

Main idea is you can add your own resource handler conditionally. E.g. if some system property is set (resources.local.path) then add resource location with value from the property. Then you set this property in development with some reasonable value like '-Dresources.local.path=file:/Users/andrey/Projects/gsp-test/src/main/resources/static/'.

Do not forget trailing slash.

viator
  • 1,413
  • 3
  • 14
  • 25
4

I am using 1.5.8.RELEASE.

It instantly updates my changes especially static files or jsp files.

If you are using Maven. You need to add this in pom.xml

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

And you have to start Spring Boot with this:

mvn clean spring-boot:run

Full example and more detail here https://www.surasint.com/spring-boot-with-auto-update-changed-files-example/

Surasin Tancharoen
  • 5,520
  • 4
  • 32
  • 40
  • 1
    This same spring-boot-devtools dependency also works on version 2.0.4.RELEASE. Now my JSPs are reloading from changes in my IDE as expected. Thanks!!! – Peter Tarlos Oct 26 '19 at 04:30
3

@eigil metioned addResources config for maven build. I'm using spring-boot-gradle-plugin in a gradle build, and I found this Spring Boot github issue , and the Spring Boot doc mentioned this option too. Just add this directive to build.gradle and run Gradle task bootRun, then resource file refreshes immediately when saved. FYI.

briskr
  • 31
  • 3
  • 1
    for Spring Boot 2.0, bootRun { sourceResources sourceSets.main } according to [reference docs](https://docs.spring.io/spring-boot/docs/2.0.0.RELEASE/gradle-plugin/reference/html/#running-your-application-reloading-resources) – briskr Mar 19 '18 at 03:00
2

I had the same issue , the solution proposed here seems logical and worked for me in breif : 1- ctrl+shift+A 2- search for registry 3- in the opened dialogue search for "compiler.automake.allow.when.app.running" and check it http://garywaddell.com/2015/11/20/spring-boot-intellij-idea-not-reloading-static-content/

ameen
  • 686
  • 5
  • 12
  • 1
    In addition, I add "Build project automatically" option. For example: http://olyv-qa.blogspot.com/2017/09/intellij-idea-apply-changes-in-spring.html – olyv Sep 08 '17 at 05:09
1

For eclipse you have to activate the Project -> "Build Automatically" option as a minimum configuration.

tObi
  • 1,864
  • 3
  • 20
  • 32
1

What I ended up using was Browsersync with grunt. browsersync and grunt watches your static resources on disk and updates the browser when you edit the files. It acts as a kind of proxy. This way you can see changes in UI immediately without building or restarting anything.

Grunt, browsersync, spring boot and angularjs are configured for you if you use JHipster which I used to setup my project.

Granted this requires a lot more tools than just an IDE and is a lot more complicated so I wouldn't recommend this for every project.

palto
  • 3,523
  • 5
  • 32
  • 38
1

spring-boot-devtools is not the solution to "hot deploy" of edited static htm/js

I configured a web facet inside intellij so that when I use it to edit html/js file inside resources/static, intellij then knows to copy the updated file to ./target and the spring boot application I have launched inside the automatically displays that content

see https://www.jetbrains.com/help/idea/2016.2/configuring-static-content-resources.html

pbirnie
  • 183
  • 3
  • 7
1

The Java version of @viator 's answer:

@Configuration
class WebMvcConfigurer extends WebMvcConfigurerAdapter {
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/dist/*.js").addResourceLocations(
            "file:src/main/typescript/dist/"
        );
    }
}
wizawu
  • 1,881
  • 21
  • 33
  • 1
    I had to exclude the dot at the beginning for this to start - "file:src/main/resources/static/". Also I only used "*.js" – Mihkel L. Mar 05 '18 at 12:21
  • @MihkelL. Yes, you are right. I just updated the answer. Thx. – wizawu Mar 06 '18 at 06:28
  • for it to work with `java -jar app.jar` I changed it to: `registry .addResourceHandler("/resources/**") .addResourceLocations("static/");` – Mihkel L. Mar 07 '18 at 11:03
1

You can do it by just adding one more dependency

you Gradle

compile group: 'org.springframework.boot', name: 'spring-boot-devtools', version: '1.3.0.RELEASE'

In you Pom.xml

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <version>1.3.0.RELEASE</version>
</dependency>
Dev Sabby
  • 1,367
  • 1
  • 11
  • 17
0

You have two possebilities how to serve static webcontent

  1. From the classpath (per default src/main/resources/static or src/main/resources/public or META-INF/resources/)
  2. From the file system (per default src/main/webapp)

If you pick solution 1) - you can safely copy the jar around as the static web content is within that jar. If you want that the server picks up changes, you need to do (auto)hotswapping.

If you pick solution 2) - everything will work out of the box, every change will be automatically picked up. HOWEVER - if you copy the final jar to a different location - things will stop working. That is unless you specify an absolute path in application.properties. For example:

spring.resources.static-locations=file:///C:/myspringbootapp/src/main/webapp

So solution 2) is easier but less portable. Solution 1) is portable but more difficult to use(ide config).

Holgi P
  • 141
  • 1
  • 3
0

For Spring Boot 2+ with gradle Kotlin dsl:

tasks.bootRun {
    sourceResources(sourceSets.getAt(SourceSet.MAIN_SOURCE_SET_NAME))
}

thanks to @briskr's answer for the gradle dsl version :)

mowwwalker
  • 16,634
  • 25
  • 104
  • 157
0

I had the same problem with live reloading of static contents in my SpringBoot porject: Now from various solutions posted in StackOverflow, I am able to get the solution. Following are the tools I used for development: IntelliJ Idea & Google Chrome in Ubuntu 18.04 I did the following:

  1. Kept the templates folder in resourses folder itself. (Some solutions I found it to be kept in webapp folder under the main, but I did not get the result)
  2. Add this configuration
    <addResources>true</addResources>
    </configuration>

to spring-maven-plugin in your POM file.

3.Please don't forget to add this dependency to POM file

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
  1. Add the Live Reload extension to your web browser.

  2. Restart the server using ' mvn clean spring-boot:run ' (only then the changes will be reflected in the build). During server startup you can see the message Live Server started at ....

  3. Load the page using localhost:8080/... and click the LiveReload extension to connect it with the server.

  4. Make any change to your static HTML file in the resources/ templates folder. Save it and check the webbrowser again, it will be reflected there...

Jilson P Jose
  • 61
  • 1
  • 4
0

This is my solution using Spring Boot 3.1.2, Spring Boot Web Starter, and Spring Boot Devtools.

Set the following properties in application-dev.properties:

spring.web.resources.static-locations=file:src/main/resources/static/
spring.devtools.restart.additional-paths=file:src/main/resources/static/

Notice that this is a development-only set up because in production static content will be served from the classpath.

What it does:

  • Devtools is instructed to restart the service whenever there are changes in the static directory. This will trigger a live reload. It pairs great with live-reload browser extensions.
  • Static resources are served from the file system instead of the classpath. It allows serving updated static files without needing to rebuild the project. Thus, eliminating the need to configure your IDE to rebuild on every save which may be a little slow, depending on the project.

I use this setup for a web project built with Vite, (vite build --watch) dumping all output in src/main/resources/static/ and Spring Boot Devtools picking up the changes almost immediately.