Introduction
I love development. Add some code. Check that it works. Ehhhh…. Why doesn’t my change work???? …… ah, I forgot the restart….
You know that? I hate it!
This is my situation: I want add some Javascript code to my Spring Boot application. So, there is no change in the application back end, only in the front end.
Unfortunately I don’t get these frontend changes into my application container fast enough. An average restart took me ~7 secs. When doing small changes, it happens that I do 5 restarts within 5 minutes. With that approach I would have to wait 35 seconds for testing an implementation. That’s too much waste.
In this article I describe what I have done to solve this. Or better: I found a way to reduce the effort. Fortunately there are some mechanisms that support resource reloading. In summary I changed IDEA to use the gradle daemon instead of it’s recommended gradle wrapper implementation. So the reload only takes ~700ms per iteration.
For more details: go on reading. Feedback is of course always appreciated.
The Setup
- Spring Boot 1.3.3
- Languages: Java, Groovy
- Resources in /src/main/resources/static/*
Reload? Restart?
There is a difference between reloading and restarting.
Reloading means the replacement of artifacts in a running system, i.e. replacing method implementations, new classes, another resource that is put into the classpath, etc. Advantage of this is, that I can rapidly iterate through the my dev steps. Disadvantage is, that there can be incompatible artifacts in the container that cause strange exceptions.
A restart performs a shutdown on the the old container and starts a new one. The advantage is a completely fresh system with it’s last recent resources. Disadvantage is the break (several seconds), you have to accept.
The spring-boot-dev way
The official documentation recommends the spring-boot-devtools [SPDev]. The plugin can be used for several things. The main issue is a restart (!) when classes in the classpath change. This is done by a special class loader provided by the Spring Boot plugin.
Unfortunately this isn’t an useful approach:
- it still is a restart, not a reload
- it only works when classes are automatically replaced by IDEA IDE. It seems that this doesn’t happen. IDEA builds the project before it starts the Spring Boot application. But that’s too late for me
- Resource files are not the concern of this plugin
So, this solution isn’t quite the right thing for me.
IntelliJ IDEA support
Unfortunately IDEA’s support for Spring Boot seems to be a bit basic (in the Community edition :-)). These are my findings:
- IDEA puts its resource data directly under build/resources.
- [JB2016] describes a HotSwap mechanism for class methods; it’s not used when resources change
- There is not automatic deployment by IDEA into this directory
So, also IDEA doesn’t offer a good way to enable faster iteration through Javascript code in a Spring Boot environment.
Gradle support
When you use gradle, there is a target called processResources. It’s faster (4.5 secs) than restarting the whole spring project (~7 secs). There is another trick to make it even faster. Gradle offers a daemon mode [GRDLD]. With some configuration, it starts the first time and then keeps in background as a daemon process.
So, this is the workaround. After having done my changes I manually have to trigger this processRessource job. It’s not as good as I expected but it reduces the processing time to a few milliseconds (700ms). That’s okay for me as for the first approach.
This is what you I have done…
… with gradle
- install latest gradle version
- read the instructions in [GRDLD]; it sets the daemon mode to on and keeps gradle running
… with IDEA
- go to IDEA and Edit your Gradle settings (File -> Settings -> Build, Execution, Deployment)
- switch OFF „Use default gradle wrapper“
- ensure that your installed gradle version is found
- (optional) go to the Gradle menu and right-mouse-click on job „processResources“. In the context menu select „Assign Shortcut…“, create one, i.e. Ctrl-Shift-#
I hope I was able to explain my approach. If there are better ways to solve that (besides from splitting this project into nodeJS project for the frontend and Spring Boot for the backend 😉 ), let me know.