Welcome to the Spring Boot Kotlin lab. We're going to learn some of the Kotlin syntax and features while developing a Spring Boot app. This lab is inspired on the Spring Boot Kotlin Tutorial, with more references to the Kotlin language features used and the format and scope adapted to a lab of one to one and half hours. Please follow the steps below.
Kotlin is a language developed by JetBrains, the company behind IntelliJ, so even though you can use any text editor, IntelliJ is recommended due to better integration with the language. Please, download and install the Ultimate or Community edition.
- Go to Spring Initializr
- Select Kotlin as a language
- Use "blog" for Artifact
- For dependencies, select: Web, JPA, and H2
- Click on Generate Project
- Unpack the downloaded project
- On command line run the application with
./mvnw spring-boot:run
- File -> new -> Project from existing sources
- Select the project folder
- Chose "Import project from external model" -> Maven
- Follow the next screens and Finish
- Open the file
com.example.blog.BlogApplication
. There are a few things you can notice:- Lack of semicolons
- Since the class has no body, the curly braces can be omitted
- The
main
function is a top-level function, meaning there's no need to create a class to hold it, like languages such as Java, C# or Scala - The default visibility is
public
, which can be omitted for the main function - If you navigate to the declaration of
runApplication
, you'll see that it's declared in a Kotlin extension class. It's part of the Spring Boot Kotlin support, enabling writing more idiomatic code
- Open the
pom.xml
file. There are a few things you can notice:kotlin-maven-plugin
: used to compile Kotlin code.sourceDirectory
andtestSourceDirectory
are also configuredkotlin-maven-allopen
: Kotlin has classes and their membersfinal
by default, which makes it inconvenient to use frameworks such as Spring that require classes to be open. The all-open plugin, which has Spring support, makes classes annotated with a specific annotation and their members open without the explicit open keyword
- Implement the class
HelloWorldResource
- Re-run the application with
./mvnw spring-boot:run
- Open your browser on http://localhost:8080/hello. You should see "Hello" on the page displayed.
hello()
is a single-expression function, so the curly braces can be omitted- For single-expression functions, explicitly declaring the return type is optional when this can be inferred by the compile
- Implement the
Article
class with the propertiestitle
andcontent
- Implement the
ArticleResource
Rest endpoint returning a list of Articles. - Re-run the application with
./mvnw spring-boot:run
- Open your browser on http://localhost:8080/article. You should see the JSON representation of the list returned
Article
- Is a class whose main purpose is to hold data, making it a good candidate to be a Data Class, which automatically provide
equals()
,hashCode()
,toString()
,componentN()
andcopy()
functions - It uses its primary constructor to declare and initialize its properties in a concise way. Properties declared in the primary constructor can be mutable (
var
) or read-only (val
)
- Is a class whose main purpose is to hold data, making it a good candidate to be a Data Class, which automatically provide
ArticleResource
- To create instances of the
Article
class, we call the constructor as if it was a regular function - Kotlin does not have a
new
keyword listOf
function is part of Kotlin Standard Library, which provides living essentials for everyday work with Kotlin- You didn't have to import
listOf
. Just like Java hasjava.lang
, Kotlin has some packages that are imported by default
- To create instances of the
- Add the properties
createdAt
of typeLocalDateTime
andid
of typeLong?
with default values - Re-run the application with
./mvnw spring-boot:run
- Open your browser on http://localhost:8080/article. You should see the new properties on the objects in the list
- Function arguments can have default values, which are used when a corresponding argument is omitted
- Kotlin's type system is aimed at eliminating the danger of null references from code. Since the
Article.id
is only specified when it's persisted (to be implemented in a following section), its supposed to allownull
, so the nullableLong?
is used - Since we are satisfied with the default values of the new parameters, we do not need to change the
Article
instantiation onArticleget.getArticles()
While JUnit 4 is still the default testing framework provided with Spring Boot, and can be used with Kotlin, JUnit 5 provides features handy with Kotlin, including autowiring of constructor / method parameters which allows to use non-nullable val
properties and the possibility to use @BeforeAll
/@AfterAll
on regular non-static methods. Follow the steps below, or, to make it easier, see the necessary changes in this diff or the expected final file content: pom.xml
and BlogApplicationTests.kt
- Exclude
junit
Maven transitive dependency fromspring-boot-starter-test
- Add
junit-jupiter-engine
Maven dependency - Add
junit-platform-surefire-provider
Maven dependency tomaven-surefire-plugin
- Refactor
BlogApplicationTests.kt
to Junit 5 - Run
./mvnw clean install
and check if the build is still successful
- Implement the
ArticleResourceTest
. Make sure to put it in thesrc/test/kotlin/com/example/blog/
folder. This short test is enough for the purpose of this lab, but I hope in real life you test more than the result size - Run
./mvnw clean install
and check if the test runs and the build is still successful
- We're using
SpringExtension
which is able to inject dependencies into test constructors. This makes it a good fit with Kotlin immutable and non-nullable properties - We use real sentences in test function names. Backticks are part of the language to help with the Java interop but they also help writing more expressive test function names, instead of the usual camel-case naming
getForEntity
is another Spring Kotlin extension. It takes advantage of Kotlin reified type parameters, overcoming Java type erasure limitation which would require the usage ofParameterizedTypeReference<List<Article>>
response.body?.size
is a safe way of accessing the nullablebody
property. It will returnsize
ifbody
is not null, and null otherwise. Other ways are also available, such asif
statments and the Elvis operator
- Annotate
Article
with@Entity
andArticle.id
with@Id @GeneratedValue
. Final result:Article
- Implement
ArticleRepository
- Use the
ArticleRepository
to retrieve the list of Articles onArticleResource
- Add the Kotlin JPA plugin and add
kotlin-maven-noarg
dependency to the pom.xml` - Re-run the application with
./mvnw spring-boot:run
- Open your browser on http://localhost:8080/article. The result is not very exciting and you won't see anything since the database is empty. We'll solve this on next section
- JPA annotations can be used in the primary constructors, contributing for a concise code
- Primary constructors will also have its dependencies automatically autowired, which is the case of
ArticleResource
- JPA requires the entities to have a zero-argument constructor. Since we added the primary constructor to
Article
, the entity is no longer satisfying that requirement. Kotlin no-arg plugin with JPA support adds a zero-argument constructor for classes annotated with@Entity
,@Embeddable
and@MappedSuperclass
- Implement the function
BlogApplication.initDatabase()
- Re-run the application with
./mvnw spring-boot:run
- Open your browser on http://localhost:8080/article. Now you should see some Articles on the page
- Now that
BlogApplication
has a body, it needs curly braces - The function
initDatabase
produces a@Bean
of typeCommandLineRunner
which implements a callback to run specific pieces of code when an application is fully started CommandLineRunner
is aFunctionalInterface
, meaning it's a Single Abstract Method (SAM) interface, which can be automatically converted from Kotlin function literals. If it got too complicated, the bottom line is the Kotlin support for functional programming allows us to implement the method the way we did, instead of having to explicitly declare a class to implement a single method
So that's it for now. Even though we haven't implemented the most exciting application, the small portion of code we wrote was able to introduce some key Kotlin features, such as:
- Basic syntax, e.g., class and function declaration, class instantiation, etc.
- Top-level functions
- Single-expression functions and optional return declaration
- Data classes
- Primary constructors
- Kotlin Standard Library
- Default imports
- Function parameters default values
- Nullable and non-nullable types
- Reified type parameters
- Lambda Expressions and function literals
- JUnit 5 integration
- JPA integration
- Kotlin Maven plugins
- Spring Kotlin extensions