2. Change Log
2023-10-09 First version
2023-10-11 Added Section 5 “Library (More Complex Example using Spring MVC Web Controller, JPA,
Repositories, H2)”
2023-10-18 Using external services, first steps in GitLab
2
CSU33012 Goetz Botterweck
4. References
Online Courses
(Bowman 2019) Bowman, Mary Ellen. Creating Your First Spring Boot Microservice, 1h
48min, Linked-in Learning, 2019.
https://www.linkedin.com/learning/creating-your-first-spring-boot-microservice/
4
CSU33012 Goetz Botterweck
7. Prerequisites
• Java
• Version 17
• java --version
should print a version number like 17.x.x
• Maven
• mvn --version
should print a version number like 3.9.x
• IDE
• IntelliJ IDEA Ultimate or
• Visual Studio Code
• Spring Tools for VSCode, https://spring.io/tools
CSU33012 Goetz Botterweck 7
8. Also useful
• Tools for Working with REST APIs
• postman
• https://www.postman.com/downloads/
• mostly app, there is also online version at https://www.postman.com/
• httpie
• https://httpie.io/
• mostly for terminal, there is also beta version of app
• Account on the local GitLab installation
• http://gitlab.scss.tcd.ie/
CSU33012 Goetz Botterweck 8
10. Apache Maven
• Download, https://maven.apache.org/download.cgi
• Install, https://maven.apache.org/install.html
• Unzip
• Make sure that Maven’s bin directory is in your path
• mvn --version
should print a version number like 3.9.x
• Apache in 5 minutes (see below)
https://maven.apache.org/guides/getting-started/maven-in-five-
minutes.html
CSU33012 Goetz Botterweck 10
Image: https://en.m.wikipedia.org/wiki/File:Apache_Maven_logo.svg
11. Apache Maven
• Tool to build and manage software projects
• Key Features
• Dependency Management
• Building Projects
• Plugin-based Architecture
• Benefits
• Standardized Build Process
• Consistent Project Structures
• Alternative: Gradle
CSU33012 Goetz Botterweck 11
12. Maven Standard Directory Structure
CSU33012 Goetz Botterweck 12
https://www.baeldung.com/maven-directory-structure
13. Maven in 5min
CSU33012 Goetz Botterweck 13
https://maven.apache.org/guides/getting-started/maven-in-five-minutes.html
mvn archetype:generate -DgroupId=com.mycompany.app -DartifactId=my-app -
DarchetypeArtifactId=maven-archetype-quickstart -DarchetypeVersion=1.4 -
DinteractiveMode=false
If running in Windows Powershell, Maven will raise a false error
(pom.xml missing). Use cmd.exe or enclose parameters in quotes.
https://stackoverflow.com/questions/16348459/error-the-goal-
you-specified-requires-a-project-to-execute-but-there-is-no-pom
14. Maven in 5min
CSU33012 Goetz Botterweck 14
https://maven.apache.org/guides/getting-started/maven-in-five-minutes.html
cd my-app
mvn package
16. Maven in 5min
CSU33012 Goetz Botterweck 16
https://maven.apache.org/guides/getting-started/maven-in-five-minutes.html
java -cp target/my-app-1.0-SNAPSHOT.jar com.mycompany.app.App
Later in Spring Boot you can start the jar directly
without classpath, somewhat like
java -jar target/my-app-1.0-SNAPSHOT.jar
Here that does not work.
18. Inversion of Control
Previously
• Main program (our program)
• Controls flow of data and
execution
• Calls components
• Sets up references between
components
CSU33012 Goetz Botterweck 18
With Inversion of Control
• Framework (or Container)
• Controls flow of data and
execution
• Is responsible for„plumbing“
everything together
• Our Program
• We only have to specify what we
need
• In Spring Boot
• through annotating parts of a Java
program with tags like
@Service
@SpringBootApplication
• Uses Dependency Injection
19. Key Concepts in Inversion of Control
• Inversion of Control (IoC)
• Framework (a.k.a. the container) is responsible for handling the start-up,
including the instantiation of components (Beans), connecting them
• Dependency Injection
• Mechanism to implement IoC
• Component does not set up dependency, instead the framework/container
„injects“ them into the component.
• Application Context
• Spring Boot‘s version of framework/container
CSU33012 Goetz Botterweck 19
20. Inversion of Control Examples
CSU33012 Goetz Botterweck 20
package ie.tcd.scss.learningspring;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class LearningSpringApplication {
public static void main(String[] args) {
SpringApplication.run(LearningSpringApplication.class, args);
}
}
Upon starting the
application Spring Boot will
scan for annotations and
instantiate and connect the
necessary components.
21. Inversion of Control Examples
CSU33012 Goetz Botterweck 21
(Turnquist, p. 6-7)
...
class BookExample {
@Bean
public BookRepository bookRepository(DataSource dataSource) {
return new BookRepository();
}
...
}
• bookRepository needs a DataSource.
• Application context needs to provide one, either has it or will go create one and return it.
• Application context is responsible for management during execution
• bookRepository executes its code while referencing the app context’s DataSource.
• BookRepository is registered in the application context under the name
bookRepository.
22. 1.4 What is Spring Boot?
CSU33012 Goetz Botterweck 22
23. Spring Boot
• Open-source, Java-based framework
• “Beginners version” of Spring
(based on Spring, makes it easier to get started)
• Conventions over Configuration
• Autoconfiguration
• Upon start application examines the parts of that application
• Adds additional Spring beans to application context
• Standalone
• No deployment, instead web server embedded in the application
CSU33012 Goetz Botterweck 23
25. Spring Boot Starters
CSU33012 Goetz Botterweck 25
https://www.baeldung.com/spring-boot-starters
<?xml version="1.0" encoding="UTF-8"?>
<project …>
...
<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>
</dependencies>
...
</project>
Adding Starter to pom.xml as dependency … … ensures additional libraries are
included and annotations are available
These are what is added to your project,
when you select „Dependencies“ in
Spring Initializr (more later)
26. Spring Boot Starters – Examples
• Spring Web – applications that offer REST-based APIs, uses Spring
MVC, uses Apache Tomcat as the default embedded container.
• Spring WebFlux – alternative to Spring Web, based on reactive
streams
• See examples in (Larsson 2023)
• See https://www.baeldung.com/spring-mvc-async-vs-webflux
• Spring Data JPA – accessing data via Java Persistence API (JPA)
• Spring Test – testing your application
• Spring Mail – sending emails
Source: https://www.baeldung.com/spring-boot-starters
CSU33012 Goetz Botterweck 26
27. 2. Creating a Web
Application with Spring
Boot
CSU33012 Goetz Botterweck 27
28. So, what do we need?
• We want to setup a basic project and its file structure
• Based on Java, Maven, Spring Boot
• We want to build a simple web application
(that responds to a HTTP request with a response)
/hello → Hello World!
CSU33012 Goetz Botterweck 28
30. Spring Initializr
CSU33012 Goetz Botterweck 30
• Fast way to set up a Spring Boot
project
• Build management
• Maven
• Gradle
• Language
• Java
• Kotlin
• Groovy
• Spring Boot Version
• Project Metadata
• Packaging
• Java Version
• Dependencies (to add
functionality)
31. Spring Initializr
CSU33012 Goetz Botterweck 31
• Fast way to set up a Spring Boot
project
• Build management
• Maven
• Gradle
• Language
• Java
• Kotlin
• Groovy
• Spring Boot Version
• Project Metadata
• Packaging
• Java Version
• Dependencies (to add
functionality)
Spring Initializr
API
start.spring.io
Website
Spring Initializr
Support in
IntelliJ IDEA
„Spring Initializr
Java Support“
vscode-spring-initializr
In VS Code
32. Spring Initializr
CSU33012 Goetz Botterweck 32
• http://start.spring.io
• Project = Maven
• Language = Java
• Spring Boot = default (3.x)
• Group = ie.tcd.scss
• Artifact = learningspring
• Packaging = Jar
• Java = 17
• Dependencies
• Spring Web
• Generate
• Download and unpack
• Open project in your IDE
44. • Go to src/test/java/ie.tcd.scss.learningspring
• Create new class HelloControllerTest
• Add content on next slide
• Right click src/test/java > Run all tests (Ctrl-Shift-F10)
• Inspect test results
CSU33012 Goetz Botterweck 44
49. Copying Text from Slides PDF
CSU33012 Goetz Botterweck 49
If text copies from PDF looks garbled
Edit > Find > Replace (Ctrl + R)
Search for: u000B (or select the weird VT character)
Replace with: n
Activate regular expressions .* (Alt + X)
Replace all (Alt + A)
67. 4. Music Styles
(Storing and Retrieving Info with Spring MVC Web
Controller)
CSU33012 Goetz Botterweck 67
68. Music Styles
• Can store mapping artist->genre
• The Weeknd → R&B
• Eminem → Hip Hop
• Michael Jackson → Pop
• Adele → Pop
• Can retrieve info on given artist
• In: Eminem
Out: Hip Hop
• Can list all artists for a given genre
• In: Pop
Out: Michael Jackson, Adele
CSU33012 Goetz Botterweck 68
69. class ArtistInfo
package com.example.musicstyles;
public class ArtistInfo {
private String artist;
private String musicStyle;
public ArtistInfo(String artist, String musicStyle) {
this.artist = artist;
this.musicStyle = musicStyle;
}
public String getArtist() {
return artist;
}
public void setArtist(String artist) {
this.artist = artist;
}
public String getMusicStyle() {
return musicStyle;
}
public void setMusicStyle(String musicStyle) {
this.musicStyle = musicStyle;
}
}
CSU33012 Goetz Botterweck 69
Image: https://unsplash.com/photos/Km5J-KCP1Mw
70. Spring Initializr
CSU33012 Goetz Botterweck 70
• http://start.spring.io
• Project = Maven
• Language = Java
• Spring Boot = default (3.x)
• Group = ie.tcd.scss
• Artifact = musicstyles
• Packaging = Jar
• Java = 17
• Dependencies
• Spring Web
• Generate
• Download and unpack
• Open project in your IDE
82. class MusicstylesController (1 of 2)
CSU33012 Goetz Botterweck 82
package ie.tcd.scss.musicstyles;
import ...
@RestController
@RequestMapping("/api")
public class MusicstylesController {
private final Map<String, String> artistStyleMap = new ConcurrentHashMap<>();
@PostMapping("/artists")
public ResponseEntity<String> addArtist(@RequestBody ArtistInfo artistInfo) {
artistStyleMap.put(artistInfo.getArtist(), artistInfo.getMusicStyle());
return ResponseEntity.ok("Artist added successfully!");
}
@GetMapping("/artists/{artistName}")
public ResponseEntity<String> getMusicStyle(@PathVariable String artistName) {
String musicStyle = artistStyleMap.get(artistName);
if (musicStyle == null) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body("Artist not found!");
}
return ResponseEntity.ok(musicStyle);
}
...
}
83. class MusicstylesController (1 of 2)
CSU33012 Goetz Botterweck 83
package ie.tcd.scss.musicstyles;
import ...
@RestController
@RequestMapping("/api")
public class MusicstylesController {
private final Map<String, String> artistStyleMap = new ConcurrentHashMap<>();
@PostMapping("/artists")
public ResponseEntity<String> addArtist(@RequestBody ArtistInfo artistInfo) {
artistStyleMap.put(artistInfo.getArtist(), artistInfo.getMusicStyle());
return ResponseEntity.ok("Artist added successfully!");
}
@GetMapping("/artists/{artistName}")
public ResponseEntity<String> getMusicStyle(@PathVariable String artistName) {
String musicStyle = artistStyleMap.get(artistName);
if (musicStyle == null) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body("Artist not found!");
}
return ResponseEntity.ok(musicStyle);
}
...
}
URL path pattern with path
variable “artistName”
84. class MusicstylesController (2 of 2)
CSU33012 Goetz Botterweck 84
package ie.tcd.scss.musicstyles;
import ...
@RestController
@RequestMapping("/api")
public class MusicstylesController {
...
@GetMapping("/musicstyles/{musicStyleName}/artists")
public ResponseEntity<List<String>> getArtists(@PathVariable String musicStyleName) {
List<String> artists = artistStyleMap.entrySet().stream()
.filter(entry -> entry.getValue().equalsIgnoreCase(musicStyleName))
.map(Map.Entry::getKey)
.collect(Collectors.toList());
if (artists.isEmpty()) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(Collections.emptyList());
}
return ResponseEntity.ok(artists);
}
}
85. class MusicstylesController (2 of 2)
CSU33012 Goetz Botterweck 85
package ie.tcd.scss.musicstyles;
import ...
@RestController
@RequestMapping("/api")
public class MusicstylesController {
...
@GetMapping("/musicstyles/{musicStyleName}/artists")
public ResponseEntity<List<String>> getArtists(@PathVariable String musicStyleName) {
List<String> artists = artistStyleMap.entrySet().stream()
.filter(entry -> entry.getValue().equalsIgnoreCase(musicStyleName))
.map(Map.Entry::getKey)
.collect(Collectors.toList());
if (artists.isEmpty()) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(Collections.emptyList());
}
return ResponseEntity.ok(artists);
}
}
Converts artistStyleMap into
stream of its entries
Filter for entries where the
value is <musicStyleName>
ignoring case
Transform each remaining
entry into its key (the artist
name in this case)
Collect the results into a
List<Strings>
Lambda
expression
URL path pattern with path
variable “musicStyleName”
86. class MusicstylesController (full source)
CSU33012 Goetz Botterweck 86
package ie.tcd.scss.musicstyles;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
@RestController
@RequestMapping("/api")
public class MusicstylesController {
private final Map<String, String> artistStyleMap = new ConcurrentHashMap<>();
@PostMapping("/artists")
public ResponseEntity<String> addArtist(@RequestBody ArtistInfo artistInfo) {
artistStyleMap.put(artistInfo.getArtist(), artistInfo.getMusicStyle());
return ResponseEntity.ok("Artist added successfully!");
}
@GetMapping("/artists/{artistName}")
public ResponseEntity<String> getMusicStyle(@PathVariable String artistName) {
String musicStyle = artistStyleMap.get(artistName);
if (musicStyle == null) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body("Artist not found!");
}
return ResponseEntity.ok(musicStyle);
}
@GetMapping("/musicstyles/{musicStyleName}/artists")
public ResponseEntity<List<String>> getArtists(@PathVariable String musicStyleName) {
List<String> artists = artistStyleMap.entrySet().stream()
.filter(entry -> entry.getValue().equalsIgnoreCase(musicStyleName))
.map(Map.Entry::getKey)
.collect(Collectors.toList());
if (artists.isEmpty()) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(Collections.emptyList());
}
return ResponseEntity.ok(artists);
}
}
99. 5. Library
(More Complex Example using Spring MVC Web
Controller, JPA, Repositories, H2)
CSU33012 Goetz Botterweck 99
100. 5.1 Setting up the Initial
Project
CSU33012 Goetz Botterweck 100
101. • Based on „Explore California“ in (Bowman 2019)
• Simplified and added explanations
• Updated to Spring Boot 3
• Changed namespaces
javax.somepackage → jakarta.somepackage
CSU33012 Goetz Botterweck 101
103. Spring Initializr
CSU33012 Goetz Botterweck 103
• http://start.spring.io
• Project = Maven
• Language = Java
• Spring Boot = default (3.x)
• Group = ie.tcd.scss
• Artifact = library
• Packaging = Jar
• Java = 17
• Dependencies
• Spring Web
• Rest Repositories
• Spring Data JPA
• H2 Database
• Generate
• Download and unpack
• Open project in your IDE
106. Creating Remote Repository (GitHub or GitLab)
CSU33012 Goetz Botterweck 106
If you want to
push an existing
repository later
do NOT create a
README
126. Simplified Domain Model
• Book
• Authors represented as simple
string
• Identifier “id” of type Integer
• Library Section
• Identifier “code” of type String
CSU33012 Goetz Botterweck 126
127. Jakarta Persistence API (JPA)
• Formerly known as Java Persistence API
• Application Programming Interface (set of interfaces) to
• handle data in Java programs
• manage the mapping of Java classes to tables in databases
• Query Language JPQL
• Implementations
• Eclipse Link (reference implementation)
• Hibernate
• Spring Data JPA uses JPA, expects a JPA implementation
CSU33012 Goetz Botterweck 127
129. H2 Database
• Minimal, fast in-memory database
• Good for early experiments, proof-of-concept implementations
• https://www.h2database.com/
• No need to install!
• https://www.baeldung.com/spring-boot-h2-database
CSU33012 Goetz Botterweck 129
133. class LibrarySection
CSU33012 Goetz Botterweck 133
package ie.tcd.scss.library.domain;
import jakarta.persistence.*;
@Entity
public class LibrarySection {
@Id
private String code;
@Column
private String name;
@OneToMany
private Set<Book> books;
}
134. Generating Missing Elements
for classes, not needed for enumerations
• Constructors
• One without any arguments – make private, so it does not get used
• One with all arguments (except @GeneratedValue id)
• Getters
• Setters
• except for @GeneratedValue id
• toString()
• equals()
• hashCode()
(Can be avoided with lombok, but for now we use the standard approach.)
CSU33012 Goetz Botterweck 134
140. Creating Spring Data Repositories
• We declare an interface that extends
public interface CrudRepository<T, I>
(CRUD = Create, Read, Update, Delete)
• T = Data type that the repository manages, e.g., Book
• I = Data type of the entity ID, e.g., Integer
• Spring Data automatically provides all necessary methods to operate
on that Repository
CSU33012 Goetz Botterweck 140
141. Creating Spring Data Repositories
For instance after declaring
public interface BookRepository extends
CrudRepository<Book, Integer>
We automatically get
Book save(Book b)
Iterable<Book> saveAll(Iterable<Book> iterable)
CSU33012 Goetz Botterweck 141
146. interface BookRepository
CSU33012 Goetz Botterweck 146
package ie.tcd.scss.library.repo;
import ie.tcd.scss.library.domain.Book;
import org.springframework.data.repository.CrudRepository;
public interface BookRepository extends CrudRepository<Book, Long> {
}
Basic default functionality
is available automatically.
If we need additional
methods, we need to
declare them as part of
the interface.
155. Details of Service
• Service class tagged @Service
• Private variable(s) holding reference(s) to all needed repositories
• Final
• Constructor
• expects references to repositories
• is „autowired“ by tag @Autowired
• Methods
• createBook()
• Create a new book
• lookup() – return (an Iterable with) all books
• total()– return total number of books
CSU33012 Goetz Botterweck 155
156. class BookService (Overview)
CSU33012 Goetz Botterweck 156
package ie.tcd.scss.library.service;
import ...
@Service
public class BookService {
private final BookRepository bookRepository;
private final LibrarySectionRepository librarySectionRepository;
@Autowired
public BookService(BookRepository bookRepository, LibrarySectionRepository librarySectionRepository) {
this.bookRepository = bookRepository;
this.librarySectionRepository = librarySectionRepository;
}
public Book createBook(String title, String authors, Integer publicationYear, Integer price, String isbn,
String keywords, String description, String librarySectionCode) {
... find librarySection
return bookRepository.save(new Book(...));
}
public Iterable<Book> lookup() {...}
public long total() {...}
}
157. How Does Autowiring and Depdendency Injection Work?
• Defining Beans
• Classes eligible for autowiring must be marked with tags like
@Component, @Service, @Repository, @Controller
• @Autowired annotation
• Instructs Spring to “inject” the dependency automatically
• Field Injection
• Constructor Injection – recommend, what we used above
• Setter Injection
CSU33012 Goetz Botterweck 157
158. How Does Autowiring and Depdendency Injection Work?
• Component Scanning
• Dependency Injection
• When a bean (e.g., BookService) is created, Spring checks (e.g. by looking
at constructor) if there are any dependencies that need to be injected.
• In the case of our BookService, it needs an instance of BookRepository
and an instance of LibrarySectionRepository
• Spring will search among the beans in its context for one that matches the
required type (e.g., BookRepository). If it finds a match, it will inject that
bean into the dependent one (BookService). Otherwise exception.
CSU33012 Goetz Botterweck 158
159. How Does Autowiring and Depdendency Injection Work?
Much more details at https://www.baeldung.com/spring-dependency-
injection
CSU33012 Goetz Botterweck 159
160. class BookService (Full Source)
CSU33012 Goetz Botterweck 160
package ie.tcd.scss.library.service;
import ie.tcd.scss.library.domain.Book;
import ie.tcd.scss.library.domain.LibrarySection;
import ie.tcd.scss.library.repo.BookRepository;
import ie.tcd.scss.library.repo.LibrarySectionRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Optional;
@Service
public class BookService {
private final BookRepository bookRepository;
private final LibrarySectionRepository librarySectionRepository;
@Autowired
public BookService(BookRepository bookRepository, LibrarySectionRepository librarySectionRepository) {
this.bookRepository = bookRepository;
this.librarySectionRepository = librarySectionRepository;
}
public Book createBook(String title, String authors, Integer publicationYear, Integer price, String isbn,
String keywords, String description, String librarySectionCode) {
LibrarySection librarySection = librarySectionRepository
.findById(librarySectionCode)
.orElseThrow(() -> new RuntimeException("Library Section does not exist: " + librarySectionCode));
return bookRepository.save(new Book(title, authors, publicationYear, price, isbn, keywords, description,
librarySection));
}
public Iterable<Book> lookup() {
return bookRepository.findAll();
}
public long total() {
return bookRepository.count();
}
}
161. class LibrarySectionService
CSU33012 Goetz Botterweck 161
package ie.tcd.scss.library.service;
import ie.tcd.scss.library.domain.LibrarySection;
import ie.tcd.scss.library.repo.LibrarySectionRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class LibrarySectionService {
private final LibrarySectionRepository librarySectionRepository;
@Autowired LibrarySectionService(LibrarySectionRepository librarySectionRepository) {
this.librarySectionRepository = librarySectionRepository;
}
public LibrarySection createLibrarySection(String code, String name) {
return librarySectionRepository
.findById(code)
.orElse(librarySectionRepository.save(new LibrarySection(code, name)));
}
public Iterable<LibrarySection> lookup() {
return librarySectionRepository.findAll();
}
public long total() {
return librarySectionRepository.count();
}
}
168. • Why Create BookController?
• To provide REST access to business logic, e.g.,
create Books and add them into a LibrarySection in one go
• We need to get extra data (code of LibrarySection)
• Two approaches
• Get extra data in request parameters
• Get extra data in Data Transfer Object (DTO)
• i.e., one class/object that can hold all data needed during the request
CSU33012 Goetz Botterweck 168
209. 5.9 RESTful APIs with
Spring Data REST
CSU33012 Goetz Botterweck 209
210. RESTful APIs
• Hypermedia-driven APIs
• Roy Fielding (2000)
• Hypermedia as the Engine of the Application State (HATEOAS)
• Expose application state via API
• Expose the API‘s documentation
• Navigation between resources
• Spring Data REST provides that out of the box!
CSU33012 Goetz Botterweck 210
211. RESTful APIs
• Hypermedia-driven APIs
• Roy Fielding (2000)
• Hypermedia as the Engine of the Application State (HATEOAS)
• Expose application state via API
• Expose the API‘s documentation
• Navigation between resources
• Spring Data REST provides that out of the box!
CSU33012 Goetz Botterweck 211