Merikanto

一簫一劍平生意,負盡狂名十五年

Spring Boot - 01 Intro

This is the first post in a series of posts to cover different aspects of Spring Boot. Please note that the entire post isn’t necessarily only written in English.

In this post, I am going to cover the basic concepts, and related files in a simple Spring Boot project, so let’s get started.


Concepts

Comparison


Old times:

The most common types of applications developed were browser-based web applications, backed by relational databases.


Nowadays:

Microservices destined for the cloud that persist data in a variety of databases

New interest in reactive programming, with non-blocking operations


Spring: ( Container + DI + Web MVC )

Offers a container: the Spring application context — creates & manages application components (= beans)

Components are wired together inside the Spring application context (beans wired in container)


Dependency Injection (DI):

A DI application relies on a separate entity (container) to create & manage all beans, and inject those into the beans that need them.

Done through: constructor argument, property accessor methods.


A DI container wires together independent components into a complete application at runtime.

Dependency injection means giving an object its instance variables. The fact that we can statically bind interfaces into the object’s method that instantiates it, serves a purpose for better lookup and stronger support for “programming to an interface“. Dynamic language is nothing but injecting dependencies runtime as the object knows nothing about the caller.


In a non-dependency-injection framework, the application is decomposed into classes where each class often has explicit linkages to other classes in the application. The linkages are the invocation of a class constructor directly in the code.

Spring is based on the concept of dependency injection.

A dependency injection framework externalize the relationship between objects within the application through convention & annotations, rather than those objects having hard-coded knowledge about each other.


Components = Beans in Spring application context:

  • Declared explicitly with Xml / Java
  • Discovered by component scanning
  • Configured by Spring Boot autoconfig

Spring Web MVC:

  • Web framework
  • Annotation-based (e.g. request-handling methods with @RequestMapping, @GetMapping, return template view name)
  • Supports Java Bean Validation API & Hibernate Validator

Spring Boot: ( Autoconfig + Starter dependencies + Runtime insights )

Start Spring Boot: log entry saying Tomcat started

  • Spring Boot applications bring everything they need within them
  • You never deploy your application to Tomcat, because Tomcat is part of your application

With starter-web & Thymeleaf, when starting the app, Spring Boot autoconfig detects those libraries and automatically:

  • Config the beans in the Spring application context to enable Spring MVC
  • Config embedded Tomcat server in the Spring application context
  • Config a view resolver for Spring MVC view rendering with Thymeleaf

Additional features:

  • Spring Boot CLI: command-line interface, alternative programming model based on Groovy scripts
  • Actuator: provides runtime insight. (e.g. metrics, health, thread dump info)
  • Additional testing support
  • Flexible specification of env properties

Config (3 Ways)

Configure the Spring application context to wire beans:

XML file: describe components & their relation to other components

1
2
3
4
5
6
7
8
9
<!-- Declares 2 beans: oneService & twoService -->

<bean id="oneService" class="demo.OneService" />

<bean id="twoService" class="demo.TwoService" />
<constructor-arg ref="oneService" />
</bean>

<!-- Inject (wire) oneService into twoService via the constructor argument -->

Java-based config:

Greater type safety, more refactorable

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// @: indicates config class, provide beans to Spring application context
@Configuration
public class ServiceConfig {

// @Bean: returned objects are added as beans in the context
@Bean
public OneService oneService() {

// constructor: return the new service
return new OneService();
}

@Bean
public TwoService twoService() {
return new TwoService(oneService()); // inject into...
}
}

Auto config:

  • Component scanning: auto discover components from application classpath, then create them as beans.

  • Autowiring: auto inject components along with the depended beans.

    Spring Boot: Extension of the Spring framework. Improvement in autoconfig (No need Xml / Java config files).

    ​ Spring Boot can make guesses of what components need to be configured and wired together,

    ​ based on entries in the classpath, environment vars.


Pom File

mvnw & mvnw.cmd: Maven wrapper scripts


In pom.xml: Use JAR packaging, instead of WAR. (Jar is better for the cloud)

  • War: Good for deploying to traditional Java app server (Tomcat), not good for most cloud platforms

    • War deploy: war packaging + web initializer class (web.xml)

    Parent pom: spring-boot-starter-parent

    • Provides dependency management for several libs frequently used in Spring projects.

    Starter dependencies: they don’t have any library code, but pull from other libraries transitively

    Web starter: includes embedded Tomcat

    • Smaller build file, easy to manage
    • Think of dependencies in terms of capabilities, but not library names
    • Stop worrying about library versions & conflicts

    Maven Plugin:

    • Provides a Maven goal for you to run with Maven
    • All dependencies are included within the executable JAR file, and are available on the runtime path
    • Produces a manifest file in the JAR file: denotes the bootstrap class (main.java) as the main class for the executable JAR

Main Class

The bootstrapped main class:

1
2
3
4
5
6
7
8
9
@SpringBootApplication
public class MainApplication {
public static void main(String[] args) {

// the run method: perform the actual bootstrapping, create the Spring application context
SpringApplication.run(MainApplication.class, args);
// The two passed params: config class, and command-line arguments (args)
}
}

The annotation @SpringBootApplication: A composite annotation that combines 3 other annotations

  • @SpringBootConfiguration: This is a config class (Specialized form of @Configuration )

  • @EnableAutoConfiguration: enable autoconfig (config any components that it thinks you need)

  • @ComponentScan:

    • Let you declare other classes with annotations like @Controller, @Service
    • Have Spring auto discover classes and register them as components int he Spring application context

Main Test Class

Baseline test class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

// use Spring Boot Runner
@RunWith(SpringRunner.class)

// Spring Boot test
@SpringBootTest
public class TacoCloudApplicationTests {

@Test
// the test method
public void contextLoads() {}
}

此 baseline 测试的作用: check if the Spring application context can be loaded successfully.


@RunWith: a JUnit annotation, provide a test runner that guides JUnit in running tests

  • 相当于: applying a plugin to JUnit to provide custom testing behavior
  • 上面例子中: the imported SpringRunner is a Spring-provided test runner (test the creation of a Spring application context)
  • SpringRunner = SpringJUnit4ClassRunner
    • Introduced in Spring 4.3, to remove association with a specific JUnit version

Web Requests

Spring’s web framework: Spring MVC

Central concept of Spring MVC: controller —— A class that handles HTTP requests and response

  • e.g. respond by optionally populating model data, then passing the request to a View to produce HTML

A sample controller:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class HomeController {

// handles requests for the root path
@GetMapping("/")

public String home() {

// returns the view name
return "home";
}
}

@Controller: Primary purpose is to identify this class as a component (for component scanning)

  • You could annotate HomeController as @Service, @Repositoy, and it still works the same (亲测,确实如此!)

Controller Test:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@RunWith(SpringRunner.class)

// Web MVC test
@WebMvcTest(HomeController.class)
public class HomeControllerTest {

// Autowire: Inject MockMVC
@Autowired
private MockMvc mockMvc;

@Test
public void testHomePage() throws Exception {

// Perform HTTP request GET (client side)
mockMvc.perform(get("/"))

// Expect: Servlet side. 3样东西:HTTP 200 OK, Home view, "Welcome to..."
.andExpect(status().isOk())
.andExpect(view().name("home"))
.andExpect(content().string(containsString("Welcome to...")));
}
}

@WebMvcTest: Arranges the test to run in Spring MVC application context

  • 上面例子中: arranges to register HomeController in Spring MVC, so you can throw requests like GET

DevTools

About dev tools:

  • Auto application restart when code changes
  • Auto browser refresh when template changes & Auto disable template cache
  • Builtin H2 console for H2 database
  • When deploying in a production environment, it will be self-disabled

With dev tools, the application is loaded into 2 separate class loaders in JVM.

  • One is loaded with files under /src/main (may change frequently)
  • Other is loaded with dependency libraries (change less often)

Cons: dependency changes won’t be available in auto restarts

  • Because the class loader containing dependency libraries isn’t automatically reloaded