Huy Pham

You have probably heard that Spring Boot makes life easier, but at your workplace you may still have to work with an old, vanilla Spring MVC project. You may feel like you are a decade behind, even though Spring boot just came out a couple of years ago. If you work on a Spring project that was created two or more years ago, then you are very likely working with a vanilla Spring MVC project, with Spring version 4.x.x. You are not alone in this, because for many Spring MVC projects developers feel reluctant to turn them into Spring boot projects. First, because things may work differently and code may break. And second, changing your project to Spring Boot means you likely need to upgrade your Spring version from 4.x.x to 5.x.x too, and that means lots of code changes. Yikes, double trouble! 

But, things are better done sooner than later, or it may get to the point that you feel like you are working with some ancient technologies. This blog post talks about the migration from a vanilla Spring MVC project (4.x.x or older) to a Spring Boot project (5.x.x).

Why Spring Boot?

Here are the main advantages that Spring Boot offers that I have found valuable.

Productivity: It takes you minutes to create a working Spring Boot project. 

Ease of use: With auto-configuration in place, Spring boot does practically everything for you, including configuration related to security, databases, servlet container/HTTP server. You can either stick with the default configurations, or do some minimal changes to have a custom configuration set up.

Code clarity: Spring boot helps you avoid writing boilerplate code. Things that appear over-engineered in a vanilla Spring MVC project (such as data source, MVC and security configuration) are either no longer needed in Spring Boot, or their configurations shrink significantly. Building and packaging a Spring Boot project is also easy; the instructions you would normally have in the Gradle jar task (such as which directories to compile and which to package in the jar file) are done automatically in the new bootJar task. Oh, and Docker works very well with Spring Boot too, for those who are Docker fans.

The migration

Here comes the technical part. 

Build file. The first thing you need to do to your old vanilla Spring project is to add Spring boot modules to the build.gradle file. You don't need to and should not indicate the version for each Spring boot starter module actually. Instead, give the version to one single spring-boot-gradle-plugin. That is enough for Spring Boot to decide what versions the other starter modules should have:

apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
classpath("org.springframework.boot:spring-boot-gradle-plugin:2.0.2.RELEASE")
 

Subsequently, all the modules spring-boot-starter-* can be declared without versions.

compile('org.springframework.boot:spring-boot-starter-aop')
compile('org.springframework.boot:spring-boot-starter-data-redis')
compile('org.springframework.boot:spring-boot-starter-jdbc')
compile('org.springframework.boot:spring-boot-starter-security')
testCompile('org.springframework.boot:spring-boot-starter-test')

Now, you can go ahead and delete all the spring-related modules you previously used for your MVC project. Generally, you can find a spring-boot-starter module for each dependency that you previously needed.

After saving and refreshing your Gradle dependencies, you will likely run into version conflicts for some libraries. In such a case, remove them where explicitly declared, and let Gradle refresh the dependency tree (in IntelliJ or Eclipse).

Previously, you would need to structure your jar file by telling Gradle which directory to compile and include in the jar file. With Spring Boot, not only is the cumbersome `jar` task disabled, and replaced with the `bootJar` task, you need to provide very little instruction on packaging your Spring Boot app in the `bootJar` task. Go ahead and delete the `jar` task if it is in your gradle.build file. 

Spring Boot comes with Tomcat embedded by default. If you prefer Jetty, make sure to exclude Tomcat from the build, and then add the Jetty dependency:

compile('org.springframework.boot:spring-boot-starter-web') {
        exclude module: "org.springframework.boot:spring-boot-starter-tomcat"
    }
compile('org.springframework.boot:spring-boot-starter-jetty')
 

The entry class Application. Without Spring Boot, you would typically have an Initializer class, which doubles as the entry class, in which you manually configure and start an embedded web server (like Jetty). With Spring boot, such a class is no longer needed. Instead, the new entry class is much simpler. The minimum requirement of the entry class is to: a) be annotated with @SpringBootApplication, and b) have a main method in which you call the static method run() of the SpringApplication class, as follows:

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

The annotation @SpringBootApplication is just an alias for a combination of three annotations, including @Configuration, @EnableAutoConfiguration and @ComponentScan. By default, to find component classes (annotated with @Component, @Service, @Repository, etc), Spring will scan all the classes in the package (and its sub-packages) that contains the Application class. You can optionally tell Spring to scan other packages for components by adding the @ComponentScan(basePackages="some.other.package") annotation to the Application class. For this reason, it is a good idea to place the Application class at the root package of your Spring components.

Besides this main entry class, you can have as many other classes annotated with @Configuration as needed. For example, you can have a separate configuration class containing beans for databases, AWS services, etc.

Security Configuration. By default, security is automatically configured. This means Spring Boot creates a default user with a password, in memory. This is good for testing, but you generaly want to have your custom configuration for authentication. To build your custom security configuration, you first need to disable the security auto configuration; this will make a custom security configuration mandatory. 

@SpringBootApplication( exclude = { SecurityAutoConfiguration.class } )

Then create a separate configuration class:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {}

Similarly, you also may want to disable the auto configuration for data source (DataSourceAutoConfiguration). If Spring Boot detects a dependency in the classpath for a certain database, such as H2, it will try to use that database to configure a data source automatically. This may not be what you want, especially if you have dependencies for multiple types of databases (H2, SQL, No-SQL) in the classpath.

Previously, you would need to create a class implementing interface WebMvcConfigurer and annotate it with @EnableWebMvc in order to enable Web MVC. With Spring Boot, you no longer need to do this. Spring Boot enables Web MVC automatically once it detects the MVC dependency in the classpath. You can safely delete such a class from our project. 

4. Spring Boot comes with several other features that require minimum setup. For example, if you want to use Redis maybe to persist Spring sessions, you just need to provide a few properties in the application.properties file:

spring.session.store-type=redis
spring.redis.host=localhost
spring.redis.port=12345
server.servlet.session.timeout=10800s
 

Spring session configuration is enabled automatically due to the presence of the spring-session-data-redis module in the classpath, so nothing more needs to be done. If you want to customize how/where Spring should keep the session id, such as in the cookie or in the header, and what the name of the header or cookie should be, then you just need to provide a bean of type HttpSessionResolver in any of the configuration classes. For example, let's say we want Spring to store the session id in the header with name "X-Auth-Token", then do the following:

public HttpSessionIdResolver httpSessionIdResolver() {
    return new HeaderHttpSessionIdResolver("X-Auth-Token"); 
} 

For the embedded web server, such as Tomcat or Jetty, you just need to provide the port. Spring boot starts the server automatically for the same reason in that it sees the dependency module in the classpath:

server.port=8080
 

5. Test also requires less configuration. First, include the test module in your gradle:

 testCompile('org.springframework.boot:spring-boot-starter-test') 

Then in your test class, tell Spring to run test with SpringRunner and a mock web environment:

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
public class MyServiceTest { }
 

To mock a bean in a test, simply annotate a bean declaration with @MockBean. This is similar to Mockito @Mock.

6. Docker. With Spring Boot, you can easily create a Gradle task to build a Docker image for your application. 

classpath("com.palantir.gradle.docker:gradle-docker:0.19.2")

docker {
    dependsOn build

    name "${project.group}/${bootJar.baseName}"
    dockerfile = file('Dockerfile')
    files bootJar.archivePath
    buildArgs(['JAR_FILE': "${bootJar.archiveName}"]) . // JAR_FILE is just an argument stated in your Dockerfile.
}

That should cover all the changes you need make to turn your vanilla Spring MVC project into a Spring Boot project.

In conclusion, migration of a project architecture (or version) requires not only effort, but also courage. The migration to Spring Boot, however, is something you can do today to ease your work tomorrow, and it is worth all of the effort you spend.

PS: Many thanks to Vaibhav Puranik and Steve Kiley for doing the proof-readings.

Guides