Since its inception in 2002, the Spring framework saw a tremendous growth in terms of functionalities which ultimately made the framework a bit complex. One of the main error-prone areas in traditional Spring programming was, of course, the enormous amount of config XML files that one had to create for a fairly large Spring project. SpringBoot was introduced by Pivotal software to deal with this by allowing autoconfiguration. SpringBoot is built on top of the Spring framework. Using SpringBoot, we can convert our application into an executable jar or war file that can be deployed. We need not create any explicit XML configuration in a SpringBoot Application.

In this tutorial, we will learn how to create a REST service using SpringBoot.

Maven Configurations

To create the project structure, we can manually do the steps below or use Spring Tool Suite to build the project.If you are using STS, you can create a Spring Boot application using New -> Spring Starter Project.Either ways, the final pom.xml created would be almost the same.

Let us see how to build the project with maven now.

In command prompt, we need to create the directory structure. Type the following command into the console:

mvn archetype:generate -DgroupID=com.javatutorials.rest -DartifactId=RestWithSpring -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false 1 mvn archetype : generate - DgroupID = com .javatutorials .rest - DartifactId = RestWithSpring - DarchetypeArtifactId = maven - archetype - quickstart - DinteractiveMode = false

This command will trigger download of many maven dependencies that are required to set up this project. At the end of the build, we should see a build success message.

Import project into eclipse using import -> maven -> existing maven projects into eclipse.

Modify pom.xml :

<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 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.javatutorials.rest</groupId> <artifactId>RestWithSpring</artifactId> <packaging>jar</packaging> <version>1.0-SNAPSHOT</version> <name>RestWithSpringBoot</name> <url>http://maven.apache.org</url> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.3.RELEASE</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>com.jayway.jsonpath</groupId> <artifactId>json-path</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>com.fasterxml.jackson.dataformat</groupId> <artifactId>jackson-dataformat-xml</artifactId> </dependency> <dependency> <groupId>org.codehaus.woodstox</groupId> <artifactId>woodstox-core-asl</artifactId> <version>4.4.1</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.6.1</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>1.5.3.RELEASE</version> </plugin> </plugins> </build> <repositories> <repository> <id>spring-releases</id> <url>https://repo.spring.io/libs-release</url> </repository> </repositories> <pluginRepositories> <pluginRepository> <id>spring-releases</id> <url>https://repo.spring.io/libs-release</url> </pluginRepository> </pluginRepositories> </project> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 < 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 http://maven.apache.org/maven-v4_0_0.xsd" > < modelVersion > 4.0.0 < / modelVersion > < groupId > com . javatutorials . rest < / groupId > < artifactId > RestWithSpring < / artifactId > < packaging > jar < / packaging > < version > 1.0 - SNAPSHOT < / version > < name > RestWithSpringBoot < / name > < url > http : //maven.apache.org</url> < parent > < groupId > org . springframework . boot < / groupId > < artifactId > spring - boot - starter - parent < / artifactId > < version > 1.5.3.RELEASE < / version > < / parent > < dependencies > < dependency > < groupId > org . springframework . boot < / groupId > < artifactId > spring - boot - starter - web < / artifactId > < / dependency > < dependency > < groupId > org . springframework . boot < / groupId > < artifactId > spring - boot - starter - test < / artifactId > < scope > test < / scope > < / dependency > < dependency > < groupId > com . jayway . jsonpath < / groupId > < artifactId > json - path < / artifactId > < scope > test < / scope > < / dependency > < dependency > < groupId > com . fasterxml . jackson . dataformat < / groupId > < artifactId > jackson - dataformat - xml < / artifactId > < / dependency > < dependency > < groupId > org . codehaus . woodstox < / groupId > < artifactId > woodstox - core - asl < / artifactId > < version > 4.4.1 < / version > < / dependency > < dependency > < groupId > junit < / groupId > < artifactId > junit < / artifactId > < version > 4.12 < / version > < scope > test < / scope > < / dependency > < / dependencies > < build > < plugins > < plugin > < groupId > org . apache . maven . plugins < / groupId > < artifactId > maven - compiler - plugin < / artifactId > < version > 3.6.1 < / version > < configuration > < source > 1.8 < / source > < target > 1.8 < / target > < / configuration > < / plugin > < plugin > < groupId > org . springframework . boot < / groupId > < artifactId > spring - boot - maven - plugin < / artifactId > < version > 1.5.3.RELEASE < / version > < / plugin > < / plugins > < / build > < repositories > < repository > < id > spring - releases < / id > < url > https : //repo.spring.io/libs-release</url> < / repository > < / repositories > < pluginRepositories > < pluginRepository > < id > spring - releases < / id > < url > https : //repo.spring.io/libs-release</url> < / pluginRepository > < / pluginRepositories > < / project >

If we don’t specify the parent as spring boot starter parent, we would have to individually show the same version number for all the spring related dependencies. If there is a need to add a separate central parent project which contains all the dependencies, we should remove the <parent> tag to ensure nothing is broken by our code. In this case, we need to add the following in pom.xml.

<dependencyManagement> <dependencies> <dependency> Import dependency management from Spring Boot <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>1.5.3.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> 1 2 3 4 5 6 7 8 9 10 11 12 <dependencyManagement> <dependencies> <dependency> Import dependency management from Spring Boot <groupId> org.springframework.boot </groupId> <artifactId> spring-boot-dependencies </artifactId> <version> 1.5.3.RELEASE </version> <type> pom </type> <scope> import </scope> </dependency> </dependencies> </dependencyManagement>

After required changes are made to pom.xml, run the following in command prompt:

mvn eclipse:clean 1 mvn eclipse : clean

mvn eclipse:eclipse 1 mvn eclipse : eclipse

On the first run, this should download all the dependencies to the M2_HOME folder.

Gradle Configurations

If we are using gradle for building, just like pom.xml of maven, we need to add build.gradle build file. We can generate the build file from existing pom.xml by navigating to the root folder of the project and typing:

gradle init 1 gradle init

Generated gradle.build file:

buildscript { repositories { mavenCentral() } dependencies { classpath("org.springframework.boot:spring-boot-gradle-plugin:1.5.2.RELEASE") } } apply plugin: 'java' apply plugin: 'eclipse' apply plugin: 'idea' apply plugin: 'org.springframework.boot' jar { baseName = 'RestWithSpringBoot' version = '0.1.0' } repositories { mavenCentral() } sourceCompatibility = 1.8 targetCompatibility = 1.8 dependencies { compile("org.springframework.boot:spring-boot-starter-web") testCompile('org.springframework.boot:spring-boot-starter-test') } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 buildscript { repositories { mavenCentral ( ) } dependencies { classpath ( "org.springframework.boot:spring-boot-gradle-plugin:1.5.2.RELEASE" ) } } apply plugin : 'java' apply plugin : 'eclipse' apply plugin : 'idea' apply plugin : 'org.springframework.boot' jar { baseName = 'RestWithSpringBoot' version = '0.1.0' } repositories { mavenCentral ( ) } sourceCompatibility = 1.8 targetCompatibility = 1.8 dependencies { compile ( "org.springframework.boot:spring-boot-starter-web" ) testCompile ( 'org.springframework.boot:spring-boot-starter-test' ) }

After successful creation of the first REST application, we may invoke gradle build by the following command:

gradle build 1 gradle build

POJO

We will define an Employee class which has details like employee ID, name, department and date of joining.

package com.javatutorials.rest.model; import java.io.Serializable; import java.util.Date; import javax.xml.bind.annotation.XmlRootElement; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.ser.std.DateSerializer; @XmlRootElement public class Employee implements Serializable{ private static final long serialVersionUID = 4560577467176811792L; private int empId; private String fullName; private String dept; @JsonFormat(pattern="yyyy-MM-dd") private Date dateOfJoining; public Employee(){} public int getEmpId() { return empId; } public void setEmpId(int empId) { this.empId = empId; } public String getFullName() { return fullName; } public void setFullName(String fullName) { this.fullName = fullName; } public String getDept() { return dept; } public void setDept(String dept) { this.dept = dept; } @JsonSerialize(using = DateSerializer.class) public Date getDateOfJoining() { return dateOfJoining; } public void setDateOfJoining(Date dateOfJoining) { this.dateOfJoining = dateOfJoining; } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 package com . javatutorials . rest . model ; import java . io . Serializable ; import java . util . Date ; import javax . xml . bind . annotation . XmlRootElement ; import com . fasterxml . jackson . annotation . JsonFormat ; import com . fasterxml . jackson . databind . annotation . JsonSerialize ; import com . fasterxml . jackson . databind . ser . std . DateSerializer ; @XmlRootElement public class Employee implements Serializable { private static final long serialVersionUID = 4560577467176811792L ; private int empId ; private String fullName ; private String dept ; @JsonFormat ( pattern = "yyyy-MM-dd" ) private Date dateOfJoining ; public Employee ( ) { } public int getEmpId ( ) { return empId ; } public void setEmpId ( int empId ) { this . empId = empId ; } public String getFullName ( ) { return fullName ; } public void setFullName ( String fullName ) { this . fullName = fullName ; } public String getDept ( ) { return dept ; } public void setDept ( String dept ) { this . dept = dept ; } @JsonSerialize ( using = DateSerializer . class ) public Date getDateOfJoining ( ) { return dateOfJoining ; } public void setDateOfJoining ( Date dateOfJoining ) { this . dateOfJoining = dateOfJoining ; } }

Note that we use @JsonSerialize annotation. This is to ensure that the Date object is serialized correctly. The required format in which dateOfJoining shall be shown in XML and JSON outputs is specified using @JsonFormat‘s parameter value.

RestController

So far, we have created the Employee POJO. We need to create a Controller that serves the HTTP GET, PUT, callPOST and DELETE requests. In addition to the response body, we will take care of returning the correct status code according to the result at the server rather than just returning 200 STATUS OK for every call.

We are going to use ResponseEntity to give the correct status codes. Our code will do the following in addition to the core functionalities:

GET request to a resource that has not been created yet will return 404 NOT FOUND

request to a resource that has not been created yet will return 404 NOT FOUND POST request with an account id which is already stored in the HashMap will return 409 CONFLICT which means that the request could not be completed due to a conflict with the current state of the resource.

request with an account id which is already stored in the HashMap will return which means that the request could not be completed due to a conflict with the current state of the resource. PUT and DELETE calls will give 204 NO CONTENT which means that the operation was successfully done at server end but no content is returned.

Note that we are using @RestController annotation. @RestController annotation is a replacement for @Controller and @ResponseBody annotations. It takes care of conversion of the responses to XML or JSON format according to the request’s accept header. There is no need to explicitly specify @Produces inside the @RequestMapping annotation as far as you have the dependencies for XML and JSON parsers correctly specified inside the project’s pom.xml.

EmployeeController.java:

package com.javatutorials.rest.controller; import java.util.*; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; 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; import com.javatutorials.rest.model.Employee; import static com.javatutorials.rest.constants.Constants.*; @RestController public class EmployeeController { private Map<Integer, Employee> empMap = new HashMap<Integer, Employee>(); @RequestMapping(value = GET_DEFAULT_EMPLOYEE, method= RequestMethod.GET) public ResponseEntity<Employee> getDefault(){ Employee emp; if(empMap.get(0) != null){ emp = empMap.get(0); } else { emp = new Employee(); emp.setEmpId(0); emp.setFullName("John Smith"); emp.setDept("HR"); emp.setDateOfJoining(new Date()); empMap.put(0, emp); } return new ResponseEntity<Employee>(emp, HttpStatus.OK); } @RequestMapping(value = GET_ONE_EMPLOYEE, method = RequestMethod.GET) public ResponseEntity<Employee> getOneEmployee(@PathVariable("id") int id){ Employee emp = null; emp = empMap.get(id); ResponseEntity<Employee> entity = null; if(emp == null){ entity = new ResponseEntity<Employee>(HttpStatus.NOT_FOUND); } else { entity = new ResponseEntity<Employee>(emp, HttpStatus.OK); } return entity; } @RequestMapping(value = GET_ALL_EMPLOYEES, method = RequestMethod.GET) public List<Employee> getAllEmployees(){ Collection<Employee> accs = empMap.values(); List<Employee> returnList = new ArrayList<Employee>(); returnList.addAll(accs); return returnList; } @RequestMapping(value = GET_ALL_EMPLOYEES, method = RequestMethod.POST) public ResponseEntity<Employee> addEmployeeDetails(@RequestBody Employee emp){ Employee oldAcc = null; ResponseEntity<Employee> entity = null; oldAcc = empMap.get(emp.getEmpId()); if(oldAcc == null){ empMap.put(emp.getEmpId(), emp); entity = new ResponseEntity<Employee>(emp, HttpStatus.CREATED); } else { entity = new ResponseEntity<Employee>(HttpStatus.CONFLICT); } return entity; } @RequestMapping(value = GET_ONE_EMPLOYEE, method = RequestMethod.PUT ) public ResponseEntity<Employee> modifyEmployeeDetails(@PathVariable("id") int id, @RequestBody Employee emp){ Employee oldAcc = empMap.get(emp.getEmpId()); if(oldAcc == null){ empMap.put(emp.getEmpId(), emp); } else { empMap.remove(emp.getEmpId()); empMap.put(emp.getEmpId(), emp); } return new ResponseEntity<Employee>(HttpStatus.NO_CONTENT); } @RequestMapping(value = GET_ONE_EMPLOYEE, method = RequestMethod.DELETE) public ResponseEntity<Employee> removeEmployee(@PathVariable("id") int id){ empMap.remove(id); return new ResponseEntity<Employee>(HttpStatus.NO_CONTENT); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 package com . javatutorials . rest . controller ; import java . util . * ; import org . springframework . http . HttpStatus ; import org . springframework . http . ResponseEntity ; 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 ; import com . javatutorials . rest . model . Employee ; import static com . javatutorials . rest . constants . Constants . * ; @RestController public class EmployeeController { private Map < Integer , Employee > empMap = new HashMap < Integer , Employee > ( ) ; @RequestMapping ( value = GET_DEFAULT_EMPLOYEE , method = RequestMethod . GET ) public ResponseEntity <Employee> getDefault ( ) { Employee emp ; if ( empMap . get ( 0 ) ! = null ) { emp = empMap . get ( 0 ) ; } else { emp = new Employee ( ) ; emp . setEmpId ( 0 ) ; emp . setFullName ( "John Smith" ) ; emp . setDept ( "HR" ) ; emp . setDateOfJoining ( new Date ( ) ) ; empMap . put ( 0 , emp ) ; } return new ResponseEntity <Employee> ( emp , HttpStatus . OK ) ; } @RequestMapping ( value = GET_ONE_EMPLOYEE , method = RequestMethod . GET ) public ResponseEntity <Employee> getOneEmployee ( @PathVariable ( "id" ) int id ) { Employee emp = null ; emp = empMap . get ( id ) ; ResponseEntity <Employee> entity = null ; if ( emp == null ) { entity = new ResponseEntity <Employee> ( HttpStatus . NOT_FOUND ) ; } else { entity = new ResponseEntity <Employee> ( emp , HttpStatus . OK ) ; } return entity ; } @RequestMapping ( value = GET_ALL_EMPLOYEES , method = RequestMethod . GET ) public List <Employee> getAllEmployees ( ) { Collection <Employee> accs = empMap . values ( ) ; List <Employee> returnList = new ArrayList <Employee> ( ) ; returnList . addAll ( accs ) ; return returnList ; } @RequestMapping ( value = GET_ALL_EMPLOYEES , method = RequestMethod . POST ) public ResponseEntity <Employee> addEmployeeDetails ( @RequestBody Employee emp ) { Employee oldAcc = null ; ResponseEntity <Employee> entity = null ; oldAcc = empMap . get ( emp . getEmpId ( ) ) ; if ( oldAcc == null ) { empMap . put ( emp . getEmpId ( ) , emp ) ; entity = new ResponseEntity <Employee> ( emp , HttpStatus . CREATED ) ; } else { entity = new ResponseEntity <Employee> ( HttpStatus . CONFLICT ) ; } return entity ; } @RequestMapping ( value = GET_ONE_EMPLOYEE , method = RequestMethod . PUT ) public ResponseEntity <Employee> modifyEmployeeDetails ( @PathVariable ( "id" ) int id , @RequestBody Employee emp ) { Employee oldAcc = empMap . get ( emp . getEmpId ( ) ) ; if ( oldAcc == null ) { empMap . put ( emp . getEmpId ( ) , emp ) ; } else { empMap . remove ( emp . getEmpId ( ) ) ; empMap . put ( emp . getEmpId ( ) , emp ) ; } return new ResponseEntity <Employee> ( HttpStatus . NO_CONTENT ) ; } @RequestMapping ( value = GET_ONE_EMPLOYEE , method = RequestMethod . DELETE ) public ResponseEntity <Employee> removeEmployee ( @PathVariable ( "id" ) int id ) { empMap . remove ( id ) ; return new ResponseEntity <Employee> ( HttpStatus . NO_CONTENT ) ; } }

Constants.java:

package com.javatutorials.rest.constants; public interface Constants { String DELIMITTER = "/"; String EMPLOYEES = "employees"; String ID = "{id}"; String DEFAULT = "default"; String GET_ALL_EMPLOYEES = DELIMITTER + EMPLOYEES; String GET_ONE_EMPLOYEE = GET_ALL_EMPLOYEES + DELIMITTER +ID; String GET_DEFAULT_EMPLOYEE = GET_ALL_EMPLOYEES + DELIMITTER + DEFAULT; } 1 2 3 4 5 6 7 8 9 10 11 12 13 package com . javatutorials . rest . constants ; public interface Constants { String DELIMITTER = "/" ; String EMPLOYEES = "employees" ; String ID = "{id}" ; String DEFAULT = "default" ; String GET_ALL_EMPLOYEES = DELIMITTER + EMPLOYEES ; String GET_ONE_EMPLOYEE = GET_ALL_EMPLOYEES + DELIMITTER + ID ; String GET_DEFAULT_EMPLOYEE = GET_ALL_EMPLOYEES + DELIMITTER + DEFAULT ; }

Making Our Application Executable

So far, we have created the model and controller which shall handle the HTTP Request. Now we need to convert this application into executable format.

We will use @SpringBootApplication for running this code as a standalone application. This is essentially the starting point of the SpringBoot Application and dependency injection is done via SpringApplication.run(StartupApplication.class, args).

package com.javatutorials.rest; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class StartupApplication { public static void main(String[] args) { SpringApplication.run(StartupApplication.class, args); } } 1 2 3 4 5 6 7 8 9 10 11 12 package com . javatutorials . rest ; import org . springframework . boot . SpringApplication ; import org . springframework . boot . autoconfigure . SpringBootApplication ; @ SpringBootApplication public class StartupApplication { public static void main ( String [ ] args ) { SpringApplication . run ( StartupApplication . class , args ) ; } }

Testing Our SpringBoot Application

This can be done in two ways

One way is to use mvn or gradle command for spring boot run

mvn spring-boot:run 1 mvn spring - boot : run

or

gradle bootRun 1 gradle bootRun

or Second way is by following the traditional approach to create jar and run it via command prompt. We can create the jar with the following maven or gradle command

mvn clean package 1 mvn clean package

OR

gradle build 1 gradle build

On successful packaging, the jar file should be available at target/RestWithSpring-1.0-SNAPSHOT.jar in case of maven build OR build/libs/RestWithSpringBoot -0.1.0.jar in case of gradle build. Run the following command to get the service up and running.

java -jar <path to your jar> 1 java - jar < path to your jar >



In order to see the output in json format, type the following in our browser:

http://localhost:8080/employees/default.json

The JSON output obtained will be:

{ "empId": 0, "fullName": "John Smith", "dept": "HR", "dateOfJoining": "2017-05-29" } 1 2 3 4 5 6 { "empId" : 0 , "fullName" : "John Smith" , "dept" : "HR" , "dateOfJoining" : "2017-05-29" }

To see the output in XML format, change the URL to:

http://localhost:8080/employees/default.xml

The XML output generated will be:

<Employee> <empId>0</empId> <fullName>John Smith</fullName> <dept>HR</dept> <dateOfJoining>2017-05-29</dateOfJoining> </Employee> 1 2 3 4 5 6 < Employee > < empId > 0 < / empId > < fullName > John Smith < / fullName > < dept > HR < / dept > < dateOfJoining > 2017 - 05 - 29 < / dateOfJoining > < / Employee >



SOAPUI can be handy in case we need to verify the HTTP Status code returned in case of a failed HTTP Request.

We have attached the screenshot of a successful POST request and a failed POST request for reference. Note that the HTTP Response headers contain 409 as response code when POST request is repeated with same Employee details.

Successful POST request:

Failed POST request due to duplicate content:

Share this: Facebook

LinkedIn

Twitter

Tumblr

Pinterest



Like this: Like Loading...