Getting started

The Spring Framework has been around for over a decade and has found a place as the de facto standard framework for developing Java applications. With such a long and storied history, some might think that Spring has settled, resting on its laurels, and is not doing anything new or exciting. Some might even say that Spring is legacy and that it’s time to look elsewhere for innovation.

Some would be wrong.

There are many exciting new things taking place in the Spring ecosystem, including work in the areas of cloud computing, big data, schema-less data persistence, reactive programming, and client-side application development. Perhaps the most exciting, most head-turning, most game-changing new thing to come to Spring in the past year or so is Spring Boot. Spring Boot offers a new paradigm for developing Spring applications with minimal friction.

— Craig Walls

Spring Boot

I suppose we all are well aware of the strengths and weaknesses of Spring Framework, specially the complex XML configurations which proved to be a nightmare for enterprise grade applications with hundreds of beans and services to support a requirement.

A lot of work has gone into Spring Boot to reduce complexity and dependencies, which largely alleviates our previous reservations. If you live in a Spring ecosystem and are moving to microservices, Spring Boot is now the obvious choice. Spring Boot allows easy set up of standalone Spring-based applications. It’s ideal for pulling up new microservices and easy to deploy. It also makes data access less of a pain due to the hibernate mappings with much less boilerplate code.

Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can “just run”. It takes an opinionated view of the Spring platform and third-party libraries so that we can get started with minimum fuss. Most Spring Boot applications need very little Spring configuration, it has following distinguishing features:

  • Easy dependency Management

  • Embed Tomcat, Jetty or Undertow directly (no need to deploy WAR files)

  • Provide opinionated ‘starter’ dependencies to simplify your build configuration

  • Automatically configure Spring and 3rd party libraries whenever possible

  • Provide production-ready features such as metrics, health checks and externalised configuration

  • Absolutely no code generation and no requirement for XML configuration

  • DevTools to auto restart server on code/config updates

  • Easy management of profile specific properties

Create & Setup Spring Boot project

Use Spring web tool or your development tool (Spring Tool Suite, Eclipse, Intellij) to create a Spring Boot project.

Then open pom.xml and add these 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>

We also need to add one more dependency. – If you want to use MySQL:

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

– or PostgreSQL:

<dependency>
	<groupId>org.postgresql</groupId>
	<artifactId>postgresql</artifactId>
	<scope>runtime</scope>
</dependency>

– or JWT and spring security:

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

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

<dependency>
	<groupId>io.jsonwebtoken</groupId>
	<artifactId>jjwt</artifactId>
	<version>0.9.1</version>
</dependency>

Configure Spring Datasource, JPA, Hibernate

Under src/main/resources folder, open application.properties and write these lines.

– For MySQL:

spring.datasource.url= jdbc:mysql://localhost:3306/testdb?useSSL=false
spring.datasource.username= root
spring.datasource.password= 123456

spring.jpa.properties.hibernate.dialect= org.hibernate.dialect.MySQL5InnoDBDialect

# Hibernate ddl auto (create, create-drop, validate, update)
spring.jpa.hibernate.ddl-auto= update

– For PostgreSQL:

spring.datasource.url= jdbc:postgresql://localhost:5432/testdb
spring.datasource.username= postgres
spring.datasource.password= 123

spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation= true
spring.jpa.properties.hibernate.dialect= org.hibernate.dialect.PostgreSQLDialect

# Hibernate ddl auto (create, create-drop, validate, update)
spring.jpa.hibernate.ddl-auto= update

– For App properties:

# App Properties
app.jwtSecret= bezKoderSecretKey
app.jwtExpirationMs= 86400000
  • spring.datasource.username & spring.datasource.password properties are the same as your database installation.

  • Spring Boot uses Hibernate for JPA implementation, we configure MySQL5InnoDBDialect for MySQL or PostgreSQLDialect for PostgreSQL

  • spring.jpa.hibernate.ddl-auto is used for database initialization. We set the value to update value so that a table will be created in the database automatically corresponding to defined data model. Any change to the model will also trigger an update to the table. For production, this property should be validate.

Models & DTOs

The various models of the application are organized under the model package, their DTOs(data transfer objects) are present under the dto package. There are different opinions about whether we should use DTOs or not, I belong to the set of minds who think we definitely should and not using DTOs makes your model layer very tightly coupled with the UI layer and that is something that no enterprise project should ever get into. DTOs let us transfer only the data that we need to share with the user interface and not the entire model object that we may have aggregated using several sub-objects and persisted in the database. The mapping of models to the DTOs can be handled using ModelMapper utility, however its only useful when your DTO is almost similar (literally) to the concerned models which is not always the case and hence I prefer using custom mapper classes. You can find some examples under the dto/mapper package.

DAOs

The data access objects (DAOs) are present in the repository package. They are all extensions of the ModelRepository Interface helping the service layer to persist and retrieve the data from MongoDB. The service layer is defined under the service package,

Security

The security setting are present under the config package and the actual configurations are done under the class present in the security package. The application has different security concepts for the admin portal and the REST APIs, for the portal I have applied the default spring session mechanism that is based on the concept of sessionID and cookies. For the REST APIs I have used JWT token based authentication mechanism.

Controllers

Last, but the most important part is the controller layer. It binds everything together right from the moment a request is intercepted till the response is prepared and sent back. The controller layer is present in the controller package, the best practices suggest that we keep this layer versioned to support multiple versions of the application and the same practice is applied here. For now code is only present under v1 but over the time I expect to have different versions having different features. The Admin portal related controllers are present in the ui package and its concerning form command objects are located under the command package. The REST API controllers are located under the api package and the corresponding request classes are located under the request package.

Request and Form Commands

Again, there are different opinions amongst the fraternity regarding the usage of separate classes for mapping the incoming request vs using the DTOs, I am personally a fan of segregating the two as far as possible to promote loose coupling amongst UI and controller layer. The request objects and the form commands do give us a way to apply an additional level of validations on the incoming requests before they get converted to the DTOs which transfer valid information to the service layer for persistence and data retrieval. We could use DTOs here and some developers prefer that approach as it reduces some additional classes, however I usually prefer to keep the validation logic separate from the transfer objects and hence am inclined to use the request/command objects ahead of them.

The static resources are grouped under the resources directory. All the UI objects and their styling aspects can be located here.

Response and Exception Handling

I have tried to experiment a bit with the RuntimeExceptions and come up with a mini framework for handling the entire application's exceptions using a few classes and the properties file. If you carefully observe the exception package, it consists of two enums - EntityType and ExceptionType. The EntityType enum contains all the entity names that we are using in the system for persistence and those which can result in a run time exception. The ExceptionType enum consists of the different entity level exceptions such as the EntityNotFound and DuplicateEntity exceptions.

The BRSException class has two static classes EntityNotFoundException and DuplicateEntityException which are the two most widely thrown exceptions from the service layer. It also contains a set of overloaded methods throwException which take the EntityType, ExceptionType and arguments to come up with a formatted message whose template is present under the custom.properties file. Using this class I was able to empower the entire services layer to throw entity exceptions in a uniform manner without cluttering the code base with all sorts of NOT_FOUND and DUPLICATE entity exceptions.

For example, while login if you try to use a email address which is not registered, an exception is raised and thrown using the following single line of code -

throw exception(USER, ENTITY_NOT_FOUND, userDto.getEmail());

This results in clubbing the names of these two enums USER(user) and ENTITY_NOT_FOUND(not.found) and coming up with a key user.not.found which is present in the custom.properties files as follows :

user.not.found=Requested user with email - {0} does not exist.

By simply replacing the {0} param with the email address included in the thrown exception you can get a well formatted message to be shown to the user or to be sent back as the response of the REST API call.

Another important part of this mini framework is the CustomizedResponseEntityExceptionHandler class. This class takes care of these RuntimeExceptions before sending a response to the API requests. Its a controller advice that checks if a service layer invocation resulted in a EntityNotFoundException or a DuplicateEntityException and sends an appropriate response to the caller.

Last, the API response are all being sent in a uniform manner using the Response class present in the dto/response package. This class allows us to create uniform objects which result in a response as shown below (this one is a response for the "api/v1/reservation/stops" api) :

{
    "status": "OK",
    "payload": [
        {
            "code": "STPA",
            "name": "Stop A",
            "detail": "Near hills"
        },
        {
            "code": "STPB",
            "name": "Stop B",
            "detail": "Near river"
        }
    ]
}

And when there is an exception (for example searching for a trip between two stops which are not linked by any bus) the following responses are sent back (result of "api/v1/reservation/tripsbystops" GET request) :

{
    "status": "NOT_FOUND",
    "errors": "No trips between source stop - 'STPD' and destination stop - 'STPC' are available at this time."
}
{
    "status": "NOT_FOUND",
    "errors": {
        "timestamp": "2019-03-13T07:47:10.990+0000",
        "message": "Requested stop with code - STPF does not exist.",
        "details": "Requested stop with code - STPF does not exist."
    }
}

As you can observe, both type of responses, one with a HTTP-200 and another with HTTP-404 the response payload doesn't change its structure and the calling framework can blindly accept the response knowing that there is a status and a error or payload field in the JSON object.

Last updated