This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
Create a Spring Boot app
To get started, go to start.spring.io and generate a new Spring Boot app. Make sure to pick at least the Web dependency. We are going to use Gradle as build tool.
Serving static content with Spring Boot
To serve our front end web app from a Spring Boot jar file, we need to first understand how Spring Boot handles static content.
By default, Spring Boot serves static content from a directory called
/static
(or/public
or/resources
or/META-INF/resources
) in the classpath or from the root of theServletContext
For example, the following is an example index.html
that we will put into src/main/resources/static/
folder.
<html lang="en">
<head>
<meta charset="utf-8">
<title>Static content test</title>
</head>
<body>
<h1>Hello World</h1>
</body>
</html>
When we start our Spring Boot app and point our web browser to it (with default configuration the URL should be localhost:8080
), Hello World should be displayed to us. Everything in src/main/resources/static/
is moved to the correct location in the packaged jar file. Now that we know how to serve static content, it should be relatively easy to serve the React app in a similar manner.
But wait! Javascript source files have to be built first, CSS minified etc. We can’t just put our React source files to src/main/resources
and expect everything to work.
That’s correct. We have to build the React app before we can move it into the /static
directory. Let’s have a look at how to do that. But first, we need to generate a new React app.
Create a React app
It’s super easy to get started with React by using create-react-app
. If you have the necessary tools installed (e.g. node
, npm
), you can just execute this command
npx create-react-app enter-app-name-here
It does not meter where exactly to place the generated web app. This projec going to use src/main/webapp
path.
Gradle build script
Before we can put our web app to production, we must create a minified bundle with npm run build
. To serve the minified bundle with Spring Boot, we have to move it to one of the directories where Spring Boot serves static content. This project is using Gradle to build and package the Spring Boot application, but the same can be achieved with other build tools. The key is to remember that in addition to building the Java code, we must also create the minified bundle of our web app and copy it to the correct directory.
To integrate a Gradle build with Node, the gradle-node-plugin
can be used.
[!TODO] The following are the most interesting parts of a Gradle build script from outdated
gradle-node-plugin
plugin
plugins {
id "com.moowork.node" version "1.3.1"
}
....
// Read more about how to configure the plugin from
// https://github.com/srs/gradle-node-plugin/blob/master/docs/node.md
node {
download = true
// Set the work directory for unpacking node
workDir = file("${project.buildDir}/nodejs")
// Set the work directory for NPM
npmWorkDir = file("${project.buildDir}/npm")
}
task appNpmInstall(type: NpmTask) {
description = "Installs all dependencies from package.json"
workingDir = file("${project.projectDir}/src/main/webapp")
args = ["install"]
}
task appNpmBuild(type: NpmTask) {
description = "Builds production version of the webapp"
workingDir = file("${project.projectDir}/src/main/webapp")
args = ["run", "build"]
}
task copyWebApp(type: Copy) {
from 'src/main/webapp/build'
into 'build/resources/main/static/.'
}
appNpmInstall
is a Gradle task that runs npm install
in the webapp
directory. Similar to appNpmInstall
, the build script declares the appNpmBuild
task that runs npm run build
to create the minified bundle of the web app. Finally, copyWebApp
is a simple Copy
task to copy the minified bundle to the static content directory. Feel free to test run these tasks in isolation and see how they work. For example, running gradle appNpmInstall
should install all of our web app dependencies and place them in src/main/webapp/node_modules/
directory.
Development flow
During web app development, you should start the web app in development mode. Therefore, instead of bundling the web app inside the Spring app, let’s serve it separately using the dev server. Go to the webapp
directory and run npm start
. This way, your web app will reload automatically if you make any changes in the source files. At the same time, you should also start the Spring Boot application.
To make the development flow with Spring Boot a little more pleasant, you can also configure spring-boot-devtools
and enable automatic restarts. When configured, the Spring Boot app restarts whenever files on the classpath change.
Build for production
To build our application for production, in addition to compiling and packaging our Java code, we must also create a minified bundle of the web app and place it inside the Jar file to one of the directories where Spring Boot serves static content. We already have our build script configured with tasks that can install our dependencies, create the minified bundle and copy it to the correct location. To make the packaging a bit more simpler, we could define some dependencies between the build tasks in our build script.
appNpmBuild.dependsOn appNpmInstall
copyWebApp.dependsOn appNpmBuild
compileJava.dependsOn copyWebApp
This makes sure that whenever Java code is compiled, web app dependencies are also installed, minified bundle is created and the bundle is copied to a static content directory. When you run gradle clean build
, you don’t have explicitly run any web app specific Gradle tasks. Once the build has finished, you can start the application via command-line.
java -jar path/to/web-app.jar
Point your browser to it and you should see the newly created React app running.