Full-stack application in Java: Quick start
Java project quick start

In the article introducing this series, I shared that I wanted to explore full-stack app development. In particular, I wanted to develop a full-stack application that helps people manage their checklists. Users will use a browser to connect to the front-end server, which, in turn, will use the API provided by the back-end server to access and modify the checklists.
This article is the first one describing what I am doing in Java + Spring. I will cover project creation and structure, and build automation. We won't have much of the application implemented by the end of this article, but it should be a good and helpful start. So buckle up and let's get cracking!
Project structure and setup
We are going to use maven for our project and configure a multi-module project with it. We want to have a module for the front end and another one for the back end. We are going to create those modules as individual projects using Spring Initializr and then we will put them together in a multi-module project. This is what I have used for both modules. And, yes, I will be adding more dependencies in future articles.
Front-end module
- Project
- Maven
- Language
- Java
- Spring Boot
- 3.5.7 (Latest stable)
- Group
- dev.jorgeortiz
- Artifact
- rexlists-fe
- Name
- RexlistsFrontEnd
- Description
- Rexlists Front End
- Package name
- dev.jorgeortiz.rexlists-fe
- Packaging
- Jar
- Properties
- YAML
- Java
- 25
- Dependencies
-
- JTE (for the templates)
- Spring Web (for the front-end application)
- Spring Boot DevTools (for live reloading)
Back-end module
- Project
- Maven
- Language
- Java
- Spring Boot
- 3.5.5 (Latest stable)
- Group
- dev.jorgeortiz
- Artifact
- rexlists-be
- Name
- RexlistsBackEnd
- Description
- Rexlists Back End
- Package name
- dev.jorgeortiz.rexlists-be
- Packaging
- Jar
- Properties
- YAML
- Java
- 25
- Dependencies
-
- Spring Web (for the front-end application)
- Spring Boot DevTools (for live reloading)
Multi-module
The Spring Boot projects we have created and downloaded serve as a starting point. They have the initial content for both modules and are good enough for producing individual applications. However, we need to introduce changes to make them work together in a single project.
We create a directory for the parent project and call it rexlists. Inside that directory, we uncompress each of the
two projects. This is the version that can be found in the initial commit of the repository that I created for this
project.
mkdir rexlists
cd rexlists
unzip ~/Downloads/rexlists-fe.zip
unzip ~/Downloads/rexlists-be.zipWe also want to move the git configuration files to the root directory of the parent project, so none of the files are altered in the process.
mv rexlists-fe/{.gitignore,.gitattributes} .
rm rexlists-be/{.gitignore,.gitattributes}Build automation
MVN configuration
Being a Spring project, build automation is included. I chose to use Maven when I created the two projects, but you can go with Gradle, if you are more comfortable with it.
Now we need to create a pom.xml for the parent project and modify the pom.xml of the modules. We will use the
pom.xml from one of the modules to reduce the amount of typing.
cp rexlists-fe/pom.xml .
The pom.xml for the project should be:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.5.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>dev.jorgeortiz</groupId>
<artifactId>rexlists</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Rexlists</name>
<description>Rexlists application project</description>
<packaging>pom</packaging>
<url/>
<licenses>
<license>
<name>Apache-2.0</name>
<url>https://www.apache.org/licenses/LICENSE-2.0.txt</url>
<distribution>repo</distribution>
</license>
</licenses>
<developers>
<developer>
<id>jdortiz</id>
<name>Jorge D. Ortiz-Fuentes</name>
<url>http://jorgeortiz.dev/</url>
</developer>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>25</java.version>
</properties>
<modules>
<module>rexlists-fe</module>
<module>rexlists-be</module>
</modules>
</project>
On the content that I copied from the pom.xml of one of the modules, I have:
- Changed the name of the
artifactIdto berexlistsandnameanddescriptionaccordingly. - Specify the packaging to be
pom, rather than the defaultjar. - Modified the license to be Apache 2.
- Added some data about me as the developer.
- Added the
<modules>section with the names of the directories of the submodules. - Removed all the dependencies from here. We may add the ones shared by all the submodules later.
- Removed the
buildsection with the plugins.
The pom.xml of the rexlists-fe module should change to be like this:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>dev.jorgeortiz</groupId>
<artifactId>rexlists</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>rexlists-fe</artifactId>
<name>Rexlists Back End</name>
<description>Rexlists Front End</description>
<url/>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>gg.jte</groupId>
<artifactId>jte-spring-boot-starter-3</artifactId>
<version>3.1.16</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>gg.jte</groupId>
<artifactId>jte-maven-plugin</artifactId>
<version>3.1.16</version>
<executions>
<execution>
<id>jte-generate</id>
<phase>generate-sources</phase>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<sourceDirectory>${project.basedir}/src/main/jte</sourceDirectory>
<contentType>Html</contentType>
<binaryStaticContent>true</binaryStaticContent>
<targetResourceDirectory>${project.build.outputDirectory}</targetResourceDirectory>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>- Change the whole
parentsection to be our parent project with norelativePath, which, in turn, inherits from the Spring Boot one. - Remove the
groupIdand theversion, because they are inherited from the parent (and we are going to use the same version in all the modules.) - Remove the
licenses,developers,scm, andpropertiessections, because they are inherited from the parent project.
And the pom.xml of the back end should have the following contents.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>dev.jorgeortiz</groupId>
<artifactId>rexlists</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>rexlists-be</artifactId>
<name>RexlistsBackEnd</name>
<description>Rexlists Back End</description>
<url/>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>- Change the whole
parentsection to be our parent project. Same as for therexlists-fe. - Remove the
groupIdand theversion. Same as for therexlists-fe. - Remove the
licenses,developers,scm, andpropertiessections. Same as for therexlists-feconfiguration. - Keep the
dependenciesandbuildsection as they are. Same as for therexlists-feconfiguration.
We also want to move some configuration to the parent project.
mv rexlists-fe/.mvn .
rm -rf rexlists-be/.mvn
And before we move on, use mvn validate to verify that all the changes are fine.
Make both modules run together
If you had a simple Spring Boot project, a mvn spring-boot:start would launch the application. However, we have
configured a multi-module project and we need to take an extra step (package or install) and run each module
separately. Let's start with the front end.
mvn -pl rexlists-fe clean package spring-boot:startSo far, so good. Now we launch the back end too.
mvn -pl rexlists-be clean package spring-boot:startIt will try to start the back end, but if will fail with an exception.
Error: Exception thrown by the agent: java.rmi.server.ExportException: Port already in use: 9001
This is the JMX management interface provided by the spring-boot-maven-plugin, and it is already being used by the
front end. We can change the port it listens to in the pom.xml of the back end. The plugin configuration should be
replaced with the following one.
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<jmxPort>9002</jmxPort>
</configuration>
</plugin>Let's try again to run the back end using the same command. This time, we get a different error.
Web server failed to start. Port 8080 was already in use.
Certainly, our front end is also using that port for its embedded Tomcat server, because it is the default one in a
Spring Boot applications. This time we can change the configuration for the back end by adding the following line to
rexlists-be/src/main/resources/application.properties.
server:
port: 8090
We can use a web browser to connect to the two servers http://localhost:8080/ and http://localhost:8090/ and
get… an error from each. 😱
Wait! All this effort to get two errors? We can indeed make a successful connection to both servers. However, they just reply with an error, because we haven't defined any content yet.
You don't need to do it now, but be aware that they can also be stopped separately using the following commands.
mvn -pl rexlists-fe spring-boot:stop
mvn -pl rexlists-be spring-boot:stopSummary
In this first article, I explained how to create the structure for our multi-module project and made the necessary fixes so that the building tasks would work as expected. I must acknowledge that most of the work was provided by the Spring Boot Initializr, that has the necessary configurability built-in, but we tweaked the configuration to make it work.
The full code repository for this project with individual commits for each part of the explanation is available for you to check what I did and where, and code along with me.
In the next article we will work on the front end to make it more useful.
Stay curious. Hack your code. See you next time!