Starting with Spring

This resource is intended for people with no previous experience with Spring, and possibly little programming experience too. If you’ve been exposed to Spring before or have worked with other app development frameworks you’re probably better off looking at other awesome resources, or even at Spring’s own documentation. If you’ve just finished learning how to program in Java, you’re starting your software development career, or have just been dumped into a huge project sprinkled with lots of Spring magic that makes no sense, this is meant for you.

You will become the next Spring Guru in your team!

What you’ll need

Why Spring?

The best way I can think of advocating for Spring is not by demonstrating what it does but rather explaining what problems it solves. Understanding why it’d be incredibly hard not to use it in a real world complex Java application is key. If right now you absolutely hate Spring for how complicated it is and how much harder your project is to understand because of it, the goal is to make you realise how it actually simplifies it and to feel grateful for it. Let’s get to it.

Dependency Injection

Imagine you want to build your own Amazon like company. For this you’ll probably need a website that can accept customer orders. Since the website could be one of many customer entry points (think about a mobile app) the actual handling can be delegated to a separate, central order processing unit. Such a unit needs to check the inventory for stock, charge the customer for the price of the item and dispatch the item. In coding terms it might look like this.

This Github Sample is by edu05
src/main/java/edu/learn/spring/dependencyinjection/vanilla/CustomerOrder.java view raw
package edu.learn.spring.dependencyinjection.vanilla;

public class CustomerOrder {
    private int itemId;
    private int customerId;

    public CustomerOrder(int itemId, int customerId) {
        this.itemId = itemId;
        this.customerId = customerId;
    }

    public int getItemId() {
        return itemId;
    }

    public int getCustomerId() {
        return customerId;
    }
}
This Github Sample is by edu05
src/main/java/edu/learn/spring/dependencyinjection/vanilla/AmazonWebsite.java view raw
package edu.learn.spring.dependencyinjection.vanilla;

public class AmazonWebsite {

    private final OrderProcessor orderProcessor;

    public AmazonWebsite(OrderProcessor orderProcessor) {
        this.orderProcessor = orderProcessor;
    }

    public void acceptOrder(CustomerOrder customerOrder) {
        orderProcessor.process(customerOrder);
    }
}
This Github Sample is by edu05
src/main/java/edu/learn/spring/dependencyinjection/vanilla/OrderProcessor.java view raw
package edu.learn.spring.dependencyinjection.vanilla;

import java.util.List;

public class OrderProcessor {
    private final List<Warehouse> warehouses;
    private final CustomerWallet customerWallet;

    public OrderProcessor(List<Warehouse> warehouses, CustomerWallet customerWallet) {
        this.warehouses = warehouses;
        this.customerWallet = customerWallet;
    }

    public void process(CustomerOrder order) {
        warehouses.stream()
                .filter(warehouse -> warehouse.isInStock(order.getItemId()))
                .findFirst()
                .ifPresent(warehouse -> {
                    customerWallet.chargeCustomer(order.getCustomerId(), order.getItemId());
                    warehouse.dispatchOrder(order.getCustomerId(), order.getItemId());
                });

    }
}
This Github Sample is by edu05
src/main/java/edu/learn/spring/dependencyinjection/vanilla/CustomerWallet.java view raw
package edu.learn.spring.dependencyinjection.vanilla;

public class CustomerWallet {
    public void chargeCustomer(int customerId, int itemId) {
        //would grab the card details of the customer with id customerId and charge for the price of the item
        //with id itemId
        System.out.println("Charging customerId=" + customerId + " for the price of itemId=" + itemId);
    }
}
This Github Sample is by edu05
src/main/java/edu/learn/spring/dependencyinjection/vanilla/Warehouse.java view raw
package edu.learn.spring.dependencyinjection.vanilla;

public class Warehouse {
    public boolean isInStock(int itemId) {
        //would check the inventory for stock
        System.out.println("Checking if itemId=" + itemId + " exists in the inventory");
        return true;
    }

    public void dispatchOrder(int customerId, int itemId) {
        //would take care of sending item with id itemId to customer with id customerId and update its stock
        System.out.println("Dispatching itemId=" + itemId + " to customerId=" + customerId);
    }
}
This Github Sample is by edu05
src/main/java/edu/learn/spring/dependencyinjection/vanilla/Application.java view raw
package edu.learn.spring.dependencyinjection.vanilla;

import java.util.ArrayList;
import java.util.List;

public class Application {

    public static void main(String[] args) {
        Warehouse warehouse1 = new Warehouse();
        Warehouse warehouse2 = new Warehouse();

        List<Warehouse> warehouses = new ArrayList<>();
        warehouses.add(warehouse1);
        warehouses.add(warehouse2);

        CustomerWallet customerWallet = new CustomerWallet();
        OrderProcessor orderProcessor = new OrderProcessor(warehouses, customerWallet);
        AmazonWebsite amazonWebsite = new AmazonWebsite(orderProcessor);

        amazonWebsite.acceptOrder(new CustomerOrder(3, 1));
    }
}

Note I have not fully implemented all of the methods, instead I have focused more on the structure of the code. All of the code is accessible via the git repo so you don’t have to waste your time copy pasting if you want to try this out for yourself. Here’s where Maven, Java 8+, Git and the IDE come into play.

The problem with this approach is you have to dedicate a number of lines to construct all of the object instances your application needs, and to make sure they’re declared in the correct order for everything to compile. This quickly becomes a boring task as the number of object instances grows in your project. One of Spring’s goals is to aid developers to focus in writing business specific code, while reducing the amount of clutter. The Spring equivalent of this example only changes in its Application class as shown.

This Github Sample is by edu05
src/main/java/edu/learn/spring/dependencyinjection/sprinkles/Application.java view raw
package edu.learn.spring.dependencyinjection.sprinkles;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;

import java.util.List;

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
        AmazonWebsite amazonWebsite = (AmazonWebsite) context.getBean("amazonWebsite");
        amazonWebsite.acceptOrder(new CustomerOrder(3, 1));
    }

    @Bean
    public AmazonWebsite amazonWebsite(OrderProcessor orderProcessor) {
        return new AmazonWebsite(orderProcessor);
    }

    @Bean
    public OrderProcessor orderProcessor(List<Warehouse> warehouses, CustomerWallet customerWallet) {
        return new OrderProcessor(warehouses, customerWallet);
    }

    @Bean
    public Warehouse warehouse1() {
        return new Warehouse();
    }

    @Bean
    public Warehouse warehouse2() {
        return new Warehouse();
    }

    @Bean
    public CustomerWallet customerWallet() {
        return new CustomerWallet();
    }
}

Now, you might argue the Spring version takes up even more lines of code, and rightly so! But that’s only because I’m going to use this example to explain Spring’s core concept. If you understand this section you’ll have dominated the most important aspect of Spring, and everything will be downhill after that.

A Spring application is formed of a Spring context. A Spring context is just a collection of Spring beans. A Spring bean is a long living* instance of an object that also has a name Spring can identify it by. Spring can only “sprinkle its magic” over object instances that are within its context.

And that’s it!

Notice the @SpringBootApplication annotation on the Application class, this lets Java know this is a Spring* application. Notice also the first line of the main() method. It constructs the Spring context by scanning the code for any methods that are annotated with @Bean and inserting those instances into it. It then returns the created context back to the caller. To prove that a Spring context is a collection of named beans I grabbed the AmazonWebsite from the context by its name and submitted an order.

You’re probably wondering how Spring could have created all the object instances in the correct order and wired them together, all outside of the main() method. I would resist the urge to understand the inner workings of how this could be achieved unless you know about Java’s Reflection - in which case…there you go…Spring does it via Java Reflection! Instead, suffice to say the SpringApplication.run() call requests Spring to scan the code looking for special annotations (for now just @Bean), building a graph of object dependencies and subsequently creating them in the correct order. Dependencies are indicated in the method signature of @Bean annotated methods. AmazonWebsite depends on an OrderProcessor, an OrderProcessor depends on a list of Warehouses and a CustomerWallet, but Warehouses don’t depend on anything for their construction, nor do CustomerWallets, so Spring will create those first. Note Spring does not require you to declare these in any particular order, what’s important is that all dependencies are declared in their method signature. Another nice trick Spring does is aggregate all beans of type Warehouse before sending them for the construction of the OrderProcessor.

Now that you’ve got your head round this gimmick of object creation you’re probably still regretting how surrounding object instantiations with an annotated method only increases the number of lines in your code, and not the opposite. Spring simplifies the creation of single instance classes (we call these singletons) by offering us to simply annotate them with @Component at the class level, completely avoiding the extra method. Have a look at the improved Spring version for this section.

AmazonWebsite, CustomerWallet, and OrderProcessor are annotated with @Component, letting Spring know it needs to create a single instance for said classes. With this strategy the dependencies are declared to Spring by the constructor arguments, and the name of the bean will be the camel cased name of the class. Note CustomerOrder is not annotated, as it simply a short lived object intended only to pass data around. The previous classes don’t hold any data, instead they describe business rules, making them appropriate to exist as a singleton. The Warehouse also doesn’t hold any data and it also only holds business logic, but we have 2 Warehouses, not one, and for that reason it is not eligible for @Component annotation.

If you’re still berating over the larger number of lines in the more concise Spring version think about how each project would fare against a growing number of single instance objects. The non-Spring version would keep growing needing to manually create each new object instance whereas the concise Spring version would only require an @Component at the top of each new class declaration. While they might take the same total amount of lines (after all @Component lines also count), I think we can agree the Spring version is simpler and allows us to focus on writing business specific code.

The problem we have solved in this section is also referred to as dependency injection or wiring.

Containerization

If you run both flavours of the previous program you might have noticed the Spring variation did not just run and finish, but kept on going. This is tantamount for the majority of production grade apps - after all, you don’t want your website going down after taking its first order! When I was learning Java my programs were always expected to finish. Programs were viewed as a mechanism to obtain an answer to a question, and a program running indefinitely was usually regarded as a bad thing. When building enterprise applications it’s quite the opposite, you want to build a resilient program that can withstand the harshest exceptions and still keep on running!

Programs that are long running in Spring are placed inside a container. A container is just a piece of software that wraps your application and provides it with an execution environment that is responsible for supplying capabilities such as security, logging or transaction management*.

In this section the Spring version remains unaltered but it’s worth explaining where the container comes from at this point. The maven dependency spring-boot-starter-web indicates Spring* ours is going to be a web application and so needs to run indefinitely. Spring places our program inside a container (Apache Tomcat by default) and then runs that. If you were to comment that dependency out the Spring program would no longer run indefinitely. Try it!

To achieve something similar in plain Java you’d have to change the Application class to something like this.

This Github Sample is by edu05
src/main/java/edu/learn/spring/containerization/vanilla/Application.java view raw
package edu.learn.spring.containerization.vanilla;

import com.sun.net.httpserver.HttpServer;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.List;

public class Application {

    public static void main(String[] args) throws IOException {
        HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0);
        server.setExecutor(null); // creates a default executor
        server.start();

        Warehouse warehouse1 = new Warehouse();
        Warehouse warehouse2 = new Warehouse();

        List<Warehouse> warehouses = new ArrayList<>();
        warehouses.add(warehouse1);
        warehouses.add(warehouse2);

        CustomerWallet customerWallet = new CustomerWallet();
        OrderProcessor orderProcessor = new OrderProcessor(warehouses, customerWallet);
        AmazonWebsite amazonWebsite = new AmazonWebsite(orderProcessor);
        amazonWebsite.acceptOrder(new CustomerOrder(3, 1));
    }
}

It doesn’t actually give us all the functionality a full container does, but it creates a HttpServer and starts it, allowing the vanilla program to also run indefinitely. This is the first time both versions are neck and neck on number of lines, and it’s only going to get better for Spring.

Boilerplate

So far we’ve been ignoring the fact that the AmazonWebsite is not really a website (thanks for waiting patiently). It is unable to receive http requests, create java objects from the payload of the incoming request (we call this process deserializing and the reverse process of converting a java object into a string representation that can be sent over the wire serializing), and to respond with appropriate http status codes. As always lets have a look how we might achieve all of this with simple Java.

I moved the HttpServer creation to AmazonWebsite, after all dealing with http requests is something very specific to a website, and I don’t like it when logic leaks from where it truly belongs. I had to add a url to my server and a specification as to what should happen to the http exchange when one is initiated (the handler). The handler consists of the deserialization of the request body into a CustomerOrder, which gets sent through to the OrderProcessor and then a response is sent back to the user acknowledging the order has been successfully submitted.

This Github Sample is by edu05
src/main/java/edu/learn/spring/boilerplate/vanilla/Application.java view raw
package edu.learn.spring.boilerplate.vanilla;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class Application {

    public static void main(String[] args) throws IOException {
        Warehouse warehouse1 = new Warehouse();
        Warehouse warehouse2 = new Warehouse();

        List<Warehouse> warehouses = new ArrayList<>();
        warehouses.add(warehouse1);
        warehouses.add(warehouse2);

        CustomerWallet customerWallet = new CustomerWallet();
        OrderProcessor orderProcessor = new OrderProcessor(warehouses, customerWallet);
        AmazonWebsite amazonWebsite = new AmazonWebsite(orderProcessor);
    }
}
This Github Sample is by edu05
src/main/java/edu/learn/spring/boilerplate/vanilla/AmazonWebsite.java view raw
package edu.learn.spring.boilerplate.vanilla;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.sun.net.httpserver.HttpServer;

import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;

public class AmazonWebsite {

    private final OrderProcessor orderProcessor;

    public AmazonWebsite(OrderProcessor orderProcessor) throws IOException {
        this.orderProcessor = orderProcessor;
        HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0);

        server.createContext("/accept-order", (exchange -> {
            if (exchange.getRequestMethod().equals("POST")) {
                throw new UnsupportedOperationException();
            }

            CustomerOrder customerOrder = new ObjectMapper().readValue(exchange.getRequestBody(), CustomerOrder.class);
            acceptOrder(customerOrder);

            String respText = "Order submitted successfully!";
            exchange.sendResponseHeaders(200, respText.getBytes().length);
            OutputStream output = exchange.getResponseBody();
            output.write(respText.getBytes());
            output.flush();
            exchange.close();
        }));
        server.setExecutor(null); // creates a default executor
        server.start();
    }

    public void acceptOrder(CustomerOrder customerOrder) {
        orderProcessor.process(customerOrder);
    }
}

When running the program you can execute this in the command line to exercise your fully fledged website.

curl -X POST http://localhost:8080/accept-order -H 'content-type: application/json' -d '{"itemId":3,"customerId":1}'

Notice all but one of the imported classes belongs to the JDK, and that’s the ObjectMapper. The ObjectMapper has a very limited use, it’s in charge of converting the request body into our CustomerOrder. That means there’s no magic at all and the code looks crystal clear right? Yay!

Maybe, but it’s dull and boring. Why? Because there’s much too much code for so little functionality. All that’s special about this AmazonWebsite is its url (accept-order), what type of payload it needs (CustomerOrder), what to with it (invoke the acceptOrder() method) and what to return. The HttpServer creation, http method check, payload deserialization, and output generation is generic code - you’d have to repeat those lines for every url on your web application. If you’re trying to beat Amazon, who have different urls for new orders, dispathed orders, past orders, recommended items, etc. those lines will quickly build up. Bear all of this in mind while taking a look at the Spring version.

This Github Sample is by edu05
src/main/java/edu/learn/spring/boilerplate/sprinkles/AmazonWebsite.java view raw
package edu.learn.spring.boilerplate.sprinkles;

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class AmazonWebsite {

    private final OrderProcessor orderProcessor;

    public AmazonWebsite(OrderProcessor orderProcessor) {
        this.orderProcessor = orderProcessor;
    }

    @PostMapping("/accept-order")
    public String acceptOrder(@RequestBody CustomerOrder customerOrder) {
        orderProcessor.process(customerOrder);
        return "Order submitted successfully!";
    }
}
This Github Sample is by edu05
src/main/java/edu/learn/spring/boilerplate/sprinkles/Application.java view raw
package edu.learn.spring.boilerplate.sprinkles;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;

@SpringBootApplication
@EnableWebMvc
public class Application {

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

    @Bean
    public Warehouse warehouse1() {
        return new Warehouse();
    }

    @Bean
    public Warehouse warehouse2() {
        return new Warehouse();
    }
}

Notice how short AmazonWebsite is. The questions in your brain should match the issues in the plain Java equivalent I mentioned earlier. @RestController declares this is a class that’ll receive http requests, @PostMapping("/accept-order") declares the url and allowed http method, and @RequestBody indicates the type of payload to expect and to deserialize. You’ll have guessed the method’s body specifies what to do when a request is received. Finally, the Application class needs the @EnableWebMvc annotation.

Hopefully you find this approach more concise than the pure Java one.

To illustrate how Spring’s version can get away with being so succinct and how there’s actually very little magic going on I have extracted the boilerplate of the vanilla’s AmazonWebsite class into an abstract Wesbite class that could be reused by any other future websites.

This Github Sample is by edu05
src/main/java/edu/learn/spring/boilerplate/vanilla_improved/Website.java view raw
package edu.learn.spring.boilerplate.vanilla_improved;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.sun.net.httpserver.HttpServer;

import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;

public abstract class Website<IN, OUT> {

    public Website(String websiteUrl, String method, Class<IN> inputType) throws IOException {
        HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0);

        server.createContext(websiteUrl, (exchange -> {
            if (exchange.getRequestMethod().equals(method)) {
                throw new UnsupportedOperationException();
            }
            ObjectMapper objectMapper = new ObjectMapper();

            //deserialize payload
            IN input = objectMapper.readValue(exchange.getRequestBody(), inputType);

            //process payload
            OUT output = process(input);

            //serialize output
            String respText = objectMapper.writeValueAsString(output);
            exchange.sendResponseHeaders(200, respText.getBytes().length);
            OutputStream outputStream = exchange.getResponseBody();
            outputStream.write(respText.getBytes());
            outputStream.flush();
            exchange.close();
        }));
        server.setExecutor(null); // creates a default executor
        server.start();
    }

    protected abstract OUT process(IN input);
}
This Github Sample is by edu05
src/main/java/edu/learn/spring/boilerplate/vanilla_improved/AmazonWebsite.java view raw
package edu.learn.spring.boilerplate.vanilla_improved;

import java.io.IOException;

public class AmazonWebsite extends Website<CustomerOrder, String> {

    private final OrderProcessor orderProcessor;

    public AmazonWebsite(OrderProcessor orderProcessor) throws IOException {
        super("/accept-order", "POST", CustomerOrder.class);
        this.orderProcessor = orderProcessor;
    }

    @Override
    protected String process(CustomerOrder input) {
        acceptOrder(input);
        return "Order submitted successfully!";
    }

    public void acceptOrder(CustomerOrder customerOrder) {
        orderProcessor.process(customerOrder);
    }
}

This time round, AmazonWebsite declares only the essence of what constitutes a class capable of receiving http requests. The url, method and the payload type go into the constructor and account for two of the annotations in Spring’s version. Extending the Website class is comparable to annotating with @RestController. Effectively, the code inside the Website class is all the boilerplate we save ourselves from writing by using Spring.

But Spring gives us so much more though. With Spring it’d be really simple to change the serialization schema from JSON to XML, to secure the website behind a username and a password, to map exceptions to different http status codes, and more. Are you willing to give this a go using only plain Java?

This is where Spring really shines. It’s able to elevate Java from a plain programming language to a tool-kit full of production grade capabilities. Decorating the code with annotations may not look natural at first, but it’s a small price to pay for all the free functionality we get. And, if you sit down to think about it, you can probably imagine how an annotation could be mapped to some plain Java code.

Thank you for reading, let me know what you think :)


*It would have been more correct to say a Spring bean is an object instance with a lifecycle. While the lifecycle of a bean normally consists of being created at the beginning of the app and shutdown at the end of the app, sometimes a bean might be shutdown mid app.
*More precisely a Spring Boot application. Spring Boot is a relatively recent sub-project of Spring to further simplify how we work with Spring.
*Not a direct quote but inspired by the definition in Server Component Patterns: Component Infrastructures Illustrated with EJB (Wiley Software Patterns Series)
*Spring Boot