• Anadi Misra

Spring Boot - Spring Framework on Steroids

Updated: Aug 1

A sneak peek into how the latest addition to spring framework takes auto configuration to a whole new level


Confession, there are times when I've looked away from spring and just got things done in Rails. Somehow rails had proven to be a far better option for a quick MVP than my favourite Java framework Spring. But then the MVP grows into a VP and then a solution. It's not easy tearing apart an existing Rails app into microservices, to know more on what's this microservices thingy is all about see this link.


Spring framework on the other hand (specially WebMVC) and the ease of writing REST services in it is my go-to framework for microservices (though I hate WebMVC as much as it love it, for their lack of a solid asset pipeline like we have in Rails) ; specially because (until now) we haven't burnt our hands on migrating a SOA based distributed system to microservices yet. However, over the years wiring spring components together has been quite some task TBH, which is exactly what spring boot solves.


Quoting from their website


Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that can 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 very little Spring configuration.

Just Run! is what intrigued me when I bumped into their post on none other than my birthday (talk about developer's life!) and I'm sure would be intriguing you too. So let's see what this is about.


Spring WebMVC Hello World!


Because I'm such a hopeless purist; I'll start with the same (un)famous example that is responsible for the creation of universe. We'd have to first add the maven dependencies (or gradle, I'll stick to maven here)


<dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>${org.springframework.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${org.springframework.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${org.springframework.version}</version>
            <scope>test</scope>
        </dependency>
</dependencies>

Don't forget the test dependency ;-) though i'll not be writing failing test here first (boo!). We'll then add web MVC configuration


@EnableWebMvc
@Configuration
public class WebConfig implements WebMvcConfigurer {
  
  public void addViewControllers(ViewControllerRegistry registry)   {
    registry.addViewController("/").setViewName("index");
  }

  @Bean
  public ThymeleafViewResolver viewResolver(){
    ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
    viewResolver.setTemplateEngine(templateEngine());
    viewResolver.setViewNames(new String[] {".html", ".xhtml"});
    return viewResolver;
  }
}

Or you could try the fancier


@EnableWebMvc
@Configuration
@ComponentScan(basePackages = { "com.anadimisra.web.controller" })
public class WebConfig implements WebMvcConfigurer {
  //...
}

You would then add an initializer


public class MainWebAppInitializer implements WebApplicationInitializer {
  @Override
  public void onStartup(final ServletContext sc) throws ServletException {

    AnnotationConfigWebApplicationContext root =
        new AnnotationConfigWebApplicationContext();

    root.scan("com.anadimisra");
    sc.addListener(new ContextLoaderListener(root));

    ServletRegistration.Dynamic appServlet =
        sc.addServlet("mvc", new DispatcherServlet(new GenericWebApplicationContext()));
    appServlet.setLoadOnStartup(1);
    appServlet.addMapping("/");
  }
}


And then finally get to add a controller which returns a string or renders hello world in a thymeleaf template


@Controller
public class HelloWorldController {
  @GetMapping("/hello")
  public String home() {
    return "Hello World!";
  }

}

Building the App with Spring Boot


Let's first write the maven file


<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.anadimisra</groupId>
    <artifactId>springy-boot</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <!-- Inherit defaults from Spring Boot -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.0.0.RELEASE</version>
    </parent>

    <!-- Add typical dependencies for a web application -->
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

    <!-- Package as an executable jar -->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

That's it! Spring boot works on the concept of starters which is basically all you get to eat to be full before you could eat the real food. Ok sorry, that's not what is, again quoting form their website


Starter POMs are a set of convenient dependency descriptors that you can include in your application. You get a one-stop-shop for all the Spring and related technology that you need, without having to hunt through sample code and copy paste loads of dependency descriptors.

These starters are where all the stuff is bundled already for you

The starters contain a lot of the dependencies that you need to get a project up and running quickly and with a consistent, supported set of managed transitive dependencies.

And then simply


package com.anadimisra;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@EnableAutoConfiguration
public class SpringBootEgApplication {

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

  @RequestMapping("/")
  @ResponseBody
  String home() {
    return "Hello World!";
  }

}


That's it! You could build this jar and run it with a simple

java -jar

Let's see what happening under the hood here, if you run




❯ mvn dependency:tree

...
[INFO] +- org.springframework.boot:spring-boot-starter-web:jar:1.0.0.RELEASE:compile
[INFO] |  +- org.springframework.boot:spring-boot-starter:jar:1.0.0.RELEASE:compile
[INFO] |  |  +- org.springframework.boot:spring-boot:jar:1.0.0.RELEASE:compile
[INFO] |  |  +- org.springframework.boot:spring-boot-autoconfigure:jar:1.0.0.RELEASE:compile
[INFO] |  |  +- org.springframework.boot:spring-boot-starter-logging:jar:1.0.0.RELEASE:compile
[INFO] |  |  |  +- org.slf4j:jcl-over-slf4j:jar:1.7.6:compile
[INFO] |  |  |  |  \- org.slf4j:slf4j-api:jar:1.7.6:compile
[INFO] |  |  |  +- org.slf4j:jul-to-slf4j:jar:1.7.6:compile
[INFO] |  |  |  +- org.slf4j:log4j-over-slf4j:jar:1.7.6:compile
[INFO] |  |  |  \- ch.qos.logback:logback-classic:jar:1.1.1:compile
[INFO] |  |  |     \- ch.qos.logback:logback-core:jar:1.1.1:compile
[INFO] |  |  \- org.yaml:snakeyaml:jar:1.13:runtime
[INFO] |  +- org.springframework.boot:spring-boot-starter-tomcat:jar:1.0.0.RELEASE:compile
[INFO] |  |  +- org.apache.tomcat.embed:tomcat-embed-core:jar:7.0.52:compile
[INFO] |  |  +- org.apache.tomcat.embed:tomcat-embed-el:jar:7.0.52:compile
[INFO] |  |  \- org.apache.tomcat.embed:tomcat-embed-logging-juli:jar:7.0.52:compile
[INFO] |  +- com.fasterxml.jackson.core:jackson-databind:jar:2.3.2:compile
[INFO] |  |  +- com.fasterxml.jackson.core:jackson-annotations:jar:2.3.0:compile
[INFO] |  |  \- com.fasterxml.jackson.core:jackson-core:jar:2.3.2:compile
[INFO] |  +- org.springframework:spring-web:jar:4.0.3.RELEASE:compile
[INFO] |  |  +- org.springframework:spring-aop:jar:4.0.3.RELEASE:compile
[INFO] |  |  |  \- aopalliance:aopalliance:jar:1.0:compile
[INFO] |  |  +- org.springframework:spring-beans:jar:4.0.3.RELEASE:compile
[INFO] |  |  +- org.springframework:spring-context:jar:4.0.3.RELEASE:compile
[INFO] |  |  \- org.springframework:spring-core:jar:4.0.3.RELEASE:compile
[INFO] |  \- org.springframework:spring-webmvc:jar:4.0.3.RELEASE:compile
[INFO] |     \- org.springframework:spring-expression:jar:4.0.3.RELEASE:compile
[INFO] +- junit:junit:jar:4.11:test
[INFO] |  \- org.hamcrest:hamcrest-core:jar:1.3:test
[INFO] +- org.mockito:mockito-core:jar:1.9.5:test
[INFO] |  \- org.objenesis:objenesis:jar:1.0:test
[INFO] \- org.hamcrest:hamcrest-library:jar:1.3:test

  1. The starter POM takes care of all dependencies including embedded tomcat server.

  2. The @EnableAutoConfiguration annotation tells Spring Boot to “guess” how you will want to configure Spring, based on the jar dependencies that you have added. Since spring-boot-starter-web added Tomcat and Spring MVC, the auto-configuration will assume that you are developing a web application and setup Spring accordingly.

  3. The @Controller, @RequestMapping and @ResponseBody annotations are Spring MVC annotations, which then go about doing their usual job.

Spring Boot therefore is an amazing tool build to truly ensure you're not writing anything of a boilerplate code if your REST API can run sticking to most of common options, the customisations is also pretty straightforward for example to use jetty instead of tomcat for your application simply remove the dependency for tomcat and add jetty


<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jetty</artifactId>
</dependency>

Happy Coding with Spring Boot!

 

Bangalore, Karnataka, India.

  • Facebook
  • Twitter

©2020 by Anadi Misra.