Why Spring Boot?
Spring Boot is a framework that bootstraps a Java web application without the XML configuration headaches that come with a typical Spring application. The framework adopts an opinionated approach to configuration by making decisions based on the use cases that fit the majority of modern web apps. In other words, it follows the philosophy of convention over configuration.
With Spring Boot, we can create an application that bundles all dependencies and it’s servlet container so it doesn’t even require a traditional WAR deployment. We’ll have a fat executable JAR that can be run as a standard Linux service.
SeatingNow
Let’s imagine we’re developing a restaurant reservation application called SeatingNow to compete with OpenTable. Users can create an account, search for restaurants, book a reservation, and receive notifications via SMS or e-mail.
The application consists of the following microservices:
- Account
- Search
- Restaurant
- Reservation
- Search
- Messaging
Each of these microservices has a REST API and its own MySQL database. The services will be accessed by desktop and mobile users via an API gateway.
In this article, we’ll focus on building the API for the Restaurant microservice.
REST Endpoints
Our application will expose the following endpoints:
-
GET /v1/reservations - Retrieves all reservations
-
GET /v1/reservations/{id} -Retrieves a specific reservation based on ID
-
POST /v1/reservations - Creates a new reservation
-
PUT /v1/reservations/{id} - Updates an existing reservation
-
DELETE /v1/reservations/{id} - Removes a specific reservation based on ID
Technology Stack
We’ll be using the following technologies for the application:
- Spring Tool Suite (STS) 3.83
- Spring Boot 1.5.1
- Spring Data JPA 1.11
- Spring MVC 4.3.6
- MySQL 5.7
- Maven 3.3.9
Getting Started
There are a few different ways that we can begin development:
- Use the Spring Boot CLI
- Use the Spring Initalizer tool
- Use an IDE like Spring Tool Suite (STS)
We’ll use the Spring Tool Suite for this project. STS is based on Eclipse and customized for developing Spring applications. It comes with built-in Tomcat server, validation of config files among other handy features that makes life easier.
Installing Spring Tool Suite
Spring Tool Suite comes packaged as a zip file and does not use an install wizard or store anything in the Windows registry. So we can just unzip the file and put it anywhere.
On first launch, it will ask us where we want our workspace to live. This is the directory that holds all of our projects and settings.
Creating the Project
Let’s launch Spring Tool Suite and select File->New -> Spring Starter project
We can configure it as a Maven project and enter the Group, Artifact, and Package as below. Click Next.
The next screen will ask to select our dependencies. These selections will get populated into Maven’s pom.xml file.
For our project, we want the following dependencies:
- Core -> Dev Tools
- This dependency makes development easier for us by adding some neat features like automatic Tomcat restarts after each file change. Not required but useful to have.
- SQL -> JPA, MySQL
- These dependencies will allow our app to communicate with a MySQL/MariaDB database. JPA is the Java Persistence framework and will do the heavy lifting on the querying side so we don’t have to write boilerplate SQL statements for routine operations.
- Web -> Web
- This dependency allows us to create a REST API based on Spring MVC
Click Finish to create the project.
Maven pom.xml
Our project has now been created so let’s look at the Maven configuration file. Double-click on the file in Package Explorer and go to the tab named “pom.xml” in the main window pane.
Parent project
We’ve got a parent project named spring-boot-start-parent
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.1.RELEASE</version>
</parent>
This configuration block declares that our project is a child of the parent project and thus inherits a host of default Maven configurations.
Dependencies
Now let’s check out the dependencies section:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<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>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
These artifacts (and their own dependencies) have all been downloaded automatically by Maven and the JARs can be viewed by expanding the Maven dependencies under Package Explorer:
Build
In order to make our final JAR executable, we’ll need to add an element named executable into this section:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<executable>true</executable>
</configuration>
</plugin>
</plugins>
</build>
Application Class
The Application class containing the main method is created for us automatically by the Project wizard.
package com.codebyamir.springboot;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ReservationApp {
public static void main(String[] args) {
SpringApplication.run(ReservationApp.class, args);
}
}
Notice that the class is annotated with @SpringBootApplication. This annotation is shorthand for three annotations: @Configuration, @EnableAutoConfiguration and @ComponentScan.
What does this do for us?
- @Configuration tags the class as a source of bean definitions for the application context.
- @EnableAutoConfiguration tells Spring Boot to start adding beans based on class path settings, other beans, and various property settings.
- @ComponentScan tells Spring to look for other components, configurations, and services in the the com.codebyamir.springboot package.
Notice that there was no XML configuration required.
Our main method delegates to Spring Boot’s SpringApplication class by calling run. SpringApplication will bootstrap our application, starting Spring which will in turn start the embedded Tomcat server.
We need to pass SeatingNow.class as an argument so SpringApplication knows the primary component.
The static run method performs a number of important setup tasks:
- Sets the default configuration
- Starts Spring application context
- Performs classpath scan
- Starts Tomcat server
If we run the project as a Java application (Right click project -> Run As -> Java Application), we see a bunch of output in the console with some fancy ASCII art:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v1.5.1.RELEASE)
2017-03-11 08:29:25.096 INFO 5484 --- [ main] com.codebyamir.springboot.SeatingNow : Starting ReservationApp on SURFACEBOOK with PID 5484 (C:\Users\amirb\Documents\workspace-sts-3.8.3.RELEASE\seatingnow-api\target\classes started by amirb in C:\Users\amirb\Documents\workspace-sts-3.8.3.RELEASE\seatingnow-api)
2017-03-11 08:29:25.100 INFO 5484 --- [ main] com.codebyamir.springboot.SeatingNow : No active profile set, falling back to default profiles: default
2017-03-11 08:29:25.188 INFO 5484 --- [ main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@5427c60c: startup date [Tue Feb 21 22:29:25 EST 2017]; root of context hierarchy
2017-03-11 08:29:26.478 INFO 5484 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration' of type [class org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2017-03-11 08:29:26.562 INFO 5484 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'validator' of type [class org.springframework.validation.beanvalidation.LocalValidatorFactoryBean] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2017-03-11 08:29:26.958 INFO 5484 --- [ main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat initialized with port(s): 8080 (http)
2017-03-11 08:29:26.978 INFO 5484 --- [ main] o.apache.catalina.core.StandardService : Starting service Tomcat
2017-03-11 08:29:26.980 INFO 5484 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet Engine: Apache Tomcat/8.5.11
2017-03-11 08:29:27.152 INFO 5484 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2017-03-11 08:29:27.153 INFO 5484 --- [ost-startStop-1] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 1969 ms
2017-03-11 08:29:27.381 INFO 5484 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean : Mapping servlet: 'dispatcherServlet' to [/]
2017-03-11 08:29:27.386 INFO 5484 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'characterEncodingFilter' to: [/*]
2017-03-11 08:29:27.387 INFO 5484 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
2017-03-11 08:29:27.387 INFO 5484 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'httpPutFormContentFilter' to: [/*]
2017-03-11 08:29:27.387 INFO 5484 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'requestContextFilter' to: [/*]
2017-03-11 08:29:27.845 INFO 5484 --- [ main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@5427c60c: startup date [Tue Feb 21 22:29:25 EST 2017]; root of context hierarchy
2017-03-11 08:29:27.950 INFO 5484 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.map<java.lang.string, java.lang.object="">> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2017-03-11 08:29:27.951 INFO 5484 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
2017-03-11 08:29:27.982 INFO 5484 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2017-03-11 08:29:27.982 INFO 5484 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2017-03-11 08:29:28.067 INFO 5484 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2017-03-11 08:29:28.215 INFO 5484 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
2017-03-11 08:29:28.275 INFO 5484 --- [ main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http)
2017-03-11 08:29:28.280 INFO 5484 --- [ main] com.codebyamir.springboot.ReservationApp : Started ReservationApp in 3.576 seconds (JVM running for 3.95)</java.util.map<java.lang.string,>
We’ve started Tomcat server on localhost with its default port 8080. Let’s check it out in our browser:
We get an error page from the Tomcat server.
What’s happening here? There is no mapping for “/” so it tries to go to “/error” but there’s no mapping for that either so it returns a 404.
This is normal so no worries. The server is running, but we haven’t defined any API endpoints yet.
Set server.error.whitelabel.enabled=false in application.properties to switch the whitelabel error page off and restore the default error page from Tomcat.
Populate the Database
One of the tenets of microservice architecture is one database per service. So let’s create and initialize the database with some sample data:
CREATE DATABASE seatingnow_reservation;
USE seatingnow_reservation;
CREATE TABLE reservation (
`id` int(1) NOT NULL AUTO_INCREMENT,
`user_id` int(1) NOT NULL,
`party_size` tinyint(1) NOT NULL,
`date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`restaurant_id` int(1) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB CHARSET=utf8mb4;
INSERT INTO reservation VALUES
(NULL,100,2,NOW(),800),
(NULL,101,3,NOW() + INTERVAL 1 DAY,800),
(NULL,102,5,NOW() + INTERVAL 2 DAY,800)
Here we are creating 3 reservations that correspond to users 100, 101, and 102.
- Amir (user_id=100) has a reservation for 2 at restaurant XYZ (restaurant_id=800) on 3-11-2017 at 6pm
- Beth (user_id=101) has a reservation for 3 at restaurant XYZ (restaurant_id=800) on 3-12-2017 at 6pm
- Joe (user_id=102) has a reservation for 5 at restaurant XYZ (restaurant_id=800) on 3-13-2017 at 6pm
The columns user_id and restaurant_id would typically be foreign key references to other table columns named user.id and restaurant.id but we are simplifying things here a bit.
Entity Class
In order to model a reservation, we need to create a class called Reservation which is annotated with @Entity marking it as a JPA entity.
We’ll store the reservation id, name of the person who made the reservation, the reservation date/time, and the size of the party.
Since our class name is in title case (Reservation) and our table name is in lower case (reservation), we’ll need to let Spring know about this by using @Table(name=”reservation”) before our class declaration.
We tell Spring the private key of our table by using the @Id annotation. The @Columnannotations are for other columns we want to use. If any of the database column names differ from our instance variables, then we must explicitly specify the database column name as part of the annotation – @Column(name=”user_id”)

Avoid camelcase column names in your database because Hibernate converts these into snake case.
package com.codebyamir.springboot.reservation;
import java.time.LocalDateTime;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
// Model class
@Entity
@Table(name="reservation")
public class Reservation {
@Id
private Long id;
@Column
private LocalDateTime dt;
@Column(name="user_id")
private Long userId;
@Column(name="restaurant_id")
private Long restaurantId;
public Long getRestaurantId() {
return restaurantId;
}
public void setRestaurantId(Long restaurantId) {
this.restaurantId = restaurantId;
}
// Hibernate will convert camel case column names to snake case!!!
// Don't use camelcase columns in DB
@Column(name="party_size")
private int partySize;
public Reservation() {}
public Reservation(Long id, Long userId, int partySize) {
this.id = id;
this.userId = userId;
this.partySize = partySize;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public LocalDateTime getDt() {
return dt;
}
public void setDt(LocalDateTime dt) {
this.dt = dt;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public int getPartySize() {
return partySize;
}
public void setPartySize(int partySize) {
this.partySize = partySize;
}
}
Repository Interface
In a typical Java application, we would expect to write a class that implements ReservationRepository. But since we’re using Spring Data JPA, we don’t have to worry about that. JPA will create an implementation on the fly during run-time.
All we have to do is extend the CrudRepository interface like below:
package com.codebyamir.springboot.reservation;
import org.springframework.data.repository.CrudRepository;
public interface ReservationRepository extends CrudRepository<Reservation,String> {
}
We specify the generic parameters for the entity and ID that we are working with (Reservation and String). We will inherit several methods for saving, deleting, and finding Reservation entities.
We may also define our own methods here if we wish like findByDate() and findByLastName(). For this example, we do not need to define any additional methods.
Service Class
We now create our service class named ReservationService and annotate it with @Service.
Service classes contain the business logic and call methods in the repository layer.
package com.codebyamir.springboot.reservation;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class ReservationService {
@Autowired
private ReservationRepository reservationRepository;
// Retrieve all rows from table and populate list with objects
public List getAllReservations() {
List reservations = new ArrayList<>();
reservationRepository.findAll().forEach(reservations::add);
return reservations;
}
// Retrieves one row from table based on given id
public Reservation getReservation(Long id) {
return reservationRepository.findOne(id);
}
// Inserts row into table
public void addReservation(Reservation reservation) {
reservationRepository.save(reservation);
}
// Updates row in table
public void updateReservation(Long id, Reservation reservation) {
reservationRepository.save(reservation);
}
// Removes row from table
public void deleteReservation(Long id) {
reservationRepository.delete(id);
}
}
Controller Class
In order to handle HTTP requests, we must add a controller. This will be a Java class named ReservationController with a @RestController annotation applied to it.
Inside this class, we define methods that handle the actions for each URI by using the @RequestMapping annotation. The controller class creates an instance of class ReservationService to perform its work thanks to the @Autowired annotation.
package com.codebyamir.springboot.reservation;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/v1")
public class ReservationController {
@Autowired
private ReservationService reservationService;
// ------------ Retrieve all reservations ------------
@RequestMapping(value = "/reservations", method = RequestMethod.GET)
public List getAllReservations() {
return reservationService.getAllReservations();
}
// ------------ Retrieve a reservation ------------
@RequestMapping(value = "/reservations/{id}", method = RequestMethod.GET)
public Reservation getReservation(@PathVariable String id) {
return reservationService.getReservation(id);
}
// ------------ Create a reservation ------------
@RequestMapping(value = "/reservations", method = RequestMethod.POST)
public void addReservation(@RequestBody Reservation reservation) {
reservationService.addReservation(reservation);
}
// ------------ Update a reservation ------------
@RequestMapping(value = "/reservations/{id}", method = RequestMethod.PUT)
public void updateReservation(@RequestBody Reservation reservation,@PathVariable String id) {
reservationService.updateReservation(id, reservation);
}
// ------------ Delete a reservation ------------
@RequestMapping(value = "/reservations/{id}", method = RequestMethod.DELETE)
public void deleteReservation(@PathVariable String id) {
reservationService.deleteReservation(id);
}
}
JPA Converter Class
JPA does not know how to serialize LocalDateTime objects which poses a problem for us since our entity class has one. The solution to this problem is to create a helper class that instructs JPA on conversion:
package com.codebyamir.springboot.reservation;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
@Converter(autoApply = true)
public class LocalDateTimeAttributeConverter implements AttributeConverter {
@Override
public Timestamp convertToDatabaseColumn(LocalDateTime locDateTime) {
return (locDateTime == null ? null : Timestamp.valueOf(locDateTime));
}
@Override
public LocalDateTime convertToEntityAttribute(Timestamp sqlTimestamp) {
return (sqlTimestamp == null ? null : sqlTimestamp.toLocalDateTime());
}
}
Configuration with application.properties
In this project, we will use an application.properties file in /src/main/resources. Spring Boot reads this file on startup to determine how to connect to our database.
server.address=127.0.0.1
spring.datasource.url=jdbc:mysql://localhost:3306/seatingnow?autoReconnect=true&useSSL=false
spring.datasource.username=dbuser
spring.datasource.password=dbpass
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
We could have also used O/S environment variables or an external application.properties file to specify these values. All the configuration options can be found here.
Start the Application
Let’s restart the application and see how it looks. The console output will tell us if there are any issues connecting to the database.
Our output below looks normal so let’s move forward and test our API.
09:39:45.496 [main] DEBUG org.springframework.boot.devtools.settings.DevToolsSettings - Included patterns for restart : []
09:39:45.500 [main] DEBUG org.springframework.boot.devtools.settings.DevToolsSettings - Excluded patterns for restart : [/spring-boot-starter/target/classes/, /spring-boot-autoconfigure/target/classes/, /spring-boot-starter-[\w-]+/, /spring-boot/target/classes/, /spring-boot-actuator/target/classes/, /spring-boot-devtools/target/classes/]
09:39:45.500 [main] DEBUG org.springframework.boot.devtools.restart.ChangeableUrls - Matching URLs for reloading : [file:/C:/Users/amirb/Documents/workspace-sts-3.8.3.RELEASE/course-api/target/classes/]
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v1.5.1.RELEASE)
2017-03-11 09:39:45.936 INFO 24732 --- [ restartedMain] c.codebyamir.springboot.ReservationApp : Starting ReservationApp on SURFACEBOOK with PID 24732 (C:\Users\amirb\Documents\workspace-sts-3.8.3.RELEASE\course-api\target\classes started by amirb in C:\Users\amirb\Documents\workspace-sts-3.8.3.RELEASE\course-api)
2017-03-11 09:39:45.937 INFO 24732 --- [ restartedMain] c.codebyamir.springboot.ReservationApp : No active profile set, falling back to default profiles: default
2017-03-11 09:39:46.013 INFO 24732 --- [ restartedMain] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@5c6c80b1: startup date [Sat Mar 11 09:39:46 EST 2017]; root of context hierarchy
2017-03-11 09:39:48.173 INFO 24732 --- [ restartedMain] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration' of type [class org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2017-03-11 09:39:48.261 INFO 24732 --- [ restartedMain] trationDelegate$BeanPostProcessorChecker : Bean 'validator' of type [class org.springframework.validation.beanvalidation.LocalValidatorFactoryBean] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2017-03-11 09:39:48.342 INFO 24732 --- [ restartedMain] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration' of type [class org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration$EnhancerBySpringCGLIB$925fe313] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2017-03-11 09:39:49.127 INFO 24732 --- [ restartedMain] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat initialized with port(s): 8080 (http)
2017-03-11 09:39:49.147 INFO 24732 --- [ restartedMain] o.apache.catalina.core.StandardService : Starting service Tomcat
2017-03-11 09:39:49.149 INFO 24732 --- [ restartedMain] org.apache.catalina.core.StandardEngine : Starting Servlet Engine: Apache Tomcat/8.5.11
2017-03-11 09:39:49.290 INFO 24732 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2017-03-11 09:39:49.291 INFO 24732 --- [ost-startStop-1] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 3283 ms
2017-03-11 09:39:49.470 INFO 24732 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean : Mapping servlet: 'dispatcherServlet' to [/]
2017-03-11 09:39:49.475 INFO 24732 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'characterEncodingFilter' to: [/*]
2017-03-11 09:39:49.475 INFO 24732 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
2017-03-11 09:39:49.475 INFO 24732 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'httpPutFormContentFilter' to: [/*]
2017-03-11 09:39:49.475 INFO 24732 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'requestContextFilter' to: [/*]
2017-03-11 09:39:50.223 INFO 24732 --- [ restartedMain] j.LocalContainerEntityManagerFactoryBean : Building JPA container EntityManagerFactory for persistence unit 'default'
2017-03-11 09:39:50.242 INFO 24732 --- [ restartedMain] o.hibernate.jpa.internal.util.LogHelper : HHH000204: Processing PersistenceUnitInfo [
name: default
...]
2017-03-11 09:39:50.337 INFO 24732 --- [ restartedMain] org.hibernate.Version : HHH000412: Hibernate Core {5.0.11.Final}
2017-03-11 09:39:50.339 INFO 24732 --- [ restartedMain] org.hibernate.cfg.Environment : HHH000206: hibernate.properties not found
2017-03-11 09:39:50.341 INFO 24732 --- [ restartedMain] org.hibernate.cfg.Environment : HHH000021: Bytecode provider name : javassist
2017-03-11 09:39:50.395 INFO 24732 --- [ restartedMain] o.hibernate.annotations.common.Version : HCANN000001: Hibernate Commons Annotations {5.0.1.Final}
2017-03-11 09:39:50.536 INFO 24732 --- [ restartedMain] org.hibernate.dialect.Dialect : HHH000400: Using dialect: org.hibernate.dialect.MySQLDialect
2017-03-11 09:39:51.442 INFO 24732 --- [ restartedMain] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2017-03-11 09:39:52.580 INFO 24732 --- [ restartedMain] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@5c6c80b1: startup date [Sat Mar 11 09:39:46 EST 2017]; root of context hierarchy
2017-03-11 09:39:52.794 INFO 24732 --- [ restartedMain] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/v1/reservations/{id}],methods=[GET]}" onto public com.codebyamir.springboot.reservation.Reservation com.codebyamir.springboot.reservation.ReservationController.getReservation(java.lang.Long)
2017-03-11 09:39:52.795 INFO 24732 --- [ restartedMain] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/v1/reservations],methods=[POST]}" onto public void com.codebyamir.springboot.reservation.ReservationController.addReservation(com.codebyamir.springboot.reservation.Reservation)
2017-03-11 09:39:52.796 INFO 24732 --- [ restartedMain] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/v1/reservations/{id}],methods=[DELETE]}" onto public void com.codebyamir.springboot.reservation.ReservationController.deleteReservation(java.lang.Long)
2017-03-11 09:39:52.796 INFO 24732 --- [ restartedMain] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/v1/reservations/{id}],methods=[PUT]}" onto public void com.codebyamir.springboot.reservation.ReservationController.updateReservation(com.codebyamir.springboot.reservation.Reservation,java.lang.Long)
2017-03-11 09:39:52.797 INFO 24732 --- [ restartedMain] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/v1/reservations],methods=[GET]}" onto public java.util.List com.codebyamir.springboot.reservation.ReservationController.getAllReservations()
2017-03-11 09:39:52.802 INFO 24732 --- [ restartedMain] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2017-03-11 09:39:52.802 INFO 24732 --- [ restartedMain] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
2017-03-11 09:39:52.852 INFO 24732 --- [ restartedMain] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2017-03-11 09:39:52.853 INFO 24732 --- [ restartedMain] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2017-03-11 09:39:52.946 INFO 24732 --- [ restartedMain] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2017-03-11 09:39:53.447 INFO 24732 --- [ restartedMain] o.s.b.d.a.OptionalLiveReloadServer : LiveReload server is running on port 35729
2017-03-11 09:39:53.547 INFO 24732 --- [ restartedMain] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
2017-03-11 09:39:53.704 INFO 24732 --- [ restartedMain] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http)
2017-03-11 09:39:53.726 INFO 24732 --- [ restartedMain] c.codebyamir.springboot.ReservationApp : Started ReservationApp in 8.205 seconds (JVM running for 8.823)
Test the API
We have a few options for testing the API. Some popular utilities are curl (command-line) and Postman (GUI). We can also use the browser for any GET requests.
Retrieve all reservations [GET /v1/reservations]
curl -X GET -H "Content-Type: application/json" http://localhost:8080/v1/reservations
[
{
"id": 1,
"dt": {
"dayOfMonth": 12,
"dayOfWeek": "SUNDAY",
"dayOfYear": 71,
"month": "MARCH",
"monthValue": 3,
"year": 2017,
"hour": 21,
"minute": 1,
"nano": 0,
"second": 11,
"chronology": {
"id": "ISO",
"calendarType": "iso8601"
}
},
"userId": 100,
"restaurantId": 800,
"partySize": 2
},
{
"id": 2,
"dt": {
"dayOfMonth": 13,
"dayOfWeek": "MONDAY",
"dayOfYear": 72,
"month": "MARCH",
"monthValue": 3,
"year": 2017,
"hour": 21,
"minute": 1,
"nano": 0,
"second": 11,
"chronology": {
"id": "ISO",
"calendarType": "iso8601"
}
},
"userId": 101,
"restaurantId": 800,
"partySize": 3
},
{
"id": 3,
"dt": {
"dayOfMonth": 14,
"dayOfWeek": "TUESDAY",
"dayOfYear": 73,
"month": "MARCH",
"monthValue": 3,
"year": 2017,
"hour": 21,
"minute": 1,
"nano": 0,
"second": 11,
"chronology": {
"id": "ISO",
"calendarType": "iso8601"
}
},
"userId": 102,
"restaurantId": 800,
"partySize": 5
}
]
Delete a reservation [DELETE /v1/reservations/2]
curl -X DELETE -H "Content-Type: application/json" -H "Cache-Control: no-cache""http://localhost:8080/v1/reservations/2"
Run the Executable JAR as a Linux Service
The executable jar is named reservation-microservice-0.0.1-SNAPSHOT.jar.
We can run it as a Linux System V init service like so:
# ln -s /home/ec2-user/reservation-microservice-0.0.1-SNAPSHOT.jar /etc/init.d/springApp
# /etc/init.d/springApp Usage: /etc/init.d/springApp {start|stop|force-stop|restart|force-reload|status|run}
The script supports the standard service start, stop, restart and status commands.
- Writes a PID file in /var/run/springApp/springApp.pid
- Writes console logs to /var/log/springApp.log