I’m a fan of Java. If you haven’t given it a chance in a while, you may not have noticed that, Java has quietly been adopting some of the best practices that make dynamic and functional languages so appealing, without sacrificing the many hundred person-years of effort that have made Java and the JVM a world-class development environment. Java is still one the world’s most popular programming languages with roughly 9 million programmers using Java. It doesn’t make sense to ignore all of that history and development effort because of a niggling feeling that Java is a little too verbose, or that XML is so last year.

Yet it can still be difficult to convince people that Java is a valid choice for modern web development. Compounding this problem is the sheer volume of choice for newbie Java developers — it can be simply overwhelming to get started with Java. This post attempts to cut through the noise by providing an opinionated starting point for building a modern Java web application using Spring Boot and Visual Studio Code. Follow along with me as we go from nothing but an editor window to a full-fledged, performant, testable, and secure web application built with Spring Boot and Java.


Part 1: Setting Up

Java development on Visual Studio Code (VS Code) requires a few extensions to be installed for an optimal experience. Thankfully, if you get stuck anywhere along the way, the VS Code documentation provides an excellent tutorial.

Installing the Java Extension Pack for Visual Studio Code

In contrast to IDEs such as IntelliJ or Eclipse, the concept of a “Java project” is entirely foreign to Visual Studio Code. Instead, VS Code provides extensions to support Java projects. Depending on the popularity of the language, the extension marketplace for VS Code can be a bit of a minefield of unsupported, deprecated, or just broken projects. Thankfully, Microsoft curates or maintains extensions for most popular languages, and a recent addition to the extension marketplace includes the concept of packs — collections of extensions that work well together to fully support a language.

The Java Extension Pack is maintained by Microsoft and bundles the most popular VS Code extensions for Java in one place. To get started with Java in VS Code, install the Java Extension Pack by navigating to the extension in the marketplace or do it through VS Code itself by opening the extension sidebar with CTRL+SHIFT+X, searching for Java Extension Pack, and clicking install.

After the necessary Java extensions are installed, opening a VS Code workspace that contains Java artifacts will cause those extensions to load and understand those artifacts and present options for working with them.

Installing Spring Boot Extensions

Spring and Spring Boot are additional libraries and frameworks for building Java applications. Like Java itself, VS Code knows nothing about Spring and requires extensions to support it. There are three extensions in particular that are worth installing to provide full support for Spring Boot: Spring Initializr, Spring Boot Tools, and Spring Boot Dashboard. Spring Initializr is an extension to generate a Spring Boot project and maintain Spring Boot dependencies. Spring Boot Tools provides additional support for editing .properties and .yml files for Spring Boot projects, and the extension provides additional support for editing .java files in Spring projects. Lastly, Spring Boot Dashboard provides a view to manage Spring Boot projects in your workspace, allowing you to quickly start, stop or debug a Spring Boot project.

Go ahead and install these extensions. They will help us get started with Spring Boot.

Configuring the JDK

You can configure the JDK used by VS Code using the Java: Configure Java Runtime command. To do this, bring up the Command Palette with CTRL+SHIFT+P and type Java: Configure Java Runtime to select the command. From here you can install a JDK and view or update your settings to make sure that the environment variables JDK_HOME and JAVA_HOME are set correctly. To set environment variables in Windows, follow this tutorial. For macOS or Linux, follow this tutorial

Configuring the Java Runtime

Part 2: Creating a Basic Spring Boot Project

Spring comes with a tool called Spring initializr which is available online at https://start.spring.io/ and is integrated into VS Code through the Spring Initializr extension we installed earlier. You can run the Initalizr through the VS Code Command Palette (CTRL+SHIFT+P) and type Spring Initializr: Generate a Maven Project. Follow the prompts to create your project with these settings:

  • Specify a project language. Java
  • Input Group Id for your project. I used com.sookocheff.
  • Input Artifact Id for your project. I used springdemo.
  • Specify Spring Boot version. 2.2.7
  • Search for dependencies. Select Spring Boot DevTools and Spring Web

Create your project in a new folder to get started. Once done, you can open the project folder in VS Code. On the left-hand sidebar in File Explorer mode you can see a Spring-Boot Dashboard heading. Within this heading you can start and stop your Spring Boot application. You can also use the standard VS Code run configuration created for you in .vscode/launch.json.

Running an application through Spring Dashboard.

Go ahead and start the application using the Spring-Boot Dashboard, which runs an embedded Tomcat server instance making your application available from localhost:8080. Navigate to that URL using your browser to see your running Tomcat server (which throws an error because nothing is implemented yet).

A running Tomcat server displaying a default error message.

What did Spring Initializr do to me?

Although it’s nice to get up and running quickly, I tend to be distrustful of magic, so let’s dive deeper into the starter project to uncover what Spring Initializr did for us.

Dependencies

During setup, we told Spring Initializr to add the Spring Boot DevTools and Spring Web dependencies. You can see these dependencies, along with a starting test framework in the pom.xml file created for your project:

<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>
        <exclusions>
            <exclusion>
                <groupId>org.junit.vintage</groupId>
                <artifactId>junit-vintage-engine</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
</dependencies>

Spring Boot DevTools

Spring Boot DevTools provides some opinionated configuration and tooling that can make the development experience a little better. First, DevTools sets some default properties for Spring libraries to disable caching during local development, and to log web requests at the DEBUG logging level. The full set of property defaults configured by DevTools is available on Github

Most dynamic programming languages provide a live reload development environment that automatically updates the code running on the server whenever code changes in the file system. DevTools provides this functionality by restarting the application server any time the classpath is modified. This restart can be combined withDevTools a browser refresh whenever a file is changed. You can install the LiveReload browser extensions to have the browser automatically refresh after any changes are detected.

Spring Web

The Spring Web dependencies is a container for a few different libraries that aid in web development. Specifically, Spring Web uses Spring MVC as a model-view-controller pattern, Jackson for JSON processing, and an embedded Tomcat server, along with the required configuration for servlet handling, error pages, and bundling your application as a WAR file for deployment to the Tomcat servlet container.

SpringApplication

In addition to setting up the required dependencies, Spring Initializr created a default Application based on your configuration. In my case, this file was called DemoApplication with the sole job of running your Spring app.

package com.sookocheff.springdemo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {

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

}

Within the public static void main method, we have a single line: SpringApplication.run. The SpringApplication class provides a convenient way to bootstrap a Spring application that is started from a main() method using the static run method.

The @SpringBootApplication annotation on your main class enables some of Spring Boot’s opinionated parts. This annotation, is actually just a combination of three annotations:

  1. @Configuration: Allows us to import additional configuration classes and beans.
  2. @EnableAutoConfiguration: Automatically configures your application based on the chosen dependencies.
  3. @ComponentScan: Automatically register Beans and Components to be managed by Spring’s IoC container. If you are not familiar with Spring Bean’s, consult this tutorial.

Structuring the rest of your application

Spring Initializr created a default main class. To add to this application, you keep the main class as the root of your project and add packages and classes as subdirectories of this main application. With this structure, the @SpringBootApplication in the main class defines a base package to search for additional Spring components using the @ComponentScan annotation.

To continue building your example, the recommended application structure looks something like this:

com
 +- sookocheff
    +- DemoApplication.java
    |
    +- customer
    |   +- Customer.java
    |   +- CustomerController.java
    |   +- CustomerService.java
    |   +- CustomerRepository.java
    |
    +- order
        +- Order.java
        +- OrderController.java
        +- OrderService.java
        +- OrderRepository.java

Part 3: A Simple Web Service

Now that we have a project setup and integrated with VS Code, we can create a simple web service. To do this, we will create simple REST endpoint that returns a JSON payload representing a player. The basic data model is defined in a simple Player class:

package com.sookocheff.springdemo.player;

public class Player {

    private final String playerId;
    private final String alias;
    private final String displayName;

    public Player(final String id,
                  final String alias,
                  final String displayName) {
        this.playerId = id;
        this.alias = alias;
        this.displayName = displayName;
    }

    public String getPlayerId() {
        return playerId;
    }

    public String getAlias() {
        return alias;
    }

    public String getDisplayName() {
        return displayName;
    }
}

With this basic data model in place, we can serve requests. The Spring Web dependency adds the Spring MVC library implementing our model-view-controller pattern for handling web requests. HTTP requests are handled by a controller identified by the @RestController annotation, and individual URLs are handled with the @GetMapping annotation.

package com.sookocheff.springdemo.player;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class PlayerController {

    @GetMapping("/player")
    public Player getPlayer(@RequestParam(value = "playerId", defaultValue = "12345") final String playerId) {
        return new Player(playerId, "soofaloofa", "Kevin Sookocheff");
    }
}

This simple application can be run using the Spring Dashboard or run configuration. Navigate to localhost:8080/player to view the simple response.

What magic happened here?

Navigating to the /player URL provides a fully serialized JSON response for the basic Player class we defined. How did this happen? Spring Boot works by looking at the classpath of your project and at any of the beans or components you have configured and connects those pieces together into a working application.

When we use the @SpringBootApplication we transitively add the @EnableAutoConfiguration. The @EnableAutoConfiguration annotation tells Spring Boot to “guess” how you will want to configure Spring, based on our dependencies. For example, our project includes Spring MVC on the classpath because it was included as part of the spring-boot-starter-web dependency. Once Spring Boot sees this dependency on the classpath, it adds the proper configuration to your application to setup Spring Web MVC with sane defaults.

The Spring Web MVC framework is designed around a DispatcherServlet that handles all the HTTP requests and responses. Spring Web MVC also requires a WebApplicationContext that tracks the instantiation of controllers and handlers. In a traditional Spring application, these components are configured using XML. What Spring Boot does for us is convert the XML-based configuration of a traditional Spring app with code-based annotations and sane defaults. If, at any time, you want to make changes to the sane defaults you can tell Spring Boot to not auto-configure a certain component by using an exclude parameter to the @SpringBootApplication annotation. The full list of auto configuration and how it works is available in the Spring Boot reference documentation

Where to go next?

Spring Boot (and Spring) is a big project, and it is hard to understand something of this scope in a single afternoon. If you want to learn more, the reference documentation for both Spring and Spring Boot are excellent resources.