Introduction
Recently I’ve been working on a job to Spring Boot-ify an existing application. This turned out to be a tricky process, because the strong opinions of Spring Boot. Spring Boot is primary geared toward new webapps and I might go so far as to say that they are geared toward new single-service webapps. This makes it relatively hard to convert an existing app, not in the least because the documentation on converting an existing app, especially one with an xml configuration, is very limited. So I thought I’d help out with a series of posts on the subject.
In this first item, we’ll be looking at Spring Boot and the minimum requirements for an existing app when you don’t want to use the starter-poms (http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#using-boot-starter-poms). We’ll create a project without depending on the spring-boot-parent and without using for example the spring-boot-starter-web pom. A nice clean, lightweight setup.
Spring Boot
In my introduction it might have sounded like Spring Boot is no good. That’s not true in the least: for new apps, it’s definitely worth looking at. It’s very easy to develop a basic app and have it running within minutes. That’s a big benefit when you want to prototype or simply get underway quickly. And obviously it’s very cool that the developed app can run anywhere, since you have the container embedded!
However, as usual, convenience comes at a price. In this case, the price is that Spring Boot is quite opinionated about how the app is supposed to be constructed. For example, it is assumed that the configuration of the app is done completely through Java Beans with annotations. If you happen to have an app that uses XML configuration, you won’t find much help in the official documentation (see also http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#using-boot-configuration-classes).
So it’s too bad we have an existing app with XML configuration. We’re pretty much on our own…
Requirements
Maybe it’s good at this point to explain why we would want to keep using the existing configuration, since it might be possible to create a new java based configuration. There are two reasons:
- Converting an existing configuration from XML to Java is possible but might be very time consuming, depending on the size of the configuration. When moving to Spring Boot, it should be possible to transport the configuration one-to-one and focus on the problems we’re introducing by adding the embedded server and new startup method, which might be quite extensive in themselves.
- Currently the app is build with Maven into a WAR file. The new structure should be a runnable WAR file, but hopefully, this WAR file wille still be deployable in a separate container (such as Tomcat or Jetty). This is not a hard requirement, because we can simply create the runnable war while wrapping the existing war, but that still puts the constraint on the existing WAR: it should be possible to run inside a container. This means that the existing app should be changed as little as possible. The reason for this decision is that the infrastructure for the deployment and the deployment mechanism are not optimized for runnable JARS/WARS yet and so we might end up in situation where both are required.
Based on these two requirements, we’re stuck with an xml based configuration which will have to be loaded from a new Spring Boot configuration.
Basic Setup
The general goal of this blog post is to create a Spring Boot app that will run, without using a spring-boot-starter pom. The reasons we don’t want to use starter poms are that
- We have an existing application with spring configuration: we shouldn’t need a bunch of “default” configuration, since we’ve already specified our own.
- The default configurations are usually somewhat heavy handed. The list of (possible) jars in for example the spring-boot-starter-web pom is quite large and we might not need any of those. The main idea here is that using a starter pom might distract us from what we’re trying to build.
- (As an expansion of the previous points) The default main class with the @SpringBootApplication annotation that you’ll find in almost every online example is somewhat useless to us: it does a lot of automagic configuration, but as I’ve said, we already have a working configuration.
So, let’s go for a basic HelloWorld app. For this demo, we’ll use Maven as a build tool, but it should work in Gradle too without much difficulty.
The code for this post is available at github.
And immediately, I’m going to mention that I lied before: we are actually using starter poms, but the lightweight ones that we’ll use for logging and the embedded container.
The pom.xml
The pom.xml file is created without using an archetype. It simply starts out as a pom.xml for a new project: it defines the groupId, the artifactId and the version of the new project. Also, it’s packaging is set to WAR, because we want to be able to run this app inside an (external) container.
The pom.xml further defines some basics for a Spring Boot application that is not inheriting from the spring-boot-parent and that can run inside an external container. Not inheriting from the spring-boot-parent gives us more freedom to choose a (different) parent, which is particularly useful in corporate environments. It also adds the dependencies for
- spring-boot,
- spring-web and
- spring-webmvc.
These are the basic dependencies required for a Spring Boot webapp. The Spring Boot dependency takes care of the Spring Boot specific stuff. The Spring Web and Spring Web MVC are used to add the functionality for a basic RESTful webapp.
Then we do define another two dependencies: the spring-boot-logging-starter and the spring-boot-tomcat-starter. Although we said upfront that we didn’t want to use the starter poms, we do use these two: they add the required set of dependencies for logging in a Spring Boot application and the dependencies for the embedded Tomcat container. The reason we do use these starter-pom dependencies is that they are relatively lightweight: they only add dependencies that are necessary.
Finally, the spring-boot-maven-plugin is added to the build configuration. It’s configuration is conform the description in the Spring Boot documentation.
The Main-class
The pom.xml file by far the most work in the app. It seems easy enough to setup when seen in it’s final form, but putting the pieces together was harder than it looks. Now comes the easy part: creating the application code.
A Spring Boot application needs a “main” class. This class is the entrypoint for the application. It has the task of defining the startup code and loading the additional configuration for the application. In the example code on github, the main class is the HelloWorldApplication.java file. (Incidentally, you might have already noticed this class as it is listed as the start-class in the spring-boot-maven-plugin in the pom.xml.)
The main class defines a public static void main method. This is the starting point of the application. Following the examples and the description in the Spring Boot documentation, this class uses the SpringApplication.run() method to start the application. Nothing fancy here.
It’s important to remark that the SpringApplication.run() method should be called with a class annotated with @Configuration, although according to the documentation you should be able to call it with an xml configuration as well. The reason the @Configuration is here in the first place is that this class also defines configuration @Beans:
- The DispatcherServlet, which defines the root for the Spring Web Mvc application.
- The TomcatEmbeddedServletContainerFactory, which provides the embedded Tomcat server functionality. This will be configured in a later stage, but for now, it’s enough to have the port hardcoded in the application.
Finally, the HelloWorldApplication class is annotated with @ComponentScan and @EnableWebMvc. These annotations are necessary to configure scanning the current package and its subpackages for annotation processing (mostly @Component and friends) and to enable the WebMvc annotation processing (for example, @RestController).
The RestController
Since we’re creating a very basic web app, we still need to demonstrate that the current configuration will give us a runnable WAR with a “hello world!” message output on a certain url.
To do this, add a class in the current package or a sub-package and annotate it with the @RestController annotation. Also, create a method in it that returns the hello world string and annotate that with the @RequestMapping annotation on a certain path and for the GET HTTP method. The @ComponentScan on the HelloWorldApplication class will make sure it gets scanned and the rest of the configuration will make sure the endpoint gets picked up and loaded on the given path.
The web.xml
When the project is built, Maven will complain that there should be a web.xml since we’re trying to build a WAR file. For now, it’s good enough to add an empty web.xml. Remember that in reality we already have an existing WAR file that we’re rewriting to a Spring Boot app, so in reality, there will be a web.xml as part of the original WAR file. It’s a little useless to spend much time on this now, so an empty file will be fine.
Conclusion
We’ve created a basic app that doesn’t use any of the major Spring Boot dependencies, but is relatively lightweight. To run the app, use (from the commandline in the root of the project)
- mvn clean package
- java -jar target\springboot-hello-world-app-1.0-SNAPSHOT.war
This blog post is part of a series. The next episode will appear soon. Stay tuned.