Skip to content

Itera/spring

Repository files navigation

theme: Itera slide-transition: true autoscale: true

Spring

inline


Agenda

  • What is Spring?
  • IoC and DI (A run thru some small example applications)
  • Spring & Context
  • Spring Boot
  • Common context issues
  • More on Spring Beans
  • Spring Boot Configuration
  • Spring Boot MVC
  • Spring MVC vs Spring Reactive Web
  • Databases - JPA & JDBC

What is Spring?

The Spring Framework is an application framework and inversion of control container for the Java platform -- Wikipedia 1


Yes - but what is Spring?

Core spring is based on the ideas of Inversion of Control (IoC) and Dependency Injection (DI) - so we'll start there.


Dependency Injection (DI)

A class is provided with the services etc that it needs rather than creating them.

Inversion of Control (IoC) - in Spring

IoC is a very open design principle - but in Spring terms it mostly refers to the spring container that provide the actual DI mechanics (creation of beans, injecting them following configuration etc).


IoC and DI applications


Initial code

We start with a simple application 2


Services

  • Calculator
  • Display

Business Logic

The calculation class performs a business operation using the services.

However - let's take a look at the code:


Main method in the Business Logic

  public void complexCalculation() {
    // Service 1
    Calculator calculator = new Calculator();

    int result = calculator.plus(2, 3);

    // Service 2
    Display display = new Display();

    display.output(String.format("2 + 3 = %d", result));
  }

Problems

  • How do we test different implementations of either service?
  • How do we even provide different implementations?

All of these require editing the business logic class.


Dependency Injection

Let's take a look at how we can manually change this over to a DI based setup.

First round - manual DI - no spring 3


Constructor vs Setter

We can do this in two ways:

Provide (inject) the required services (dependencies) via:

  • the constructor
  • setters

Setter injection

  private Calculator calculator;
  private Display display;

  public void setCalculator(Calculator calculator) {
    this.calculator = calculator;
  }

  public void setDisplay(Display display) {
    this.display = display;
  }

Constructor injection

  private final Calculator calculator;
  private final Display display;

  public CalculationConstructorInjection(Calculator calculator,
    Display display) {
    this.calculator = calculator;
    this.display = display;
  }

Orchestration

OK - but how do we set up (or orchestrate) the application?

  public static void main(String[] args) {
    // Services
    Calculator calculator = new Calculator();
    Display display = new Display();

    // Setter injection
    CalculationSetterInjection calculationSetterInjection = new CalculationSetterInjection();
    calculationSetterInjection.setCalculator(calculator);
    calculationSetterInjection.setDisplay(display);

    calculationSetterInjection.complexCalculation();

    // Constructor injection
    CalculationConstructorInjection calculationConstructorInjection =
      new CalculationConstructorInjection(calculator, display);

    calculationConstructorInjection.complexCalculation();
  }

Exercise

Exercise 1

Convert to use constructor based dependency injection


Spring?

So far we have seen DI but had to orchestrate the application by hand.

Spring provides an IoC container - objects define what they need and the IoC container can then provide the required dependencies via DI.

We'll look at three ways:

  • Old style (spring - with XML configured beans)
  • Annotation style (spring - with annotated classes)
  • Spring Boot

Spring Beans

A spring bean is any object that is managed by the Spring IoC container.

A spring bean is usually a singleton (this is the default bean scope - we will look at scopes later on).


Spring - XML

First steps are to grab some java libraries 4

We'll be using spring's context and beans.


Application context

Spring provides a set of classes (based around BeanFactory) that allows us to configure the IoC container.

However - in nearly every project it is far far more common to use spring's application context for this.


applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
    http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd">

  <bean id="display" class="com.itera.spring.Display"/>
  <bean id="calculator" class="com.itera.spring.Calculator"/>

  <bean id="calculationConstructorInjection" class="com.itera.spring.CalculationConstructorInjection">
    <constructor-arg name="calculator" ref="calculator"/>
    <constructor-arg name="display" ref="display"/>
  </bean>

  <bean id="calculationSetterInjection" class="com.itera.spring.CalculationSetterInjection">
    <property name="calculator" ref="calculator"/>
    <property name="display" ref="display"/>
  </bean>
</beans>

Using the context

    // Load the context
    ApplicationContext context =
      new ClassPathXmlApplicationContext("applicationContext.xml");

    // Get a bean by type
    CalculationSetterInjection calculationSetterInjection =
      context.getBean(CalculationSetterInjection.class);

    calculationSetterInjection.complexCalculation();

    // Get a bean by name
    CalculationConstructorInjection calculationConstructorInjection =
        (CalculationConstructorInjection) context.getBean("calculationConstructorInjection");
    calculationConstructorInjection.complexCalculation();

Exercise

Exercise 2

Complete the context for a constructor injected application


Problems

This works - but - it means that the XML file is tightly coupled to the class structures.

If we change the java code we have to remember to adjust this file.


Spring - Annotations

Let's modify the previous version using spring's component scanning mechanism (annotations) 5


Application context

The context file becomes a lot smaller - it simply configures what packages to scan


applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd">

  <context:annotation-config/>
  <context:component-scan base-package="com.itera.spring"/>
</beans>

Annotating classes

Classes get a class level annotation stating what sort of bean they are (@Service, @Component, @Repository etc)

Injection points are often marked @Autowired 6


Using the context

The code in Application is exactly the same as for the XML version


Exercise

Exercise 3

Annotate the application correctly


Notes

These examples are very simple. some other things we need to consider are

  • bean scopes (is it a singleton? etc)
  • qualifiers (requiring a bean and there are multiple implementations available)

Problems

  • Still a lot of boiler plate
  • Managing dependencies in a larger project is still challenging

Spring Boot

Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can "just run". We take an opinionated view of the Spring platform and third-party libraries so you can get started with minimum fuss. Most Spring Boot applications need minimal Spring configuration. -- Spring.io 7


Spring Boot tries to simplify:

  • Setup
  • Dependency Management
  • Configuration

Spring Boot Starters

Spring Boot provides different starters - so that you can add support for different functionality.

We'll take a look at what's available after we've looked at the same test app in a Spring Boot version.8


Spring Boot Application

  • Classes keep the same annotations as before
  • Main class gets annotated @SpringBootApplication
  • We will implement the CommandLineRunner as it is a command line app

@SpringBootApplication
public class  Application implements CommandLineRunner {
  private final ApplicationContext context;

  public Application(ApplicationContext context) {
    this.context = context;
  }

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

  @Override
  public void run(String... args) {
    CalculationSetterInjection calculationSetterInjection =
      context.getBean(CalculationSetterInjection.class);

    calculationSetterInjection.complexCalculation();

    CalculationConstructorInjection calculationConstructorInjection =
      context.getBean(CalculationConstructorInjection.class);

    calculationConstructorInjection.complexCalculation();
  }
}

Spring Initializr

https://start.spring.io/

Under the Add Dependencies button you can see what starter packs you can add.


Exercise

Exercise 4

Create a new spring boot application.

See exercise4/README.md


Common context issues


Spring complains if it cannot build a valid context

Usually it will be one of two issues:

  • Cannot find a bean it needs
  • Finds more than one match

How to fix

First - dig down through the stack trace - spring will try and tell you what it didn't manage to do.

Things to remember:

  • Missing annotation on a @Component or @Service or similar?
  • Missing configuration or auto configuration?
  • Search by type (interface) or name can give more than one hit - can you use @Qualifier?
  • Component scanning also scans dependencies (if the package name is correct)
    • did you get more than you bargained for?
    • did something that was included expect certain dependencies that are not available?

More on Spring Beans

Spring beans have a scope which defines lifecycle

  • singleton (default)
  • prototype

Spring web-aware only

  • request
  • session
  • application
  • websocket

Singleton bean

The standard spring bean.

The spring container will always return the same bean.

Prototype bean

The spring container will return a new instance every time.


Web aware

Lifetime of web aware beans

  • request - single http request
  • session - http session
  • websocket - a websocket
  • application - servlet context

Spring Boot Configuration

  • Property Files
  • Yaml files
  • Profiles

Defining property file location

@Configuration
@PropertySource("classpath:somefile.properties")
public class SomeConfiguration {}

One or more files

// Single
@PropertySource("classpath:somefile.properties")

// Multiple - java 8 and above
@PropertySource("classpath:somefile.properties")
@PropertySource("classpath:anotherfile.properties")

// Multiple - any java version
@PropertySources({
  @PropertySource("classpath:somefile.properties")
  @PropertySource("classpath:anotherfile.properties")
})

For multiple files - if a name collision occurs then the last file read wins.


Using property values

Simplest with @Value injection

@Value( "${config.property.name}" )
private String configProperty;

You can inject Environment and use that:

@Autowired
private Environment env;

env.getProperty("config.property.name");

ConfigurationProperties

@Configuration // spring boot before 2.1 needs this in addition
@ConfigurationProperties(prefix = "db")
public class SomeConfig {
  private String username;
  private String password;
}

This will read properties db.username and db.password

It is a standard java bean - so you must define setters and getters (or use lombok or a kotlin data class)

You can nest configuration classes and build out a property hierarchy.


File names/types/profiles

  • application.properties
  • application-profileName.properties
  • properties vs yaml

application*.properties/yaml are handled by default - you do not need to specify a location - just inject @Value and you're done.


Profiles

We can specify at runtime what profiles are active.

Spring boot will load application-profileName.* only if profile with name profileName is active.


Properties vs YAML

Yaml can be used and is often useful for properties that are nested in nature.

Yaml does not work with PropertySource - but works fine with ConfigurationProperty and default property (application*) loading.


Spring Boot MVC

  • Resources
  • Requests/sessions
  • Responses

Add the web starter:

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

Resources

Get all items

@RestController
public class ExampleController {
    private final SomeService service;

    public ExampleController(SomeService service) {
      this.service = service;
    }

    @GetMapping("/")
    @ResponseBody
    public List<Example> getAllExamples() {
        return service.examples();
    }
}

PathVariable

GET /3

@GetMapping("/{id}")
@ResponseBody
public Example getExample(@PathVariable Integer id) {
    return service.example(id);
}

RequestParam

GET /?id=3

@GetMapping("/")
@ResponseBody
public Example getExample(@RequestParam Integer id) {
    return service.example(id);
}

RequestParam can also retrieve from form posts and file uploads


RequestBody

@PostMapping("/")
@ResponseBody
public Example addExample(@RequestBody Example example) {
    return service.addExample(example);
}

Exercise

Exercise 5

Create a new spring boot application using spring MVC.

See exercise5/README.md


Example

Spring MVC project run through9

This time in kotlin with gradle using the kotlin DSL - just for fun.

Initially created with spring initializer by choosing kotlin and gradle on https://start.spring.io/


Spring Web vs Spring Reactive Web

Spring web uses traditional synchronous coding - and for client calls uses classes like RestTemplate.

Spring Reactive Web brings asynchronous calling - and uses WebClient for client calls.

We'll build a simple echo server to investigate 10


Handler

Takes a request and returns some response

@Component
public class EchoHandler {
  public Mono<ServerResponse> echo(ServerRequest request) {
    return ServerResponse
        .ok()
        .contentType(MediaType.TEXT_PLAIN)
        .body(BodyInserters.fromValue(request.queryParam("val").orElse("No value")));
  }
}

Routing

Route a url to a handler

@Configuration
public class EchoRouter {
  @Bean
  public RouterFunction<ServerResponse> route(EchoHandler handler) {
    return RouterFunctions
        .route(RequestPredicates.GET("/echo")
            .and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), handler::echo);
  }
}

Testing the router/handler

Test class that uses SpringExtension to run.

Add a spring boot test annotation asking for a random port - this will put a WebTestClient object into the spring context.

@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class EchoRouterTest {
  @Autowired
  private WebTestClient webTestClient;

  @Test
  void testEmptyEcho() {
    webTestClient
        .get().uri("/echo")
        .accept(MediaType.TEXT_PLAIN)
        .exchange()
        .expectStatus().isOk()
        .expectBody(String.class).isEqualTo("No value");
  }

Run and test

Note that when we call these URLs - our entire code is asynchronous. It is spring itself that is handling synchronicity.


WebClient

We'll add a call to a remote server to test this 11

Add a config param to application.properties for the echo server url:

echo.server.url=http://localhost:8000

WebClient handler

Create a new handler and inject the configuration:

@Component
public class RemoteEchoHandler {
  private final WebClient client;

  public RemoteEchoHandler(@Value("${echo.server.url}") String echoServerUrl) {
    this.client = WebClient.create(echoServerUrl);
  }

  ...
}

Handler call

Use the client to run a simple get call passing on the val parameter from the request

client.get().uri(uriBuilder -> uriBuilder
        .queryParam("val", request.queryParam("val").orElse("No value"))
        .build())
        .retrieve()
        .bodyToMono(String.class)

Handler call

This can then be chained together with the response code

public Mono<ServerResponse> echo(ServerRequest request) {
    return client.get().uri(uriBuilder -> uriBuilder
        .queryParam("val", request.queryParam("val").orElse("No value"))
        .build())
        .retrieve()
        .bodyToMono(String.class)
        .flatMap(body -> ServerResponse.ok().contentType(MediaType.TEXT_PLAIN).body(BodyInserters.fromValue(body)));
  }

Run and test

Again - when we call these URLs - our code is asynchronous. It is spring itself that is handling synchronicity.


Other reactive spring

Reactive spring is also often used with things like spring data - allowing the use of Mono and Flux to allow asynchronous calls from the database out through spring web to outermost layer where spring itself executes the code chain.


Databases

  • Spring Data JPA
  • Spring Data JDBC

Starters:

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

Flyway

Database migration

		<dependency>
			<groupId>org.flywaydb</groupId>
			<artifactId>flyway-core</artifactId>
		</dependency>

H2

In memory DB

		<dependency>
			<groupId>com.h2database</groupId>
			<artifactId>h2</artifactId>
			<scope>runtime</scope>
		</dependency>

Flyway migrations

Flyway will automatically apply migrations found under db/migration in the classpath

Migration files are SQL

E.g. V1__create_demo_parent_table.sql

CREATE TABLE demo_parent (
  id INT AUTO_INCREMENT,
  name VARCHAR(255)
);

JPA Models

Spring data JPA uses standard javax.persistence annotations.

Annotated spring beans. Column names match to field names if not annotated.

@Entity
@Table(name = "demo_parent")
public class Parent {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  Long id;

  String name;

  @OneToMany(mappedBy = "parent", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
  Set<Child> children;

  // constructors, getters, setters
}

JPA repository

Spring data JPA provides a repository view with standard methods (get by ID etc)

public interface ParentRepository extends JpaRepository<Parent, Long> {
}

JPA repository methods

You can add methods to the interface (spring provides the implementation) where the method name is used to generate the underlying query:

  List<Parent> findByName(String name);

You can also specify queries in JPQL or SQL

// JPQL
@Query("SELECT c FROM cases c WHERE c.status = 1")
Collection<Case> findAllOpenCases();

// SQL
@Query(value = "SELECT * FROM cases c WHERE c.status = 1", nativeQuery = true)
Collection<Case> findAllOpenCases();

Testing

Let's take a look at using the JPA repositories/models by looking at some @DataJpaTest integration tests.12


JDBC Models

Spring data JDBC doesn't require domain beans - coding takes place using standard java.sql.* classes.

However - it is often useful to model the data as plain spring beans and provide a row mapper implementation.

public class ItemRowMapper implements RowMapper<Item> {
  @Override
  public Item mapRow(ResultSet resultSet, int i) throws SQLException {
    Item item = new Item();

    item.setId(resultSet.getLong("id"));
    item.setName(resultSet.getString("name"));

    return item;
  }
}

JDBC queries

We can inject a JdbcMapper and query the database (using a row mapper if we have created one)

    String query = "SELECT * FROM demo_item WHERE id = ?";

    Item item = jdbcTemplate.queryForObject(query, new ItemRowMapper(), itemId);

For example see the Spring JDBC integration test.13


Further Reading

  • Spring Auto-configuration
  • Spring Security / OAuth
  • Rest Repositories
  • Spring Web Services (XML/SOAP)
  • Spring Cloud
  • Project Reactor (reactive java - Mono/Flux)

Many other useful sites out there - my current goto is

https://www.baeldung.com/

Footnotes

  1. https://en.wikipedia.org/wiki/Spring_Framework

  2. initial/pom.xml

  3. initial-manual/pom.xml

  4. initial-spring/xml/pom.xml

  5. initial-spring/annotations/pom.xml

  6. From Spring 4.3 a spring bean class with only one constructor does not need the autowired annotation - spring will wire it

  7. https://spring.io/projects/spring-boot/

  8. initial-spring-boot/pom.xml

  9. spring-boot-web-example

  10. spring-webclient-example

  11. For testing - echo-server

  12. spring-boot-db-example - ParentRepositoryIT/ChildRepositoryIT

  13. spring-boot-db-example - ItemIT

About

Fagkveld on Spring

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published