Happy New Year, folks! If you follow my blog posts, you would notice that my preferred tech stack that I mostly use for projects is React, Python (Flask), and AWS / Heroku for deployment. I have also dabbled a bit in PHP-land with WordPress. Recently, I decided to try out an entirely new framework used to develop web applications called Spring Boot, which is based on Java.
However you choose to implement it, the basic core concepts of the web remain the same. “Frameworks” are nothing but tools to help us solve problems we face and make life easier for us developers. So, sometimes, it’s good to step out of the comfort zone, and give something new a try to make sure that we aren’t “stuck in our ways” and also to see how other tools are solving the same problems.
This post contains the notes I made while developing a basic application in Spring Boot. I had a background in creating web applications with Python Flask and JavaScript Frameworks. If you are in the same boat I was, this post will definitely help you figure out what Spring is, how things work in the Spring world, and how you can get started with it. Time to time, I have included references to things in Flask world, so you will have a solid conceptual understanding and will be able to see past the jargon. I have also included references at the end of the post, in case you want to dive deep, I recommend checking them out.
Let’s start with understanding the basics of the Spring framework.
Following are the three basic “things” that it provides. (There’s more, but these three are the most basic ones)
Let’s take a look at each of these one by one.
Spring framework is essentially a “dependency injection container” at it’s core Let’s dissect and understand this statement.
A
that is responsible for finding a product by it’s name, you would need to access the database to do that. Let’s say another Java class called DataSource
is responsible for connecting to the database. So, DataSource
is a dependency for class A.DataSource
inside the new method. That would work, but it is not a good practice. What if a DataSource
opens up a socket connection from our Java program to the database? Spawning a new DataSource for every method then would be very expensive. Wouldn’t it be nice if we didn’t have to worry about finding and managing the dependencies and somehow the dependencies are managed and provided to the class for us?DataSource
inside the class ourselves, we just accept it as an argument to our constructor and use that for everything! Now, our class A
doesn’t know about the application class anymore, or how to construct DataSources. Basically, now class A says “if you want to use me, you need to give me a datasource as an argument.”DataSource
when we use class A. Wouldn’t it be nice if someone knew that our class A has a DataSource dependency, and then that someone would somehow construct both the objects for us! Well, that someone is called as a … you guessed it right … a “dependency injection container”.ApplicationContext
is that someone who has control over all our classes and then can take care of all the dependencies of all our classes for us.Now that we have sufficient information about the Spring framework, let’s set up all the things we would need to develop a simple REST API. What should we build? Well, whenever I am trying out any new framework, I begin by implementing a very simple ToDo app.
If you want the full code with many functionalities of a ToDo app implemented, visit this repo. In this post, we will just focus on setting up the barebone files and classes needed to get started with developing a simple REST API using Spring MVC and also understand the concepts behind them. Let’s get started!
Side Note: If reading this list makes you feel intimidated, don’t be. Even I had no idea what most of these are and what they do. So, I have included details about each of them. At the end, I realized that I already knew the concepts behind these and the names is mostly Java terminologies.
We will use Spring Initializr to set up our project. Spring Initializr is a tool that generates a Spring Boot project structure for us.
One of the core things behind Spring Initializr are the Spring Boot Starters. Spring Boot Starters are curated sets of dependencies that manage all the dependencies we need and wire them up for us. This enables us to avoid manually adding the dependencies in Maven. (Side Note: what is Maven? Maven is a build and dependency manager tool for Java, like we have npm in JS-world)
Here is a snapshot of my selections in start.spring.io
When you click on “Generate”, it will download the preconfigured project as a zip file, which we can use.
The other way to use start.spring.io is to select “Spring Initializr” directly in the “Create New Project” window in IntelliJ, which will then ask you to enter the same details.
The other way to use start.spring.io is to select “Spring Initializr” directly in the “Create New Project” window in IntelliJ, which will then ask you to enter the same details.
I hope all the jargon is clear now. Here, we will be using use Spring Data JPA along with Hibernate as our JPA provider. Let’s get started by creating classes for entities that we need for our application.
Entities in JPA are nothing but POJOs representing data that can be persisted to the database. Each entity represents a table stored in a database. Every instance of an entity represents a row in the table. (like we used to do in Flask with models.py) We’ll keep it simple by having just two entity classes -
So, there’ll be a many to one relationship between them, i.e. a todo will have one user as the author, and one user can have many todos. Pretty simple for starting.
For creating the POJOs, we will create a package src/main/java/com.yourorg.yourapp/domain
. I have also linked the GitHub repo for this, so check that out as well if you get confused.
Question: How will Hibernate (our ORM) understand that this POJO we are creating is a entity? (In Flask, we extend a class from the ORM). Here, we will use (Annotations) to tell ORM “Hey, this class is Entity, so do your thing”. We will also be specifying the relationships between our entities in a similar way. Do read the code snippets below (along with the comments) to get a clear idea.
User
Class:
package com.rajrajhans.SpringTodoApp.domain;
import javax.persistence.*;import java.util.Objects;import java.util.Set;
@Entity // Telling Hibernate that this is a entity classpublic class User {
@Id // Telling Hibernate that "id" property is the primary key @GeneratedValue(strategy = GenerationType.AUTO) // Telling Hibernate that "id" is gonna be auto generated by our database private long user_id;
private String firstName; private String lastName;
@OneToMany(mappedBy = "author") // mappedBy indicates that this side is the inverse // side, and that the mapping is defined by the attribute // author at the other side of the association private Set<Todo> todos;
public User() { // Doing this because JPA requires a zero arg constructor }
public User(String firstName, String lastName, Set<Todo> todos) { this.firstName = firstName; this.lastName = lastName; this.todos = todos; }
public long getId() { return user_id; }
public void setId(long id) { this.user_id = id; }
// getters and setters for rest of the properties omitted for brevity
@Override public boolean equals(Object o) { // implementing a custom equals method to make sure if (this == o) return true; // that two objects of class "User" are compared based on their ids if (o == null || getClass() != o.getClass()) return false; // so that Hibernate and Set will consider two objects with same id as equal User user = (User) o; return user_id == user.user_id; }
@Override public int hashCode() { return Objects.hash(user_id); }}
Todo
Class:
package com.rajrajhans.SpringTodoApp.domain;
import javax.persistence.*;import java.util.Objects;import java.util.Set;
@Entitypublic class Todo {
@Id // Telling Hibernate that "id" property is the primary key @GeneratedValue(strategy = GenerationType.AUTO) // Telling Hibernate that "id" is gonna be auto generated by our database private long id;
private String task;
@ManyToOne @JoinColumn(name="user_id") private User author;
public Todo() { // Doing this because JPA requires a zero arg constructor }
public Todo(String task, User author) { this.task = task; this.author = author; }
// getters and setters for the properties omitted for brevity
@Override public boolean equals(Object o) { // implementing a custom equals method to make sure if (this == o) return true; // that two objects of class "User" are compared based on their ids if (o == null || getClass() != o.getClass()) return false; // so that Hibernate and Set will consider two objects with same id as equal Todo todo = (Todo) o; return id == todo.id; }
@Override public int hashCode() { return Objects.hash(id); }}
Great, at this point, our entity classes are ready. We have established the entity mapping, but that is not enough. We need to somehow tell JPA to do CRUD operations on the database for us. We want to tell it things like “Hey, add this todo as a row in the table”, or “update this row with this new data”, or “Hey, give me todos that this user has made”.
To make all this things happen, we will now add “Repository” interfaces for both User
and Todo
which use the class provided to us by Spring Data JPA.
CrudRepository
in Spring Data JPA?CrudRepository
which provides generic CRUD operations on a repository of specif ic type.Following is the code for UserRepository
and ClassRepository
interfaces. They are in src/main/com.yourorg.yourapp/repositories
Here is the link for them in the GitHub repo.
Code for UserRepository
:
package com.rajrajhans.SpringTodoApp.repositories;
import com.rajrajhans.SpringTodoApp.domain.User;import org.springframework.data.repository.CrudRepository;
public interface UserRepository extends CrudRepository<User, Long> { // Since it is a generic, we are passing the type (User) and the ID type (which is long) // We are just providing the interface, Spring will provide us with the implementation
// if we want some special operations which are not there in CrudRepository, then we can include it here}
Similarly, for TodoRepository
:
package com.rajrajhans.SpringTodoApp.repositories;
import com.rajrajhans.SpringTodoApp.domain.Todo;import org.springframework.data.repository.CrudRepository;
public interface TodoRepository extends CrudRepository<Todo, Long> { // Since it is a generic, we are passing the type (Todo) and the ID type (which is long) // We are just providing the interface, Spring will provide us with the implementation
// if we want some special operations which are not there in CrudRepository, then we can include it here}
We are using a simple H2 in memory database at this point. It has a nice GUI (think like phpmyadmin for MySQL), but we need to enable it first. To do that, go to src/main/resources/application.properties
and add this line:
spring.h2.console.enabled=true
Now, once you run the Spring project, you can get a nice GUI for our database at localhost:8080/h2-console
. You can see the entities that we coded there, along with the options to run SQL queries against them. Pretty neat!
Here’s a diagram which explains the Request Processing Workflow of Spring Web MVC
@Controller
annotation. This will register the class as a Spring Bean and as a Controller in Spring MVC.@RequestMapping
Following is the code for TodoController
which we are mapping to /todos
package com.rajrajhans.SpringTodoApp.controllers;
import com.rajrajhans.SpringTodoApp.repositories.TodoRepository;import org.springframework.web.bind.annotation.RestController;import org.springframework.ui.Model;import org.springframework.web.bind.annotation.RequestMapping;
@RestController // telling Spring that this is a Controller for a REST APIpublic class TodoController {
private final TodoRepository todorepo; // Adding a "dependency" on the TodoRepository
public TodoController(TodoRepository todorepo) {// Now when Spring instantiates this controller, // it will also inject a TodoRepository instance this.todorepo = todorepo; }
@RequestMapping("/todos") // mapping the route "/todos" to getTodos public String getTodos(Model model){ // The argument is basically a Model that we are going to // return back to the view after adding our own things to it model.addAttribute("todos", todorepo.findAll());
return "todos"; }}// Note: this code is just to illustrate how to create a Controller// To see the actual controller for the Todo REST API, see the linked github repo
At runtime, when Spring gets a request at /todos
, it is going to execute the getTodos
method and it’s going to provide that method a Model object, and our code is going to add a attribute called “todos” to that model by executing a findAll()
on todorepo (which will give us a list of todos). Then, this model will return back to our view layer, and it is going to have an attribute called todos
on it, then we’ll be able to utilize that value inside of our view layer to send a response back to the client.
@Service
Here’s the code:
package com.rajrajhans.SpringTodoApp.services;import org.springframework.stereotype.Service;
@Servicepublic class TodoService { // CRUD Methods for our resource here // These CRUD methods will use the Repository we created in prior step!}
We can then have this TodoService
class as the dependency in our TodoController
and then call service methods from there!
@Controller
and a @Service
?@Service
annotation for classes that are in our service layer that perform some kind of “business service”@Controller
for telling Spring that a particular class serves the role of a Controller, which means it’s methods will handle the Request Mapping using the @RequestMapping
annotation.DispatchServlet
(think of this like the main controller that sits at the front of everything and takes in requests) class will dispatch all the incoming HTTP Requests towards appropriate Controller Classes. The Controller Class can then call Services to perform business logic and then send back the response.In this post, I covered many of the concepts I learned behind the Spring Framework, and how the jargon is actually the concepts you might already know but just in a different form. I also covered on how you can set up a Spring Boot Project for a simple REST API. I have implemented many more functionalities in the REST API, you can check that out in this repository.