Pages

Sunday, July 15, 2018

Spring Boot Complete Example

This post provides a complete example of using spring boot to develop a loosely coupled REST service. Using spring boot, we can develop production ready java application which will run independently, as stand alone application with minimal dependencies and less coupling with other applications. Most of the time, spring boot application will provide a cohesive service and the boundaries of the service is clearly defined. Let's deep into the our example.

For this tutorial, I am going to use Spring Boot 2.0.3.RELEASE which requires the java 8 or 9 and Maven 3.2+, Eclipse as an IDE.

Creating Maven jar module

Since, we are going to bundle our application as a .jar file, we can use eclipse IDE support to generate maven jar module after integrating maven with Eclipse IDE. The recent version of Eclipse comes with integrated maven plugin. So you don't need to explicitly add maven plugin into eclipse. I am not going to explain, how to create maven jar module with eclipse within this post. If you want to know it, you can read my another post here which clearly explains about creating maven modules with eclipse. If you create a maven project by using 'quickstart' artifact, you will get a project structure similar to the following.



I have created a maven module called 'customer' with 'com.semika' as groupId and 'customer' as artifact id. You can choose what ever package structure, you want. Your inner package structure will change based on it.

App.java and AppTest.java files will be removed soon. Have a look on pom.xml file which contains information about the project and configuration details used by Maven to build the project. You can remove Junit dependency for now, since this tutorial does not cover unit testing features.

I want to highlight one important element here. 

<packaging>jar</packaging>

This is where, we tell maven to bundle our application as a .jar file for deployment. 


Adding spring boot features

Now, what we have is, typical maven jar module. How we are going to convert this into spring boot application? 

All spring boot dependencies are defined under org.springframework.boot group id within the maven repository. The spring-boot-starter-parent is a project which has some default settings and basic required configurations that we can use in order to quickly start using spring boot. We can inherits these default settings by adding following element into our pom.xml file.

<!-- Inherit defaults from Spring Boot --> 
<parent> 
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId> 
    <version>2.0.3.RELEASE</version> 
</parent>

Connecting to a database

Now, let's see, how we can connect our spring boot application with a database or how we integrate a data source to our spring boot application. Spring framework provides a great support for accessing a database from direct JDBC access to ORM technologies like Hibernate. 

The javax.sql.DataSource interface provides standard methods in order to work with a database by creating data source with a connection pool. There are several implementation like BoneCP, Apache common DBCP2 and spring's default HikariCP.If We use the spring-boot-starter-jdbc or spring-boot-starter-data-jpa “starters”, we automatically get a dependency to HikariCP. We are going to use 'spring-boot-starter-data-jpa' for data access later on this tutorial. 

Now, time has come to add 'application.properties' file to our project. In spring boot application, this file contains all the configuration properties and the file should be available on classpath. I am going to delete 'App.java' and 'AppTest.java' file and create a new folder as 'resources' inside the 'main' folder, parallel to 'java' folder. When building modules by using maven, the files inside the 'resources' folder are made available to classpath. We don't need to do any extract things. 

Let's create a file as 'application.properties' inside the resources folder. I am going to connect my spring boot application to a MySql database. The minimal properties required to create a datasoruce for spring boot application, are as follows. 

spring.datasource.url=jdbc:mysql://localhost/springboot?useSSL=false
spring.datasource.username=root
spring.datasource.password=abc123
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect
spring.jpa.show-sql=false

Here property name convention was not selected randomly. Spring's datasource configuration properties should be prefix with spring.datasource.*. If you want to set up a specific data source implementation, the property names should be specify with respective prefix as spring.datasource.hikari.*, spring.datasource.tomcat.*, spring.datasource.dbcp2.*.

Since we are going to connect to MySql database, mysql-java connector maven dependency should be added to our pom.xml file as follows.

<dependency> 
     <groupId>mysql</groupId> 
     <artifactId>mysql-connector-java</artifactId> 
</dependency>

Adding Main Application class

Every spring boot application should have a main class with main() method defined. Generally this class is named as "Application.java" and should locate in the root package above the other classes. This class is normally annotated with few annotations. 

  • @EnableAutoConfiguration - This annotation enables auto-configuration for our spring boot application which attempts to automatically configure our Spring application based on the jar dependencies that we have added. 
  • @ComponentScan - This enables spring bean dependency injection feature by using @Autowired annotation. All of our application components which were annotated with @Component, @Service, @Repository or @Controller are automatically registered as Spring Beans. These beans can be injected by using @Autowired annotation. 
  • @Configuration - This enables Java based configurations for spring boot application. Usually the class that defines the main method is a good candidate to annotate with this annotation.

I am going go create a new class as "Application.java" inside the com.semika package, which is the root for my spring boot application.

package com.semika;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@EnableAutoConfiguration
@ComponentScan
@Configuration

public class Application {

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

In stead of using all three annotation, we can use only @SpringBootApplication annotation which is equivalent to using @Configuration, @EnableAutoConfiguration, and @ComponentScan with their default attributes, as shown in the following example.

package com.semika;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@SpringBootApplication
public class Application {
     public static void main(String[] args) {
        SpringApplication app = new SpringApplication(Application.class);
        app.run(args); 
     }
}

Adding data access features with spring-data-JPA

Now, let's see, how we can integrate data access features to our spring boot applications. Data access classes are called 'Repositories' in spring boot application. JPA (Java Persistence API) is a standard technology that lets us “map” objects to relational databases. 

The spring-boot-starter-data-jpa starter project provides a quick way to get started with data access for spring boot application. It provides the following key dependencies: 
  • Using Hibernate to map objects with database tables. 
  • Spring Data JPA which can be used to write JPA-based repositories. 
  • Core ORM support from the Spring Framework.
In order to add data access features to our spring boot application, we should add the following maven dependency to our pom.xml file. After adding bellow dependency, we can use usual JPA annotations to map objects with relational database table. 

<dependency> 
      <groupId>org.springframework.boot</groupId> 
      <artifactId>spring-boot-starter-data-jpa</artifactId> 
</dependency>

Let's create a new package as 'customer' inside the root folder which is com.semika where the 'Application.java' class is located by now. Inside customer folder, new entity class is crated as 'Customer.java'. For now, my customer database table has three attributes as follows.











package com.semika.customer;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="customer") 
public class Customer {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
 
    @Column(name="first_name")
    private String firstName;
 
    @Column(name="last_name")
    private String lastName;
 
    @Column(name="address")
    private String address;

    public Customer() {
       super();
    }

    // getters and setters
}

Spring data JPA repositories are interfaces that can be defined to access data. JPA queries are created automatically from the method names. For example, findAll() method in 'CustomerRepository.java' class fetches all the customers. The findByFirstName(String firstName) method will fetch all the customers for a given first name. 

The central interface in the Spring Data repository abstraction is Repository interface. It takes the domain class to manage as well as the ID type of the domain class as type arguments.The CrudRepository interface provides sophisticated CRUD functionality for the entity class that is being managed. Our repository interfaces should extend from CrudRepository interface.

Our 'CustomerRepository.java' interface will be as follows:

package com.semika.customer;

import org.springframework.data.repository.CrudRepository;

public interface CustomerRepository extends CrudRepository<Customer Long> {

}


You may find for the implementation class? Spring data JPA provides the implementation for most of the data access scenarios. We don't need to implement explicitly those methods. If you want to read more about spring data JPA, you can read the reference documentation here.

Further, I am going to add 'CustomerService.java' interface and it's implementation 'CustomerServiceImpl.java' class in order to keep our business logic in a separate layer.

package com.semika.customer;

public interface CustomerService {
    public Iterable<Customer> findAll(); 
}


package com.semika.customer;
package com.semika.customer;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class CustomerServiceImpl implements CustomerService {

    @Autowired
    private CustomerRepository customerRepository;
 
    public Iterable<Customer> findAll() {
        return customerRepository.findAll(); 
    }
}

You can see that,CustomerRepository is injected to CustomerServiceImpl class using @Autowired annotation. We did enable this by adding @ComponentScan annotation via @SpringBootApplication to our 'Application.java' class early in this tutorial.

Adding web features

Now, it's time to build and test our application. Suppose, client makes HTTP requests to fetch all the customers data. So our spring boot application should response to HTTP requests. Spring MVC provides 'Controllers' which accepts HTTP requests and responses to those. Here, we are going to add some spring MVC features to our spring boot application. By using spring-boot-starter-web project, we can integrate some basic MVC features to our spring boot application so that we can write simple Controller class which responses client's HTTP requests.

We should add the following maven dependency to our project.

<dependency> 
     <groupId>org.springframework.boot</groupId> 
     <artifactId>spring-boot-starter-web</artifactId> 
</dependency>

package com.semika.customer;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class CustomerController {

    @Autowired
    private CustomerService  customerService;
 
    @RequestMapping("/customers") 
    @ResponseBody
    public Iterable<Customer> findAll() {
       Iterable<Customer> customers = customerService.findAll();
       return customers;
    }
}

@RestController is a stereotype annotation in Spring MVC which provides hints for people reading the code and for Spring that the class plays a specific role. That is, it contains the gates to enter into the application. In this case, our class is a web @Controller, so Spring considers it when handling incoming web requests. The @RestController annotation tells Spring to render the resulting string directly back to the caller.

The @RequestMapping annotation provides “routing” information. It tells Spring that any HTTP request with the '/customers' path should be mapped to the findAll() method.

These two annotations are spring MVC annotations. They are not specific to spring boot. We added this spring MVC web features in order to test our application by making some web requests. With the adding of 'spring-boot-starter-web' to a spring boot application, when running it, spring boot application starts up it's own web container and runs with in it.

So now, our project structure should be as follows.


Building application

Spring boot jar file is called a self-contained executable jar file that we can run directly in production environment. Executable jars are archives containing your compiled classes along with all of the jar dependencies that your code needs to run. In our example, since we used 'spring-boot-starter-web', when running the jar file, it starts internal web container in order to run the application. 

To create an executable jar, we need to add the spring-boot-maven-plugin to our pom.xml. To do so, insert the following lines just below the plugins section.

<plugins> 
     <plugin> 
          <groupId>org.springframework.boot</groupId> 
          <artifactId>spring-boot-maven-plugin</artifactId> 
     </plugin> 
</plugins>

You might notice that some of the configurations for above plugin is missing here. Since we are using 'spring-boot-starter-parent', we don't need to worry about those, because those are already included within the parent project. For example, parent project POM includes <executions> configuration to bind the repackage goal.

Let's look at our final pom.xml file now:

<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/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.semika.user</groupId>
  <artifactId>customer</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>
  
  <!-- Inherit defaults from Spring Boot -->
  <parent>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-parent</artifactId>
      <version>2.0.3.RELEASE</version>
  </parent>

  <name>customer</name>
  <url>http://maven.apache.org</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>
  
  <!-- Building an executable jar file -->
  
  <build>
      <plugins>
         <plugin>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-maven-plugin</artifactId>
         </plugin>
      </plugins>
  </build>

  <dependencies>
        <!-- Adding spring data jpa features -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
 
        <!-- Java MySQL connector -->
        <dependency>
             <groupId>mysql</groupId>
             <artifactId>mysql-connector-java</artifactId>
        </dependency>
 
        <!-- Integrating spring MVC features -->
        <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
  </dependencies>
</project>


Let's build the application. Go the project root folder where pom.xml file is located and run the following command.

mvn package

Inside the target folder, you can see our executable jar file created as 'customer-0.0.1-SNAPSHOT.jar'.

Running the application:

From the same folder, run the following command to run the jar file. 

java -jar target/customer-0.0.1-SNAPSHOT.jar

If you analyze the logs when starting up the application, you can discover many important things. The console output at the server starting is as follows:


If you see the logs near the bottom, it starts the Tomcat server on port 8080. If you access the http://localhost:8080/customers URL from the browser, you will get JSON response of customers as the response.

If you want to start the application on different port than the default one, you can specify the port by suing --server.port option as follows.

java --server.port=9000 -jar target/customer-0.0.1-SNAPSHOT.jar

If you want to start the application with debug enabled, you can use the following command:

java -Xdebug -Xrunjdwp:server=y,transport=dt_socket,address=8000,suspend=n -jar target/customer-0.0.1-SNAPSHOT.jar

To start the application with debug log enabled, you can use the following command

java -jar target/customer-0.0.1-SNAPSHOT.jar --debug

If you want to specify the sever running port in 'application.properties' file, you can include the following property into the file. 

server.port=${port:9000}

With above entry in 'application.properties' file, in stead of using --server.port option, you can simply user --port option with java -jar command in order to specify the port.

Most of the time, your configuration properties are different from environment to environment. For environment like, development, production and testing, you might need to keep different set of configuration properties. You can do this by keeping different configuration profiles for each environment. You should create the configuration properties file in the following format in order to achieve this.

application-${profile}.properties

Let's say you need to keep two configuration profiles for 'development' and 'production' environment separately. In this case, you should create two property files as 'application-development.properties' and 'application-production.properties'. When starting the application using java -jar command, you should specify the profile with -D parameter as follows: 

java -jar -Dspring.profiles.active=production customer-0.0.1-SNAPSHOT.jar

I hope this post will be helpful  specially for beginners who try to learn about spring boot application and Micro services.

References : Spring Boot Reference Guide

2 comments:

  1. Hello,

    I've some compilation errors:
    [ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.7.0:compile (default-compile) on project customer: Compilation failure
    [ERROR] /customer/src/main/java/com/semika/CustomerServiceImpl.java:[2,1] class, interface, or enum expected
    [ERROR] -> [Help 1]
    org.apache.maven.lifecycle.LifecycleExecutionException: Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.7.0:compile (default-compile) on project customer: Compilation failure
    /customer/src/main/java/com/semika/CustomerServiceImpl.java:[2,1] class, interface, or enum expected

    I can't see the error in the mentionned file:
    [root@781f7175d689 customer]# cat /customer/src/main/java/com/semika/CustomerServiceImpl.java
    package com.semika.customer;
    package com.semika.customer;

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;

    @Service
    public class CustomerServiceImpl implements CustomerService {

    @Autowired
    private CustomerRepository customerRepository;

    public Iterable findAll() {
    return customerRepository.findAll();
    }
    }

    Could you help me please ?

    Regards

    Julien

    ReplyDelete

Share

Widgets